1// Copyright 2016 The etcd Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package auth 16 17import ( 18 "bytes" 19 "context" 20 "encoding/binary" 21 "errors" 22 "sort" 23 "strings" 24 "sync" 25 "sync/atomic" 26 "time" 27 28 "go.etcd.io/etcd/auth/authpb" 29 "go.etcd.io/etcd/etcdserver/api/v3rpc/rpctypes" 30 pb "go.etcd.io/etcd/etcdserver/etcdserverpb" 31 "go.etcd.io/etcd/mvcc/backend" 32 33 "github.com/coreos/pkg/capnslog" 34 "go.uber.org/zap" 35 "golang.org/x/crypto/bcrypt" 36 "google.golang.org/grpc/credentials" 37 "google.golang.org/grpc/metadata" 38 "google.golang.org/grpc/peer" 39) 40 41var ( 42 enableFlagKey = []byte("authEnabled") 43 authEnabled = []byte{1} 44 authDisabled = []byte{0} 45 46 revisionKey = []byte("authRevision") 47 48 authBucketName = []byte("auth") 49 authUsersBucketName = []byte("authUsers") 50 authRolesBucketName = []byte("authRoles") 51 52 plog = capnslog.NewPackageLogger("go.etcd.io/etcd", "auth") 53 54 ErrRootUserNotExist = errors.New("auth: root user does not exist") 55 ErrRootRoleNotExist = errors.New("auth: root user does not have root role") 56 ErrUserAlreadyExist = errors.New("auth: user already exists") 57 ErrUserEmpty = errors.New("auth: user name is empty") 58 ErrUserNotFound = errors.New("auth: user not found") 59 ErrRoleAlreadyExist = errors.New("auth: role already exists") 60 ErrRoleNotFound = errors.New("auth: role not found") 61 ErrRoleEmpty = errors.New("auth: role name is empty") 62 ErrAuthFailed = errors.New("auth: authentication failed, invalid user ID or password") 63 ErrNoPasswordUser = errors.New("auth: authentication failed, password was given for no password user") 64 ErrPermissionDenied = errors.New("auth: permission denied") 65 ErrRoleNotGranted = errors.New("auth: role is not granted to the user") 66 ErrPermissionNotGranted = errors.New("auth: permission is not granted to the role") 67 ErrAuthNotEnabled = errors.New("auth: authentication is not enabled") 68 ErrAuthOldRevision = errors.New("auth: revision in header is old") 69 ErrInvalidAuthToken = errors.New("auth: invalid auth token") 70 ErrInvalidAuthOpts = errors.New("auth: invalid auth options") 71 ErrInvalidAuthMgmt = errors.New("auth: invalid auth management") 72 ErrInvalidAuthMethod = errors.New("auth: invalid auth signature method") 73 ErrMissingKey = errors.New("auth: missing key data") 74 ErrKeyMismatch = errors.New("auth: public and private keys don't match") 75 ErrVerifyOnly = errors.New("auth: token signing attempted with verify-only key") 76) 77 78const ( 79 rootUser = "root" 80 rootRole = "root" 81 82 tokenTypeSimple = "simple" 83 tokenTypeJWT = "jwt" 84 85 revBytesLen = 8 86) 87 88type AuthInfo struct { 89 Username string 90 Revision uint64 91} 92 93// AuthenticateParamIndex is used for a key of context in the parameters of Authenticate() 94type AuthenticateParamIndex struct{} 95 96// AuthenticateParamSimpleTokenPrefix is used for a key of context in the parameters of Authenticate() 97type AuthenticateParamSimpleTokenPrefix struct{} 98 99// saveConsistentIndexFunc is used to sync consistentIndex to backend, now reusing store.saveIndex 100type saveConsistentIndexFunc func(tx backend.BatchTx) 101 102// AuthStore defines auth storage interface. 103type AuthStore interface { 104 // AuthEnable turns on the authentication feature 105 AuthEnable() error 106 107 // AuthDisable turns off the authentication feature 108 AuthDisable() 109 110 // IsAuthEnabled returns true if the authentication feature is enabled. 111 IsAuthEnabled() bool 112 113 // Authenticate does authentication based on given user name and password 114 Authenticate(ctx context.Context, username, password string) (*pb.AuthenticateResponse, error) 115 116 // Recover recovers the state of auth store from the given backend 117 Recover(b backend.Backend) 118 119 // UserAdd adds a new user 120 UserAdd(r *pb.AuthUserAddRequest) (*pb.AuthUserAddResponse, error) 121 122 // UserDelete deletes a user 123 UserDelete(r *pb.AuthUserDeleteRequest) (*pb.AuthUserDeleteResponse, error) 124 125 // UserChangePassword changes a password of a user 126 UserChangePassword(r *pb.AuthUserChangePasswordRequest) (*pb.AuthUserChangePasswordResponse, error) 127 128 // UserGrantRole grants a role to the user 129 UserGrantRole(r *pb.AuthUserGrantRoleRequest) (*pb.AuthUserGrantRoleResponse, error) 130 131 // UserGet gets the detailed information of a users 132 UserGet(r *pb.AuthUserGetRequest) (*pb.AuthUserGetResponse, error) 133 134 // UserRevokeRole revokes a role of a user 135 UserRevokeRole(r *pb.AuthUserRevokeRoleRequest) (*pb.AuthUserRevokeRoleResponse, error) 136 137 // RoleAdd adds a new role 138 RoleAdd(r *pb.AuthRoleAddRequest) (*pb.AuthRoleAddResponse, error) 139 140 // RoleGrantPermission grants a permission to a role 141 RoleGrantPermission(r *pb.AuthRoleGrantPermissionRequest) (*pb.AuthRoleGrantPermissionResponse, error) 142 143 // RoleGet gets the detailed information of a role 144 RoleGet(r *pb.AuthRoleGetRequest) (*pb.AuthRoleGetResponse, error) 145 146 // RoleRevokePermission gets the detailed information of a role 147 RoleRevokePermission(r *pb.AuthRoleRevokePermissionRequest) (*pb.AuthRoleRevokePermissionResponse, error) 148 149 // RoleDelete gets the detailed information of a role 150 RoleDelete(r *pb.AuthRoleDeleteRequest) (*pb.AuthRoleDeleteResponse, error) 151 152 // UserList gets a list of all users 153 UserList(r *pb.AuthUserListRequest) (*pb.AuthUserListResponse, error) 154 155 // RoleList gets a list of all roles 156 RoleList(r *pb.AuthRoleListRequest) (*pb.AuthRoleListResponse, error) 157 158 // IsPutPermitted checks put permission of the user 159 IsPutPermitted(authInfo *AuthInfo, key []byte) error 160 161 // IsRangePermitted checks range permission of the user 162 IsRangePermitted(authInfo *AuthInfo, key, rangeEnd []byte) error 163 164 // IsDeleteRangePermitted checks delete-range permission of the user 165 IsDeleteRangePermitted(authInfo *AuthInfo, key, rangeEnd []byte) error 166 167 // IsAdminPermitted checks admin permission of the user 168 IsAdminPermitted(authInfo *AuthInfo) error 169 170 // GenTokenPrefix produces a random string in a case of simple token 171 // in a case of JWT, it produces an empty string 172 GenTokenPrefix() (string, error) 173 174 // Revision gets current revision of authStore 175 Revision() uint64 176 177 // CheckPassword checks a given pair of username and password is correct 178 CheckPassword(username, password string) (uint64, error) 179 180 // Close does cleanup of AuthStore 181 Close() error 182 183 // AuthInfoFromCtx gets AuthInfo from gRPC's context 184 AuthInfoFromCtx(ctx context.Context) (*AuthInfo, error) 185 186 // AuthInfoFromTLS gets AuthInfo from TLS info of gRPC's context 187 AuthInfoFromTLS(ctx context.Context) *AuthInfo 188 189 // WithRoot generates and installs a token that can be used as a root credential 190 WithRoot(ctx context.Context) context.Context 191 192 // HasRole checks that user has role 193 HasRole(user, role string) bool 194 195 // SetConsistentIndexSyncer sets consistentIndex syncer 196 SetConsistentIndexSyncer(syncer saveConsistentIndexFunc) 197} 198 199type TokenProvider interface { 200 info(ctx context.Context, token string, revision uint64) (*AuthInfo, bool) 201 assign(ctx context.Context, username string, revision uint64) (string, error) 202 enable() 203 disable() 204 205 invalidateUser(string) 206 genTokenPrefix() (string, error) 207} 208 209type authStore struct { 210 // atomic operations; need 64-bit align, or 32-bit tests will crash 211 revision uint64 212 213 lg *zap.Logger 214 be backend.Backend 215 enabled bool 216 enabledMu sync.RWMutex 217 218 rangePermCache map[string]*unifiedRangePermissions // username -> unifiedRangePermissions 219 220 tokenProvider TokenProvider 221 syncConsistentIndex saveConsistentIndexFunc 222 bcryptCost int // the algorithm cost / strength for hashing auth passwords 223} 224 225func (as *authStore) SetConsistentIndexSyncer(syncer saveConsistentIndexFunc) { 226 as.syncConsistentIndex = syncer 227} 228func (as *authStore) AuthEnable() error { 229 as.enabledMu.Lock() 230 defer as.enabledMu.Unlock() 231 if as.enabled { 232 if as.lg != nil { 233 as.lg.Info("authentication is already enabled; ignored auth enable request") 234 } else { 235 plog.Noticef("Authentication already enabled") 236 } 237 return nil 238 } 239 b := as.be 240 tx := b.BatchTx() 241 tx.Lock() 242 defer func() { 243 tx.Unlock() 244 b.ForceCommit() 245 }() 246 247 u := getUser(as.lg, tx, rootUser) 248 if u == nil { 249 return ErrRootUserNotExist 250 } 251 252 if !hasRootRole(u) { 253 return ErrRootRoleNotExist 254 } 255 256 tx.UnsafePut(authBucketName, enableFlagKey, authEnabled) 257 258 as.enabled = true 259 as.tokenProvider.enable() 260 261 as.rangePermCache = make(map[string]*unifiedRangePermissions) 262 263 as.setRevision(getRevision(tx)) 264 265 if as.lg != nil { 266 as.lg.Info("enabled authentication") 267 } else { 268 plog.Noticef("Authentication enabled") 269 } 270 return nil 271} 272 273func (as *authStore) AuthDisable() { 274 as.enabledMu.Lock() 275 defer as.enabledMu.Unlock() 276 if !as.enabled { 277 return 278 } 279 b := as.be 280 tx := b.BatchTx() 281 tx.Lock() 282 tx.UnsafePut(authBucketName, enableFlagKey, authDisabled) 283 as.commitRevision(tx) 284 as.saveConsistentIndex(tx) 285 tx.Unlock() 286 b.ForceCommit() 287 288 as.enabled = false 289 as.tokenProvider.disable() 290 291 if as.lg != nil { 292 as.lg.Info("disabled authentication") 293 } else { 294 plog.Noticef("Authentication disabled") 295 } 296} 297 298func (as *authStore) Close() error { 299 as.enabledMu.Lock() 300 defer as.enabledMu.Unlock() 301 if !as.enabled { 302 return nil 303 } 304 as.tokenProvider.disable() 305 return nil 306} 307 308func (as *authStore) Authenticate(ctx context.Context, username, password string) (*pb.AuthenticateResponse, error) { 309 if !as.IsAuthEnabled() { 310 return nil, ErrAuthNotEnabled 311 } 312 313 tx := as.be.BatchTx() 314 tx.Lock() 315 defer tx.Unlock() 316 317 user := getUser(as.lg, tx, username) 318 if user == nil { 319 return nil, ErrAuthFailed 320 } 321 322 if user.Options != nil && user.Options.NoPassword { 323 return nil, ErrAuthFailed 324 } 325 326 // Password checking is already performed in the API layer, so we don't need to check for now. 327 // Staleness of password can be detected with OCC in the API layer, too. 328 329 token, err := as.tokenProvider.assign(ctx, username, as.Revision()) 330 if err != nil { 331 return nil, err 332 } 333 334 if as.lg != nil { 335 as.lg.Debug( 336 "authenticated a user", 337 zap.String("user-name", username), 338 zap.String("token", token), 339 ) 340 } else { 341 plog.Debugf("authorized %s, token is %s", username, token) 342 } 343 return &pb.AuthenticateResponse{Token: token}, nil 344} 345 346func (as *authStore) CheckPassword(username, password string) (uint64, error) { 347 if !as.IsAuthEnabled() { 348 return 0, ErrAuthNotEnabled 349 } 350 351 var user *authpb.User 352 // CompareHashAndPassword is very expensive, so we use closures 353 // to avoid putting it in the critical section of the tx lock. 354 revision, err := func() (uint64, error) { 355 tx := as.be.BatchTx() 356 tx.Lock() 357 defer tx.Unlock() 358 359 user = getUser(as.lg, tx, username) 360 if user == nil { 361 return 0, ErrAuthFailed 362 } 363 364 if user.Options != nil && user.Options.NoPassword { 365 return 0, ErrNoPasswordUser 366 } 367 368 return getRevision(tx), nil 369 }() 370 if err != nil { 371 return 0, err 372 } 373 374 if bcrypt.CompareHashAndPassword(user.Password, []byte(password)) != nil { 375 if as.lg != nil { 376 as.lg.Info("invalid password", zap.String("user-name", username)) 377 } else { 378 plog.Noticef("authentication failed, invalid password for user %s", username) 379 } 380 return 0, ErrAuthFailed 381 } 382 return revision, nil 383} 384 385func (as *authStore) Recover(be backend.Backend) { 386 enabled := false 387 as.be = be 388 tx := be.BatchTx() 389 tx.Lock() 390 _, vs := tx.UnsafeRange(authBucketName, enableFlagKey, nil, 0) 391 if len(vs) == 1 { 392 if bytes.Equal(vs[0], authEnabled) { 393 enabled = true 394 } 395 } 396 397 as.setRevision(getRevision(tx)) 398 399 tx.Unlock() 400 401 as.enabledMu.Lock() 402 as.enabled = enabled 403 as.enabledMu.Unlock() 404} 405 406func (as *authStore) UserAdd(r *pb.AuthUserAddRequest) (*pb.AuthUserAddResponse, error) { 407 if len(r.Name) == 0 { 408 return nil, ErrUserEmpty 409 } 410 411 var hashed []byte 412 var err error 413 414 noPassword := r.Options != nil && r.Options.NoPassword 415 if !noPassword { 416 hashed, err = bcrypt.GenerateFromPassword([]byte(r.Password), as.bcryptCost) 417 if err != nil { 418 if as.lg != nil { 419 as.lg.Warn( 420 "failed to bcrypt hash password", 421 zap.String("user-name", r.Name), 422 zap.Error(err), 423 ) 424 } else { 425 plog.Errorf("failed to hash password: %s", err) 426 } 427 return nil, err 428 } 429 } 430 431 tx := as.be.BatchTx() 432 tx.Lock() 433 defer tx.Unlock() 434 435 user := getUser(as.lg, tx, r.Name) 436 if user != nil { 437 return nil, ErrUserAlreadyExist 438 } 439 440 options := r.Options 441 if options == nil { 442 options = &authpb.UserAddOptions{ 443 NoPassword: false, 444 } 445 } 446 447 newUser := &authpb.User{ 448 Name: []byte(r.Name), 449 Password: hashed, 450 Options: options, 451 } 452 453 putUser(as.lg, tx, newUser) 454 455 as.commitRevision(tx) 456 as.saveConsistentIndex(tx) 457 458 if as.lg != nil { 459 as.lg.Info("added a user", zap.String("user-name", r.Name)) 460 } else { 461 plog.Noticef("added a new user: %s", r.Name) 462 } 463 return &pb.AuthUserAddResponse{}, nil 464} 465 466func (as *authStore) UserDelete(r *pb.AuthUserDeleteRequest) (*pb.AuthUserDeleteResponse, error) { 467 if as.enabled && r.Name == rootUser { 468 if as.lg != nil { 469 as.lg.Warn("cannot delete 'root' user", zap.String("user-name", r.Name)) 470 } else { 471 plog.Errorf("the user root must not be deleted") 472 } 473 return nil, ErrInvalidAuthMgmt 474 } 475 476 tx := as.be.BatchTx() 477 tx.Lock() 478 defer tx.Unlock() 479 480 user := getUser(as.lg, tx, r.Name) 481 if user == nil { 482 return nil, ErrUserNotFound 483 } 484 485 delUser(tx, r.Name) 486 487 as.commitRevision(tx) 488 as.saveConsistentIndex(tx) 489 490 as.invalidateCachedPerm(r.Name) 491 as.tokenProvider.invalidateUser(r.Name) 492 493 if as.lg != nil { 494 as.lg.Info( 495 "deleted a user", 496 zap.String("user-name", r.Name), 497 zap.Strings("user-roles", user.Roles), 498 ) 499 } else { 500 plog.Noticef("deleted a user: %s", r.Name) 501 } 502 return &pb.AuthUserDeleteResponse{}, nil 503} 504 505func (as *authStore) UserChangePassword(r *pb.AuthUserChangePasswordRequest) (*pb.AuthUserChangePasswordResponse, error) { 506 // TODO(mitake): measure the cost of bcrypt.GenerateFromPassword() 507 // If the cost is too high, we should move the encryption to outside of the raft 508 hashed, err := bcrypt.GenerateFromPassword([]byte(r.Password), as.bcryptCost) 509 if err != nil { 510 if as.lg != nil { 511 as.lg.Warn( 512 "failed to bcrypt hash password", 513 zap.String("user-name", r.Name), 514 zap.Error(err), 515 ) 516 } else { 517 plog.Errorf("failed to hash password: %s", err) 518 } 519 return nil, err 520 } 521 522 tx := as.be.BatchTx() 523 tx.Lock() 524 defer tx.Unlock() 525 526 user := getUser(as.lg, tx, r.Name) 527 if user == nil { 528 return nil, ErrUserNotFound 529 } 530 531 updatedUser := &authpb.User{ 532 Name: []byte(r.Name), 533 Roles: user.Roles, 534 Password: hashed, 535 Options: user.Options, 536 } 537 538 putUser(as.lg, tx, updatedUser) 539 540 as.commitRevision(tx) 541 as.saveConsistentIndex(tx) 542 543 as.invalidateCachedPerm(r.Name) 544 as.tokenProvider.invalidateUser(r.Name) 545 546 if as.lg != nil { 547 as.lg.Info( 548 "changed a password of a user", 549 zap.String("user-name", r.Name), 550 zap.Strings("user-roles", user.Roles), 551 ) 552 } else { 553 plog.Noticef("changed a password of a user: %s", r.Name) 554 } 555 return &pb.AuthUserChangePasswordResponse{}, nil 556} 557 558func (as *authStore) UserGrantRole(r *pb.AuthUserGrantRoleRequest) (*pb.AuthUserGrantRoleResponse, error) { 559 tx := as.be.BatchTx() 560 tx.Lock() 561 defer tx.Unlock() 562 563 user := getUser(as.lg, tx, r.User) 564 if user == nil { 565 return nil, ErrUserNotFound 566 } 567 568 if r.Role != rootRole { 569 role := getRole(tx, r.Role) 570 if role == nil { 571 return nil, ErrRoleNotFound 572 } 573 } 574 575 idx := sort.SearchStrings(user.Roles, r.Role) 576 if idx < len(user.Roles) && user.Roles[idx] == r.Role { 577 if as.lg != nil { 578 as.lg.Warn( 579 "ignored grant role request to a user", 580 zap.String("user-name", r.User), 581 zap.Strings("user-roles", user.Roles), 582 zap.String("duplicate-role-name", r.Role), 583 ) 584 } else { 585 plog.Warningf("user %s is already granted role %s", r.User, r.Role) 586 } 587 return &pb.AuthUserGrantRoleResponse{}, nil 588 } 589 590 user.Roles = append(user.Roles, r.Role) 591 sort.Strings(user.Roles) 592 593 putUser(as.lg, tx, user) 594 595 as.invalidateCachedPerm(r.User) 596 597 as.commitRevision(tx) 598 as.saveConsistentIndex(tx) 599 600 if as.lg != nil { 601 as.lg.Info( 602 "granted a role to a user", 603 zap.String("user-name", r.User), 604 zap.Strings("user-roles", user.Roles), 605 zap.String("added-role-name", r.Role), 606 ) 607 } else { 608 plog.Noticef("granted role %s to user %s", r.Role, r.User) 609 } 610 return &pb.AuthUserGrantRoleResponse{}, nil 611} 612 613func (as *authStore) UserGet(r *pb.AuthUserGetRequest) (*pb.AuthUserGetResponse, error) { 614 tx := as.be.BatchTx() 615 tx.Lock() 616 user := getUser(as.lg, tx, r.Name) 617 tx.Unlock() 618 619 if user == nil { 620 return nil, ErrUserNotFound 621 } 622 623 var resp pb.AuthUserGetResponse 624 resp.Roles = append(resp.Roles, user.Roles...) 625 return &resp, nil 626} 627 628func (as *authStore) UserList(r *pb.AuthUserListRequest) (*pb.AuthUserListResponse, error) { 629 tx := as.be.BatchTx() 630 tx.Lock() 631 users := getAllUsers(as.lg, tx) 632 tx.Unlock() 633 634 resp := &pb.AuthUserListResponse{Users: make([]string, len(users))} 635 for i := range users { 636 resp.Users[i] = string(users[i].Name) 637 } 638 return resp, nil 639} 640 641func (as *authStore) UserRevokeRole(r *pb.AuthUserRevokeRoleRequest) (*pb.AuthUserRevokeRoleResponse, error) { 642 if as.enabled && r.Name == rootUser && r.Role == rootRole { 643 if as.lg != nil { 644 as.lg.Warn( 645 "'root' user cannot revoke 'root' role", 646 zap.String("user-name", r.Name), 647 zap.String("role-name", r.Role), 648 ) 649 } else { 650 plog.Errorf("the role root must not be revoked from the user root") 651 } 652 return nil, ErrInvalidAuthMgmt 653 } 654 655 tx := as.be.BatchTx() 656 tx.Lock() 657 defer tx.Unlock() 658 659 user := getUser(as.lg, tx, r.Name) 660 if user == nil { 661 return nil, ErrUserNotFound 662 } 663 664 updatedUser := &authpb.User{ 665 Name: user.Name, 666 Password: user.Password, 667 Options: user.Options, 668 } 669 670 for _, role := range user.Roles { 671 if role != r.Role { 672 updatedUser.Roles = append(updatedUser.Roles, role) 673 } 674 } 675 676 if len(updatedUser.Roles) == len(user.Roles) { 677 return nil, ErrRoleNotGranted 678 } 679 680 putUser(as.lg, tx, updatedUser) 681 682 as.invalidateCachedPerm(r.Name) 683 684 as.commitRevision(tx) 685 as.saveConsistentIndex(tx) 686 687 if as.lg != nil { 688 as.lg.Info( 689 "revoked a role from a user", 690 zap.String("user-name", r.Name), 691 zap.Strings("old-user-roles", user.Roles), 692 zap.Strings("new-user-roles", updatedUser.Roles), 693 zap.String("revoked-role-name", r.Role), 694 ) 695 } else { 696 plog.Noticef("revoked role %s from user %s", r.Role, r.Name) 697 } 698 return &pb.AuthUserRevokeRoleResponse{}, nil 699} 700 701func (as *authStore) RoleGet(r *pb.AuthRoleGetRequest) (*pb.AuthRoleGetResponse, error) { 702 tx := as.be.BatchTx() 703 tx.Lock() 704 defer tx.Unlock() 705 706 var resp pb.AuthRoleGetResponse 707 708 role := getRole(tx, r.Role) 709 if role == nil { 710 return nil, ErrRoleNotFound 711 } 712 resp.Perm = append(resp.Perm, role.KeyPermission...) 713 return &resp, nil 714} 715 716func (as *authStore) RoleList(r *pb.AuthRoleListRequest) (*pb.AuthRoleListResponse, error) { 717 tx := as.be.BatchTx() 718 tx.Lock() 719 roles := getAllRoles(as.lg, tx) 720 tx.Unlock() 721 722 resp := &pb.AuthRoleListResponse{Roles: make([]string, len(roles))} 723 for i := range roles { 724 resp.Roles[i] = string(roles[i].Name) 725 } 726 return resp, nil 727} 728 729func (as *authStore) RoleRevokePermission(r *pb.AuthRoleRevokePermissionRequest) (*pb.AuthRoleRevokePermissionResponse, error) { 730 tx := as.be.BatchTx() 731 tx.Lock() 732 defer tx.Unlock() 733 734 role := getRole(tx, r.Role) 735 if role == nil { 736 return nil, ErrRoleNotFound 737 } 738 739 updatedRole := &authpb.Role{ 740 Name: role.Name, 741 } 742 743 for _, perm := range role.KeyPermission { 744 if !bytes.Equal(perm.Key, r.Key) || !bytes.Equal(perm.RangeEnd, r.RangeEnd) { 745 updatedRole.KeyPermission = append(updatedRole.KeyPermission, perm) 746 } 747 } 748 749 if len(role.KeyPermission) == len(updatedRole.KeyPermission) { 750 return nil, ErrPermissionNotGranted 751 } 752 753 putRole(as.lg, tx, updatedRole) 754 755 // TODO(mitake): currently single role update invalidates every cache 756 // It should be optimized. 757 as.clearCachedPerm() 758 759 as.commitRevision(tx) 760 as.saveConsistentIndex(tx) 761 762 if as.lg != nil { 763 as.lg.Info( 764 "revoked a permission on range", 765 zap.String("role-name", r.Role), 766 zap.String("key", string(r.Key)), 767 zap.String("range-end", string(r.RangeEnd)), 768 ) 769 } else { 770 plog.Noticef("revoked key %s from role %s", r.Key, r.Role) 771 } 772 return &pb.AuthRoleRevokePermissionResponse{}, nil 773} 774 775func (as *authStore) RoleDelete(r *pb.AuthRoleDeleteRequest) (*pb.AuthRoleDeleteResponse, error) { 776 if as.enabled && r.Role == rootRole { 777 if as.lg != nil { 778 as.lg.Warn("cannot delete 'root' role", zap.String("role-name", r.Role)) 779 } else { 780 plog.Errorf("the role root must not be deleted") 781 } 782 return nil, ErrInvalidAuthMgmt 783 } 784 785 tx := as.be.BatchTx() 786 tx.Lock() 787 defer tx.Unlock() 788 789 role := getRole(tx, r.Role) 790 if role == nil { 791 return nil, ErrRoleNotFound 792 } 793 794 delRole(tx, r.Role) 795 796 users := getAllUsers(as.lg, tx) 797 for _, user := range users { 798 updatedUser := &authpb.User{ 799 Name: user.Name, 800 Password: user.Password, 801 Options: user.Options, 802 } 803 804 for _, role := range user.Roles { 805 if role != r.Role { 806 updatedUser.Roles = append(updatedUser.Roles, role) 807 } 808 } 809 810 if len(updatedUser.Roles) == len(user.Roles) { 811 continue 812 } 813 814 putUser(as.lg, tx, updatedUser) 815 816 as.invalidateCachedPerm(string(user.Name)) 817 } 818 819 as.commitRevision(tx) 820 as.saveConsistentIndex(tx) 821 822 if as.lg != nil { 823 as.lg.Info("deleted a role", zap.String("role-name", r.Role)) 824 } else { 825 plog.Noticef("deleted role %s", r.Role) 826 } 827 return &pb.AuthRoleDeleteResponse{}, nil 828} 829 830func (as *authStore) RoleAdd(r *pb.AuthRoleAddRequest) (*pb.AuthRoleAddResponse, error) { 831 if len(r.Name) == 0 { 832 return nil, ErrRoleEmpty 833 } 834 835 tx := as.be.BatchTx() 836 tx.Lock() 837 defer tx.Unlock() 838 839 role := getRole(tx, r.Name) 840 if role != nil { 841 return nil, ErrRoleAlreadyExist 842 } 843 844 newRole := &authpb.Role{ 845 Name: []byte(r.Name), 846 } 847 848 putRole(as.lg, tx, newRole) 849 850 as.commitRevision(tx) 851 as.saveConsistentIndex(tx) 852 853 if as.lg != nil { 854 as.lg.Info("created a role", zap.String("role-name", r.Name)) 855 } else { 856 plog.Noticef("Role %s is created", r.Name) 857 } 858 return &pb.AuthRoleAddResponse{}, nil 859} 860 861func (as *authStore) authInfoFromToken(ctx context.Context, token string) (*AuthInfo, bool) { 862 return as.tokenProvider.info(ctx, token, as.Revision()) 863} 864 865type permSlice []*authpb.Permission 866 867func (perms permSlice) Len() int { 868 return len(perms) 869} 870 871func (perms permSlice) Less(i, j int) bool { 872 return bytes.Compare(perms[i].Key, perms[j].Key) < 0 873} 874 875func (perms permSlice) Swap(i, j int) { 876 perms[i], perms[j] = perms[j], perms[i] 877} 878 879func (as *authStore) RoleGrantPermission(r *pb.AuthRoleGrantPermissionRequest) (*pb.AuthRoleGrantPermissionResponse, error) { 880 tx := as.be.BatchTx() 881 tx.Lock() 882 defer tx.Unlock() 883 884 role := getRole(tx, r.Name) 885 if role == nil { 886 return nil, ErrRoleNotFound 887 } 888 889 idx := sort.Search(len(role.KeyPermission), func(i int) bool { 890 return bytes.Compare(role.KeyPermission[i].Key, r.Perm.Key) >= 0 891 }) 892 893 if idx < len(role.KeyPermission) && bytes.Equal(role.KeyPermission[idx].Key, r.Perm.Key) && bytes.Equal(role.KeyPermission[idx].RangeEnd, r.Perm.RangeEnd) { 894 // update existing permission 895 role.KeyPermission[idx].PermType = r.Perm.PermType 896 } else { 897 // append new permission to the role 898 newPerm := &authpb.Permission{ 899 Key: r.Perm.Key, 900 RangeEnd: r.Perm.RangeEnd, 901 PermType: r.Perm.PermType, 902 } 903 904 role.KeyPermission = append(role.KeyPermission, newPerm) 905 sort.Sort(permSlice(role.KeyPermission)) 906 } 907 908 putRole(as.lg, tx, role) 909 910 // TODO(mitake): currently single role update invalidates every cache 911 // It should be optimized. 912 as.clearCachedPerm() 913 914 as.commitRevision(tx) 915 as.saveConsistentIndex(tx) 916 917 if as.lg != nil { 918 as.lg.Info( 919 "granted/updated a permission to a user", 920 zap.String("user-name", r.Name), 921 zap.String("permission-name", authpb.Permission_Type_name[int32(r.Perm.PermType)]), 922 ) 923 } else { 924 plog.Noticef("role %s's permission of key %s is updated as %s", r.Name, r.Perm.Key, authpb.Permission_Type_name[int32(r.Perm.PermType)]) 925 } 926 return &pb.AuthRoleGrantPermissionResponse{}, nil 927} 928 929func (as *authStore) isOpPermitted(userName string, revision uint64, key, rangeEnd []byte, permTyp authpb.Permission_Type) error { 930 // TODO(mitake): this function would be costly so we need a caching mechanism 931 if !as.IsAuthEnabled() { 932 return nil 933 } 934 935 // only gets rev == 0 when passed AuthInfo{}; no user given 936 if revision == 0 { 937 return ErrUserEmpty 938 } 939 rev := as.Revision() 940 if revision < rev { 941 if as.lg != nil { 942 as.lg.Warn("request auth revision is less than current node auth revision", 943 zap.Uint64("current node auth revision", rev), 944 zap.Uint64("request auth revision", revision), 945 zap.ByteString("request key", key), 946 zap.Error(ErrAuthOldRevision)) 947 } else { 948 plog.Warningf("request auth revision is less than current node auth revision,"+ 949 "current node auth revision is %d,"+ 950 "request auth revision is %d,"+ 951 "request key is %s, "+ 952 "err is %v", rev, revision, key, ErrAuthOldRevision) 953 } 954 return ErrAuthOldRevision 955 } 956 957 tx := as.be.BatchTx() 958 tx.Lock() 959 defer tx.Unlock() 960 961 user := getUser(as.lg, tx, userName) 962 if user == nil { 963 if as.lg != nil { 964 as.lg.Warn("cannot find a user for permission check", zap.String("user-name", userName)) 965 } else { 966 plog.Errorf("invalid user name %s for permission checking", userName) 967 } 968 return ErrPermissionDenied 969 } 970 971 // root role should have permission on all ranges 972 if hasRootRole(user) { 973 return nil 974 } 975 976 if as.isRangeOpPermitted(tx, userName, key, rangeEnd, permTyp) { 977 return nil 978 } 979 980 return ErrPermissionDenied 981} 982 983func (as *authStore) IsPutPermitted(authInfo *AuthInfo, key []byte) error { 984 return as.isOpPermitted(authInfo.Username, authInfo.Revision, key, nil, authpb.WRITE) 985} 986 987func (as *authStore) IsRangePermitted(authInfo *AuthInfo, key, rangeEnd []byte) error { 988 return as.isOpPermitted(authInfo.Username, authInfo.Revision, key, rangeEnd, authpb.READ) 989} 990 991func (as *authStore) IsDeleteRangePermitted(authInfo *AuthInfo, key, rangeEnd []byte) error { 992 return as.isOpPermitted(authInfo.Username, authInfo.Revision, key, rangeEnd, authpb.WRITE) 993} 994 995func (as *authStore) IsAdminPermitted(authInfo *AuthInfo) error { 996 if !as.IsAuthEnabled() { 997 return nil 998 } 999 if authInfo == nil || authInfo.Username == "" { 1000 return ErrUserEmpty 1001 } 1002 1003 tx := as.be.BatchTx() 1004 tx.Lock() 1005 u := getUser(as.lg, tx, authInfo.Username) 1006 tx.Unlock() 1007 1008 if u == nil { 1009 return ErrUserNotFound 1010 } 1011 1012 if !hasRootRole(u) { 1013 return ErrPermissionDenied 1014 } 1015 1016 return nil 1017} 1018 1019func getUser(lg *zap.Logger, tx backend.BatchTx, username string) *authpb.User { 1020 _, vs := tx.UnsafeRange(authUsersBucketName, []byte(username), nil, 0) 1021 if len(vs) == 0 { 1022 return nil 1023 } 1024 1025 user := &authpb.User{} 1026 err := user.Unmarshal(vs[0]) 1027 if err != nil { 1028 if lg != nil { 1029 lg.Panic( 1030 "failed to unmarshal 'authpb.User'", 1031 zap.String("user-name", username), 1032 zap.Error(err), 1033 ) 1034 } else { 1035 plog.Panicf("failed to unmarshal user struct (name: %s): %s", username, err) 1036 } 1037 } 1038 return user 1039} 1040 1041func getAllUsers(lg *zap.Logger, tx backend.BatchTx) []*authpb.User { 1042 _, vs := tx.UnsafeRange(authUsersBucketName, []byte{0}, []byte{0xff}, -1) 1043 if len(vs) == 0 { 1044 return nil 1045 } 1046 1047 users := make([]*authpb.User, len(vs)) 1048 for i := range vs { 1049 user := &authpb.User{} 1050 err := user.Unmarshal(vs[i]) 1051 if err != nil { 1052 if lg != nil { 1053 lg.Panic("failed to unmarshal 'authpb.User'", zap.Error(err)) 1054 } else { 1055 plog.Panicf("failed to unmarshal user struct: %s", err) 1056 } 1057 } 1058 users[i] = user 1059 } 1060 return users 1061} 1062 1063func putUser(lg *zap.Logger, tx backend.BatchTx, user *authpb.User) { 1064 b, err := user.Marshal() 1065 if err != nil { 1066 if lg != nil { 1067 lg.Panic("failed to unmarshal 'authpb.User'", zap.Error(err)) 1068 } else { 1069 plog.Panicf("failed to marshal user struct (name: %s): %s", user.Name, err) 1070 } 1071 } 1072 tx.UnsafePut(authUsersBucketName, user.Name, b) 1073} 1074 1075func delUser(tx backend.BatchTx, username string) { 1076 tx.UnsafeDelete(authUsersBucketName, []byte(username)) 1077} 1078 1079func getRole(tx backend.BatchTx, rolename string) *authpb.Role { 1080 _, vs := tx.UnsafeRange(authRolesBucketName, []byte(rolename), nil, 0) 1081 if len(vs) == 0 { 1082 return nil 1083 } 1084 1085 role := &authpb.Role{} 1086 err := role.Unmarshal(vs[0]) 1087 if err != nil { 1088 plog.Panicf("failed to unmarshal role struct (name: %s): %s", rolename, err) 1089 } 1090 return role 1091} 1092 1093func getAllRoles(lg *zap.Logger, tx backend.BatchTx) []*authpb.Role { 1094 _, vs := tx.UnsafeRange(authRolesBucketName, []byte{0}, []byte{0xff}, -1) 1095 if len(vs) == 0 { 1096 return nil 1097 } 1098 1099 roles := make([]*authpb.Role, len(vs)) 1100 for i := range vs { 1101 role := &authpb.Role{} 1102 err := role.Unmarshal(vs[i]) 1103 if err != nil { 1104 if lg != nil { 1105 lg.Panic("failed to unmarshal 'authpb.Role'", zap.Error(err)) 1106 } else { 1107 plog.Panicf("failed to unmarshal role struct: %s", err) 1108 } 1109 } 1110 roles[i] = role 1111 } 1112 return roles 1113} 1114 1115func putRole(lg *zap.Logger, tx backend.BatchTx, role *authpb.Role) { 1116 b, err := role.Marshal() 1117 if err != nil { 1118 if lg != nil { 1119 lg.Panic( 1120 "failed to marshal 'authpb.Role'", 1121 zap.String("role-name", string(role.Name)), 1122 zap.Error(err), 1123 ) 1124 } else { 1125 plog.Panicf("failed to marshal role struct (name: %s): %s", role.Name, err) 1126 } 1127 } 1128 1129 tx.UnsafePut(authRolesBucketName, role.Name, b) 1130} 1131 1132func delRole(tx backend.BatchTx, rolename string) { 1133 tx.UnsafeDelete(authRolesBucketName, []byte(rolename)) 1134} 1135 1136func (as *authStore) IsAuthEnabled() bool { 1137 as.enabledMu.RLock() 1138 defer as.enabledMu.RUnlock() 1139 return as.enabled 1140} 1141 1142// NewAuthStore creates a new AuthStore. 1143func NewAuthStore(lg *zap.Logger, be backend.Backend, tp TokenProvider, bcryptCost int) *authStore { 1144 if bcryptCost < bcrypt.MinCost || bcryptCost > bcrypt.MaxCost { 1145 if lg != nil { 1146 lg.Warn( 1147 "use default bcrypt cost instead of the invalid given cost", 1148 zap.Int("min-cost", bcrypt.MinCost), 1149 zap.Int("max-cost", bcrypt.MaxCost), 1150 zap.Int("default-cost", bcrypt.DefaultCost), 1151 zap.Int("given-cost", bcryptCost)) 1152 } else { 1153 plog.Warningf("Use default bcrypt-cost %d instead of the invalid value %d", 1154 bcrypt.DefaultCost, bcryptCost) 1155 } 1156 1157 bcryptCost = bcrypt.DefaultCost 1158 } 1159 1160 tx := be.BatchTx() 1161 tx.Lock() 1162 1163 tx.UnsafeCreateBucket(authBucketName) 1164 tx.UnsafeCreateBucket(authUsersBucketName) 1165 tx.UnsafeCreateBucket(authRolesBucketName) 1166 1167 enabled := false 1168 _, vs := tx.UnsafeRange(authBucketName, enableFlagKey, nil, 0) 1169 if len(vs) == 1 { 1170 if bytes.Equal(vs[0], authEnabled) { 1171 enabled = true 1172 } 1173 } 1174 1175 as := &authStore{ 1176 revision: getRevision(tx), 1177 lg: lg, 1178 be: be, 1179 enabled: enabled, 1180 rangePermCache: make(map[string]*unifiedRangePermissions), 1181 tokenProvider: tp, 1182 bcryptCost: bcryptCost, 1183 } 1184 1185 if enabled { 1186 as.tokenProvider.enable() 1187 } 1188 1189 if as.Revision() == 0 { 1190 as.commitRevision(tx) 1191 } 1192 1193 as.setupMetricsReporter() 1194 1195 tx.Unlock() 1196 be.ForceCommit() 1197 1198 return as 1199} 1200 1201func hasRootRole(u *authpb.User) bool { 1202 // u.Roles is sorted in UserGrantRole(), so we can use binary search. 1203 idx := sort.SearchStrings(u.Roles, rootRole) 1204 return idx != len(u.Roles) && u.Roles[idx] == rootRole 1205} 1206 1207func (as *authStore) commitRevision(tx backend.BatchTx) { 1208 atomic.AddUint64(&as.revision, 1) 1209 revBytes := make([]byte, revBytesLen) 1210 binary.BigEndian.PutUint64(revBytes, as.Revision()) 1211 tx.UnsafePut(authBucketName, revisionKey, revBytes) 1212} 1213 1214func getRevision(tx backend.BatchTx) uint64 { 1215 _, vs := tx.UnsafeRange(authBucketName, revisionKey, nil, 0) 1216 if len(vs) != 1 { 1217 // this can happen in the initialization phase 1218 return 0 1219 } 1220 return binary.BigEndian.Uint64(vs[0]) 1221} 1222 1223func (as *authStore) setRevision(rev uint64) { 1224 atomic.StoreUint64(&as.revision, rev) 1225} 1226 1227func (as *authStore) Revision() uint64 { 1228 return atomic.LoadUint64(&as.revision) 1229} 1230 1231func (as *authStore) AuthInfoFromTLS(ctx context.Context) (ai *AuthInfo) { 1232 peer, ok := peer.FromContext(ctx) 1233 if !ok || peer == nil || peer.AuthInfo == nil { 1234 return nil 1235 } 1236 1237 tlsInfo := peer.AuthInfo.(credentials.TLSInfo) 1238 for _, chains := range tlsInfo.State.VerifiedChains { 1239 if len(chains) < 1 { 1240 continue 1241 } 1242 ai = &AuthInfo{ 1243 Username: chains[0].Subject.CommonName, 1244 Revision: as.Revision(), 1245 } 1246 md, ok := metadata.FromIncomingContext(ctx) 1247 if !ok { 1248 return nil 1249 } 1250 1251 // gRPC-gateway proxy request to etcd server includes Grpcgateway-Accept 1252 // header. The proxy uses etcd client server certificate. If the certificate 1253 // has a CommonName we should never use this for authentication. 1254 if gw := md["grpcgateway-accept"]; len(gw) > 0 { 1255 if as.lg != nil { 1256 as.lg.Warn( 1257 "ignoring common name in gRPC-gateway proxy request", 1258 zap.String("common-name", ai.Username), 1259 zap.String("user-name", ai.Username), 1260 zap.Uint64("revision", ai.Revision), 1261 ) 1262 } else { 1263 plog.Warningf("ignoring common name in gRPC-gateway proxy request %s", ai.Username) 1264 } 1265 return nil 1266 } 1267 if as.lg != nil { 1268 as.lg.Debug( 1269 "found command name", 1270 zap.String("common-name", ai.Username), 1271 zap.String("user-name", ai.Username), 1272 zap.Uint64("revision", ai.Revision), 1273 ) 1274 } else { 1275 plog.Debugf("found common name %s", ai.Username) 1276 } 1277 break 1278 } 1279 return ai 1280} 1281 1282func (as *authStore) AuthInfoFromCtx(ctx context.Context) (*AuthInfo, error) { 1283 md, ok := metadata.FromIncomingContext(ctx) 1284 if !ok { 1285 return nil, nil 1286 } 1287 1288 //TODO(mitake|hexfusion) review unifying key names 1289 ts, ok := md[rpctypes.TokenFieldNameGRPC] 1290 if !ok { 1291 ts, ok = md[rpctypes.TokenFieldNameSwagger] 1292 } 1293 if !ok { 1294 return nil, nil 1295 } 1296 1297 token := ts[0] 1298 authInfo, uok := as.authInfoFromToken(ctx, token) 1299 if !uok { 1300 if as.lg != nil { 1301 as.lg.Warn("invalid auth token", zap.String("token", token)) 1302 } else { 1303 plog.Warningf("invalid auth token: %s", token) 1304 } 1305 return nil, ErrInvalidAuthToken 1306 } 1307 1308 return authInfo, nil 1309} 1310 1311func (as *authStore) GenTokenPrefix() (string, error) { 1312 return as.tokenProvider.genTokenPrefix() 1313} 1314 1315func decomposeOpts(lg *zap.Logger, optstr string) (string, map[string]string, error) { 1316 opts := strings.Split(optstr, ",") 1317 tokenType := opts[0] 1318 1319 typeSpecificOpts := make(map[string]string) 1320 for i := 1; i < len(opts); i++ { 1321 pair := strings.Split(opts[i], "=") 1322 1323 if len(pair) != 2 { 1324 if lg != nil { 1325 lg.Warn("invalid token option", zap.String("option", optstr)) 1326 } else { 1327 plog.Errorf("invalid token specific option: %s", optstr) 1328 } 1329 return "", nil, ErrInvalidAuthOpts 1330 } 1331 1332 if _, ok := typeSpecificOpts[pair[0]]; ok { 1333 if lg != nil { 1334 lg.Warn( 1335 "invalid token option", 1336 zap.String("option", optstr), 1337 zap.String("duplicate-parameter", pair[0]), 1338 ) 1339 } else { 1340 plog.Errorf("invalid token specific option, duplicated parameters (%s): %s", pair[0], optstr) 1341 } 1342 return "", nil, ErrInvalidAuthOpts 1343 } 1344 1345 typeSpecificOpts[pair[0]] = pair[1] 1346 } 1347 1348 return tokenType, typeSpecificOpts, nil 1349 1350} 1351 1352// NewTokenProvider creates a new token provider. 1353func NewTokenProvider( 1354 lg *zap.Logger, 1355 tokenOpts string, 1356 indexWaiter func(uint64) <-chan struct{}, 1357 TokenTTL time.Duration) (TokenProvider, error) { 1358 tokenType, typeSpecificOpts, err := decomposeOpts(lg, tokenOpts) 1359 if err != nil { 1360 return nil, ErrInvalidAuthOpts 1361 } 1362 1363 switch tokenType { 1364 case tokenTypeSimple: 1365 if lg != nil { 1366 lg.Warn("simple token is not cryptographically signed") 1367 } else { 1368 plog.Warningf("simple token is not cryptographically signed") 1369 } 1370 return newTokenProviderSimple(lg, indexWaiter, TokenTTL), nil 1371 1372 case tokenTypeJWT: 1373 return newTokenProviderJWT(lg, typeSpecificOpts) 1374 1375 case "": 1376 return newTokenProviderNop() 1377 1378 default: 1379 if lg != nil { 1380 lg.Warn( 1381 "unknown token type", 1382 zap.String("type", tokenType), 1383 zap.Error(ErrInvalidAuthOpts), 1384 ) 1385 } else { 1386 plog.Errorf("unknown token type: %s", tokenType) 1387 } 1388 return nil, ErrInvalidAuthOpts 1389 } 1390} 1391 1392func (as *authStore) WithRoot(ctx context.Context) context.Context { 1393 if !as.IsAuthEnabled() { 1394 return ctx 1395 } 1396 1397 var ctxForAssign context.Context 1398 if ts, ok := as.tokenProvider.(*tokenSimple); ok && ts != nil { 1399 ctx1 := context.WithValue(ctx, AuthenticateParamIndex{}, uint64(0)) 1400 prefix, err := ts.genTokenPrefix() 1401 if err != nil { 1402 if as.lg != nil { 1403 as.lg.Warn( 1404 "failed to generate prefix of internally used token", 1405 zap.Error(err), 1406 ) 1407 } else { 1408 plog.Errorf("failed to generate prefix of internally used token") 1409 } 1410 return ctx 1411 } 1412 ctxForAssign = context.WithValue(ctx1, AuthenticateParamSimpleTokenPrefix{}, prefix) 1413 } else { 1414 ctxForAssign = ctx 1415 } 1416 1417 token, err := as.tokenProvider.assign(ctxForAssign, "root", as.Revision()) 1418 if err != nil { 1419 // this must not happen 1420 if as.lg != nil { 1421 as.lg.Warn( 1422 "failed to assign token for lease revoking", 1423 zap.Error(err), 1424 ) 1425 } else { 1426 plog.Errorf("failed to assign token for lease revoking: %s", err) 1427 } 1428 return ctx 1429 } 1430 1431 mdMap := map[string]string{ 1432 rpctypes.TokenFieldNameGRPC: token, 1433 } 1434 tokenMD := metadata.New(mdMap) 1435 1436 // use "mdIncomingKey{}" since it's called from local etcdserver 1437 return metadata.NewIncomingContext(ctx, tokenMD) 1438} 1439 1440func (as *authStore) HasRole(user, role string) bool { 1441 tx := as.be.BatchTx() 1442 tx.Lock() 1443 u := getUser(as.lg, tx, user) 1444 tx.Unlock() 1445 1446 if u == nil { 1447 if as.lg != nil { 1448 as.lg.Warn( 1449 "'has-role' requested for non-existing user", 1450 zap.String("user-name", user), 1451 zap.String("role-name", role), 1452 ) 1453 } else { 1454 plog.Warningf("tried to check user %s has role %s, but user %s doesn't exist", user, role, user) 1455 } 1456 return false 1457 } 1458 1459 for _, r := range u.Roles { 1460 if role == r { 1461 return true 1462 } 1463 } 1464 return false 1465} 1466 1467func (as *authStore) BcryptCost() int { 1468 return as.bcryptCost 1469} 1470 1471func (as *authStore) saveConsistentIndex(tx backend.BatchTx) { 1472 if as.syncConsistentIndex != nil { 1473 as.syncConsistentIndex(tx) 1474 } else { 1475 if as.lg != nil { 1476 as.lg.Error("failed to save consistentIndex,syncConsistentIndex is nil") 1477 } else { 1478 plog.Error("failed to save consistentIndex,syncConsistentIndex is nil") 1479 } 1480 } 1481} 1482 1483func (as *authStore) setupMetricsReporter() { 1484 reportCurrentAuthRevMu.Lock() 1485 reportCurrentAuthRev = func() float64 { 1486 return float64(as.Revision()) 1487 } 1488 reportCurrentAuthRevMu.Unlock() 1489} 1490