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