1package api
2
3import (
4	"encoding/json"
5	"fmt"
6	"io"
7	"io/ioutil"
8	"net/url"
9	"time"
10
11	"github.com/mitchellh/mapstructure"
12)
13
14const (
15	// ACLClientType is the client type token
16	ACLClientType = "client"
17
18	// ACLManagementType is the management type token
19	ACLManagementType = "management"
20)
21
22type ACLLink struct {
23	ID   string
24	Name string
25}
26
27type ACLTokenPolicyLink = ACLLink
28type ACLTokenRoleLink = ACLLink
29
30// ACLToken represents an ACL Token
31type ACLToken struct {
32	CreateIndex       uint64
33	ModifyIndex       uint64
34	AccessorID        string
35	SecretID          string
36	Description       string
37	Policies          []*ACLTokenPolicyLink `json:",omitempty"`
38	Roles             []*ACLTokenRoleLink   `json:",omitempty"`
39	ServiceIdentities []*ACLServiceIdentity `json:",omitempty"`
40	NodeIdentities    []*ACLNodeIdentity    `json:",omitempty"`
41	Local             bool
42	AuthMethod        string        `json:",omitempty"`
43	ExpirationTTL     time.Duration `json:",omitempty"`
44	ExpirationTime    *time.Time    `json:",omitempty"`
45	CreateTime        time.Time     `json:",omitempty"`
46	Hash              []byte        `json:",omitempty"`
47
48	// DEPRECATED (ACL-Legacy-Compat)
49	// Rules will only be present for legacy tokens returned via the new APIs
50	Rules string `json:",omitempty"`
51
52	// Namespace is the namespace the ACLToken is associated with.
53	// Namespaces are a Consul Enterprise feature.
54	Namespace string `json:",omitempty"`
55
56	// Partition is the partition the ACLToken is associated with.
57	// Partitions are a Consul Enterprise feature.
58	Partition string `json:",omitempty"`
59
60	// AuthMethodNamespace is the namespace the token's AuthMethod is associated with.
61	// Namespacing is a Consul Enterprise feature.
62	AuthMethodNamespace string `json:",omitempty"`
63}
64
65type ACLTokenListEntry struct {
66	CreateIndex       uint64
67	ModifyIndex       uint64
68	AccessorID        string
69	SecretID          string
70	Description       string
71	Policies          []*ACLTokenPolicyLink `json:",omitempty"`
72	Roles             []*ACLTokenRoleLink   `json:",omitempty"`
73	ServiceIdentities []*ACLServiceIdentity `json:",omitempty"`
74	NodeIdentities    []*ACLNodeIdentity    `json:",omitempty"`
75	Local             bool
76	AuthMethod        string     `json:",omitempty"`
77	ExpirationTime    *time.Time `json:",omitempty"`
78	CreateTime        time.Time
79	Hash              []byte
80	Legacy            bool
81
82	// Namespace is the namespace the ACLTokenListEntry is associated with.
83	// Namespacing is a Consul Enterprise feature.
84	Namespace string `json:",omitempty"`
85
86	// Partition is the partition the ACLTokenListEntry is associated with.
87	// Partitions are a Consul Enterprise feature.
88	Partition string `json:",omitempty"`
89
90	// AuthMethodNamespace is the namespace the token's AuthMethod is associated with.
91	// Namespacing is a Consul Enterprise feature.
92	AuthMethodNamespace string `json:",omitempty"`
93}
94
95// ACLEntry is used to represent a legacy ACL token
96// The legacy tokens are deprecated.
97type ACLEntry struct {
98	CreateIndex uint64
99	ModifyIndex uint64
100	ID          string
101	Name        string
102	Type        string
103	Rules       string
104}
105
106// ACLReplicationStatus is used to represent the status of ACL replication.
107type ACLReplicationStatus struct {
108	Enabled              bool
109	Running              bool
110	SourceDatacenter     string
111	ReplicationType      string
112	ReplicatedIndex      uint64
113	ReplicatedRoleIndex  uint64
114	ReplicatedTokenIndex uint64
115	LastSuccess          time.Time
116	LastError            time.Time
117	LastErrorMessage     string
118}
119
120// ACLServiceIdentity represents a high-level grant of all necessary privileges
121// to assume the identity of the named Service in the Catalog and within
122// Connect.
123type ACLServiceIdentity struct {
124	ServiceName string
125	Datacenters []string `json:",omitempty"`
126}
127
128// ACLNodeIdentity represents a high-level grant of all necessary privileges
129// to assume the identity of the named Node in the Catalog and within Connect.
130type ACLNodeIdentity struct {
131	NodeName   string
132	Datacenter string
133}
134
135// ACLPolicy represents an ACL Policy.
136type ACLPolicy struct {
137	ID          string
138	Name        string
139	Description string
140	Rules       string
141	Datacenters []string
142	Hash        []byte
143	CreateIndex uint64
144	ModifyIndex uint64
145
146	// Namespace is the namespace the ACLPolicy is associated with.
147	// Namespacing is a Consul Enterprise feature.
148	Namespace string `json:",omitempty"`
149
150	// Partition is the partition the ACLPolicy is associated with.
151	// Partitions are a Consul Enterprise feature.
152	Partition string `json:",omitempty"`
153}
154
155type ACLPolicyListEntry struct {
156	ID          string
157	Name        string
158	Description string
159	Datacenters []string
160	Hash        []byte
161	CreateIndex uint64
162	ModifyIndex uint64
163
164	// Namespace is the namespace the ACLPolicyListEntry is associated with.
165	// Namespacing is a Consul Enterprise feature.
166	Namespace string `json:",omitempty"`
167
168	// Partition is the partition the ACLPolicyListEntry is associated with.
169	// Partitions are a Consul Enterprise feature.
170	Partition string `json:",omitempty"`
171}
172
173type ACLRolePolicyLink = ACLLink
174
175// ACLRole represents an ACL Role.
176type ACLRole struct {
177	ID                string
178	Name              string
179	Description       string
180	Policies          []*ACLRolePolicyLink  `json:",omitempty"`
181	ServiceIdentities []*ACLServiceIdentity `json:",omitempty"`
182	NodeIdentities    []*ACLNodeIdentity    `json:",omitempty"`
183	Hash              []byte
184	CreateIndex       uint64
185	ModifyIndex       uint64
186
187	// Namespace is the namespace the ACLRole is associated with.
188	// Namespacing is a Consul Enterprise feature.
189	Namespace string `json:",omitempty"`
190
191	// Partition is the partition the ACLRole is associated with.
192	// Partitions are a Consul Enterprise feature.
193	Partition string `json:",omitempty"`
194}
195
196// BindingRuleBindType is the type of binding rule mechanism used.
197type BindingRuleBindType string
198
199const (
200	// BindingRuleBindTypeService binds to a service identity with the given name.
201	BindingRuleBindTypeService BindingRuleBindType = "service"
202
203	// BindingRuleBindTypeRole binds to pre-existing roles with the given name.
204	BindingRuleBindTypeRole BindingRuleBindType = "role"
205)
206
207type ACLBindingRule struct {
208	ID          string
209	Description string
210	AuthMethod  string
211	Selector    string
212	BindType    BindingRuleBindType
213	BindName    string
214
215	CreateIndex uint64
216	ModifyIndex uint64
217
218	// Namespace is the namespace the ACLBindingRule is associated with.
219	// Namespacing is a Consul Enterprise feature.
220	Namespace string `json:",omitempty"`
221
222	// Partition is the partition the ACLBindingRule is associated with.
223	// Partitions are a Consul Enterprise feature.
224	Partition string `json:",omitempty"`
225}
226
227type ACLAuthMethod struct {
228	Name        string
229	Type        string
230	DisplayName string        `json:",omitempty"`
231	Description string        `json:",omitempty"`
232	MaxTokenTTL time.Duration `json:",omitempty"`
233
234	// TokenLocality defines the kind of token that this auth method produces.
235	// This can be either 'local' or 'global'. If empty 'local' is assumed.
236	TokenLocality string `json:",omitempty"`
237
238	// Configuration is arbitrary configuration for the auth method. This
239	// should only contain primitive values and containers (such as lists and
240	// maps).
241	Config map[string]interface{}
242
243	CreateIndex uint64
244	ModifyIndex uint64
245
246	// NamespaceRules apply only on auth methods defined in the default namespace.
247	// Namespacing is a Consul Enterprise feature.
248	NamespaceRules []*ACLAuthMethodNamespaceRule `json:",omitempty"`
249
250	// Namespace is the namespace the ACLAuthMethod is associated with.
251	// Namespacing is a Consul Enterprise feature.
252	Namespace string `json:",omitempty"`
253
254	// Partition is the partition the ACLAuthMethod is associated with.
255	// Partitions are a Consul Enterprise feature.
256	Partition string `json:",omitempty"`
257}
258
259func (m *ACLAuthMethod) MarshalJSON() ([]byte, error) {
260	type Alias ACLAuthMethod
261	exported := &struct {
262		MaxTokenTTL string `json:",omitempty"`
263		*Alias
264	}{
265		MaxTokenTTL: m.MaxTokenTTL.String(),
266		Alias:       (*Alias)(m),
267	}
268	if m.MaxTokenTTL == 0 {
269		exported.MaxTokenTTL = ""
270	}
271
272	return json.Marshal(exported)
273}
274
275func (m *ACLAuthMethod) UnmarshalJSON(data []byte) error {
276	type Alias ACLAuthMethod
277	aux := &struct {
278		MaxTokenTTL string
279		*Alias
280	}{
281		Alias: (*Alias)(m),
282	}
283	if err := json.Unmarshal(data, &aux); err != nil {
284		return err
285	}
286	var err error
287	if aux.MaxTokenTTL != "" {
288		if m.MaxTokenTTL, err = time.ParseDuration(aux.MaxTokenTTL); err != nil {
289			return err
290		}
291	}
292
293	return nil
294}
295
296type ACLAuthMethodNamespaceRule struct {
297	// Selector is an expression that matches against verified identity
298	// attributes returned from the auth method during login.
299	Selector string `json:",omitempty"`
300
301	// BindNamespace is the target namespace of the binding. Can be lightly
302	// templated using HIL ${foo} syntax from available field names.
303	//
304	// If empty it's created in the same namespace as the auth method.
305	BindNamespace string `json:",omitempty"`
306}
307
308type ACLAuthMethodListEntry struct {
309	Name        string
310	Type        string
311	DisplayName string        `json:",omitempty"`
312	Description string        `json:",omitempty"`
313	MaxTokenTTL time.Duration `json:",omitempty"`
314
315	// TokenLocality defines the kind of token that this auth method produces.
316	// This can be either 'local' or 'global'. If empty 'local' is assumed.
317	TokenLocality string `json:",omitempty"`
318	CreateIndex   uint64
319	ModifyIndex   uint64
320
321	// Namespace is the namespace the ACLAuthMethodListEntry is associated with.
322	// Namespacing is a Consul Enterprise feature.
323	Namespace string `json:",omitempty"`
324
325	// Partition is the partition the ACLAuthMethodListEntry is associated with.
326	// Partitions are a Consul Enterprise feature.
327	Partition string `json:",omitempty"`
328}
329
330// This is nearly identical to the ACLAuthMethod MarshalJSON
331func (m *ACLAuthMethodListEntry) MarshalJSON() ([]byte, error) {
332	type Alias ACLAuthMethodListEntry
333	exported := &struct {
334		MaxTokenTTL string `json:",omitempty"`
335		*Alias
336	}{
337		MaxTokenTTL: m.MaxTokenTTL.String(),
338		Alias:       (*Alias)(m),
339	}
340	if m.MaxTokenTTL == 0 {
341		exported.MaxTokenTTL = ""
342	}
343
344	return json.Marshal(exported)
345}
346
347// This is nearly identical to the ACLAuthMethod UnmarshalJSON
348func (m *ACLAuthMethodListEntry) UnmarshalJSON(data []byte) error {
349	type Alias ACLAuthMethodListEntry
350	aux := &struct {
351		MaxTokenTTL string
352		*Alias
353	}{
354		Alias: (*Alias)(m),
355	}
356
357	if err := json.Unmarshal(data, &aux); err != nil {
358		return err
359	}
360	var err error
361	if aux.MaxTokenTTL != "" {
362		if m.MaxTokenTTL, err = time.ParseDuration(aux.MaxTokenTTL); err != nil {
363			return err
364		}
365	}
366
367	return nil
368}
369
370// ParseKubernetesAuthMethodConfig takes a raw config map and returns a parsed
371// KubernetesAuthMethodConfig.
372func ParseKubernetesAuthMethodConfig(raw map[string]interface{}) (*KubernetesAuthMethodConfig, error) {
373	var config KubernetesAuthMethodConfig
374	decodeConf := &mapstructure.DecoderConfig{
375		Result:           &config,
376		WeaklyTypedInput: true,
377	}
378
379	decoder, err := mapstructure.NewDecoder(decodeConf)
380	if err != nil {
381		return nil, err
382	}
383
384	if err := decoder.Decode(raw); err != nil {
385		return nil, fmt.Errorf("error decoding config: %s", err)
386	}
387
388	return &config, nil
389}
390
391// KubernetesAuthMethodConfig is the config for the built-in Consul auth method
392// for Kubernetes.
393type KubernetesAuthMethodConfig struct {
394	Host              string `json:",omitempty"`
395	CACert            string `json:",omitempty"`
396	ServiceAccountJWT string `json:",omitempty"`
397}
398
399// RenderToConfig converts this into a map[string]interface{} suitable for use
400// in the ACLAuthMethod.Config field.
401func (c *KubernetesAuthMethodConfig) RenderToConfig() map[string]interface{} {
402	return map[string]interface{}{
403		"Host":              c.Host,
404		"CACert":            c.CACert,
405		"ServiceAccountJWT": c.ServiceAccountJWT,
406	}
407}
408
409// OIDCAuthMethodConfig is the config for the built-in Consul auth method for
410// OIDC and JWT.
411type OIDCAuthMethodConfig struct {
412	// common for type=oidc and type=jwt
413	JWTSupportedAlgs    []string          `json:",omitempty"`
414	BoundAudiences      []string          `json:",omitempty"`
415	ClaimMappings       map[string]string `json:",omitempty"`
416	ListClaimMappings   map[string]string `json:",omitempty"`
417	OIDCDiscoveryURL    string            `json:",omitempty"`
418	OIDCDiscoveryCACert string            `json:",omitempty"`
419	// just for type=oidc
420	OIDCClientID        string   `json:",omitempty"`
421	OIDCClientSecret    string   `json:",omitempty"`
422	OIDCScopes          []string `json:",omitempty"`
423	OIDCACRValues       []string `json:",omitempty"`
424	AllowedRedirectURIs []string `json:",omitempty"`
425	VerboseOIDCLogging  bool     `json:",omitempty"`
426	// just for type=jwt
427	JWKSURL              string        `json:",omitempty"`
428	JWKSCACert           string        `json:",omitempty"`
429	JWTValidationPubKeys []string      `json:",omitempty"`
430	BoundIssuer          string        `json:",omitempty"`
431	ExpirationLeeway     time.Duration `json:",omitempty"`
432	NotBeforeLeeway      time.Duration `json:",omitempty"`
433	ClockSkewLeeway      time.Duration `json:",omitempty"`
434}
435
436// RenderToConfig converts this into a map[string]interface{} suitable for use
437// in the ACLAuthMethod.Config field.
438func (c *OIDCAuthMethodConfig) RenderToConfig() map[string]interface{} {
439	return map[string]interface{}{
440		// common for type=oidc and type=jwt
441		"JWTSupportedAlgs":    c.JWTSupportedAlgs,
442		"BoundAudiences":      c.BoundAudiences,
443		"ClaimMappings":       c.ClaimMappings,
444		"ListClaimMappings":   c.ListClaimMappings,
445		"OIDCDiscoveryURL":    c.OIDCDiscoveryURL,
446		"OIDCDiscoveryCACert": c.OIDCDiscoveryCACert,
447		// just for type=oidc
448		"OIDCClientID":        c.OIDCClientID,
449		"OIDCClientSecret":    c.OIDCClientSecret,
450		"OIDCScopes":          c.OIDCScopes,
451		"OIDCACRValues":       c.OIDCACRValues,
452		"AllowedRedirectURIs": c.AllowedRedirectURIs,
453		"VerboseOIDCLogging":  c.VerboseOIDCLogging,
454		// just for type=jwt
455		"JWKSURL":              c.JWKSURL,
456		"JWKSCACert":           c.JWKSCACert,
457		"JWTValidationPubKeys": c.JWTValidationPubKeys,
458		"BoundIssuer":          c.BoundIssuer,
459		"ExpirationLeeway":     c.ExpirationLeeway,
460		"NotBeforeLeeway":      c.NotBeforeLeeway,
461		"ClockSkewLeeway":      c.ClockSkewLeeway,
462	}
463}
464
465type ACLLoginParams struct {
466	AuthMethod  string
467	BearerToken string
468	Meta        map[string]string `json:",omitempty"`
469}
470
471type ACLOIDCAuthURLParams struct {
472	AuthMethod  string
473	RedirectURI string
474	ClientNonce string
475	Meta        map[string]string `json:",omitempty"`
476}
477
478// ACL can be used to query the ACL endpoints
479type ACL struct {
480	c *Client
481}
482
483// ACL returns a handle to the ACL endpoints
484func (c *Client) ACL() *ACL {
485	return &ACL{c}
486}
487
488// Bootstrap is used to perform a one-time ACL bootstrap operation on a cluster
489// to get the first management token.
490func (a *ACL) Bootstrap() (*ACLToken, *WriteMeta, error) {
491	r := a.c.newRequest("PUT", "/v1/acl/bootstrap")
492	rtt, resp, err := a.c.doRequest(r)
493	if err != nil {
494		return nil, nil, err
495	}
496	defer closeResponseBody(resp)
497	if err := requireOK(resp); err != nil {
498		return nil, nil, err
499	}
500	wm := &WriteMeta{RequestTime: rtt}
501	var out ACLToken
502	if err := decodeBody(resp, &out); err != nil {
503		return nil, nil, err
504	}
505	return &out, wm, nil
506}
507
508// Create is used to generate a new token with the given parameters
509//
510// Deprecated: Use TokenCreate instead.
511func (a *ACL) Create(acl *ACLEntry, q *WriteOptions) (string, *WriteMeta, error) {
512	r := a.c.newRequest("PUT", "/v1/acl/create")
513	r.setWriteOptions(q)
514	r.obj = acl
515	rtt, resp, err := a.c.doRequest(r)
516	if err != nil {
517		return "", nil, err
518	}
519	defer closeResponseBody(resp)
520	if err := requireOK(resp); err != nil {
521		return "", nil, err
522	}
523
524	wm := &WriteMeta{RequestTime: rtt}
525	var out struct{ ID string }
526	if err := decodeBody(resp, &out); err != nil {
527		return "", nil, err
528	}
529	return out.ID, wm, nil
530}
531
532// Update is used to update the rules of an existing token
533//
534// Deprecated: Use TokenUpdate instead.
535func (a *ACL) Update(acl *ACLEntry, q *WriteOptions) (*WriteMeta, error) {
536	r := a.c.newRequest("PUT", "/v1/acl/update")
537	r.setWriteOptions(q)
538	r.obj = acl
539	rtt, resp, err := a.c.doRequest(r)
540	if err != nil {
541		return nil, err
542	}
543	defer closeResponseBody(resp)
544	if err := requireOK(resp); err != nil {
545		return nil, err
546	}
547	wm := &WriteMeta{RequestTime: rtt}
548	return wm, nil
549}
550
551// Destroy is used to destroy a given ACL token ID
552//
553// Deprecated: Use TokenDelete instead.
554func (a *ACL) Destroy(id string, q *WriteOptions) (*WriteMeta, error) {
555	r := a.c.newRequest("PUT", "/v1/acl/destroy/"+id)
556	r.setWriteOptions(q)
557	rtt, resp, err := a.c.doRequest(r)
558	if err != nil {
559		return nil, err
560	}
561	if err := requireOK(resp); err != nil {
562		return nil, err
563	}
564	closeResponseBody(resp)
565
566	wm := &WriteMeta{RequestTime: rtt}
567	return wm, nil
568}
569
570// Clone is used to return a new token cloned from an existing one
571//
572// Deprecated: Use TokenClone instead.
573func (a *ACL) Clone(id string, q *WriteOptions) (string, *WriteMeta, error) {
574	r := a.c.newRequest("PUT", "/v1/acl/clone/"+id)
575	r.setWriteOptions(q)
576	rtt, resp, err := a.c.doRequest(r)
577	if err != nil {
578		return "", nil, err
579	}
580	defer closeResponseBody(resp)
581	if err := requireOK(resp); err != nil {
582		return "", nil, err
583	}
584
585	wm := &WriteMeta{RequestTime: rtt}
586	var out struct{ ID string }
587	if err := decodeBody(resp, &out); err != nil {
588		return "", nil, err
589	}
590	return out.ID, wm, nil
591}
592
593// Info is used to query for information about an ACL token
594//
595// Deprecated: Use TokenRead instead.
596func (a *ACL) Info(id string, q *QueryOptions) (*ACLEntry, *QueryMeta, error) {
597	r := a.c.newRequest("GET", "/v1/acl/info/"+id)
598	r.setQueryOptions(q)
599	rtt, resp, err := a.c.doRequest(r)
600	if err != nil {
601		return nil, nil, err
602	}
603	defer closeResponseBody(resp)
604	if err := requireOK(resp); err != nil {
605		return nil, nil, err
606	}
607	qm := &QueryMeta{}
608	parseQueryMeta(resp, qm)
609	qm.RequestTime = rtt
610
611	var entries []*ACLEntry
612	if err := decodeBody(resp, &entries); err != nil {
613		return nil, nil, err
614	}
615	if len(entries) > 0 {
616		return entries[0], qm, nil
617	}
618	return nil, qm, nil
619}
620
621// List is used to get all the ACL tokens
622//
623// Deprecated: Use TokenList instead.
624func (a *ACL) List(q *QueryOptions) ([]*ACLEntry, *QueryMeta, error) {
625	r := a.c.newRequest("GET", "/v1/acl/list")
626	r.setQueryOptions(q)
627	rtt, resp, err := a.c.doRequest(r)
628	if err != nil {
629		return nil, nil, err
630	}
631	defer closeResponseBody(resp)
632	if err := requireOK(resp); err != nil {
633		return nil, nil, err
634	}
635	qm := &QueryMeta{}
636	parseQueryMeta(resp, qm)
637	qm.RequestTime = rtt
638
639	var entries []*ACLEntry
640	if err := decodeBody(resp, &entries); err != nil {
641		return nil, nil, err
642	}
643	return entries, qm, nil
644}
645
646// Replication returns the status of the ACL replication process in the datacenter
647func (a *ACL) Replication(q *QueryOptions) (*ACLReplicationStatus, *QueryMeta, error) {
648	r := a.c.newRequest("GET", "/v1/acl/replication")
649	r.setQueryOptions(q)
650	rtt, resp, err := a.c.doRequest(r)
651	if err != nil {
652		return nil, nil, err
653	}
654	defer closeResponseBody(resp)
655	if err := requireOK(resp); err != nil {
656		return nil, nil, err
657	}
658	qm := &QueryMeta{}
659	parseQueryMeta(resp, qm)
660	qm.RequestTime = rtt
661
662	var entries *ACLReplicationStatus
663	if err := decodeBody(resp, &entries); err != nil {
664		return nil, nil, err
665	}
666	return entries, qm, nil
667}
668
669// TokenCreate creates a new ACL token. If either the AccessorID or SecretID fields
670// of the ACLToken structure are empty they will be filled in by Consul.
671func (a *ACL) TokenCreate(token *ACLToken, q *WriteOptions) (*ACLToken, *WriteMeta, error) {
672	r := a.c.newRequest("PUT", "/v1/acl/token")
673	r.setWriteOptions(q)
674	r.obj = token
675	rtt, resp, err := a.c.doRequest(r)
676	if err != nil {
677		return nil, nil, err
678	}
679	defer closeResponseBody(resp)
680	if err := requireOK(resp); err != nil {
681		return nil, nil, err
682	}
683	wm := &WriteMeta{RequestTime: rtt}
684	var out ACLToken
685	if err := decodeBody(resp, &out); err != nil {
686		return nil, nil, err
687	}
688
689	return &out, wm, nil
690}
691
692// TokenUpdate updates a token in place without modifying its AccessorID or SecretID. A valid
693// AccessorID must be set in the ACLToken structure passed to this function but the SecretID may
694// be omitted and will be filled in by Consul with its existing value.
695func (a *ACL) TokenUpdate(token *ACLToken, q *WriteOptions) (*ACLToken, *WriteMeta, error) {
696	if token.AccessorID == "" {
697		return nil, nil, fmt.Errorf("Must specify an AccessorID for Token Updating")
698	}
699	r := a.c.newRequest("PUT", "/v1/acl/token/"+token.AccessorID)
700	r.setWriteOptions(q)
701	r.obj = token
702	rtt, resp, err := a.c.doRequest(r)
703	if err != nil {
704		return nil, nil, err
705	}
706	defer closeResponseBody(resp)
707	if err := requireOK(resp); err != nil {
708		return nil, nil, err
709	}
710	wm := &WriteMeta{RequestTime: rtt}
711	var out ACLToken
712	if err := decodeBody(resp, &out); err != nil {
713		return nil, nil, err
714	}
715
716	return &out, wm, nil
717}
718
719// TokenClone will create a new token with the same policies and locality as the original
720// token but will have its own auto-generated AccessorID and SecretID as well having the
721// description passed to this function. The tokenID parameter must be a valid Accessor ID
722// of an existing token.
723func (a *ACL) TokenClone(tokenID string, description string, q *WriteOptions) (*ACLToken, *WriteMeta, error) {
724	if tokenID == "" {
725		return nil, nil, fmt.Errorf("Must specify a tokenID for Token Cloning")
726	}
727
728	r := a.c.newRequest("PUT", "/v1/acl/token/"+tokenID+"/clone")
729	r.setWriteOptions(q)
730	r.obj = struct{ Description string }{description}
731	rtt, resp, err := a.c.doRequest(r)
732	if err != nil {
733		return nil, nil, err
734	}
735	defer closeResponseBody(resp)
736	if err := requireOK(resp); err != nil {
737		return nil, nil, err
738	}
739	wm := &WriteMeta{RequestTime: rtt}
740	var out ACLToken
741	if err := decodeBody(resp, &out); err != nil {
742		return nil, nil, err
743	}
744
745	return &out, wm, nil
746}
747
748// TokenDelete removes a single ACL token. The tokenID parameter must be a valid
749// Accessor ID of an existing token.
750func (a *ACL) TokenDelete(tokenID string, q *WriteOptions) (*WriteMeta, error) {
751	r := a.c.newRequest("DELETE", "/v1/acl/token/"+tokenID)
752	r.setWriteOptions(q)
753	rtt, resp, err := a.c.doRequest(r)
754	if err != nil {
755		return nil, err
756	}
757	if err := requireOK(resp); err != nil {
758		return nil, err
759	}
760	closeResponseBody(resp)
761
762	wm := &WriteMeta{RequestTime: rtt}
763	return wm, nil
764}
765
766// TokenRead retrieves the full token details. The tokenID parameter must be a valid
767// Accessor ID of an existing token.
768func (a *ACL) TokenRead(tokenID string, q *QueryOptions) (*ACLToken, *QueryMeta, error) {
769	r := a.c.newRequest("GET", "/v1/acl/token/"+tokenID)
770	r.setQueryOptions(q)
771	rtt, resp, err := a.c.doRequest(r)
772	if err != nil {
773		return nil, nil, err
774	}
775	defer closeResponseBody(resp)
776	if err := requireOK(resp); err != nil {
777		return nil, nil, err
778	}
779	qm := &QueryMeta{}
780	parseQueryMeta(resp, qm)
781	qm.RequestTime = rtt
782
783	var out ACLToken
784	if err := decodeBody(resp, &out); err != nil {
785		return nil, nil, err
786	}
787
788	return &out, qm, nil
789}
790
791// TokenReadSelf retrieves the full token details of the token currently
792// assigned to the API Client. In this manner its possible to read a token
793// by its Secret ID.
794func (a *ACL) TokenReadSelf(q *QueryOptions) (*ACLToken, *QueryMeta, error) {
795	r := a.c.newRequest("GET", "/v1/acl/token/self")
796	r.setQueryOptions(q)
797	rtt, resp, err := a.c.doRequest(r)
798	if err != nil {
799		return nil, nil, err
800	}
801	defer closeResponseBody(resp)
802	if err := requireOK(resp); err != nil {
803		return nil, nil, err
804	}
805	qm := &QueryMeta{}
806	parseQueryMeta(resp, qm)
807	qm.RequestTime = rtt
808
809	var out ACLToken
810	if err := decodeBody(resp, &out); err != nil {
811		return nil, nil, err
812	}
813
814	return &out, qm, nil
815}
816
817// TokenList lists all tokens. The listing does not contain any SecretIDs as those
818// may only be retrieved by a call to TokenRead.
819func (a *ACL) TokenList(q *QueryOptions) ([]*ACLTokenListEntry, *QueryMeta, error) {
820	r := a.c.newRequest("GET", "/v1/acl/tokens")
821	r.setQueryOptions(q)
822	rtt, resp, err := a.c.doRequest(r)
823	if err != nil {
824		return nil, nil, err
825	}
826	defer closeResponseBody(resp)
827	if err := requireOK(resp); err != nil {
828		return nil, nil, err
829	}
830	qm := &QueryMeta{}
831	parseQueryMeta(resp, qm)
832	qm.RequestTime = rtt
833
834	var entries []*ACLTokenListEntry
835	if err := decodeBody(resp, &entries); err != nil {
836		return nil, nil, err
837	}
838	return entries, qm, nil
839}
840
841// PolicyCreate will create a new policy. It is not allowed for the policy parameters
842// ID field to be set as this will be generated by Consul while processing the request.
843func (a *ACL) PolicyCreate(policy *ACLPolicy, q *WriteOptions) (*ACLPolicy, *WriteMeta, error) {
844	if policy.ID != "" {
845		return nil, nil, fmt.Errorf("Cannot specify an ID in Policy Creation")
846	}
847	r := a.c.newRequest("PUT", "/v1/acl/policy")
848	r.setWriteOptions(q)
849	r.obj = policy
850	rtt, resp, err := a.c.doRequest(r)
851	if err != nil {
852		return nil, nil, err
853	}
854	defer closeResponseBody(resp)
855	if err := requireOK(resp); err != nil {
856		return nil, nil, err
857	}
858	wm := &WriteMeta{RequestTime: rtt}
859	var out ACLPolicy
860	if err := decodeBody(resp, &out); err != nil {
861		return nil, nil, err
862	}
863
864	return &out, wm, nil
865}
866
867// PolicyUpdate updates a policy. The ID field of the policy parameter must be set to an
868// existing policy ID
869func (a *ACL) PolicyUpdate(policy *ACLPolicy, q *WriteOptions) (*ACLPolicy, *WriteMeta, error) {
870	if policy.ID == "" {
871		return nil, nil, fmt.Errorf("Must specify an ID in Policy Update")
872	}
873
874	r := a.c.newRequest("PUT", "/v1/acl/policy/"+policy.ID)
875	r.setWriteOptions(q)
876	r.obj = policy
877	rtt, resp, err := a.c.doRequest(r)
878	if err != nil {
879		return nil, nil, err
880	}
881	defer closeResponseBody(resp)
882	if err := requireOK(resp); err != nil {
883		return nil, nil, err
884	}
885	wm := &WriteMeta{RequestTime: rtt}
886	var out ACLPolicy
887	if err := decodeBody(resp, &out); err != nil {
888		return nil, nil, err
889	}
890
891	return &out, wm, nil
892}
893
894// PolicyDelete deletes a policy given its ID.
895func (a *ACL) PolicyDelete(policyID string, q *WriteOptions) (*WriteMeta, error) {
896	r := a.c.newRequest("DELETE", "/v1/acl/policy/"+policyID)
897	r.setWriteOptions(q)
898	rtt, resp, err := a.c.doRequest(r)
899	if err != nil {
900		return nil, err
901	}
902	closeResponseBody(resp)
903	if err := requireOK(resp); err != nil {
904		return nil, err
905	}
906
907	wm := &WriteMeta{RequestTime: rtt}
908	return wm, nil
909}
910
911// PolicyRead retrieves the policy details including the rule set.
912func (a *ACL) PolicyRead(policyID string, q *QueryOptions) (*ACLPolicy, *QueryMeta, error) {
913	r := a.c.newRequest("GET", "/v1/acl/policy/"+policyID)
914	r.setQueryOptions(q)
915	rtt, resp, err := a.c.doRequest(r)
916	if err != nil {
917		return nil, nil, err
918	}
919	defer closeResponseBody(resp)
920	if err := requireOK(resp); err != nil {
921		return nil, nil, err
922	}
923	qm := &QueryMeta{}
924	parseQueryMeta(resp, qm)
925	qm.RequestTime = rtt
926
927	var out ACLPolicy
928	if err := decodeBody(resp, &out); err != nil {
929		return nil, nil, err
930	}
931
932	return &out, qm, nil
933}
934
935// PolicyReadByName retrieves the policy details including the rule set with name.
936func (a *ACL) PolicyReadByName(policyName string, q *QueryOptions) (*ACLPolicy, *QueryMeta, error) {
937	r := a.c.newRequest("GET", "/v1/acl/policy/name/"+url.QueryEscape(policyName))
938	r.setQueryOptions(q)
939	rtt, resp, err := a.c.doRequest(r)
940	if err != nil {
941		return nil, nil, err
942	}
943	defer closeResponseBody(resp)
944	found, resp, err := requireNotFoundOrOK(resp)
945	if err != nil {
946		return nil, nil, err
947	}
948
949	qm := &QueryMeta{}
950	parseQueryMeta(resp, qm)
951	qm.RequestTime = rtt
952
953	if !found {
954		return nil, qm, nil
955	}
956
957	var out ACLPolicy
958	if err := decodeBody(resp, &out); err != nil {
959		return nil, nil, err
960	}
961
962	return &out, qm, nil
963}
964
965// PolicyList retrieves a listing of all policies. The listing does not include the
966// rules for any policy as those should be retrieved by subsequent calls to PolicyRead.
967func (a *ACL) PolicyList(q *QueryOptions) ([]*ACLPolicyListEntry, *QueryMeta, error) {
968	r := a.c.newRequest("GET", "/v1/acl/policies")
969	r.setQueryOptions(q)
970	rtt, resp, err := a.c.doRequest(r)
971	if err != nil {
972		return nil, nil, err
973	}
974	defer closeResponseBody(resp)
975	if err := requireOK(resp); err != nil {
976		return nil, nil, err
977	}
978	qm := &QueryMeta{}
979	parseQueryMeta(resp, qm)
980	qm.RequestTime = rtt
981
982	var entries []*ACLPolicyListEntry
983	if err := decodeBody(resp, &entries); err != nil {
984		return nil, nil, err
985	}
986	return entries, qm, nil
987}
988
989// RulesTranslate translates the legacy rule syntax into the current syntax.
990//
991// Deprecated: Support for the legacy syntax translation will be removed
992// when legacy ACL support is removed.
993func (a *ACL) RulesTranslate(rules io.Reader) (string, error) {
994	r := a.c.newRequest("POST", "/v1/acl/rules/translate")
995	r.body = rules
996	r.header.Set("Content-Type", "text/plain")
997	rtt, resp, err := a.c.doRequest(r)
998	if err != nil {
999		return "", err
1000	}
1001	defer closeResponseBody(resp)
1002	if err := requireOK(resp); err != nil {
1003		return "", err
1004	}
1005
1006	qm := &QueryMeta{}
1007	parseQueryMeta(resp, qm)
1008	qm.RequestTime = rtt
1009
1010	ruleBytes, err := ioutil.ReadAll(resp.Body)
1011	if err != nil {
1012		return "", fmt.Errorf("Failed to read translated rule body: %v", err)
1013	}
1014
1015	return string(ruleBytes), nil
1016}
1017
1018// RulesTranslateToken translates the rules associated with the legacy syntax
1019// into the current syntax and returns the results.
1020//
1021// Deprecated: Support for the legacy syntax translation will be removed
1022// when legacy ACL support is removed.
1023func (a *ACL) RulesTranslateToken(tokenID string) (string, error) {
1024	r := a.c.newRequest("GET", "/v1/acl/rules/translate/"+tokenID)
1025	rtt, resp, err := a.c.doRequest(r)
1026	if err != nil {
1027		return "", err
1028	}
1029	defer closeResponseBody(resp)
1030	if err := requireOK(resp); err != nil {
1031		return "", err
1032	}
1033	qm := &QueryMeta{}
1034	parseQueryMeta(resp, qm)
1035	qm.RequestTime = rtt
1036
1037	ruleBytes, err := ioutil.ReadAll(resp.Body)
1038	if err != nil {
1039		return "", fmt.Errorf("Failed to read translated rule body: %v", err)
1040	}
1041
1042	return string(ruleBytes), nil
1043}
1044
1045// RoleCreate will create a new role. It is not allowed for the role parameters
1046// ID field to be set as this will be generated by Consul while processing the request.
1047func (a *ACL) RoleCreate(role *ACLRole, q *WriteOptions) (*ACLRole, *WriteMeta, error) {
1048	if role.ID != "" {
1049		return nil, nil, fmt.Errorf("Cannot specify an ID in Role Creation")
1050	}
1051
1052	r := a.c.newRequest("PUT", "/v1/acl/role")
1053	r.setWriteOptions(q)
1054	r.obj = role
1055	rtt, resp, err := a.c.doRequest(r)
1056	if err != nil {
1057		return nil, nil, err
1058	}
1059	defer closeResponseBody(resp)
1060	if err := requireOK(resp); err != nil {
1061		return nil, nil, err
1062	}
1063	wm := &WriteMeta{RequestTime: rtt}
1064	var out ACLRole
1065	if err := decodeBody(resp, &out); err != nil {
1066		return nil, nil, err
1067	}
1068
1069	return &out, wm, nil
1070}
1071
1072// RoleUpdate updates a role. The ID field of the role parameter must be set to an
1073// existing role ID
1074func (a *ACL) RoleUpdate(role *ACLRole, q *WriteOptions) (*ACLRole, *WriteMeta, error) {
1075	if role.ID == "" {
1076		return nil, nil, fmt.Errorf("Must specify an ID in Role Update")
1077	}
1078
1079	r := a.c.newRequest("PUT", "/v1/acl/role/"+role.ID)
1080	r.setWriteOptions(q)
1081	r.obj = role
1082	rtt, resp, err := a.c.doRequest(r)
1083	if err != nil {
1084		return nil, nil, err
1085	}
1086	defer closeResponseBody(resp)
1087	if err := requireOK(resp); err != nil {
1088		return nil, nil, err
1089	}
1090	wm := &WriteMeta{RequestTime: rtt}
1091	var out ACLRole
1092	if err := decodeBody(resp, &out); err != nil {
1093		return nil, nil, err
1094	}
1095
1096	return &out, wm, nil
1097}
1098
1099// RoleDelete deletes a role given its ID.
1100func (a *ACL) RoleDelete(roleID string, q *WriteOptions) (*WriteMeta, error) {
1101	r := a.c.newRequest("DELETE", "/v1/acl/role/"+roleID)
1102	r.setWriteOptions(q)
1103	rtt, resp, err := a.c.doRequest(r)
1104	if err != nil {
1105		return nil, err
1106	}
1107	if err := requireOK(resp); err != nil {
1108		return nil, err
1109	}
1110	closeResponseBody(resp)
1111
1112	wm := &WriteMeta{RequestTime: rtt}
1113	return wm, nil
1114}
1115
1116// RoleRead retrieves the role details (by ID). Returns nil if not found.
1117func (a *ACL) RoleRead(roleID string, q *QueryOptions) (*ACLRole, *QueryMeta, error) {
1118	r := a.c.newRequest("GET", "/v1/acl/role/"+roleID)
1119	r.setQueryOptions(q)
1120	rtt, resp, err := a.c.doRequest(r)
1121	if err != nil {
1122		return nil, nil, err
1123	}
1124	defer closeResponseBody(resp)
1125	found, resp, err := requireNotFoundOrOK(resp)
1126	if err != nil {
1127		return nil, nil, err
1128	}
1129
1130	qm := &QueryMeta{}
1131	parseQueryMeta(resp, qm)
1132	qm.RequestTime = rtt
1133
1134	if !found {
1135		return nil, qm, nil
1136	}
1137
1138	var out ACLRole
1139	if err := decodeBody(resp, &out); err != nil {
1140		return nil, nil, err
1141	}
1142
1143	return &out, qm, nil
1144}
1145
1146// RoleReadByName retrieves the role details (by name). Returns nil if not found.
1147func (a *ACL) RoleReadByName(roleName string, q *QueryOptions) (*ACLRole, *QueryMeta, error) {
1148	r := a.c.newRequest("GET", "/v1/acl/role/name/"+url.QueryEscape(roleName))
1149	r.setQueryOptions(q)
1150	rtt, resp, err := a.c.doRequest(r)
1151	if err != nil {
1152		return nil, nil, err
1153	}
1154	defer closeResponseBody(resp)
1155	found, resp, err := requireNotFoundOrOK(resp)
1156	if err != nil {
1157		return nil, nil, err
1158	}
1159
1160	qm := &QueryMeta{}
1161	parseQueryMeta(resp, qm)
1162	qm.RequestTime = rtt
1163
1164	if !found {
1165		return nil, qm, nil
1166	}
1167
1168	var out ACLRole
1169	if err := decodeBody(resp, &out); err != nil {
1170		return nil, nil, err
1171	}
1172
1173	return &out, qm, nil
1174}
1175
1176// RoleList retrieves a listing of all roles. The listing does not include some
1177// metadata for the role as those should be retrieved by subsequent calls to
1178// RoleRead.
1179func (a *ACL) RoleList(q *QueryOptions) ([]*ACLRole, *QueryMeta, error) {
1180	r := a.c.newRequest("GET", "/v1/acl/roles")
1181	r.setQueryOptions(q)
1182	rtt, resp, err := a.c.doRequest(r)
1183	if err != nil {
1184		return nil, nil, err
1185	}
1186	defer closeResponseBody(resp)
1187	if err := requireOK(resp); err != nil {
1188		return nil, nil, err
1189	}
1190	qm := &QueryMeta{}
1191	parseQueryMeta(resp, qm)
1192	qm.RequestTime = rtt
1193
1194	var entries []*ACLRole
1195	if err := decodeBody(resp, &entries); err != nil {
1196		return nil, nil, err
1197	}
1198	return entries, qm, nil
1199}
1200
1201// AuthMethodCreate will create a new auth method.
1202func (a *ACL) AuthMethodCreate(method *ACLAuthMethod, q *WriteOptions) (*ACLAuthMethod, *WriteMeta, error) {
1203	if method.Name == "" {
1204		return nil, nil, fmt.Errorf("Must specify a Name in Auth Method Creation")
1205	}
1206
1207	r := a.c.newRequest("PUT", "/v1/acl/auth-method")
1208	r.setWriteOptions(q)
1209	r.obj = method
1210	rtt, resp, err := a.c.doRequest(r)
1211	if err != nil {
1212		return nil, nil, err
1213	}
1214	defer closeResponseBody(resp)
1215	if err := requireOK(resp); err != nil {
1216		return nil, nil, err
1217	}
1218	wm := &WriteMeta{RequestTime: rtt}
1219	var out ACLAuthMethod
1220	if err := decodeBody(resp, &out); err != nil {
1221		return nil, nil, err
1222	}
1223
1224	return &out, wm, nil
1225}
1226
1227// AuthMethodUpdate updates an auth method.
1228func (a *ACL) AuthMethodUpdate(method *ACLAuthMethod, q *WriteOptions) (*ACLAuthMethod, *WriteMeta, error) {
1229	if method.Name == "" {
1230		return nil, nil, fmt.Errorf("Must specify a Name in Auth Method Update")
1231	}
1232
1233	r := a.c.newRequest("PUT", "/v1/acl/auth-method/"+url.QueryEscape(method.Name))
1234	r.setWriteOptions(q)
1235	r.obj = method
1236	rtt, resp, err := a.c.doRequest(r)
1237	if err != nil {
1238		return nil, nil, err
1239	}
1240	defer closeResponseBody(resp)
1241	if err := requireOK(resp); err != nil {
1242		return nil, nil, err
1243	}
1244	wm := &WriteMeta{RequestTime: rtt}
1245	var out ACLAuthMethod
1246	if err := decodeBody(resp, &out); err != nil {
1247		return nil, nil, err
1248	}
1249
1250	return &out, wm, nil
1251}
1252
1253// AuthMethodDelete deletes an auth method given its Name.
1254func (a *ACL) AuthMethodDelete(methodName string, q *WriteOptions) (*WriteMeta, error) {
1255	if methodName == "" {
1256		return nil, fmt.Errorf("Must specify a Name in Auth Method Delete")
1257	}
1258
1259	r := a.c.newRequest("DELETE", "/v1/acl/auth-method/"+url.QueryEscape(methodName))
1260	r.setWriteOptions(q)
1261	rtt, resp, err := a.c.doRequest(r)
1262	if err != nil {
1263		return nil, err
1264	}
1265	if err := requireOK(resp); err != nil {
1266		return nil, err
1267	}
1268	closeResponseBody(resp)
1269
1270	wm := &WriteMeta{RequestTime: rtt}
1271	return wm, nil
1272}
1273
1274// AuthMethodRead retrieves the auth method. Returns nil if not found.
1275func (a *ACL) AuthMethodRead(methodName string, q *QueryOptions) (*ACLAuthMethod, *QueryMeta, error) {
1276	if methodName == "" {
1277		return nil, nil, fmt.Errorf("Must specify a Name in Auth Method Read")
1278	}
1279
1280	r := a.c.newRequest("GET", "/v1/acl/auth-method/"+url.QueryEscape(methodName))
1281	r.setQueryOptions(q)
1282	rtt, resp, err := a.c.doRequest(r)
1283	if err != nil {
1284		return nil, nil, err
1285	}
1286	defer closeResponseBody(resp)
1287	found, resp, err := requireNotFoundOrOK(resp)
1288	if err != nil {
1289		return nil, nil, err
1290	}
1291
1292	qm := &QueryMeta{}
1293	parseQueryMeta(resp, qm)
1294	qm.RequestTime = rtt
1295
1296	if !found {
1297		return nil, qm, nil
1298	}
1299
1300	var out ACLAuthMethod
1301	if err := decodeBody(resp, &out); err != nil {
1302		return nil, nil, err
1303	}
1304
1305	return &out, qm, nil
1306}
1307
1308// AuthMethodList retrieves a listing of all auth methods. The listing does not
1309// include some metadata for the auth method as those should be retrieved by
1310// subsequent calls to AuthMethodRead.
1311func (a *ACL) AuthMethodList(q *QueryOptions) ([]*ACLAuthMethodListEntry, *QueryMeta, error) {
1312	r := a.c.newRequest("GET", "/v1/acl/auth-methods")
1313	r.setQueryOptions(q)
1314	rtt, resp, err := a.c.doRequest(r)
1315	if err != nil {
1316		return nil, nil, err
1317	}
1318	defer closeResponseBody(resp)
1319	if err := requireOK(resp); err != nil {
1320		return nil, nil, err
1321	}
1322	qm := &QueryMeta{}
1323	parseQueryMeta(resp, qm)
1324	qm.RequestTime = rtt
1325
1326	var entries []*ACLAuthMethodListEntry
1327	if err := decodeBody(resp, &entries); err != nil {
1328		return nil, nil, err
1329	}
1330	return entries, qm, nil
1331}
1332
1333// BindingRuleCreate will create a new binding rule. It is not allowed for the
1334// binding rule parameter's ID field to be set as this will be generated by
1335// Consul while processing the request.
1336func (a *ACL) BindingRuleCreate(rule *ACLBindingRule, q *WriteOptions) (*ACLBindingRule, *WriteMeta, error) {
1337	if rule.ID != "" {
1338		return nil, nil, fmt.Errorf("Cannot specify an ID in Binding Rule Creation")
1339	}
1340
1341	r := a.c.newRequest("PUT", "/v1/acl/binding-rule")
1342	r.setWriteOptions(q)
1343	r.obj = rule
1344	rtt, resp, err := a.c.doRequest(r)
1345	if err != nil {
1346		return nil, nil, err
1347	}
1348	defer closeResponseBody(resp)
1349	if err := requireOK(resp); err != nil {
1350		return nil, nil, err
1351	}
1352	wm := &WriteMeta{RequestTime: rtt}
1353	var out ACLBindingRule
1354	if err := decodeBody(resp, &out); err != nil {
1355		return nil, nil, err
1356	}
1357
1358	return &out, wm, nil
1359}
1360
1361// BindingRuleUpdate updates a binding rule. The ID field of the role binding
1362// rule parameter must be set to an existing binding rule ID.
1363func (a *ACL) BindingRuleUpdate(rule *ACLBindingRule, q *WriteOptions) (*ACLBindingRule, *WriteMeta, error) {
1364	if rule.ID == "" {
1365		return nil, nil, fmt.Errorf("Must specify an ID in Binding Rule Update")
1366	}
1367
1368	r := a.c.newRequest("PUT", "/v1/acl/binding-rule/"+rule.ID)
1369	r.setWriteOptions(q)
1370	r.obj = rule
1371	rtt, resp, err := a.c.doRequest(r)
1372	if err != nil {
1373		return nil, nil, err
1374	}
1375	defer closeResponseBody(resp)
1376	if err := requireOK(resp); err != nil {
1377		return nil, nil, err
1378	}
1379	wm := &WriteMeta{RequestTime: rtt}
1380	var out ACLBindingRule
1381	if err := decodeBody(resp, &out); err != nil {
1382		return nil, nil, err
1383	}
1384
1385	return &out, wm, nil
1386}
1387
1388// BindingRuleDelete deletes a binding rule given its ID.
1389func (a *ACL) BindingRuleDelete(bindingRuleID string, q *WriteOptions) (*WriteMeta, error) {
1390	r := a.c.newRequest("DELETE", "/v1/acl/binding-rule/"+bindingRuleID)
1391	r.setWriteOptions(q)
1392	rtt, resp, err := a.c.doRequest(r)
1393	if err != nil {
1394		return nil, err
1395	}
1396	defer closeResponseBody(resp)
1397	if err := requireOK(resp); err != nil {
1398		return nil, err
1399	}
1400
1401	wm := &WriteMeta{RequestTime: rtt}
1402	return wm, nil
1403}
1404
1405// BindingRuleRead retrieves the binding rule details. Returns nil if not found.
1406func (a *ACL) BindingRuleRead(bindingRuleID string, q *QueryOptions) (*ACLBindingRule, *QueryMeta, error) {
1407	r := a.c.newRequest("GET", "/v1/acl/binding-rule/"+bindingRuleID)
1408	r.setQueryOptions(q)
1409	rtt, resp, err := a.c.doRequest(r)
1410	if err != nil {
1411		return nil, nil, err
1412	}
1413	defer closeResponseBody(resp)
1414	found, resp, err := requireNotFoundOrOK(resp)
1415	if err != nil {
1416		return nil, nil, err
1417	}
1418
1419	qm := &QueryMeta{}
1420	parseQueryMeta(resp, qm)
1421	qm.RequestTime = rtt
1422
1423	if !found {
1424		return nil, qm, nil
1425	}
1426
1427	var out ACLBindingRule
1428	if err := decodeBody(resp, &out); err != nil {
1429		return nil, nil, err
1430	}
1431
1432	return &out, qm, nil
1433}
1434
1435// BindingRuleList retrieves a listing of all binding rules.
1436func (a *ACL) BindingRuleList(methodName string, q *QueryOptions) ([]*ACLBindingRule, *QueryMeta, error) {
1437	r := a.c.newRequest("GET", "/v1/acl/binding-rules")
1438	if methodName != "" {
1439		r.params.Set("authmethod", methodName)
1440	}
1441	r.setQueryOptions(q)
1442	rtt, resp, err := a.c.doRequest(r)
1443	if err != nil {
1444		return nil, nil, err
1445	}
1446	defer closeResponseBody(resp)
1447	if err := requireOK(resp); err != nil {
1448		return nil, nil, err
1449	}
1450	qm := &QueryMeta{}
1451	parseQueryMeta(resp, qm)
1452	qm.RequestTime = rtt
1453
1454	var entries []*ACLBindingRule
1455	if err := decodeBody(resp, &entries); err != nil {
1456		return nil, nil, err
1457	}
1458	return entries, qm, nil
1459}
1460
1461// Login is used to exchange auth method credentials for a newly-minted Consul Token.
1462func (a *ACL) Login(auth *ACLLoginParams, q *WriteOptions) (*ACLToken, *WriteMeta, error) {
1463	r := a.c.newRequest("POST", "/v1/acl/login")
1464	r.setWriteOptions(q)
1465	r.obj = auth
1466
1467	rtt, resp, err := a.c.doRequest(r)
1468	if err != nil {
1469		return nil, nil, err
1470	}
1471	defer closeResponseBody(resp)
1472	if err := requireOK(resp); err != nil {
1473		return nil, nil, err
1474	}
1475	wm := &WriteMeta{RequestTime: rtt}
1476	var out ACLToken
1477	if err := decodeBody(resp, &out); err != nil {
1478		return nil, nil, err
1479	}
1480	return &out, wm, nil
1481}
1482
1483// Logout is used to destroy a Consul Token created via Login().
1484func (a *ACL) Logout(q *WriteOptions) (*WriteMeta, error) {
1485	r := a.c.newRequest("POST", "/v1/acl/logout")
1486	r.setWriteOptions(q)
1487	rtt, resp, err := a.c.doRequest(r)
1488	if err != nil {
1489		return nil, err
1490	}
1491	if err := requireOK(resp); err != nil {
1492		return nil, err
1493	}
1494	closeResponseBody(resp)
1495
1496	wm := &WriteMeta{RequestTime: rtt}
1497	return wm, nil
1498}
1499
1500// OIDCAuthURL requests an authorization URL to start an OIDC login flow.
1501func (a *ACL) OIDCAuthURL(auth *ACLOIDCAuthURLParams, q *WriteOptions) (string, *WriteMeta, error) {
1502	if auth.AuthMethod == "" {
1503		return "", nil, fmt.Errorf("Must specify an auth method name")
1504	}
1505
1506	r := a.c.newRequest("POST", "/v1/acl/oidc/auth-url")
1507	r.setWriteOptions(q)
1508	r.obj = auth
1509
1510	rtt, resp, err := a.c.doRequest(r)
1511	if err != nil {
1512		return "", nil, err
1513	}
1514	defer closeResponseBody(resp)
1515	if err := requireOK(resp); err != nil {
1516		return "", nil, err
1517	}
1518
1519	wm := &WriteMeta{RequestTime: rtt}
1520	var out aclOIDCAuthURLResponse
1521	if err := decodeBody(resp, &out); err != nil {
1522		return "", nil, err
1523	}
1524	return out.AuthURL, wm, nil
1525}
1526
1527type aclOIDCAuthURLResponse struct {
1528	AuthURL string
1529}
1530
1531type ACLOIDCCallbackParams struct {
1532	AuthMethod  string
1533	State       string
1534	Code        string
1535	ClientNonce string
1536}
1537
1538// OIDCCallback is the callback endpoint to complete an OIDC login.
1539func (a *ACL) OIDCCallback(auth *ACLOIDCCallbackParams, q *WriteOptions) (*ACLToken, *WriteMeta, error) {
1540	if auth.AuthMethod == "" {
1541		return nil, nil, fmt.Errorf("Must specify an auth method name")
1542	}
1543
1544	r := a.c.newRequest("POST", "/v1/acl/oidc/callback")
1545	r.setWriteOptions(q)
1546	r.obj = auth
1547
1548	rtt, resp, err := a.c.doRequest(r)
1549	if err != nil {
1550		return nil, nil, err
1551	}
1552	defer closeResponseBody(resp)
1553	if err := requireOK(resp); err != nil {
1554		return nil, nil, err
1555	}
1556	wm := &WriteMeta{RequestTime: rtt}
1557	var out ACLToken
1558	if err := decodeBody(resp, &out); err != nil {
1559		return nil, nil, err
1560	}
1561	return &out, wm, nil
1562}
1563