1package 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} 589