blob: 53a052363e29fa13319715d61187af0bfdd30a50 [file] [log] [blame]
William Kurkianea869482019-04-09 15:16:11 -04001package api
2
3import (
4 "fmt"
5 "io"
6 "io/ioutil"
7 "time"
8)
9
10const (
11 // ACLClientType is the client type token
12 ACLClientType = "client"
13
14 // ACLManagementType is the management type token
15 ACLManagementType = "management"
16)
17
18type ACLTokenPolicyLink struct {
19 ID string
20 Name string
21}
22
23// ACLToken represents an ACL Token
24type ACLToken struct {
25 CreateIndex uint64
26 ModifyIndex uint64
27 AccessorID string
28 SecretID string
29 Description string
30 Policies []*ACLTokenPolicyLink
31 Local bool
32 CreateTime time.Time `json:",omitempty"`
33 Hash []byte `json:",omitempty"`
34
35 // DEPRECATED (ACL-Legacy-Compat)
36 // Rules will only be present for legacy tokens returned via the new APIs
37 Rules string `json:",omitempty"`
38}
39
40type ACLTokenListEntry struct {
41 CreateIndex uint64
42 ModifyIndex uint64
43 AccessorID string
44 Description string
45 Policies []*ACLTokenPolicyLink
46 Local bool
47 CreateTime time.Time
48 Hash []byte
49 Legacy bool
50}
51
52// ACLEntry is used to represent a legacy ACL token
53// The legacy tokens are deprecated.
54type ACLEntry struct {
55 CreateIndex uint64
56 ModifyIndex uint64
57 ID string
58 Name string
59 Type string
60 Rules string
61}
62
63// ACLReplicationStatus is used to represent the status of ACL replication.
64type ACLReplicationStatus struct {
65 Enabled bool
66 Running bool
67 SourceDatacenter string
68 ReplicationType string
69 ReplicatedIndex uint64
70 ReplicatedTokenIndex uint64
71 LastSuccess time.Time
72 LastError time.Time
73}
74
75// ACLPolicy represents an ACL Policy.
76type ACLPolicy struct {
77 ID string
78 Name string
79 Description string
80 Rules string
81 Datacenters []string
82 Hash []byte
83 CreateIndex uint64
84 ModifyIndex uint64
85}
86
87type ACLPolicyListEntry struct {
88 ID string
89 Name string
90 Description string
91 Datacenters []string
92 Hash []byte
93 CreateIndex uint64
94 ModifyIndex uint64
95}
96
97// ACL can be used to query the ACL endpoints
98type ACL struct {
99 c *Client
100}
101
102// ACL returns a handle to the ACL endpoints
103func (c *Client) ACL() *ACL {
104 return &ACL{c}
105}
106
107// Bootstrap is used to perform a one-time ACL bootstrap operation on a cluster
108// to get the first management token.
109func (a *ACL) Bootstrap() (*ACLToken, *WriteMeta, error) {
110 r := a.c.newRequest("PUT", "/v1/acl/bootstrap")
111 rtt, resp, err := requireOK(a.c.doRequest(r))
112 if err != nil {
113 return nil, nil, err
114 }
115 defer resp.Body.Close()
116
117 wm := &WriteMeta{RequestTime: rtt}
118 var out ACLToken
119 if err := decodeBody(resp, &out); err != nil {
120 return nil, nil, err
121 }
122 return &out, wm, nil
123}
124
125// Create is used to generate a new token with the given parameters
126//
127// Deprecated: Use TokenCreate instead.
128func (a *ACL) Create(acl *ACLEntry, q *WriteOptions) (string, *WriteMeta, error) {
129 r := a.c.newRequest("PUT", "/v1/acl/create")
130 r.setWriteOptions(q)
131 r.obj = acl
132 rtt, resp, err := requireOK(a.c.doRequest(r))
133 if err != nil {
134 return "", nil, err
135 }
136 defer resp.Body.Close()
137
138 wm := &WriteMeta{RequestTime: rtt}
139 var out struct{ ID string }
140 if err := decodeBody(resp, &out); err != nil {
141 return "", nil, err
142 }
143 return out.ID, wm, nil
144}
145
146// Update is used to update the rules of an existing token
147//
148// Deprecated: Use TokenUpdate instead.
149func (a *ACL) Update(acl *ACLEntry, q *WriteOptions) (*WriteMeta, error) {
150 r := a.c.newRequest("PUT", "/v1/acl/update")
151 r.setWriteOptions(q)
152 r.obj = acl
153 rtt, resp, err := requireOK(a.c.doRequest(r))
154 if err != nil {
155 return nil, err
156 }
157 defer resp.Body.Close()
158
159 wm := &WriteMeta{RequestTime: rtt}
160 return wm, nil
161}
162
163// Destroy is used to destroy a given ACL token ID
164//
165// Deprecated: Use TokenDelete instead.
166func (a *ACL) Destroy(id string, q *WriteOptions) (*WriteMeta, error) {
167 r := a.c.newRequest("PUT", "/v1/acl/destroy/"+id)
168 r.setWriteOptions(q)
169 rtt, resp, err := requireOK(a.c.doRequest(r))
170 if err != nil {
171 return nil, err
172 }
173 resp.Body.Close()
174
175 wm := &WriteMeta{RequestTime: rtt}
176 return wm, nil
177}
178
179// Clone is used to return a new token cloned from an existing one
180//
181// Deprecated: Use TokenClone instead.
182func (a *ACL) Clone(id string, q *WriteOptions) (string, *WriteMeta, error) {
183 r := a.c.newRequest("PUT", "/v1/acl/clone/"+id)
184 r.setWriteOptions(q)
185 rtt, resp, err := requireOK(a.c.doRequest(r))
186 if err != nil {
187 return "", nil, err
188 }
189 defer resp.Body.Close()
190
191 wm := &WriteMeta{RequestTime: rtt}
192 var out struct{ ID string }
193 if err := decodeBody(resp, &out); err != nil {
194 return "", nil, err
195 }
196 return out.ID, wm, nil
197}
198
199// Info is used to query for information about an ACL token
200//
201// Deprecated: Use TokenRead instead.
202func (a *ACL) Info(id string, q *QueryOptions) (*ACLEntry, *QueryMeta, error) {
203 r := a.c.newRequest("GET", "/v1/acl/info/"+id)
204 r.setQueryOptions(q)
205 rtt, resp, err := requireOK(a.c.doRequest(r))
206 if err != nil {
207 return nil, nil, err
208 }
209 defer resp.Body.Close()
210
211 qm := &QueryMeta{}
212 parseQueryMeta(resp, qm)
213 qm.RequestTime = rtt
214
215 var entries []*ACLEntry
216 if err := decodeBody(resp, &entries); err != nil {
217 return nil, nil, err
218 }
219 if len(entries) > 0 {
220 return entries[0], qm, nil
221 }
222 return nil, qm, nil
223}
224
225// List is used to get all the ACL tokens
226//
227// Deprecated: Use TokenList instead.
228func (a *ACL) List(q *QueryOptions) ([]*ACLEntry, *QueryMeta, error) {
229 r := a.c.newRequest("GET", "/v1/acl/list")
230 r.setQueryOptions(q)
231 rtt, resp, err := requireOK(a.c.doRequest(r))
232 if err != nil {
233 return nil, nil, err
234 }
235 defer resp.Body.Close()
236
237 qm := &QueryMeta{}
238 parseQueryMeta(resp, qm)
239 qm.RequestTime = rtt
240
241 var entries []*ACLEntry
242 if err := decodeBody(resp, &entries); err != nil {
243 return nil, nil, err
244 }
245 return entries, qm, nil
246}
247
248// Replication returns the status of the ACL replication process in the datacenter
249func (a *ACL) Replication(q *QueryOptions) (*ACLReplicationStatus, *QueryMeta, error) {
250 r := a.c.newRequest("GET", "/v1/acl/replication")
251 r.setQueryOptions(q)
252 rtt, resp, err := requireOK(a.c.doRequest(r))
253 if err != nil {
254 return nil, nil, err
255 }
256 defer resp.Body.Close()
257
258 qm := &QueryMeta{}
259 parseQueryMeta(resp, qm)
260 qm.RequestTime = rtt
261
262 var entries *ACLReplicationStatus
263 if err := decodeBody(resp, &entries); err != nil {
264 return nil, nil, err
265 }
266 return entries, qm, nil
267}
268
269// TokenCreate creates a new ACL token. It requires that the AccessorID and SecretID fields
270// of the ACLToken structure to be empty as these will be filled in by Consul.
271func (a *ACL) TokenCreate(token *ACLToken, q *WriteOptions) (*ACLToken, *WriteMeta, error) {
272 if token.AccessorID != "" {
273 return nil, nil, fmt.Errorf("Cannot specify an AccessorID in Token Creation")
274 }
275
276 if token.SecretID != "" {
277 return nil, nil, fmt.Errorf("Cannot specify a SecretID in Token Creation")
278 }
279
280 r := a.c.newRequest("PUT", "/v1/acl/token")
281 r.setWriteOptions(q)
282 r.obj = token
283 rtt, resp, err := requireOK(a.c.doRequest(r))
284 if err != nil {
285 return nil, nil, err
286 }
287 defer resp.Body.Close()
288
289 wm := &WriteMeta{RequestTime: rtt}
290 var out ACLToken
291 if err := decodeBody(resp, &out); err != nil {
292 return nil, nil, err
293 }
294
295 return &out, wm, nil
296}
297
298// TokenUpdate updates a token in place without modifying its AccessorID or SecretID. A valid
299// AccessorID must be set in the ACLToken structure passed to this function but the SecretID may
300// be omitted and will be filled in by Consul with its existing value.
301func (a *ACL) TokenUpdate(token *ACLToken, q *WriteOptions) (*ACLToken, *WriteMeta, error) {
302 if token.AccessorID == "" {
303 return nil, nil, fmt.Errorf("Must specify an AccessorID for Token Updating")
304 }
305 r := a.c.newRequest("PUT", "/v1/acl/token/"+token.AccessorID)
306 r.setWriteOptions(q)
307 r.obj = token
308 rtt, resp, err := requireOK(a.c.doRequest(r))
309 if err != nil {
310 return nil, nil, err
311 }
312 defer resp.Body.Close()
313
314 wm := &WriteMeta{RequestTime: rtt}
315 var out ACLToken
316 if err := decodeBody(resp, &out); err != nil {
317 return nil, nil, err
318 }
319
320 return &out, wm, nil
321}
322
323// TokenClone will create a new token with the same policies and locality as the original
324// token but will have its own auto-generated AccessorID and SecretID as well having the
325// description passed to this function. The tokenID parameter must be a valid Accessor ID
326// of an existing token.
327func (a *ACL) TokenClone(tokenID string, description string, q *WriteOptions) (*ACLToken, *WriteMeta, error) {
328 if tokenID == "" {
329 return nil, nil, fmt.Errorf("Must specify a tokenID for Token Cloning")
330 }
331
332 r := a.c.newRequest("PUT", "/v1/acl/token/"+tokenID+"/clone")
333 r.setWriteOptions(q)
334 r.obj = struct{ Description string }{description}
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 wm := &WriteMeta{RequestTime: rtt}
342 var out ACLToken
343 if err := decodeBody(resp, &out); err != nil {
344 return nil, nil, err
345 }
346
347 return &out, wm, nil
348}
349
350// TokenDelete removes a single ACL token. The tokenID parameter must be a valid
351// Accessor ID of an existing token.
352func (a *ACL) TokenDelete(tokenID string, q *WriteOptions) (*WriteMeta, error) {
353 r := a.c.newRequest("DELETE", "/v1/acl/token/"+tokenID)
354 r.setWriteOptions(q)
355 rtt, resp, err := requireOK(a.c.doRequest(r))
356 if err != nil {
357 return nil, err
358 }
359 resp.Body.Close()
360
361 wm := &WriteMeta{RequestTime: rtt}
362 return wm, nil
363}
364
365// TokenRead retrieves the full token details. The tokenID parameter must be a valid
366// Accessor ID of an existing token.
367func (a *ACL) TokenRead(tokenID string, q *QueryOptions) (*ACLToken, *QueryMeta, error) {
368 r := a.c.newRequest("GET", "/v1/acl/token/"+tokenID)
369 r.setQueryOptions(q)
370 rtt, resp, err := requireOK(a.c.doRequest(r))
371 if err != nil {
372 return nil, nil, err
373 }
374 defer resp.Body.Close()
375
376 qm := &QueryMeta{}
377 parseQueryMeta(resp, qm)
378 qm.RequestTime = rtt
379
380 var out ACLToken
381 if err := decodeBody(resp, &out); err != nil {
382 return nil, nil, err
383 }
384
385 return &out, qm, nil
386}
387
388// TokenReadSelf retrieves the full token details of the token currently
389// assigned to the API Client. In this manner its possible to read a token
390// by its Secret ID.
391func (a *ACL) TokenReadSelf(q *QueryOptions) (*ACLToken, *QueryMeta, error) {
392 r := a.c.newRequest("GET", "/v1/acl/token/self")
393 r.setQueryOptions(q)
394 rtt, resp, err := requireOK(a.c.doRequest(r))
395 if err != nil {
396 return nil, nil, err
397 }
398 defer resp.Body.Close()
399
400 qm := &QueryMeta{}
401 parseQueryMeta(resp, qm)
402 qm.RequestTime = rtt
403
404 var out ACLToken
405 if err := decodeBody(resp, &out); err != nil {
406 return nil, nil, err
407 }
408
409 return &out, qm, nil
410}
411
412// TokenList lists all tokens. The listing does not contain any SecretIDs as those
413// may only be retrieved by a call to TokenRead.
414func (a *ACL) TokenList(q *QueryOptions) ([]*ACLTokenListEntry, *QueryMeta, error) {
415 r := a.c.newRequest("GET", "/v1/acl/tokens")
416 r.setQueryOptions(q)
417 rtt, resp, err := requireOK(a.c.doRequest(r))
418 if err != nil {
419 return nil, nil, err
420 }
421 defer resp.Body.Close()
422
423 qm := &QueryMeta{}
424 parseQueryMeta(resp, qm)
425 qm.RequestTime = rtt
426
427 var entries []*ACLTokenListEntry
428 if err := decodeBody(resp, &entries); err != nil {
429 return nil, nil, err
430 }
431 return entries, qm, nil
432}
433
434// PolicyCreate will create a new policy. It is not allowed for the policy parameters
435// ID field to be set as this will be generated by Consul while processing the request.
436func (a *ACL) PolicyCreate(policy *ACLPolicy, q *WriteOptions) (*ACLPolicy, *WriteMeta, error) {
437 if policy.ID != "" {
438 return nil, nil, fmt.Errorf("Cannot specify an ID in Policy Creation")
439 }
440
441 r := a.c.newRequest("PUT", "/v1/acl/policy")
442 r.setWriteOptions(q)
443 r.obj = policy
444 rtt, resp, err := requireOK(a.c.doRequest(r))
445 if err != nil {
446 return nil, nil, err
447 }
448 defer resp.Body.Close()
449
450 wm := &WriteMeta{RequestTime: rtt}
451 var out ACLPolicy
452 if err := decodeBody(resp, &out); err != nil {
453 return nil, nil, err
454 }
455
456 return &out, wm, nil
457}
458
459// PolicyUpdate updates a policy. The ID field of the policy parameter must be set to an
460// existing policy ID
461func (a *ACL) PolicyUpdate(policy *ACLPolicy, q *WriteOptions) (*ACLPolicy, *WriteMeta, error) {
462 if policy.ID == "" {
463 return nil, nil, fmt.Errorf("Must specify an ID in Policy Creation")
464 }
465
466 r := a.c.newRequest("PUT", "/v1/acl/policy/"+policy.ID)
467 r.setWriteOptions(q)
468 r.obj = policy
469 rtt, resp, err := requireOK(a.c.doRequest(r))
470 if err != nil {
471 return nil, nil, err
472 }
473 defer resp.Body.Close()
474
475 wm := &WriteMeta{RequestTime: rtt}
476 var out ACLPolicy
477 if err := decodeBody(resp, &out); err != nil {
478 return nil, nil, err
479 }
480
481 return &out, wm, nil
482}
483
484// PolicyDelete deletes a policy given its ID.
485func (a *ACL) PolicyDelete(policyID string, q *WriteOptions) (*WriteMeta, error) {
486 r := a.c.newRequest("DELETE", "/v1/acl/policy/"+policyID)
487 r.setWriteOptions(q)
488 rtt, resp, err := requireOK(a.c.doRequest(r))
489 if err != nil {
490 return nil, err
491 }
492 resp.Body.Close()
493
494 wm := &WriteMeta{RequestTime: rtt}
495 return wm, nil
496}
497
498// PolicyRead retrieves the policy details including the rule set.
499func (a *ACL) PolicyRead(policyID string, q *QueryOptions) (*ACLPolicy, *QueryMeta, error) {
500 r := a.c.newRequest("GET", "/v1/acl/policy/"+policyID)
501 r.setQueryOptions(q)
502 rtt, resp, err := requireOK(a.c.doRequest(r))
503 if err != nil {
504 return nil, nil, err
505 }
506 defer resp.Body.Close()
507
508 qm := &QueryMeta{}
509 parseQueryMeta(resp, qm)
510 qm.RequestTime = rtt
511
512 var out ACLPolicy
513 if err := decodeBody(resp, &out); err != nil {
514 return nil, nil, err
515 }
516
517 return &out, qm, nil
518}
519
520// PolicyList retrieves a listing of all policies. The listing does not include the
521// rules for any policy as those should be retrieved by subsequent calls to PolicyRead.
522func (a *ACL) PolicyList(q *QueryOptions) ([]*ACLPolicyListEntry, *QueryMeta, error) {
523 r := a.c.newRequest("GET", "/v1/acl/policies")
524 r.setQueryOptions(q)
525 rtt, resp, err := requireOK(a.c.doRequest(r))
526 if err != nil {
527 return nil, nil, err
528 }
529 defer resp.Body.Close()
530
531 qm := &QueryMeta{}
532 parseQueryMeta(resp, qm)
533 qm.RequestTime = rtt
534
535 var entries []*ACLPolicyListEntry
536 if err := decodeBody(resp, &entries); err != nil {
537 return nil, nil, err
538 }
539 return entries, qm, nil
540}
541
542// RulesTranslate translates the legacy rule syntax into the current syntax.
543//
544// Deprecated: Support for the legacy syntax translation will be removed
545// when legacy ACL support is removed.
546func (a *ACL) RulesTranslate(rules io.Reader) (string, error) {
547 r := a.c.newRequest("POST", "/v1/acl/rules/translate")
548 r.body = rules
549 rtt, resp, err := requireOK(a.c.doRequest(r))
550 if err != nil {
551 return "", err
552 }
553 defer resp.Body.Close()
554 qm := &QueryMeta{}
555 parseQueryMeta(resp, qm)
556 qm.RequestTime = rtt
557
558 ruleBytes, err := ioutil.ReadAll(resp.Body)
559 if err != nil {
560 return "", fmt.Errorf("Failed to read translated rule body: %v", err)
561 }
562
563 return string(ruleBytes), nil
564}
565
566// RulesTranslateToken translates the rules associated with the legacy syntax
567// into the current syntax and returns the results.
568//
569// Deprecated: Support for the legacy syntax translation will be removed
570// when legacy ACL support is removed.
571func (a *ACL) RulesTranslateToken(tokenID string) (string, error) {
572 r := a.c.newRequest("GET", "/v1/acl/rules/translate/"+tokenID)
573 rtt, resp, err := requireOK(a.c.doRequest(r))
574 if err != nil {
575 return "", err
576 }
577 defer resp.Body.Close()
578 qm := &QueryMeta{}
579 parseQueryMeta(resp, qm)
580 qm.RequestTime = rtt
581
582 ruleBytes, err := ioutil.ReadAll(resp.Body)
583 if err != nil {
584 return "", fmt.Errorf("Failed to read translated rule body: %v", err)
585 }
586
587 return string(ruleBytes), nil
588}