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