github.com/ferranbt/nomad@v0.9.3-0.20190607002617-85c449b7667c/nomad/acl_endpoint.go (about) 1 package nomad 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "strings" 9 "time" 10 11 metrics "github.com/armon/go-metrics" 12 log "github.com/hashicorp/go-hclog" 13 memdb "github.com/hashicorp/go-memdb" 14 "github.com/hashicorp/nomad/helper/uuid" 15 "github.com/hashicorp/nomad/nomad/state" 16 "github.com/hashicorp/nomad/nomad/structs" 17 ) 18 19 var ( 20 // aclDisabled is returned when an ACL endpoint is hit but ACLs are not enabled 21 aclDisabled = fmt.Errorf("ACL support disabled") 22 ) 23 24 const ( 25 // aclBootstrapReset is the file name to create in the data dir. It's only contents 26 // should be the reset index 27 aclBootstrapReset = "acl-bootstrap-reset" 28 ) 29 30 // ACL endpoint is used for manipulating ACL tokens and policies 31 type ACL struct { 32 srv *Server 33 logger log.Logger 34 } 35 36 // UpsertPolicies is used to create or update a set of policies 37 func (a *ACL) UpsertPolicies(args *structs.ACLPolicyUpsertRequest, reply *structs.GenericResponse) error { 38 // Ensure ACLs are enabled, and always flow modification requests to the authoritative region 39 if !a.srv.config.ACLEnabled { 40 return aclDisabled 41 } 42 args.Region = a.srv.config.AuthoritativeRegion 43 44 if done, err := a.srv.forward("ACL.UpsertPolicies", args, args, reply); done { 45 return err 46 } 47 defer metrics.MeasureSince([]string{"nomad", "acl", "upsert_policies"}, time.Now()) 48 49 // Check management level permissions 50 if acl, err := a.srv.ResolveToken(args.AuthToken); err != nil { 51 return err 52 } else if acl == nil || !acl.IsManagement() { 53 return structs.ErrPermissionDenied 54 } 55 56 // Validate non-zero set of policies 57 if len(args.Policies) == 0 { 58 return fmt.Errorf("must specify as least one policy") 59 } 60 61 // Validate each policy, compute hash 62 for idx, policy := range args.Policies { 63 if err := policy.Validate(); err != nil { 64 return fmt.Errorf("policy %d invalid: %v", idx, err) 65 } 66 policy.SetHash() 67 } 68 69 // Update via Raft 70 _, index, err := a.srv.raftApply(structs.ACLPolicyUpsertRequestType, args) 71 if err != nil { 72 return err 73 } 74 75 // Update the index 76 reply.Index = index 77 return nil 78 } 79 80 // DeletePolicies is used to delete policies 81 func (a *ACL) DeletePolicies(args *structs.ACLPolicyDeleteRequest, reply *structs.GenericResponse) error { 82 // Ensure ACLs are enabled, and always flow modification requests to the authoritative region 83 if !a.srv.config.ACLEnabled { 84 return aclDisabled 85 } 86 args.Region = a.srv.config.AuthoritativeRegion 87 88 if done, err := a.srv.forward("ACL.DeletePolicies", args, args, reply); done { 89 return err 90 } 91 defer metrics.MeasureSince([]string{"nomad", "acl", "delete_policies"}, time.Now()) 92 93 // Check management level permissions 94 if acl, err := a.srv.ResolveToken(args.AuthToken); err != nil { 95 return err 96 } else if acl == nil || !acl.IsManagement() { 97 return structs.ErrPermissionDenied 98 } 99 100 // Validate non-zero set of policies 101 if len(args.Names) == 0 { 102 return fmt.Errorf("must specify as least one policy") 103 } 104 105 // Update via Raft 106 _, index, err := a.srv.raftApply(structs.ACLPolicyDeleteRequestType, args) 107 if err != nil { 108 return err 109 } 110 111 // Update the index 112 reply.Index = index 113 return nil 114 } 115 116 // ListPolicies is used to list the policies 117 func (a *ACL) ListPolicies(args *structs.ACLPolicyListRequest, reply *structs.ACLPolicyListResponse) error { 118 if !a.srv.config.ACLEnabled { 119 return aclDisabled 120 } 121 if done, err := a.srv.forward("ACL.ListPolicies", args, args, reply); done { 122 return err 123 } 124 defer metrics.MeasureSince([]string{"nomad", "acl", "list_policies"}, time.Now()) 125 126 // Check management level permissions 127 acl, err := a.srv.ResolveToken(args.AuthToken) 128 if err != nil { 129 return err 130 } else if acl == nil { 131 return structs.ErrPermissionDenied 132 } 133 134 // If it is not a management token determine the policies that may be listed 135 mgt := acl.IsManagement() 136 var policies map[string]struct{} 137 if !mgt { 138 snap, err := a.srv.fsm.State().Snapshot() 139 if err != nil { 140 return err 141 } 142 143 token, err := snap.ACLTokenBySecretID(nil, args.AuthToken) 144 if err != nil { 145 return err 146 } 147 if token == nil { 148 return structs.ErrTokenNotFound 149 } 150 151 policies = make(map[string]struct{}, len(token.Policies)) 152 for _, p := range token.Policies { 153 policies[p] = struct{}{} 154 } 155 } 156 157 // Setup the blocking query 158 opts := blockingOptions{ 159 queryOpts: &args.QueryOptions, 160 queryMeta: &reply.QueryMeta, 161 run: func(ws memdb.WatchSet, state *state.StateStore) error { 162 // Iterate over all the policies 163 var err error 164 var iter memdb.ResultIterator 165 if prefix := args.QueryOptions.Prefix; prefix != "" { 166 iter, err = state.ACLPolicyByNamePrefix(ws, prefix) 167 } else { 168 iter, err = state.ACLPolicies(ws) 169 } 170 if err != nil { 171 return err 172 } 173 174 // Convert all the policies to a list stub 175 reply.Policies = nil 176 for { 177 raw := iter.Next() 178 if raw == nil { 179 break 180 } 181 policy := raw.(*structs.ACLPolicy) 182 if _, ok := policies[policy.Name]; ok || mgt { 183 reply.Policies = append(reply.Policies, policy.Stub()) 184 } 185 } 186 187 // Use the last index that affected the policy table 188 index, err := state.Index("acl_policy") 189 if err != nil { 190 return err 191 } 192 193 // Ensure we never set the index to zero, otherwise a blocking query cannot be used. 194 // We floor the index at one, since realistically the first write must have a higher index. 195 if index == 0 { 196 index = 1 197 } 198 reply.Index = index 199 return nil 200 }} 201 return a.srv.blockingRPC(&opts) 202 } 203 204 // GetPolicy is used to get a specific policy 205 func (a *ACL) GetPolicy(args *structs.ACLPolicySpecificRequest, reply *structs.SingleACLPolicyResponse) error { 206 if !a.srv.config.ACLEnabled { 207 return aclDisabled 208 } 209 if done, err := a.srv.forward("ACL.GetPolicy", args, args, reply); done { 210 return err 211 } 212 defer metrics.MeasureSince([]string{"nomad", "acl", "get_policy"}, time.Now()) 213 214 // Check management level permissions 215 acl, err := a.srv.ResolveToken(args.AuthToken) 216 if err != nil { 217 return err 218 } else if acl == nil { 219 return structs.ErrPermissionDenied 220 } 221 222 // If it is not a management token determine if it can get this policy 223 mgt := acl.IsManagement() 224 if !mgt { 225 snap, err := a.srv.fsm.State().Snapshot() 226 if err != nil { 227 return err 228 } 229 230 token, err := snap.ACLTokenBySecretID(nil, args.AuthToken) 231 if err != nil { 232 return err 233 } 234 if token == nil { 235 return structs.ErrTokenNotFound 236 } 237 238 found := false 239 for _, p := range token.Policies { 240 if p == args.Name { 241 found = true 242 break 243 } 244 } 245 246 if !found { 247 return structs.ErrPermissionDenied 248 } 249 } 250 251 // Setup the blocking query 252 opts := blockingOptions{ 253 queryOpts: &args.QueryOptions, 254 queryMeta: &reply.QueryMeta, 255 run: func(ws memdb.WatchSet, state *state.StateStore) error { 256 // Look for the policy 257 out, err := state.ACLPolicyByName(ws, args.Name) 258 if err != nil { 259 return err 260 } 261 262 // Setup the output 263 reply.Policy = out 264 if out != nil { 265 reply.Index = out.ModifyIndex 266 } else { 267 // Use the last index that affected the policy table 268 index, err := state.Index("acl_policy") 269 if err != nil { 270 return err 271 } 272 reply.Index = index 273 } 274 return nil 275 }} 276 return a.srv.blockingRPC(&opts) 277 } 278 279 // GetPolicies is used to get a set of policies 280 func (a *ACL) GetPolicies(args *structs.ACLPolicySetRequest, reply *structs.ACLPolicySetResponse) error { 281 if !a.srv.config.ACLEnabled { 282 return aclDisabled 283 } 284 if done, err := a.srv.forward("ACL.GetPolicies", args, args, reply); done { 285 return err 286 } 287 defer metrics.MeasureSince([]string{"nomad", "acl", "get_policies"}, time.Now()) 288 289 var token *structs.ACLToken 290 var err error 291 if args.AuthToken == "" { 292 // No need to look up the anonymous token 293 token = structs.AnonymousACLToken 294 } else { 295 // For client typed tokens, allow them to query any policies associated with that token. 296 // This is used by clients which are resolving the policies to enforce. Any associated 297 // policies need to be fetched so that the client can determine what to allow. 298 token, err = a.srv.State().ACLTokenBySecretID(nil, args.AuthToken) 299 if err != nil { 300 return err 301 } 302 } 303 304 if token == nil { 305 return structs.ErrTokenNotFound 306 } 307 if token.Type != structs.ACLManagementToken && !token.PolicySubset(args.Names) { 308 return structs.ErrPermissionDenied 309 } 310 311 // Setup the blocking query 312 opts := blockingOptions{ 313 queryOpts: &args.QueryOptions, 314 queryMeta: &reply.QueryMeta, 315 run: func(ws memdb.WatchSet, state *state.StateStore) error { 316 // Setup the output 317 reply.Policies = make(map[string]*structs.ACLPolicy, len(args.Names)) 318 319 // Look for the policy 320 for _, policyName := range args.Names { 321 out, err := state.ACLPolicyByName(ws, policyName) 322 if err != nil { 323 return err 324 } 325 if out != nil { 326 reply.Policies[policyName] = out 327 } 328 } 329 330 // Use the last index that affected the policy table 331 index, err := state.Index("acl_policy") 332 if err != nil { 333 return err 334 } 335 reply.Index = index 336 return nil 337 }} 338 return a.srv.blockingRPC(&opts) 339 } 340 341 // Bootstrap is used to bootstrap the initial token 342 func (a *ACL) Bootstrap(args *structs.ACLTokenBootstrapRequest, reply *structs.ACLTokenUpsertResponse) error { 343 // Ensure ACLs are enabled, and always flow modification requests to the authoritative region 344 if !a.srv.config.ACLEnabled { 345 return aclDisabled 346 } 347 args.Region = a.srv.config.AuthoritativeRegion 348 349 if done, err := a.srv.forward("ACL.Bootstrap", args, args, reply); done { 350 return err 351 } 352 defer metrics.MeasureSince([]string{"nomad", "acl", "bootstrap"}, time.Now()) 353 354 // Always ignore the reset index from the arguments 355 args.ResetIndex = 0 356 357 // Snapshot the state 358 state, err := a.srv.State().Snapshot() 359 if err != nil { 360 return err 361 } 362 363 // Verify bootstrap is possible. The state store method re-verifies this, 364 // but we do an early check to avoid raft transactions when possible. 365 ok, resetIdx, err := state.CanBootstrapACLToken() 366 if err != nil { 367 return err 368 } 369 if !ok { 370 // Check if there is a reset index specified 371 specifiedIndex := a.fileBootstrapResetIndex() 372 if specifiedIndex == 0 { 373 return fmt.Errorf("ACL bootstrap already done (reset index: %d)", resetIdx) 374 } else if specifiedIndex != resetIdx { 375 return fmt.Errorf("Invalid bootstrap reset index (specified %d, reset index: %d)", specifiedIndex, resetIdx) 376 } 377 378 // Setup the reset index to allow bootstrapping again 379 args.ResetIndex = resetIdx 380 } 381 382 // Create a new global management token, override any parameter 383 args.Token = &structs.ACLToken{ 384 AccessorID: uuid.Generate(), 385 SecretID: uuid.Generate(), 386 Name: "Bootstrap Token", 387 Type: structs.ACLManagementToken, 388 Global: true, 389 CreateTime: time.Now().UTC(), 390 } 391 args.Token.SetHash() 392 393 // Update via Raft 394 _, index, err := a.srv.raftApply(structs.ACLTokenBootstrapRequestType, args) 395 if err != nil { 396 return err 397 } 398 399 // Populate the response. We do a lookup against the state to 400 // pickup the proper create / modify times. 401 state, err = a.srv.State().Snapshot() 402 if err != nil { 403 return err 404 } 405 out, err := state.ACLTokenByAccessorID(nil, args.Token.AccessorID) 406 if err != nil { 407 return fmt.Errorf("token lookup failed: %v", err) 408 } 409 reply.Tokens = append(reply.Tokens, out) 410 411 // Update the index 412 reply.Index = index 413 return nil 414 } 415 416 // fileBootstrapResetIndex is used to read the reset file from <data-dir>/acl-bootstrap-reset 417 func (a *ACL) fileBootstrapResetIndex() uint64 { 418 // Determine the file path to check 419 path := filepath.Join(a.srv.config.DataDir, aclBootstrapReset) 420 421 // Read the file 422 raw, err := ioutil.ReadFile(path) 423 if err != nil { 424 if !os.IsNotExist(err) { 425 a.logger.Error("failed to read bootstrap file", "path", path, "error", err) 426 } 427 return 0 428 } 429 430 // Attempt to parse the file 431 var resetIdx uint64 432 if _, err := fmt.Sscanf(string(raw), "%d", &resetIdx); err != nil { 433 a.logger.Error("failed to parse bootstrap file", "path", path, "error", err) 434 return 0 435 } 436 437 // Return the reset index 438 a.logger.Warn("bootstrap file parsed", "path", path, "reset_index", resetIdx) 439 return resetIdx 440 } 441 442 // UpsertTokens is used to create or update a set of tokens 443 func (a *ACL) UpsertTokens(args *structs.ACLTokenUpsertRequest, reply *structs.ACLTokenUpsertResponse) error { 444 // Ensure ACLs are enabled, and always flow modification requests to the authoritative region 445 if !a.srv.config.ACLEnabled { 446 return aclDisabled 447 } 448 449 // Validate non-zero set of tokens 450 if len(args.Tokens) == 0 { 451 return fmt.Errorf("must specify as least one token") 452 } 453 454 // Force the request to the authoritative region if we are creating global tokens 455 hasGlobal := false 456 allGlobal := true 457 for _, token := range args.Tokens { 458 if token.Global { 459 hasGlobal = true 460 } else { 461 allGlobal = false 462 } 463 } 464 465 // Disallow mixed requests with global and non-global tokens since we forward 466 // the entire request as a single batch. 467 if hasGlobal { 468 if !allGlobal { 469 return fmt.Errorf("cannot upsert mixed global and non-global tokens") 470 } 471 472 // Force the request to the authoritative region if it has global 473 args.Region = a.srv.config.AuthoritativeRegion 474 } 475 476 if done, err := a.srv.forward("ACL.UpsertTokens", args, args, reply); done { 477 return err 478 } 479 defer metrics.MeasureSince([]string{"nomad", "acl", "upsert_tokens"}, time.Now()) 480 481 // Check management level permissions 482 if acl, err := a.srv.ResolveToken(args.AuthToken); err != nil { 483 return err 484 } else if acl == nil || !acl.IsManagement() { 485 return structs.ErrPermissionDenied 486 } 487 488 // Snapshot the state 489 state, err := a.srv.State().Snapshot() 490 if err != nil { 491 return err 492 } 493 494 // Validate each token 495 for idx, token := range args.Tokens { 496 if err := token.Validate(); err != nil { 497 return fmt.Errorf("token %d invalid: %v", idx, err) 498 } 499 500 // Generate an accessor and secret ID if new 501 if token.AccessorID == "" { 502 token.AccessorID = uuid.Generate() 503 token.SecretID = uuid.Generate() 504 token.CreateTime = time.Now().UTC() 505 506 } else { 507 // Verify the token exists 508 out, err := state.ACLTokenByAccessorID(nil, token.AccessorID) 509 if err != nil { 510 return fmt.Errorf("token lookup failed: %v", err) 511 } 512 if out == nil { 513 return fmt.Errorf("cannot find token %s", token.AccessorID) 514 } 515 516 // Cannot toggle the "Global" mode 517 if token.Global != out.Global { 518 return fmt.Errorf("cannot toggle global mode of %s", token.AccessorID) 519 } 520 } 521 522 // Compute the token hash 523 token.SetHash() 524 } 525 526 // Update via Raft 527 _, index, err := a.srv.raftApply(structs.ACLTokenUpsertRequestType, args) 528 if err != nil { 529 return err 530 } 531 532 // Populate the response. We do a lookup against the state to 533 // pickup the proper create / modify times. 534 state, err = a.srv.State().Snapshot() 535 if err != nil { 536 return err 537 } 538 for _, token := range args.Tokens { 539 out, err := state.ACLTokenByAccessorID(nil, token.AccessorID) 540 if err != nil { 541 return fmt.Errorf("token lookup failed: %v", err) 542 } 543 reply.Tokens = append(reply.Tokens, out) 544 } 545 546 // Update the index 547 reply.Index = index 548 return nil 549 } 550 551 // DeleteTokens is used to delete tokens 552 func (a *ACL) DeleteTokens(args *structs.ACLTokenDeleteRequest, reply *structs.GenericResponse) error { 553 // Ensure ACLs are enabled, and always flow modification requests to the authoritative region 554 if !a.srv.config.ACLEnabled { 555 return aclDisabled 556 } 557 558 // Validate non-zero set of tokens 559 if len(args.AccessorIDs) == 0 { 560 return fmt.Errorf("must specify as least one token") 561 } 562 563 if done, err := a.srv.forward("ACL.DeleteTokens", args, args, reply); done { 564 return err 565 } 566 defer metrics.MeasureSince([]string{"nomad", "acl", "delete_tokens"}, time.Now()) 567 568 // Check management level permissions 569 if acl, err := a.srv.ResolveToken(args.AuthToken); err != nil { 570 return err 571 } else if acl == nil || !acl.IsManagement() { 572 return structs.ErrPermissionDenied 573 } 574 575 // Snapshot the state 576 state, err := a.srv.State().Snapshot() 577 if err != nil { 578 return err 579 } 580 581 // Determine if we are deleting local or global tokens 582 hasGlobal := false 583 allGlobal := true 584 nonexistentTokens := make([]string, 0) 585 for _, accessor := range args.AccessorIDs { 586 token, err := state.ACLTokenByAccessorID(nil, accessor) 587 if err != nil { 588 return fmt.Errorf("token lookup failed: %v", err) 589 } 590 if token == nil { 591 nonexistentTokens = append(nonexistentTokens, accessor) 592 continue 593 } 594 if token.Global { 595 hasGlobal = true 596 } else { 597 allGlobal = false 598 } 599 } 600 601 if len(nonexistentTokens) != 0 { 602 return fmt.Errorf("Cannot delete nonexistent tokens: %v", strings.Join(nonexistentTokens, ", ")) 603 } 604 605 // Disallow mixed requests with global and non-global tokens since we forward 606 // the entire request as a single batch. 607 if hasGlobal { 608 if !allGlobal { 609 return fmt.Errorf("cannot delete mixed global and non-global tokens") 610 } 611 612 // Force the request to the authoritative region if it has global 613 if a.srv.config.Region != a.srv.config.AuthoritativeRegion { 614 args.Region = a.srv.config.AuthoritativeRegion 615 _, err := a.srv.forward("ACL.DeleteTokens", args, args, reply) 616 return err 617 } 618 } 619 620 // Update via Raft 621 _, index, err := a.srv.raftApply(structs.ACLTokenDeleteRequestType, args) 622 if err != nil { 623 return err 624 } 625 626 // Update the index 627 reply.Index = index 628 return nil 629 } 630 631 // ListTokens is used to list the tokens 632 func (a *ACL) ListTokens(args *structs.ACLTokenListRequest, reply *structs.ACLTokenListResponse) error { 633 if !a.srv.config.ACLEnabled { 634 return aclDisabled 635 } 636 if done, err := a.srv.forward("ACL.ListTokens", args, args, reply); done { 637 return err 638 } 639 defer metrics.MeasureSince([]string{"nomad", "acl", "list_tokens"}, time.Now()) 640 641 // Check management level permissions 642 if acl, err := a.srv.ResolveToken(args.AuthToken); err != nil { 643 return err 644 } else if acl == nil || !acl.IsManagement() { 645 return structs.ErrPermissionDenied 646 } 647 648 // Setup the blocking query 649 opts := blockingOptions{ 650 queryOpts: &args.QueryOptions, 651 queryMeta: &reply.QueryMeta, 652 run: func(ws memdb.WatchSet, state *state.StateStore) error { 653 // Iterate over all the tokens 654 var err error 655 var iter memdb.ResultIterator 656 if prefix := args.QueryOptions.Prefix; prefix != "" { 657 iter, err = state.ACLTokenByAccessorIDPrefix(ws, prefix) 658 } else if args.GlobalOnly { 659 iter, err = state.ACLTokensByGlobal(ws, true) 660 } else { 661 iter, err = state.ACLTokens(ws) 662 } 663 if err != nil { 664 return err 665 } 666 667 // Convert all the tokens to a list stub 668 reply.Tokens = nil 669 for { 670 raw := iter.Next() 671 if raw == nil { 672 break 673 } 674 token := raw.(*structs.ACLToken) 675 reply.Tokens = append(reply.Tokens, token.Stub()) 676 } 677 678 // Use the last index that affected the token table 679 index, err := state.Index("acl_token") 680 if err != nil { 681 return err 682 } 683 reply.Index = index 684 return nil 685 }} 686 return a.srv.blockingRPC(&opts) 687 } 688 689 // GetToken is used to get a specific token 690 func (a *ACL) GetToken(args *structs.ACLTokenSpecificRequest, reply *structs.SingleACLTokenResponse) error { 691 if !a.srv.config.ACLEnabled { 692 return aclDisabled 693 } 694 if done, err := a.srv.forward("ACL.GetToken", args, args, reply); done { 695 return err 696 } 697 defer metrics.MeasureSince([]string{"nomad", "acl", "get_token"}, time.Now()) 698 699 acl, err := a.srv.ResolveToken(args.AuthToken) 700 if err != nil { 701 return err 702 } 703 704 // Ensure ACLs are enabled and this call is made with one 705 if acl == nil { 706 return structs.ErrPermissionDenied 707 } 708 709 // Setup the blocking query 710 opts := blockingOptions{ 711 queryOpts: &args.QueryOptions, 712 queryMeta: &reply.QueryMeta, 713 run: func(ws memdb.WatchSet, state *state.StateStore) error { 714 // Look for the token 715 out, err := state.ACLTokenByAccessorID(ws, args.AccessorID) 716 if err != nil { 717 return err 718 } 719 720 if out == nil { 721 // If the token doesn't resolve, only allow management tokens to 722 // block. 723 if !acl.IsManagement() { 724 return structs.ErrPermissionDenied 725 } 726 727 // Check management level permissions or that the secret ID matches the 728 // accessor ID 729 } else if !acl.IsManagement() && out.SecretID != args.AuthToken { 730 return structs.ErrPermissionDenied 731 } 732 733 // Setup the output 734 reply.Token = out 735 if out != nil { 736 reply.Index = out.ModifyIndex 737 } else { 738 // Use the last index that affected the token table 739 index, err := state.Index("acl_token") 740 if err != nil { 741 return err 742 } 743 reply.Index = index 744 } 745 return nil 746 }} 747 return a.srv.blockingRPC(&opts) 748 } 749 750 // GetTokens is used to get a set of token 751 func (a *ACL) GetTokens(args *structs.ACLTokenSetRequest, reply *structs.ACLTokenSetResponse) error { 752 if !a.srv.config.ACLEnabled { 753 return aclDisabled 754 } 755 if done, err := a.srv.forward("ACL.GetTokens", args, args, reply); done { 756 return err 757 } 758 defer metrics.MeasureSince([]string{"nomad", "acl", "get_tokens"}, time.Now()) 759 760 // Check management level permissions 761 if acl, err := a.srv.ResolveToken(args.AuthToken); err != nil { 762 return err 763 } else if acl == nil || !acl.IsManagement() { 764 return structs.ErrPermissionDenied 765 } 766 767 // Setup the blocking query 768 opts := blockingOptions{ 769 queryOpts: &args.QueryOptions, 770 queryMeta: &reply.QueryMeta, 771 run: func(ws memdb.WatchSet, state *state.StateStore) error { 772 // Setup the output 773 reply.Tokens = make(map[string]*structs.ACLToken, len(args.AccessorIDS)) 774 775 // Look for the token 776 for _, accessor := range args.AccessorIDS { 777 out, err := state.ACLTokenByAccessorID(ws, accessor) 778 if err != nil { 779 return err 780 } 781 if out != nil { 782 reply.Tokens[out.AccessorID] = out 783 } 784 } 785 786 // Use the last index that affected the token table 787 index, err := state.Index("acl_token") 788 if err != nil { 789 return err 790 } 791 reply.Index = index 792 return nil 793 }} 794 return a.srv.blockingRPC(&opts) 795 } 796 797 // ResolveToken is used to lookup a specific token by a secret ID. This is used for enforcing ACLs by clients. 798 func (a *ACL) ResolveToken(args *structs.ResolveACLTokenRequest, reply *structs.ResolveACLTokenResponse) error { 799 if !a.srv.config.ACLEnabled { 800 return aclDisabled 801 } 802 if done, err := a.srv.forward("ACL.ResolveToken", args, args, reply); done { 803 return err 804 } 805 defer metrics.MeasureSince([]string{"nomad", "acl", "resolve_token"}, time.Now()) 806 807 // Setup the query meta 808 a.srv.setQueryMeta(&reply.QueryMeta) 809 810 // Snapshot the state 811 state, err := a.srv.State().Snapshot() 812 if err != nil { 813 return err 814 } 815 816 // Look for the token 817 out, err := state.ACLTokenBySecretID(nil, args.SecretID) 818 if err != nil { 819 return err 820 } 821 822 // Setup the output 823 reply.Token = out 824 if out != nil { 825 reply.Index = out.ModifyIndex 826 } else { 827 // Use the last index that affected the token table 828 index, err := state.Index("acl_token") 829 if err != nil { 830 return err 831 } 832 reply.Index = index 833 } 834 return nil 835 }