github.imxd.top/hashicorp/consul@v1.4.5/agent/consul/state/acl.go (about) 1 package state 2 3 import ( 4 "fmt" 5 6 "github.com/hashicorp/consul/agent/structs" 7 "github.com/hashicorp/go-memdb" 8 ) 9 10 type TokenPoliciesIndex struct { 11 } 12 13 func (s *TokenPoliciesIndex) FromObject(obj interface{}) (bool, [][]byte, error) { 14 token, ok := obj.(*structs.ACLToken) 15 if !ok { 16 return false, nil, fmt.Errorf("object is not an ACLToken") 17 } 18 19 links := token.Policies 20 21 numLinks := len(links) 22 if numLinks == 0 { 23 return false, nil, nil 24 } 25 26 vals := make([][]byte, 0, numLinks) 27 for _, link := range links { 28 vals = append(vals, []byte(link.ID+"\x00")) 29 } 30 31 return true, vals, nil 32 } 33 34 func (s *TokenPoliciesIndex) FromArgs(args ...interface{}) ([]byte, error) { 35 if len(args) != 1 { 36 return nil, fmt.Errorf("must provide only a single argument") 37 } 38 arg, ok := args[0].(string) 39 if !ok { 40 return nil, fmt.Errorf("argument must be a string: %#v", args[0]) 41 } 42 // Add the null character as a terminator 43 arg += "\x00" 44 return []byte(arg), nil 45 } 46 47 func (s *TokenPoliciesIndex) PrefixFromArgs(args ...interface{}) ([]byte, error) { 48 val, err := s.FromArgs(args...) 49 if err != nil { 50 return nil, err 51 } 52 53 // Strip the null terminator, the rest is a prefix 54 n := len(val) 55 if n > 0 { 56 return val[:n-1], nil 57 } 58 return val, nil 59 } 60 61 func tokensTableSchema() *memdb.TableSchema { 62 return &memdb.TableSchema{ 63 Name: "acl-tokens", 64 Indexes: map[string]*memdb.IndexSchema{ 65 "accessor": &memdb.IndexSchema{ 66 Name: "accessor", 67 // DEPRECATED (ACL-Legacy-Compat) - we should not AllowMissing here once legacy compat is removed 68 AllowMissing: true, 69 Unique: true, 70 Indexer: &memdb.UUIDFieldIndex{ 71 Field: "AccessorID", 72 }, 73 }, 74 "id": &memdb.IndexSchema{ 75 Name: "id", 76 AllowMissing: false, 77 Unique: true, 78 Indexer: &memdb.StringFieldIndex{ 79 Field: "SecretID", 80 Lowercase: false, 81 }, 82 }, 83 "policies": &memdb.IndexSchema{ 84 Name: "policies", 85 // Need to allow missing for the anonymous token 86 AllowMissing: true, 87 Unique: false, 88 Indexer: &TokenPoliciesIndex{}, 89 }, 90 "local": &memdb.IndexSchema{ 91 Name: "local", 92 AllowMissing: false, 93 Unique: false, 94 Indexer: &memdb.ConditionalIndex{ 95 Conditional: func(obj interface{}) (bool, error) { 96 if token, ok := obj.(*structs.ACLToken); ok { 97 return token.Local, nil 98 } 99 return false, nil 100 }, 101 }, 102 }, 103 104 //DEPRECATED (ACL-Legacy-Compat) - This index is only needed while we support upgrading v1 to v2 acls 105 // This table indexes all the ACL tokens that do not have an AccessorID 106 "needs-upgrade": &memdb.IndexSchema{ 107 Name: "needs-upgrade", 108 AllowMissing: false, 109 Unique: false, 110 Indexer: &memdb.ConditionalIndex{ 111 Conditional: func(obj interface{}) (bool, error) { 112 if token, ok := obj.(*structs.ACLToken); ok { 113 return token.AccessorID == "", nil 114 } 115 return false, nil 116 }, 117 }, 118 }, 119 }, 120 } 121 } 122 123 func policiesTableSchema() *memdb.TableSchema { 124 return &memdb.TableSchema{ 125 Name: "acl-policies", 126 Indexes: map[string]*memdb.IndexSchema{ 127 "id": &memdb.IndexSchema{ 128 Name: "id", 129 AllowMissing: false, 130 Unique: true, 131 Indexer: &memdb.UUIDFieldIndex{ 132 Field: "ID", 133 }, 134 }, 135 "name": &memdb.IndexSchema{ 136 Name: "name", 137 AllowMissing: false, 138 Unique: true, 139 Indexer: &memdb.StringFieldIndex{ 140 Field: "Name", 141 // TODO (ACL-V2) - should we coerce to lowercase? 142 Lowercase: true, 143 }, 144 }, 145 }, 146 } 147 } 148 149 func init() { 150 registerSchema(tokensTableSchema) 151 registerSchema(policiesTableSchema) 152 } 153 154 // ACLTokens is used when saving a snapshot 155 func (s *Snapshot) ACLTokens() (memdb.ResultIterator, error) { 156 // DEPRECATED (ACL-Legacy-Compat) - This could use the "id" index when we remove v1 compat 157 iter, err := s.tx.Get("acl-tokens", "id") 158 if err != nil { 159 return nil, err 160 } 161 return iter, nil 162 } 163 164 // ACLToken is used when restoring from a snapshot. For general inserts, use ACL. 165 func (s *Restore) ACLToken(token *structs.ACLToken) error { 166 if err := s.tx.Insert("acl-tokens", token); err != nil { 167 return fmt.Errorf("failed restoring acl token: %s", err) 168 } 169 170 if err := indexUpdateMaxTxn(s.tx, token.ModifyIndex, "acl-tokens"); err != nil { 171 return fmt.Errorf("failed updating index: %s", err) 172 } 173 return nil 174 } 175 176 // ACLPolicies is used when saving a snapshot 177 func (s *Snapshot) ACLPolicies() (memdb.ResultIterator, error) { 178 iter, err := s.tx.Get("acl-policies", "id") 179 if err != nil { 180 return nil, err 181 } 182 return iter, nil 183 } 184 185 func (s *Restore) ACLPolicy(policy *structs.ACLPolicy) error { 186 if err := s.tx.Insert("acl-policies", policy); err != nil { 187 return fmt.Errorf("failed restoring acl policy: %s", err) 188 } 189 190 if err := indexUpdateMaxTxn(s.tx, policy.ModifyIndex, "acl-policies"); err != nil { 191 return fmt.Errorf("failed updating index: %s", err) 192 } 193 return nil 194 } 195 196 // ACLBootstrap is used to perform a one-time ACL bootstrap operation on a 197 // cluster to get the first management token. 198 func (s *Store) ACLBootstrap(idx, resetIndex uint64, token *structs.ACLToken, legacy bool) error { 199 tx := s.db.Txn(true) 200 defer tx.Abort() 201 202 // We must have initialized before this will ever be possible. 203 existing, err := tx.First("index", "id", "acl-token-bootstrap") 204 if err != nil { 205 return fmt.Errorf("bootstrap check failed: %v", err) 206 } 207 if existing != nil { 208 if resetIndex == 0 { 209 return structs.ACLBootstrapNotAllowedErr 210 } else if resetIndex != existing.(*IndexEntry).Value { 211 return structs.ACLBootstrapInvalidResetIndexErr 212 } 213 } 214 215 if err := s.aclTokenSetTxn(tx, idx, token, false, false, legacy); err != nil { 216 return fmt.Errorf("failed inserting bootstrap token: %v", err) 217 } 218 if err := indexUpdateMaxTxn(tx, idx, "acl-tokens"); err != nil { 219 return fmt.Errorf("failed updating index: %s", err) 220 } 221 if err := tx.Insert("index", &IndexEntry{"acl-token-bootstrap", idx}); err != nil { 222 return fmt.Errorf("failed to mark ACL bootstrapping as complete: %v", err) 223 } 224 tx.Commit() 225 return nil 226 } 227 228 // CanBootstrapACLToken checks if bootstrapping is possible and returns the reset index 229 func (s *Store) CanBootstrapACLToken() (bool, uint64, error) { 230 txn := s.db.Txn(false) 231 232 // Lookup the bootstrap sentinel 233 out, err := txn.First("index", "id", "acl-token-bootstrap") 234 if err != nil { 235 return false, 0, err 236 } 237 238 // No entry, we haven't bootstrapped yet 239 if out == nil { 240 return true, 0, nil 241 } 242 243 // Return the reset index if we've already bootstrapped 244 return false, out.(*IndexEntry).Value, nil 245 } 246 247 func (s *Store) resolveTokenPolicyLinks(tx *memdb.Txn, token *structs.ACLToken, allowMissing bool) error { 248 for linkIndex, link := range token.Policies { 249 if link.ID != "" { 250 policy, err := s.getPolicyWithTxn(tx, nil, link.ID, "id") 251 252 if err != nil { 253 return err 254 } 255 256 if policy != nil { 257 // the name doesn't matter here 258 token.Policies[linkIndex].Name = policy.Name 259 } else if !allowMissing { 260 return fmt.Errorf("No such policy with ID: %s", link.ID) 261 } 262 } else { 263 return fmt.Errorf("Encountered a Token with policies linked by Name in the state store") 264 } 265 } 266 return nil 267 } 268 269 // fixupTokenPolicyLinks is to be used when retrieving tokens from memdb. The policy links could have gotten 270 // stale when a linked policy was deleted or renamed. This will correct them and generate a newly allocated 271 // token only when fixes are needed. If the policy links are still accurate then we just return the original 272 // token. 273 func (s *Store) fixupTokenPolicyLinks(tx *memdb.Txn, original *structs.ACLToken) (*structs.ACLToken, error) { 274 owned := false 275 token := original 276 277 cloneToken := func(t *structs.ACLToken, copyNumLinks int) *structs.ACLToken { 278 clone := *t 279 clone.Policies = make([]structs.ACLTokenPolicyLink, copyNumLinks) 280 copy(clone.Policies, t.Policies[:copyNumLinks]) 281 return &clone 282 } 283 284 for linkIndex, link := range original.Policies { 285 if link.ID == "" { 286 return nil, fmt.Errorf("Detected corrupted token within the state store - missing policy link ID") 287 } 288 289 policy, err := s.getPolicyWithTxn(tx, nil, link.ID, "id") 290 291 if err != nil { 292 return nil, err 293 } 294 295 if policy == nil { 296 if !owned { 297 // clone the token as we cannot touch the original 298 token = cloneToken(original, linkIndex) 299 owned = true 300 } 301 // if already owned then we just don't append it. 302 } else if policy.Name != link.Name { 303 if !owned { 304 token = cloneToken(original, linkIndex) 305 owned = true 306 } 307 308 // append the corrected policy 309 token.Policies = append(token.Policies, structs.ACLTokenPolicyLink{ID: link.ID, Name: policy.Name}) 310 } else if owned { 311 token.Policies = append(token.Policies, link) 312 } 313 } 314 315 return token, nil 316 } 317 318 // ACLTokenSet is used to insert an ACL rule into the state store. 319 func (s *Store) ACLTokenSet(idx uint64, token *structs.ACLToken, legacy bool) error { 320 tx := s.db.Txn(true) 321 defer tx.Abort() 322 323 // Call set on the ACL 324 if err := s.aclTokenSetTxn(tx, idx, token, false, false, legacy); err != nil { 325 return err 326 } 327 328 if err := indexUpdateMaxTxn(tx, idx, "acl-tokens"); err != nil { 329 return fmt.Errorf("failed updating index: %s", err) 330 } 331 332 tx.Commit() 333 return nil 334 } 335 336 func (s *Store) ACLTokenBatchSet(idx uint64, tokens structs.ACLTokens, cas bool) error { 337 tx := s.db.Txn(true) 338 defer tx.Abort() 339 340 for _, token := range tokens { 341 // this is only used when doing batch insertions for upgrades and replication. Therefore 342 // we take whatever those said. 343 if err := s.aclTokenSetTxn(tx, idx, token, cas, true, false); err != nil { 344 return err 345 } 346 } 347 348 if err := indexUpdateMaxTxn(tx, idx, "acl-tokens"); err != nil { 349 return fmt.Errorf("failed updating index: %s", err) 350 } 351 352 tx.Commit() 353 return nil 354 } 355 356 // aclTokenSetTxn is the inner method used to insert an ACL token with the 357 // proper indexes into the state store. 358 func (s *Store) aclTokenSetTxn(tx *memdb.Txn, idx uint64, token *structs.ACLToken, cas, allowMissingPolicyIDs, legacy bool) error { 359 // Check that the ID is set 360 if token.SecretID == "" { 361 return ErrMissingACLTokenSecret 362 } 363 364 if !legacy && token.AccessorID == "" { 365 return ErrMissingACLTokenAccessor 366 } 367 368 // DEPRECATED (ACL-Legacy-Compat) 369 if token.Rules != "" { 370 // When we update a legacy acl token we may have to correct old HCL to 371 // prevent the propagation of older syntax into the state store and 372 // into in-memory representations. 373 correctedRules := structs.SanitizeLegacyACLTokenRules(token.Rules) 374 if correctedRules != "" { 375 token.Rules = correctedRules 376 } 377 } 378 379 // Check for an existing ACL 380 // DEPRECATED (ACL-Legacy-Compat) - transition to using accessor index instead of secret once v1 compat is removed 381 existing, err := tx.First("acl-tokens", "id", token.SecretID) 382 if err != nil { 383 return fmt.Errorf("failed token lookup: %s", err) 384 } 385 386 var original *structs.ACLToken 387 388 if existing != nil { 389 original = existing.(*structs.ACLToken) 390 } 391 392 if cas { 393 // set-if-unset case 394 if token.ModifyIndex == 0 && original != nil { 395 return nil 396 } 397 // token already deleted 398 if token.ModifyIndex != 0 && original == nil { 399 return nil 400 } 401 // check for other modifications 402 if token.ModifyIndex != 0 && token.ModifyIndex != original.ModifyIndex { 403 return nil 404 } 405 } 406 407 if legacy && original != nil { 408 if len(original.Policies) > 0 || original.Type == "" { 409 return fmt.Errorf("failed inserting acl token: cannot use legacy endpoint to modify a non-legacy token") 410 } 411 412 token.AccessorID = original.AccessorID 413 } 414 415 if err := s.resolveTokenPolicyLinks(tx, token, allowMissingPolicyIDs); err != nil { 416 return err 417 } 418 419 // Set the indexes 420 if original != nil { 421 if original.AccessorID != "" && token.AccessorID != original.AccessorID { 422 return fmt.Errorf("The ACL Token AccessorID field is immutable") 423 } 424 425 if token.SecretID != original.SecretID { 426 return fmt.Errorf("The ACL Token SecretID field is immutable") 427 } 428 429 token.CreateIndex = original.CreateIndex 430 token.ModifyIndex = idx 431 } else { 432 token.CreateIndex = idx 433 token.ModifyIndex = idx 434 } 435 436 // Insert the ACL 437 if err := tx.Insert("acl-tokens", token); err != nil { 438 return fmt.Errorf("failed inserting acl token: %v", err) 439 } 440 441 return nil 442 } 443 444 // ACLTokenGetBySecret is used to look up an existing ACL token by its SecretID. 445 func (s *Store) ACLTokenGetBySecret(ws memdb.WatchSet, secret string) (uint64, *structs.ACLToken, error) { 446 return s.aclTokenGet(ws, secret, "id") 447 } 448 449 // ACLTokenGetByAccessor is used to look up an existing ACL token by its AccessorID. 450 func (s *Store) ACLTokenGetByAccessor(ws memdb.WatchSet, accessor string) (uint64, *structs.ACLToken, error) { 451 return s.aclTokenGet(ws, accessor, "accessor") 452 } 453 454 // aclTokenGet looks up a token using one of the indexes provided 455 func (s *Store) aclTokenGet(ws memdb.WatchSet, value, index string) (uint64, *structs.ACLToken, error) { 456 tx := s.db.Txn(false) 457 defer tx.Abort() 458 459 token, err := s.aclTokenGetTxn(tx, ws, value, index) 460 if err != nil { 461 return 0, nil, fmt.Errorf("failed acl token lookup: %v", err) 462 } 463 464 idx := maxIndexTxn(tx, "acl-tokens") 465 return idx, token, nil 466 } 467 468 func (s *Store) ACLTokenBatchGet(ws memdb.WatchSet, accessors []string) (uint64, structs.ACLTokens, error) { 469 tx := s.db.Txn(false) 470 defer tx.Abort() 471 472 tokens := make(structs.ACLTokens, 0) 473 for _, accessor := range accessors { 474 token, err := s.aclTokenGetTxn(tx, ws, accessor, "accessor") 475 if err != nil { 476 return 0, nil, fmt.Errorf("failed acl token lookup: %v", err) 477 } 478 479 // token == nil is valid and will indic 480 if token != nil { 481 tokens = append(tokens, token) 482 } 483 } 484 485 idx := maxIndexTxn(tx, "acl-tokens") 486 487 return idx, tokens, nil 488 } 489 490 func (s *Store) aclTokenGetTxn(tx *memdb.Txn, ws memdb.WatchSet, value, index string) (*structs.ACLToken, error) { 491 watchCh, rawToken, err := tx.FirstWatch("acl-tokens", index, value) 492 if err != nil { 493 return nil, fmt.Errorf("failed acl token lookup: %v", err) 494 } 495 ws.Add(watchCh) 496 497 if rawToken != nil { 498 token, err := s.fixupTokenPolicyLinks(tx, rawToken.(*structs.ACLToken)) 499 if err != nil { 500 return nil, err 501 } 502 return token, nil 503 } 504 505 return nil, nil 506 } 507 508 // ACLTokenList is used to list out all of the ACLs in the state store. 509 func (s *Store) ACLTokenList(ws memdb.WatchSet, local, global bool, policy string) (uint64, structs.ACLTokens, error) { 510 tx := s.db.Txn(false) 511 defer tx.Abort() 512 513 var iter memdb.ResultIterator 514 var err error 515 516 // Note global == local works when both are true or false. It is not valid to set both 517 // to false but for defaulted structs (zero values for both) we want it to list out 518 // all tokens so our checks just ensure that global == local 519 520 if policy != "" { 521 iter, err = tx.Get("acl-tokens", "policies", policy) 522 if err == nil && global != local { 523 iter = memdb.NewFilterIterator(iter, func(raw interface{}) bool { 524 token, ok := raw.(*structs.ACLToken) 525 if !ok { 526 return false 527 } 528 529 if global && !token.Local { 530 return true 531 } else if local && token.Local { 532 return true 533 } 534 535 return false 536 }) 537 } 538 } else if global == local { 539 iter, err = tx.Get("acl-tokens", "id") 540 } else if global { 541 iter, err = tx.Get("acl-tokens", "local", false) 542 } else { 543 iter, err = tx.Get("acl-tokens", "local", true) 544 } 545 546 if err != nil { 547 return 0, nil, fmt.Errorf("failed acl token lookup: %v", err) 548 } 549 ws.Add(iter.WatchCh()) 550 551 var result structs.ACLTokens 552 for raw := iter.Next(); raw != nil; raw = iter.Next() { 553 token, err := s.fixupTokenPolicyLinks(tx, raw.(*structs.ACLToken)) 554 555 if err != nil { 556 return 0, nil, err 557 } 558 result = append(result, token) 559 } 560 561 // Get the table index. 562 idx := maxIndexTxn(tx, "acl-tokens") 563 564 return idx, result, nil 565 } 566 567 func (s *Store) ACLTokenListUpgradeable(max int) (structs.ACLTokens, <-chan struct{}, error) { 568 tx := s.db.Txn(false) 569 defer tx.Abort() 570 571 iter, err := tx.Get("acl-tokens", "needs-upgrade", true) 572 if err != nil { 573 return nil, nil, fmt.Errorf("failed acl token listing: %v", err) 574 } 575 576 var tokens structs.ACLTokens 577 i := 0 578 for token := iter.Next(); token != nil; token = iter.Next() { 579 tokens = append(tokens, token.(*structs.ACLToken)) 580 i += 1 581 if i >= max { 582 return tokens, nil, nil 583 } 584 } 585 586 return tokens, iter.WatchCh(), nil 587 } 588 589 // ACLTokenDeleteBySecret is used to remove an existing ACL from the state store. If 590 // the ACL does not exist this is a no-op and no error is returned. 591 func (s *Store) ACLTokenDeleteBySecret(idx uint64, secret string) error { 592 return s.aclTokenDelete(idx, secret, "id") 593 } 594 595 // ACLTokenDeleteByAccessor is used to remove an existing ACL from the state store. If 596 // the ACL does not exist this is a no-op and no error is returned. 597 func (s *Store) ACLTokenDeleteByAccessor(idx uint64, accessor string) error { 598 return s.aclTokenDelete(idx, accessor, "accessor") 599 } 600 601 func (s *Store) ACLTokenBatchDelete(idx uint64, tokenIDs []string) error { 602 tx := s.db.Txn(true) 603 defer tx.Abort() 604 605 for _, tokenID := range tokenIDs { 606 if err := s.aclTokenDeleteTxn(tx, idx, tokenID, "accessor"); err != nil { 607 return err 608 } 609 } 610 611 tx.Commit() 612 return nil 613 } 614 615 func (s *Store) aclTokenDelete(idx uint64, value, index string) error { 616 tx := s.db.Txn(true) 617 defer tx.Abort() 618 619 if err := s.aclTokenDeleteTxn(tx, idx, value, index); err != nil { 620 return err 621 } 622 623 tx.Commit() 624 return nil 625 } 626 627 func (s *Store) aclTokenDeleteTxn(tx *memdb.Txn, idx uint64, value, index string) error { 628 // Look up the existing token 629 token, err := tx.First("acl-tokens", index, value) 630 if err != nil { 631 return fmt.Errorf("failed acl token lookup: %v", err) 632 } 633 634 if token == nil { 635 return nil 636 } 637 638 if token.(*structs.ACLToken).AccessorID == structs.ACLTokenAnonymousID { 639 return fmt.Errorf("Deletion of the builtin anonymous token is not permitted") 640 } 641 642 if err := tx.Delete("acl-tokens", token); err != nil { 643 return fmt.Errorf("failed deleting acl token: %v", err) 644 } 645 if err := indexUpdateMaxTxn(tx, idx, "acl-tokens"); err != nil { 646 return fmt.Errorf("failed updating index: %v", err) 647 } 648 return nil 649 } 650 651 func (s *Store) ACLPolicyBatchSet(idx uint64, policies structs.ACLPolicies) error { 652 tx := s.db.Txn(true) 653 defer tx.Abort() 654 655 for _, policy := range policies { 656 if err := s.aclPolicySetTxn(tx, idx, policy); err != nil { 657 return err 658 } 659 } 660 661 if err := indexUpdateMaxTxn(tx, idx, "acl-policies"); err != nil { 662 return fmt.Errorf("failed updating index: %s", err) 663 } 664 665 tx.Commit() 666 return nil 667 } 668 669 func (s *Store) ACLPolicySet(idx uint64, policy *structs.ACLPolicy) error { 670 tx := s.db.Txn(true) 671 defer tx.Abort() 672 673 if err := s.aclPolicySetTxn(tx, idx, policy); err != nil { 674 return err 675 } 676 if err := indexUpdateMaxTxn(tx, idx, "acl-policies"); err != nil { 677 return fmt.Errorf("failed updating index: %s", err) 678 } 679 680 tx.Commit() 681 return nil 682 } 683 684 func (s *Store) aclPolicySetTxn(tx *memdb.Txn, idx uint64, policy *structs.ACLPolicy) error { 685 // Check that the ID is set 686 if policy.ID == "" { 687 return ErrMissingACLPolicyID 688 } 689 690 if policy.Name == "" { 691 return ErrMissingACLPolicyName 692 } 693 694 existing, err := tx.First("acl-policies", "id", policy.ID) 695 if err != nil { 696 return fmt.Errorf("failed acl policy lookup: %v", err) 697 } 698 699 if existing != nil { 700 policyMatch := existing.(*structs.ACLPolicy) 701 702 if policy.ID == structs.ACLPolicyGlobalManagementID { 703 // Only the name and description are modifiable 704 if policy.Rules != policyMatch.Rules { 705 return fmt.Errorf("Changing the Rules for the builtin global-management policy is not permitted") 706 } 707 708 if policy.Datacenters != nil && len(policy.Datacenters) != 0 { 709 return fmt.Errorf("Changing the Datacenters of the builtin global-management policy is not permitted") 710 } 711 } 712 } 713 714 // ensure the name is unique (cannot conflict with another policy with a different ID) 715 nameMatch, err := tx.First("acl-policies", "name", policy.Name) 716 if err != nil { 717 return fmt.Errorf("failed acl policy lookup: %v", err) 718 } 719 if nameMatch != nil && policy.ID != nameMatch.(*structs.ACLPolicy).ID { 720 return fmt.Errorf("A policy with name %q already exists", policy.Name) 721 } 722 723 // Set the indexes 724 if existing != nil { 725 policy.CreateIndex = existing.(*structs.ACLPolicy).CreateIndex 726 policy.ModifyIndex = idx 727 } else { 728 policy.CreateIndex = idx 729 policy.ModifyIndex = idx 730 } 731 732 // Insert the ACL 733 if err := tx.Insert("acl-policies", policy); err != nil { 734 return fmt.Errorf("failed inserting acl policy: %v", err) 735 } 736 return nil 737 } 738 739 func (s *Store) ACLPolicyGetByID(ws memdb.WatchSet, id string) (uint64, *structs.ACLPolicy, error) { 740 return s.aclPolicyGet(ws, id, "id") 741 } 742 743 func (s *Store) ACLPolicyGetByName(ws memdb.WatchSet, name string) (uint64, *structs.ACLPolicy, error) { 744 return s.aclPolicyGet(ws, name, "name") 745 } 746 747 func (s *Store) ACLPolicyBatchGet(ws memdb.WatchSet, ids []string) (uint64, structs.ACLPolicies, error) { 748 tx := s.db.Txn(false) 749 defer tx.Abort() 750 751 policies := make(structs.ACLPolicies, 0) 752 for _, pid := range ids { 753 policy, err := s.getPolicyWithTxn(tx, ws, pid, "id") 754 if err != nil { 755 return 0, nil, err 756 } 757 758 if policy != nil { 759 policies = append(policies, policy) 760 } 761 } 762 763 idx := maxIndexTxn(tx, "acl-policies") 764 765 return idx, policies, nil 766 } 767 768 func (s *Store) getPolicyWithTxn(tx *memdb.Txn, ws memdb.WatchSet, value, index string) (*structs.ACLPolicy, error) { 769 watchCh, policy, err := tx.FirstWatch("acl-policies", index, value) 770 if err != nil { 771 return nil, fmt.Errorf("failed acl policy lookup: %v", err) 772 } 773 ws.Add(watchCh) 774 775 if err != nil || policy == nil { 776 return nil, err 777 } 778 779 return policy.(*structs.ACLPolicy), nil 780 } 781 782 func (s *Store) aclPolicyGet(ws memdb.WatchSet, value, index string) (uint64, *structs.ACLPolicy, error) { 783 tx := s.db.Txn(false) 784 defer tx.Abort() 785 786 policy, err := s.getPolicyWithTxn(tx, ws, value, index) 787 if err != nil { 788 return 0, nil, err 789 } 790 791 idx := maxIndexTxn(tx, "acl-policies") 792 793 return idx, policy, nil 794 } 795 796 func (s *Store) ACLPolicyList(ws memdb.WatchSet) (uint64, structs.ACLPolicies, error) { 797 tx := s.db.Txn(false) 798 defer tx.Abort() 799 800 iter, err := tx.Get("acl-policies", "id") 801 if err != nil { 802 return 0, nil, fmt.Errorf("failed acl policy lookup: %v", err) 803 } 804 ws.Add(iter.WatchCh()) 805 806 var result structs.ACLPolicies 807 for policy := iter.Next(); policy != nil; policy = iter.Next() { 808 result = append(result, policy.(*structs.ACLPolicy)) 809 } 810 811 // Get the table index. 812 idx := maxIndexTxn(tx, "acl-policies") 813 814 return idx, result, nil 815 } 816 817 func (s *Store) ACLPolicyDeleteByID(idx uint64, id string) error { 818 return s.aclPolicyDelete(idx, id, "id") 819 } 820 821 func (s *Store) ACLPolicyDeleteByName(idx uint64, name string) error { 822 return s.aclPolicyDelete(idx, name, "name") 823 } 824 825 func (s *Store) ACLPolicyBatchDelete(idx uint64, policyIDs []string) error { 826 tx := s.db.Txn(true) 827 defer tx.Abort() 828 829 for _, policyID := range policyIDs { 830 if err := s.aclPolicyDeleteTxn(tx, idx, policyID, "id"); err != nil { 831 return err 832 } 833 } 834 835 if err := indexUpdateMaxTxn(tx, idx, "acl-policies"); err != nil { 836 return fmt.Errorf("failed updating index: %v", err) 837 } 838 tx.Commit() 839 return nil 840 } 841 842 func (s *Store) aclPolicyDelete(idx uint64, value, index string) error { 843 tx := s.db.Txn(true) 844 defer tx.Abort() 845 846 if err := s.aclPolicyDeleteTxn(tx, idx, value, index); err != nil { 847 return err 848 } 849 if err := indexUpdateMaxTxn(tx, idx, "acl-policies"); err != nil { 850 return fmt.Errorf("failed updating index: %v", err) 851 } 852 853 tx.Commit() 854 return nil 855 } 856 857 func (s *Store) aclPolicyDeleteTxn(tx *memdb.Txn, idx uint64, value, index string) error { 858 // Look up the existing token 859 rawPolicy, err := tx.First("acl-policies", index, value) 860 if err != nil { 861 return fmt.Errorf("failed acl policy lookup: %v", err) 862 } 863 864 if rawPolicy == nil { 865 return nil 866 } 867 868 policy := rawPolicy.(*structs.ACLPolicy) 869 870 if policy.ID == structs.ACLPolicyGlobalManagementID { 871 return fmt.Errorf("Deletion of the builtin global-management policy is not permitted") 872 } 873 874 if err := tx.Delete("acl-policies", policy); err != nil { 875 return fmt.Errorf("failed deleting acl policy: %v", err) 876 } 877 return nil 878 }