1package api
2
3import (
4	"fmt"
5	"io"
6	"io/ioutil"
7	"net/url"
8	"time"
9
10	"github.com/mitchellh/mapstructure"
11)
12
13const (
14	// ACLClientType is the client type token
15	ACLClientType = "client"
16
17	// ACLManagementType is the management type token
18	ACLManagementType = "management"
19)
20
21type ACLTokenPolicyLink struct {
22	ID   string
23	Name string
24}
25type ACLTokenRoleLink struct {
26	ID   string
27	Name string
28}
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	Local             bool
41	ExpirationTTL     time.Duration `json:",omitempty"`
42	ExpirationTime    *time.Time    `json:",omitempty"`
43	CreateTime        time.Time     `json:",omitempty"`
44	Hash              []byte        `json:",omitempty"`
45
46	// DEPRECATED (ACL-Legacy-Compat)
47	// Rules will only be present for legacy tokens returned via the new APIs
48	Rules string `json:",omitempty"`
49}
50
51type ACLTokenListEntry struct {
52	CreateIndex       uint64
53	ModifyIndex       uint64
54	AccessorID        string
55	Description       string
56	Policies          []*ACLTokenPolicyLink `json:",omitempty"`
57	Roles             []*ACLTokenRoleLink   `json:",omitempty"`
58	ServiceIdentities []*ACLServiceIdentity `json:",omitempty"`
59	Local             bool
60	ExpirationTime    *time.Time `json:",omitempty"`
61	CreateTime        time.Time
62	Hash              []byte
63	Legacy            bool
64}
65
66// ACLEntry is used to represent a legacy ACL token
67// The legacy tokens are deprecated.
68type ACLEntry struct {
69	CreateIndex uint64
70	ModifyIndex uint64
71	ID          string
72	Name        string
73	Type        string
74	Rules       string
75}
76
77// ACLReplicationStatus is used to represent the status of ACL replication.
78type ACLReplicationStatus struct {
79	Enabled              bool
80	Running              bool
81	SourceDatacenter     string
82	ReplicationType      string
83	ReplicatedIndex      uint64
84	ReplicatedRoleIndex  uint64
85	ReplicatedTokenIndex uint64
86	LastSuccess          time.Time
87	LastError            time.Time
88}
89
90// ACLServiceIdentity represents a high-level grant of all necessary privileges
91// to assume the identity of the named Service in the Catalog and within
92// Connect.
93type ACLServiceIdentity struct {
94	ServiceName string
95	Datacenters []string `json:",omitempty"`
96}
97
98// ACLPolicy represents an ACL Policy.
99type ACLPolicy struct {
100	ID          string
101	Name        string
102	Description string
103	Rules       string
104	Datacenters []string
105	Hash        []byte
106	CreateIndex uint64
107	ModifyIndex uint64
108}
109
110type ACLPolicyListEntry struct {
111	ID          string
112	Name        string
113	Description string
114	Datacenters []string
115	Hash        []byte
116	CreateIndex uint64
117	ModifyIndex uint64
118}
119
120type ACLRolePolicyLink struct {
121	ID   string
122	Name string
123}
124
125// ACLRole represents an ACL Role.
126type ACLRole struct {
127	ID                string
128	Name              string
129	Description       string
130	Policies          []*ACLRolePolicyLink  `json:",omitempty"`
131	ServiceIdentities []*ACLServiceIdentity `json:",omitempty"`
132	Hash              []byte
133	CreateIndex       uint64
134	ModifyIndex       uint64
135}
136
137// BindingRuleBindType is the type of binding rule mechanism used.
138type BindingRuleBindType string
139
140const (
141	// BindingRuleBindTypeService binds to a service identity with the given name.
142	BindingRuleBindTypeService BindingRuleBindType = "service"
143
144	// BindingRuleBindTypeRole binds to pre-existing roles with the given name.
145	BindingRuleBindTypeRole BindingRuleBindType = "role"
146)
147
148type ACLBindingRule struct {
149	ID          string
150	Description string
151	AuthMethod  string
152	Selector    string
153	BindType    BindingRuleBindType
154	BindName    string
155
156	CreateIndex uint64
157	ModifyIndex uint64
158}
159
160type ACLAuthMethod struct {
161	Name        string
162	Type        string
163	Description string
164
165	// Configuration is arbitrary configuration for the auth method. This
166	// should only contain primitive values and containers (such as lists and
167	// maps).
168	Config map[string]interface{}
169
170	CreateIndex uint64
171	ModifyIndex uint64
172}
173
174type ACLAuthMethodListEntry struct {
175	Name        string
176	Type        string
177	Description string
178	CreateIndex uint64
179	ModifyIndex uint64
180}
181
182// ParseKubernetesAuthMethodConfig takes a raw config map and returns a parsed
183// KubernetesAuthMethodConfig.
184func ParseKubernetesAuthMethodConfig(raw map[string]interface{}) (*KubernetesAuthMethodConfig, error) {
185	var config KubernetesAuthMethodConfig
186	decodeConf := &mapstructure.DecoderConfig{
187		Result:           &config,
188		WeaklyTypedInput: true,
189	}
190
191	decoder, err := mapstructure.NewDecoder(decodeConf)
192	if err != nil {
193		return nil, err
194	}
195
196	if err := decoder.Decode(raw); err != nil {
197		return nil, fmt.Errorf("error decoding config: %s", err)
198	}
199
200	return &config, nil
201}
202
203// KubernetesAuthMethodConfig is the config for the built-in Consul auth method
204// for Kubernetes.
205type KubernetesAuthMethodConfig struct {
206	Host              string `json:",omitempty"`
207	CACert            string `json:",omitempty"`
208	ServiceAccountJWT string `json:",omitempty"`
209}
210
211// RenderToConfig converts this into a map[string]interface{} suitable for use
212// in the ACLAuthMethod.Config field.
213func (c *KubernetesAuthMethodConfig) RenderToConfig() map[string]interface{} {
214	return map[string]interface{}{
215		"Host":              c.Host,
216		"CACert":            c.CACert,
217		"ServiceAccountJWT": c.ServiceAccountJWT,
218	}
219}
220
221type ACLLoginParams struct {
222	AuthMethod  string
223	BearerToken string
224	Meta        map[string]string `json:",omitempty"`
225}
226
227// ACL can be used to query the ACL endpoints
228type ACL struct {
229	c *Client
230}
231
232// ACL returns a handle to the ACL endpoints
233func (c *Client) ACL() *ACL {
234	return &ACL{c}
235}
236
237// Bootstrap is used to perform a one-time ACL bootstrap operation on a cluster
238// to get the first management token.
239func (a *ACL) Bootstrap() (*ACLToken, *WriteMeta, error) {
240	r := a.c.newRequest("PUT", "/v1/acl/bootstrap")
241	rtt, resp, err := requireOK(a.c.doRequest(r))
242	if err != nil {
243		return nil, nil, err
244	}
245	defer resp.Body.Close()
246
247	wm := &WriteMeta{RequestTime: rtt}
248	var out ACLToken
249	if err := decodeBody(resp, &out); err != nil {
250		return nil, nil, err
251	}
252	return &out, wm, nil
253}
254
255// Create is used to generate a new token with the given parameters
256//
257// Deprecated: Use TokenCreate instead.
258func (a *ACL) Create(acl *ACLEntry, q *WriteOptions) (string, *WriteMeta, error) {
259	r := a.c.newRequest("PUT", "/v1/acl/create")
260	r.setWriteOptions(q)
261	r.obj = acl
262	rtt, resp, err := requireOK(a.c.doRequest(r))
263	if err != nil {
264		return "", nil, err
265	}
266	defer resp.Body.Close()
267
268	wm := &WriteMeta{RequestTime: rtt}
269	var out struct{ ID string }
270	if err := decodeBody(resp, &out); err != nil {
271		return "", nil, err
272	}
273	return out.ID, wm, nil
274}
275
276// Update is used to update the rules of an existing token
277//
278// Deprecated: Use TokenUpdate instead.
279func (a *ACL) Update(acl *ACLEntry, q *WriteOptions) (*WriteMeta, error) {
280	r := a.c.newRequest("PUT", "/v1/acl/update")
281	r.setWriteOptions(q)
282	r.obj = acl
283	rtt, resp, err := requireOK(a.c.doRequest(r))
284	if err != nil {
285		return nil, err
286	}
287	defer resp.Body.Close()
288
289	wm := &WriteMeta{RequestTime: rtt}
290	return wm, nil
291}
292
293// Destroy is used to destroy a given ACL token ID
294//
295// Deprecated: Use TokenDelete instead.
296func (a *ACL) Destroy(id string, q *WriteOptions) (*WriteMeta, error) {
297	r := a.c.newRequest("PUT", "/v1/acl/destroy/"+id)
298	r.setWriteOptions(q)
299	rtt, resp, err := requireOK(a.c.doRequest(r))
300	if err != nil {
301		return nil, err
302	}
303	resp.Body.Close()
304
305	wm := &WriteMeta{RequestTime: rtt}
306	return wm, nil
307}
308
309// Clone is used to return a new token cloned from an existing one
310//
311// Deprecated: Use TokenClone instead.
312func (a *ACL) Clone(id string, q *WriteOptions) (string, *WriteMeta, error) {
313	r := a.c.newRequest("PUT", "/v1/acl/clone/"+id)
314	r.setWriteOptions(q)
315	rtt, resp, err := requireOK(a.c.doRequest(r))
316	if err != nil {
317		return "", nil, err
318	}
319	defer resp.Body.Close()
320
321	wm := &WriteMeta{RequestTime: rtt}
322	var out struct{ ID string }
323	if err := decodeBody(resp, &out); err != nil {
324		return "", nil, err
325	}
326	return out.ID, wm, nil
327}
328
329// Info is used to query for information about an ACL token
330//
331// Deprecated: Use TokenRead instead.
332func (a *ACL) Info(id string, q *QueryOptions) (*ACLEntry, *QueryMeta, error) {
333	r := a.c.newRequest("GET", "/v1/acl/info/"+id)
334	r.setQueryOptions(q)
335	rtt, resp, err := requireOK(a.c.doRequest(r))
336	if err != nil {
337		return nil, nil, err
338	}
339	defer resp.Body.Close()
340
341	qm := &QueryMeta{}
342	parseQueryMeta(resp, qm)
343	qm.RequestTime = rtt
344
345	var entries []*ACLEntry
346	if err := decodeBody(resp, &entries); err != nil {
347		return nil, nil, err
348	}
349	if len(entries) > 0 {
350		return entries[0], qm, nil
351	}
352	return nil, qm, nil
353}
354
355// List is used to get all the ACL tokens
356//
357// Deprecated: Use TokenList instead.
358func (a *ACL) List(q *QueryOptions) ([]*ACLEntry, *QueryMeta, error) {
359	r := a.c.newRequest("GET", "/v1/acl/list")
360	r.setQueryOptions(q)
361	rtt, resp, err := requireOK(a.c.doRequest(r))
362	if err != nil {
363		return nil, nil, err
364	}
365	defer resp.Body.Close()
366
367	qm := &QueryMeta{}
368	parseQueryMeta(resp, qm)
369	qm.RequestTime = rtt
370
371	var entries []*ACLEntry
372	if err := decodeBody(resp, &entries); err != nil {
373		return nil, nil, err
374	}
375	return entries, qm, nil
376}
377
378// Replication returns the status of the ACL replication process in the datacenter
379func (a *ACL) Replication(q *QueryOptions) (*ACLReplicationStatus, *QueryMeta, error) {
380	r := a.c.newRequest("GET", "/v1/acl/replication")
381	r.setQueryOptions(q)
382	rtt, resp, err := requireOK(a.c.doRequest(r))
383	if err != nil {
384		return nil, nil, err
385	}
386	defer resp.Body.Close()
387
388	qm := &QueryMeta{}
389	parseQueryMeta(resp, qm)
390	qm.RequestTime = rtt
391
392	var entries *ACLReplicationStatus
393	if err := decodeBody(resp, &entries); err != nil {
394		return nil, nil, err
395	}
396	return entries, qm, nil
397}
398
399// TokenCreate creates a new ACL token. If either the AccessorID or SecretID fields
400// of the ACLToken structure are empty they will be filled in by Consul.
401func (a *ACL) TokenCreate(token *ACLToken, q *WriteOptions) (*ACLToken, *WriteMeta, error) {
402	r := a.c.newRequest("PUT", "/v1/acl/token")
403	r.setWriteOptions(q)
404	r.obj = token
405	rtt, resp, err := requireOK(a.c.doRequest(r))
406	if err != nil {
407		return nil, nil, err
408	}
409	defer resp.Body.Close()
410
411	wm := &WriteMeta{RequestTime: rtt}
412	var out ACLToken
413	if err := decodeBody(resp, &out); err != nil {
414		return nil, nil, err
415	}
416
417	return &out, wm, nil
418}
419
420// TokenUpdate updates a token in place without modifying its AccessorID or SecretID. A valid
421// AccessorID must be set in the ACLToken structure passed to this function but the SecretID may
422// be omitted and will be filled in by Consul with its existing value.
423func (a *ACL) TokenUpdate(token *ACLToken, q *WriteOptions) (*ACLToken, *WriteMeta, error) {
424	if token.AccessorID == "" {
425		return nil, nil, fmt.Errorf("Must specify an AccessorID for Token Updating")
426	}
427	r := a.c.newRequest("PUT", "/v1/acl/token/"+token.AccessorID)
428	r.setWriteOptions(q)
429	r.obj = token
430	rtt, resp, err := requireOK(a.c.doRequest(r))
431	if err != nil {
432		return nil, nil, err
433	}
434	defer resp.Body.Close()
435
436	wm := &WriteMeta{RequestTime: rtt}
437	var out ACLToken
438	if err := decodeBody(resp, &out); err != nil {
439		return nil, nil, err
440	}
441
442	return &out, wm, nil
443}
444
445// TokenClone will create a new token with the same policies and locality as the original
446// token but will have its own auto-generated AccessorID and SecretID as well having the
447// description passed to this function. The tokenID parameter must be a valid Accessor ID
448// of an existing token.
449func (a *ACL) TokenClone(tokenID string, description string, q *WriteOptions) (*ACLToken, *WriteMeta, error) {
450	if tokenID == "" {
451		return nil, nil, fmt.Errorf("Must specify a tokenID for Token Cloning")
452	}
453
454	r := a.c.newRequest("PUT", "/v1/acl/token/"+tokenID+"/clone")
455	r.setWriteOptions(q)
456	r.obj = struct{ Description string }{description}
457	rtt, resp, err := requireOK(a.c.doRequest(r))
458	if err != nil {
459		return nil, nil, err
460	}
461	defer resp.Body.Close()
462
463	wm := &WriteMeta{RequestTime: rtt}
464	var out ACLToken
465	if err := decodeBody(resp, &out); err != nil {
466		return nil, nil, err
467	}
468
469	return &out, wm, nil
470}
471
472// TokenDelete removes a single ACL token. The tokenID parameter must be a valid
473// Accessor ID of an existing token.
474func (a *ACL) TokenDelete(tokenID string, q *WriteOptions) (*WriteMeta, error) {
475	r := a.c.newRequest("DELETE", "/v1/acl/token/"+tokenID)
476	r.setWriteOptions(q)
477	rtt, resp, err := requireOK(a.c.doRequest(r))
478	if err != nil {
479		return nil, err
480	}
481	resp.Body.Close()
482
483	wm := &WriteMeta{RequestTime: rtt}
484	return wm, nil
485}
486
487// TokenRead retrieves the full token details. The tokenID parameter must be a valid
488// Accessor ID of an existing token.
489func (a *ACL) TokenRead(tokenID string, q *QueryOptions) (*ACLToken, *QueryMeta, error) {
490	r := a.c.newRequest("GET", "/v1/acl/token/"+tokenID)
491	r.setQueryOptions(q)
492	rtt, resp, err := requireOK(a.c.doRequest(r))
493	if err != nil {
494		return nil, nil, err
495	}
496	defer resp.Body.Close()
497
498	qm := &QueryMeta{}
499	parseQueryMeta(resp, qm)
500	qm.RequestTime = rtt
501
502	var out ACLToken
503	if err := decodeBody(resp, &out); err != nil {
504		return nil, nil, err
505	}
506
507	return &out, qm, nil
508}
509
510// TokenReadSelf retrieves the full token details of the token currently
511// assigned to the API Client. In this manner its possible to read a token
512// by its Secret ID.
513func (a *ACL) TokenReadSelf(q *QueryOptions) (*ACLToken, *QueryMeta, error) {
514	r := a.c.newRequest("GET", "/v1/acl/token/self")
515	r.setQueryOptions(q)
516	rtt, resp, err := requireOK(a.c.doRequest(r))
517	if err != nil {
518		return nil, nil, err
519	}
520	defer resp.Body.Close()
521
522	qm := &QueryMeta{}
523	parseQueryMeta(resp, qm)
524	qm.RequestTime = rtt
525
526	var out ACLToken
527	if err := decodeBody(resp, &out); err != nil {
528		return nil, nil, err
529	}
530
531	return &out, qm, nil
532}
533
534// TokenList lists all tokens. The listing does not contain any SecretIDs as those
535// may only be retrieved by a call to TokenRead.
536func (a *ACL) TokenList(q *QueryOptions) ([]*ACLTokenListEntry, *QueryMeta, error) {
537	r := a.c.newRequest("GET", "/v1/acl/tokens")
538	r.setQueryOptions(q)
539	rtt, resp, err := requireOK(a.c.doRequest(r))
540	if err != nil {
541		return nil, nil, err
542	}
543	defer resp.Body.Close()
544
545	qm := &QueryMeta{}
546	parseQueryMeta(resp, qm)
547	qm.RequestTime = rtt
548
549	var entries []*ACLTokenListEntry
550	if err := decodeBody(resp, &entries); err != nil {
551		return nil, nil, err
552	}
553	return entries, qm, nil
554}
555
556// PolicyCreate will create a new policy. It is not allowed for the policy parameters
557// ID field to be set as this will be generated by Consul while processing the request.
558func (a *ACL) PolicyCreate(policy *ACLPolicy, q *WriteOptions) (*ACLPolicy, *WriteMeta, error) {
559	if policy.ID != "" {
560		return nil, nil, fmt.Errorf("Cannot specify an ID in Policy Creation")
561	}
562	r := a.c.newRequest("PUT", "/v1/acl/policy")
563	r.setWriteOptions(q)
564	r.obj = policy
565	rtt, resp, err := requireOK(a.c.doRequest(r))
566	if err != nil {
567		return nil, nil, err
568	}
569	defer resp.Body.Close()
570
571	wm := &WriteMeta{RequestTime: rtt}
572	var out ACLPolicy
573	if err := decodeBody(resp, &out); err != nil {
574		return nil, nil, err
575	}
576
577	return &out, wm, nil
578}
579
580// PolicyUpdate updates a policy. The ID field of the policy parameter must be set to an
581// existing policy ID
582func (a *ACL) PolicyUpdate(policy *ACLPolicy, q *WriteOptions) (*ACLPolicy, *WriteMeta, error) {
583	if policy.ID == "" {
584		return nil, nil, fmt.Errorf("Must specify an ID in Policy Update")
585	}
586
587	r := a.c.newRequest("PUT", "/v1/acl/policy/"+policy.ID)
588	r.setWriteOptions(q)
589	r.obj = policy
590	rtt, resp, err := requireOK(a.c.doRequest(r))
591	if err != nil {
592		return nil, nil, err
593	}
594	defer resp.Body.Close()
595
596	wm := &WriteMeta{RequestTime: rtt}
597	var out ACLPolicy
598	if err := decodeBody(resp, &out); err != nil {
599		return nil, nil, err
600	}
601
602	return &out, wm, nil
603}
604
605// PolicyDelete deletes a policy given its ID.
606func (a *ACL) PolicyDelete(policyID string, q *WriteOptions) (*WriteMeta, error) {
607	r := a.c.newRequest("DELETE", "/v1/acl/policy/"+policyID)
608	r.setWriteOptions(q)
609	rtt, resp, err := requireOK(a.c.doRequest(r))
610	if err != nil {
611		return nil, err
612	}
613	resp.Body.Close()
614
615	wm := &WriteMeta{RequestTime: rtt}
616	return wm, nil
617}
618
619// PolicyRead retrieves the policy details including the rule set.
620func (a *ACL) PolicyRead(policyID string, q *QueryOptions) (*ACLPolicy, *QueryMeta, error) {
621	r := a.c.newRequest("GET", "/v1/acl/policy/"+policyID)
622	r.setQueryOptions(q)
623	rtt, resp, err := requireOK(a.c.doRequest(r))
624	if err != nil {
625		return nil, nil, err
626	}
627	defer resp.Body.Close()
628
629	qm := &QueryMeta{}
630	parseQueryMeta(resp, qm)
631	qm.RequestTime = rtt
632
633	var out ACLPolicy
634	if err := decodeBody(resp, &out); err != nil {
635		return nil, nil, err
636	}
637
638	return &out, qm, nil
639}
640
641// PolicyList retrieves a listing of all policies. The listing does not include the
642// rules for any policy as those should be retrieved by subsequent calls to PolicyRead.
643func (a *ACL) PolicyList(q *QueryOptions) ([]*ACLPolicyListEntry, *QueryMeta, error) {
644	r := a.c.newRequest("GET", "/v1/acl/policies")
645	r.setQueryOptions(q)
646	rtt, resp, err := requireOK(a.c.doRequest(r))
647	if err != nil {
648		return nil, nil, err
649	}
650	defer resp.Body.Close()
651
652	qm := &QueryMeta{}
653	parseQueryMeta(resp, qm)
654	qm.RequestTime = rtt
655
656	var entries []*ACLPolicyListEntry
657	if err := decodeBody(resp, &entries); err != nil {
658		return nil, nil, err
659	}
660	return entries, qm, nil
661}
662
663// RulesTranslate translates the legacy rule syntax into the current syntax.
664//
665// Deprecated: Support for the legacy syntax translation will be removed
666// when legacy ACL support is removed.
667func (a *ACL) RulesTranslate(rules io.Reader) (string, error) {
668	r := a.c.newRequest("POST", "/v1/acl/rules/translate")
669	r.body = rules
670	rtt, resp, err := requireOK(a.c.doRequest(r))
671	if err != nil {
672		return "", err
673	}
674	defer resp.Body.Close()
675	qm := &QueryMeta{}
676	parseQueryMeta(resp, qm)
677	qm.RequestTime = rtt
678
679	ruleBytes, err := ioutil.ReadAll(resp.Body)
680	if err != nil {
681		return "", fmt.Errorf("Failed to read translated rule body: %v", err)
682	}
683
684	return string(ruleBytes), nil
685}
686
687// RulesTranslateToken translates the rules associated with the legacy syntax
688// into the current syntax and returns the results.
689//
690// Deprecated: Support for the legacy syntax translation will be removed
691// when legacy ACL support is removed.
692func (a *ACL) RulesTranslateToken(tokenID string) (string, error) {
693	r := a.c.newRequest("GET", "/v1/acl/rules/translate/"+tokenID)
694	rtt, resp, err := requireOK(a.c.doRequest(r))
695	if err != nil {
696		return "", err
697	}
698	defer resp.Body.Close()
699	qm := &QueryMeta{}
700	parseQueryMeta(resp, qm)
701	qm.RequestTime = rtt
702
703	ruleBytes, err := ioutil.ReadAll(resp.Body)
704	if err != nil {
705		return "", fmt.Errorf("Failed to read translated rule body: %v", err)
706	}
707
708	return string(ruleBytes), nil
709}
710
711// RoleCreate will create a new role. It is not allowed for the role parameters
712// ID field to be set as this will be generated by Consul while processing the request.
713func (a *ACL) RoleCreate(role *ACLRole, q *WriteOptions) (*ACLRole, *WriteMeta, error) {
714	if role.ID != "" {
715		return nil, nil, fmt.Errorf("Cannot specify an ID in Role Creation")
716	}
717
718	r := a.c.newRequest("PUT", "/v1/acl/role")
719	r.setWriteOptions(q)
720	r.obj = role
721	rtt, resp, err := requireOK(a.c.doRequest(r))
722	if err != nil {
723		return nil, nil, err
724	}
725	defer resp.Body.Close()
726
727	wm := &WriteMeta{RequestTime: rtt}
728	var out ACLRole
729	if err := decodeBody(resp, &out); err != nil {
730		return nil, nil, err
731	}
732
733	return &out, wm, nil
734}
735
736// RoleUpdate updates a role. The ID field of the role parameter must be set to an
737// existing role ID
738func (a *ACL) RoleUpdate(role *ACLRole, q *WriteOptions) (*ACLRole, *WriteMeta, error) {
739	if role.ID == "" {
740		return nil, nil, fmt.Errorf("Must specify an ID in Role Update")
741	}
742
743	r := a.c.newRequest("PUT", "/v1/acl/role/"+role.ID)
744	r.setWriteOptions(q)
745	r.obj = role
746	rtt, resp, err := requireOK(a.c.doRequest(r))
747	if err != nil {
748		return nil, nil, err
749	}
750	defer resp.Body.Close()
751
752	wm := &WriteMeta{RequestTime: rtt}
753	var out ACLRole
754	if err := decodeBody(resp, &out); err != nil {
755		return nil, nil, err
756	}
757
758	return &out, wm, nil
759}
760
761// RoleDelete deletes a role given its ID.
762func (a *ACL) RoleDelete(roleID string, q *WriteOptions) (*WriteMeta, error) {
763	r := a.c.newRequest("DELETE", "/v1/acl/role/"+roleID)
764	r.setWriteOptions(q)
765	rtt, resp, err := requireOK(a.c.doRequest(r))
766	if err != nil {
767		return nil, err
768	}
769	resp.Body.Close()
770
771	wm := &WriteMeta{RequestTime: rtt}
772	return wm, nil
773}
774
775// RoleRead retrieves the role details (by ID). Returns nil if not found.
776func (a *ACL) RoleRead(roleID string, q *QueryOptions) (*ACLRole, *QueryMeta, error) {
777	r := a.c.newRequest("GET", "/v1/acl/role/"+roleID)
778	r.setQueryOptions(q)
779	found, rtt, resp, err := requireNotFoundOrOK(a.c.doRequest(r))
780	if err != nil {
781		return nil, nil, err
782	}
783	defer resp.Body.Close()
784
785	qm := &QueryMeta{}
786	parseQueryMeta(resp, qm)
787	qm.RequestTime = rtt
788
789	if !found {
790		return nil, qm, nil
791	}
792
793	var out ACLRole
794	if err := decodeBody(resp, &out); err != nil {
795		return nil, nil, err
796	}
797
798	return &out, qm, nil
799}
800
801// RoleReadByName retrieves the role details (by name). Returns nil if not found.
802func (a *ACL) RoleReadByName(roleName string, q *QueryOptions) (*ACLRole, *QueryMeta, error) {
803	r := a.c.newRequest("GET", "/v1/acl/role/name/"+url.QueryEscape(roleName))
804	r.setQueryOptions(q)
805	found, rtt, resp, err := requireNotFoundOrOK(a.c.doRequest(r))
806	if err != nil {
807		return nil, nil, err
808	}
809	defer resp.Body.Close()
810
811	qm := &QueryMeta{}
812	parseQueryMeta(resp, qm)
813	qm.RequestTime = rtt
814
815	if !found {
816		return nil, qm, nil
817	}
818
819	var out ACLRole
820	if err := decodeBody(resp, &out); err != nil {
821		return nil, nil, err
822	}
823
824	return &out, qm, nil
825}
826
827// RoleList retrieves a listing of all roles. The listing does not include some
828// metadata for the role as those should be retrieved by subsequent calls to
829// RoleRead.
830func (a *ACL) RoleList(q *QueryOptions) ([]*ACLRole, *QueryMeta, error) {
831	r := a.c.newRequest("GET", "/v1/acl/roles")
832	r.setQueryOptions(q)
833	rtt, resp, err := requireOK(a.c.doRequest(r))
834	if err != nil {
835		return nil, nil, err
836	}
837	defer resp.Body.Close()
838
839	qm := &QueryMeta{}
840	parseQueryMeta(resp, qm)
841	qm.RequestTime = rtt
842
843	var entries []*ACLRole
844	if err := decodeBody(resp, &entries); err != nil {
845		return nil, nil, err
846	}
847	return entries, qm, nil
848}
849
850// AuthMethodCreate will create a new auth method.
851func (a *ACL) AuthMethodCreate(method *ACLAuthMethod, q *WriteOptions) (*ACLAuthMethod, *WriteMeta, error) {
852	if method.Name == "" {
853		return nil, nil, fmt.Errorf("Must specify a Name in Auth Method Creation")
854	}
855
856	r := a.c.newRequest("PUT", "/v1/acl/auth-method")
857	r.setWriteOptions(q)
858	r.obj = method
859	rtt, resp, err := requireOK(a.c.doRequest(r))
860	if err != nil {
861		return nil, nil, err
862	}
863	defer resp.Body.Close()
864
865	wm := &WriteMeta{RequestTime: rtt}
866	var out ACLAuthMethod
867	if err := decodeBody(resp, &out); err != nil {
868		return nil, nil, err
869	}
870
871	return &out, wm, nil
872}
873
874// AuthMethodUpdate updates an auth method.
875func (a *ACL) AuthMethodUpdate(method *ACLAuthMethod, q *WriteOptions) (*ACLAuthMethod, *WriteMeta, error) {
876	if method.Name == "" {
877		return nil, nil, fmt.Errorf("Must specify a Name in Auth Method Update")
878	}
879
880	r := a.c.newRequest("PUT", "/v1/acl/auth-method/"+url.QueryEscape(method.Name))
881	r.setWriteOptions(q)
882	r.obj = method
883	rtt, resp, err := requireOK(a.c.doRequest(r))
884	if err != nil {
885		return nil, nil, err
886	}
887	defer resp.Body.Close()
888
889	wm := &WriteMeta{RequestTime: rtt}
890	var out ACLAuthMethod
891	if err := decodeBody(resp, &out); err != nil {
892		return nil, nil, err
893	}
894
895	return &out, wm, nil
896}
897
898// AuthMethodDelete deletes an auth method given its Name.
899func (a *ACL) AuthMethodDelete(methodName string, q *WriteOptions) (*WriteMeta, error) {
900	if methodName == "" {
901		return nil, fmt.Errorf("Must specify a Name in Auth Method Delete")
902	}
903
904	r := a.c.newRequest("DELETE", "/v1/acl/auth-method/"+url.QueryEscape(methodName))
905	r.setWriteOptions(q)
906	rtt, resp, err := requireOK(a.c.doRequest(r))
907	if err != nil {
908		return nil, err
909	}
910	resp.Body.Close()
911
912	wm := &WriteMeta{RequestTime: rtt}
913	return wm, nil
914}
915
916// AuthMethodRead retrieves the auth method. Returns nil if not found.
917func (a *ACL) AuthMethodRead(methodName string, q *QueryOptions) (*ACLAuthMethod, *QueryMeta, error) {
918	if methodName == "" {
919		return nil, nil, fmt.Errorf("Must specify a Name in Auth Method Read")
920	}
921
922	r := a.c.newRequest("GET", "/v1/acl/auth-method/"+url.QueryEscape(methodName))
923	r.setQueryOptions(q)
924	found, rtt, resp, err := requireNotFoundOrOK(a.c.doRequest(r))
925	if err != nil {
926		return nil, nil, err
927	}
928	defer resp.Body.Close()
929
930	qm := &QueryMeta{}
931	parseQueryMeta(resp, qm)
932	qm.RequestTime = rtt
933
934	if !found {
935		return nil, qm, nil
936	}
937
938	var out ACLAuthMethod
939	if err := decodeBody(resp, &out); err != nil {
940		return nil, nil, err
941	}
942
943	return &out, qm, nil
944}
945
946// AuthMethodList retrieves a listing of all auth methods. The listing does not
947// include some metadata for the auth method as those should be retrieved by
948// subsequent calls to AuthMethodRead.
949func (a *ACL) AuthMethodList(q *QueryOptions) ([]*ACLAuthMethodListEntry, *QueryMeta, error) {
950	r := a.c.newRequest("GET", "/v1/acl/auth-methods")
951	r.setQueryOptions(q)
952	rtt, resp, err := requireOK(a.c.doRequest(r))
953	if err != nil {
954		return nil, nil, err
955	}
956	defer resp.Body.Close()
957
958	qm := &QueryMeta{}
959	parseQueryMeta(resp, qm)
960	qm.RequestTime = rtt
961
962	var entries []*ACLAuthMethodListEntry
963	if err := decodeBody(resp, &entries); err != nil {
964		return nil, nil, err
965	}
966	return entries, qm, nil
967}
968
969// BindingRuleCreate will create a new binding rule. It is not allowed for the
970// binding rule parameter's ID field to be set as this will be generated by
971// Consul while processing the request.
972func (a *ACL) BindingRuleCreate(rule *ACLBindingRule, q *WriteOptions) (*ACLBindingRule, *WriteMeta, error) {
973	if rule.ID != "" {
974		return nil, nil, fmt.Errorf("Cannot specify an ID in Binding Rule Creation")
975	}
976
977	r := a.c.newRequest("PUT", "/v1/acl/binding-rule")
978	r.setWriteOptions(q)
979	r.obj = rule
980	rtt, resp, err := requireOK(a.c.doRequest(r))
981	if err != nil {
982		return nil, nil, err
983	}
984	defer resp.Body.Close()
985
986	wm := &WriteMeta{RequestTime: rtt}
987	var out ACLBindingRule
988	if err := decodeBody(resp, &out); err != nil {
989		return nil, nil, err
990	}
991
992	return &out, wm, nil
993}
994
995// BindingRuleUpdate updates a binding rule. The ID field of the role binding
996// rule parameter must be set to an existing binding rule ID.
997func (a *ACL) BindingRuleUpdate(rule *ACLBindingRule, q *WriteOptions) (*ACLBindingRule, *WriteMeta, error) {
998	if rule.ID == "" {
999		return nil, nil, fmt.Errorf("Must specify an ID in Binding Rule Update")
1000	}
1001
1002	r := a.c.newRequest("PUT", "/v1/acl/binding-rule/"+rule.ID)
1003	r.setWriteOptions(q)
1004	r.obj = rule
1005	rtt, resp, err := requireOK(a.c.doRequest(r))
1006	if err != nil {
1007		return nil, nil, err
1008	}
1009	defer resp.Body.Close()
1010
1011	wm := &WriteMeta{RequestTime: rtt}
1012	var out ACLBindingRule
1013	if err := decodeBody(resp, &out); err != nil {
1014		return nil, nil, err
1015	}
1016
1017	return &out, wm, nil
1018}
1019
1020// BindingRuleDelete deletes a binding rule given its ID.
1021func (a *ACL) BindingRuleDelete(bindingRuleID string, q *WriteOptions) (*WriteMeta, error) {
1022	r := a.c.newRequest("DELETE", "/v1/acl/binding-rule/"+bindingRuleID)
1023	r.setWriteOptions(q)
1024	rtt, resp, err := requireOK(a.c.doRequest(r))
1025	if err != nil {
1026		return nil, err
1027	}
1028	resp.Body.Close()
1029
1030	wm := &WriteMeta{RequestTime: rtt}
1031	return wm, nil
1032}
1033
1034// BindingRuleRead retrieves the binding rule details. Returns nil if not found.
1035func (a *ACL) BindingRuleRead(bindingRuleID string, q *QueryOptions) (*ACLBindingRule, *QueryMeta, error) {
1036	r := a.c.newRequest("GET", "/v1/acl/binding-rule/"+bindingRuleID)
1037	r.setQueryOptions(q)
1038	found, rtt, resp, err := requireNotFoundOrOK(a.c.doRequest(r))
1039	if err != nil {
1040		return nil, nil, err
1041	}
1042	defer resp.Body.Close()
1043
1044	qm := &QueryMeta{}
1045	parseQueryMeta(resp, qm)
1046	qm.RequestTime = rtt
1047
1048	if !found {
1049		return nil, qm, nil
1050	}
1051
1052	var out ACLBindingRule
1053	if err := decodeBody(resp, &out); err != nil {
1054		return nil, nil, err
1055	}
1056
1057	return &out, qm, nil
1058}
1059
1060// BindingRuleList retrieves a listing of all binding rules.
1061func (a *ACL) BindingRuleList(methodName string, q *QueryOptions) ([]*ACLBindingRule, *QueryMeta, error) {
1062	r := a.c.newRequest("GET", "/v1/acl/binding-rules")
1063	if methodName != "" {
1064		r.params.Set("authmethod", methodName)
1065	}
1066	r.setQueryOptions(q)
1067	rtt, resp, err := requireOK(a.c.doRequest(r))
1068	if err != nil {
1069		return nil, nil, err
1070	}
1071	defer resp.Body.Close()
1072
1073	qm := &QueryMeta{}
1074	parseQueryMeta(resp, qm)
1075	qm.RequestTime = rtt
1076
1077	var entries []*ACLBindingRule
1078	if err := decodeBody(resp, &entries); err != nil {
1079		return nil, nil, err
1080	}
1081	return entries, qm, nil
1082}
1083
1084// Login is used to exchange auth method credentials for a newly-minted Consul Token.
1085func (a *ACL) Login(auth *ACLLoginParams, q *WriteOptions) (*ACLToken, *WriteMeta, error) {
1086	r := a.c.newRequest("POST", "/v1/acl/login")
1087	r.setWriteOptions(q)
1088	r.obj = auth
1089
1090	rtt, resp, err := requireOK(a.c.doRequest(r))
1091	if err != nil {
1092		return nil, nil, err
1093	}
1094	defer resp.Body.Close()
1095
1096	wm := &WriteMeta{RequestTime: rtt}
1097	var out ACLToken
1098	if err := decodeBody(resp, &out); err != nil {
1099		return nil, nil, err
1100	}
1101	return &out, wm, nil
1102}
1103
1104// Logout is used to destroy a Consul Token created via Login().
1105func (a *ACL) Logout(q *WriteOptions) (*WriteMeta, error) {
1106	r := a.c.newRequest("POST", "/v1/acl/logout")
1107	r.setWriteOptions(q)
1108	rtt, resp, err := requireOK(a.c.doRequest(r))
1109	if err != nil {
1110		return nil, err
1111	}
1112	resp.Body.Close()
1113
1114	wm := &WriteMeta{RequestTime: rtt}
1115	return wm, nil
1116}
1117