storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/iam.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 2018-2019 MinIO, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package cmd 18 19 import ( 20 "bytes" 21 "context" 22 "encoding/base64" 23 "encoding/json" 24 "errors" 25 "fmt" 26 "math/rand" 27 "strings" 28 "sync" 29 "time" 30 31 humanize "github.com/dustin/go-humanize" 32 "github.com/minio/minio-go/v7/pkg/set" 33 34 "storj.io/minio/cmd/logger" 35 "storj.io/minio/pkg/auth" 36 iampolicy "storj.io/minio/pkg/iam/policy" 37 "storj.io/minio/pkg/madmin" 38 ) 39 40 // UsersSysType - defines the type of users and groups system that is 41 // active on the server. 42 type UsersSysType string 43 44 // Types of users configured in the server. 45 const ( 46 // This mode uses the internal users system in MinIO. 47 MinIOUsersSysType UsersSysType = "MinIOUsersSys" 48 49 // This mode uses users and groups from a configured LDAP 50 // server. 51 LDAPUsersSysType UsersSysType = "LDAPUsersSys" 52 ) 53 54 const ( 55 // IAM configuration directory. 56 iamConfigPrefix = minioConfigPrefix + "/iam" 57 58 // IAM users directory. 59 iamConfigUsersPrefix = iamConfigPrefix + "/users/" 60 61 // IAM service accounts directory. 62 iamConfigServiceAccountsPrefix = iamConfigPrefix + "/service-accounts/" 63 64 // IAM groups directory. 65 iamConfigGroupsPrefix = iamConfigPrefix + "/groups/" 66 67 // IAM policies directory. 68 iamConfigPoliciesPrefix = iamConfigPrefix + "/policies/" 69 70 // IAM sts directory. 71 iamConfigSTSPrefix = iamConfigPrefix + "/sts/" 72 73 // IAM Policy DB prefixes. 74 iamConfigPolicyDBPrefix = iamConfigPrefix + "/policydb/" 75 iamConfigPolicyDBUsersPrefix = iamConfigPolicyDBPrefix + "users/" 76 iamConfigPolicyDBSTSUsersPrefix = iamConfigPolicyDBPrefix + "sts-users/" 77 iamConfigPolicyDBServiceAccountsPrefix = iamConfigPolicyDBPrefix + "service-accounts/" 78 iamConfigPolicyDBGroupsPrefix = iamConfigPolicyDBPrefix + "groups/" 79 80 // IAM identity file which captures identity credentials. 81 iamIdentityFile = "identity.json" 82 83 // IAM policy file which provides policies for each users. 84 iamPolicyFile = "policy.json" 85 86 // IAM group members file 87 iamGroupMembersFile = "members.json" 88 89 // IAM format file 90 iamFormatFile = "format.json" 91 92 iamFormatVersion1 = 1 93 ) 94 95 const ( 96 statusEnabled = "enabled" 97 statusDisabled = "disabled" 98 ) 99 100 type iamFormat struct { 101 Version int `json:"version"` 102 } 103 104 func newIAMFormatVersion1() iamFormat { 105 return iamFormat{Version: iamFormatVersion1} 106 } 107 108 func getIAMFormatFilePath() string { 109 return iamConfigPrefix + SlashSeparator + iamFormatFile 110 } 111 112 func getUserIdentityPath(user string, userType IAMUserType) string { 113 var basePath string 114 switch userType { 115 case srvAccUser: 116 basePath = iamConfigServiceAccountsPrefix 117 case stsUser: 118 basePath = iamConfigSTSPrefix 119 default: 120 basePath = iamConfigUsersPrefix 121 } 122 return pathJoin(basePath, user, iamIdentityFile) 123 } 124 125 func getGroupInfoPath(group string) string { 126 return pathJoin(iamConfigGroupsPrefix, group, iamGroupMembersFile) 127 } 128 129 func getPolicyDocPath(name string) string { 130 return pathJoin(iamConfigPoliciesPrefix, name, iamPolicyFile) 131 } 132 133 func getMappedPolicyPath(name string, userType IAMUserType, isGroup bool) string { 134 if isGroup { 135 return pathJoin(iamConfigPolicyDBGroupsPrefix, name+".json") 136 } 137 switch userType { 138 case srvAccUser: 139 return pathJoin(iamConfigPolicyDBServiceAccountsPrefix, name+".json") 140 case stsUser: 141 return pathJoin(iamConfigPolicyDBSTSUsersPrefix, name+".json") 142 default: 143 return pathJoin(iamConfigPolicyDBUsersPrefix, name+".json") 144 } 145 } 146 147 // UserIdentity represents a user's secret key and their status 148 type UserIdentity struct { 149 Version int `json:"version"` 150 Credentials auth.Credentials `json:"credentials"` 151 } 152 153 func newUserIdentity(cred auth.Credentials) UserIdentity { 154 return UserIdentity{Version: 1, Credentials: cred} 155 } 156 157 // GroupInfo contains info about a group 158 type GroupInfo struct { 159 Version int `json:"version"` 160 Status string `json:"status"` 161 Members []string `json:"members"` 162 } 163 164 func newGroupInfo(members []string) GroupInfo { 165 return GroupInfo{Version: 1, Status: statusEnabled, Members: members} 166 } 167 168 // MappedPolicy represents a policy name mapped to a user or group 169 type MappedPolicy struct { 170 Version int `json:"version"` 171 Policies string `json:"policy"` 172 } 173 174 // converts a mapped policy into a slice of distinct policies 175 func (mp MappedPolicy) toSlice() []string { 176 var policies []string 177 for _, policy := range strings.Split(mp.Policies, ",") { 178 policy = strings.TrimSpace(policy) 179 if policy == "" { 180 continue 181 } 182 policies = append(policies, policy) 183 } 184 return policies 185 } 186 187 func (mp MappedPolicy) policySet() set.StringSet { 188 var policies []string 189 for _, policy := range strings.Split(mp.Policies, ",") { 190 policy = strings.TrimSpace(policy) 191 if policy == "" { 192 continue 193 } 194 policies = append(policies, policy) 195 } 196 return set.CreateStringSet(policies...) 197 } 198 199 func newMappedPolicy(policy string) MappedPolicy { 200 return MappedPolicy{Version: 1, Policies: policy} 201 } 202 203 // IAMSys - config system. 204 type IAMSys struct { 205 sync.Mutex 206 207 usersSysType UsersSysType 208 209 // map of policy names to policy definitions 210 iamPolicyDocsMap map[string]iampolicy.Policy 211 // map of usernames to credentials 212 iamUsersMap map[string]auth.Credentials 213 // map of group names to group info 214 iamGroupsMap map[string]GroupInfo 215 // map of user names to groups they are a member of 216 iamUserGroupMemberships map[string]set.StringSet 217 // map of usernames/temporary access keys to policy names 218 iamUserPolicyMap map[string]MappedPolicy 219 // map of group names to policy names 220 iamGroupPolicyMap map[string]MappedPolicy 221 222 // Persistence layer for IAM subsystem 223 store IAMStorageAPI 224 225 // configLoaded will be closed and remain so after first load. 226 configLoaded chan struct{} 227 } 228 229 // IAMUserType represents a user type inside MinIO server 230 type IAMUserType int 231 232 const ( 233 regularUser IAMUserType = iota 234 stsUser 235 srvAccUser 236 ) 237 238 // key options 239 type options struct { 240 ttl int64 //expiry in seconds 241 } 242 243 // IAMStorageAPI defines an interface for the IAM persistence layer 244 type IAMStorageAPI interface { 245 lock() 246 unlock() 247 248 rlock() 249 runlock() 250 251 migrateBackendFormat(context.Context) error 252 253 loadPolicyDoc(ctx context.Context, policy string, m map[string]iampolicy.Policy) error 254 loadPolicyDocs(ctx context.Context, m map[string]iampolicy.Policy) error 255 256 loadUser(ctx context.Context, user string, userType IAMUserType, m map[string]auth.Credentials) error 257 loadUsers(ctx context.Context, userType IAMUserType, m map[string]auth.Credentials) error 258 259 loadGroup(ctx context.Context, group string, m map[string]GroupInfo) error 260 loadGroups(ctx context.Context, m map[string]GroupInfo) error 261 262 loadMappedPolicy(ctx context.Context, name string, userType IAMUserType, isGroup bool, m map[string]MappedPolicy) error 263 loadMappedPolicies(ctx context.Context, userType IAMUserType, isGroup bool, m map[string]MappedPolicy) error 264 265 loadAll(context.Context, *IAMSys) error 266 267 saveIAMConfig(ctx context.Context, item interface{}, path string, opts ...options) error 268 loadIAMConfig(ctx context.Context, item interface{}, path string) error 269 deleteIAMConfig(ctx context.Context, path string) error 270 271 savePolicyDoc(ctx context.Context, policyName string, p iampolicy.Policy) error 272 saveMappedPolicy(ctx context.Context, name string, userType IAMUserType, isGroup bool, mp MappedPolicy, opts ...options) error 273 saveUserIdentity(ctx context.Context, name string, userType IAMUserType, u UserIdentity, opts ...options) error 274 saveGroupInfo(ctx context.Context, group string, gi GroupInfo) error 275 276 deletePolicyDoc(ctx context.Context, policyName string) error 277 deleteMappedPolicy(ctx context.Context, name string, userType IAMUserType, isGroup bool) error 278 deleteUserIdentity(ctx context.Context, name string, userType IAMUserType) error 279 deleteGroupInfo(ctx context.Context, name string) error 280 281 watch(context.Context, *IAMSys) 282 } 283 284 // LoadGroup - loads a specific group from storage, and updates the 285 // memberships cache. If the specified group does not exist in 286 // storage, it is removed from in-memory maps as well - this 287 // simplifies the implementation for group removal. This is called 288 // only via IAM notifications. 289 func (sys *IAMSys) LoadGroup(objAPI ObjectLayer, group string) error { 290 if !sys.Initialized() { 291 return errServerNotInitialized 292 } 293 294 sys.store.lock() 295 defer sys.store.unlock() 296 297 err := sys.store.loadGroup(context.Background(), group, sys.iamGroupsMap) 298 if err != nil && err != errNoSuchGroup { 299 return err 300 } 301 302 if err == errNoSuchGroup { 303 // group does not exist - so remove from memory. 304 sys.removeGroupFromMembershipsMap(group) 305 delete(sys.iamGroupsMap, group) 306 delete(sys.iamGroupPolicyMap, group) 307 return nil 308 } 309 310 gi := sys.iamGroupsMap[group] 311 312 // Updating the group memberships cache happens in two steps: 313 // 314 // 1. Remove the group from each user's list of memberships. 315 // 2. Add the group to each member's list of memberships. 316 // 317 // This ensures that regardless of members being added or 318 // removed, the cache stays current. 319 sys.removeGroupFromMembershipsMap(group) 320 sys.updateGroupMembershipsMap(group, &gi) 321 return nil 322 } 323 324 // LoadPolicy - reloads a specific canned policy from backend disks or etcd. 325 func (sys *IAMSys) LoadPolicy(objAPI ObjectLayer, policyName string) error { 326 if !sys.Initialized() { 327 return errServerNotInitialized 328 } 329 330 sys.store.lock() 331 defer sys.store.unlock() 332 333 return sys.store.loadPolicyDoc(context.Background(), policyName, sys.iamPolicyDocsMap) 334 } 335 336 // LoadPolicyMapping - loads the mapped policy for a user or group 337 // from storage into server memory. 338 func (sys *IAMSys) LoadPolicyMapping(objAPI ObjectLayer, userOrGroup string, isGroup bool) error { 339 if !sys.Initialized() { 340 return errServerNotInitialized 341 } 342 343 sys.store.lock() 344 defer sys.store.unlock() 345 346 var err error 347 if isGroup { 348 err = sys.store.loadMappedPolicy(context.Background(), userOrGroup, regularUser, isGroup, sys.iamGroupPolicyMap) 349 } else { 350 err = sys.store.loadMappedPolicy(context.Background(), userOrGroup, regularUser, isGroup, sys.iamUserPolicyMap) 351 } 352 353 // Ignore policy not mapped error 354 if err != nil && err != errNoSuchPolicy { 355 return err 356 } 357 358 return nil 359 } 360 361 // LoadUser - reloads a specific user from backend disks or etcd. 362 func (sys *IAMSys) LoadUser(objAPI ObjectLayer, accessKey string, userType IAMUserType) error { 363 if !sys.Initialized() { 364 return errServerNotInitialized 365 } 366 367 sys.store.lock() 368 defer sys.store.unlock() 369 370 err := sys.store.loadUser(context.Background(), accessKey, userType, sys.iamUsersMap) 371 if err != nil { 372 return err 373 } 374 err = sys.store.loadMappedPolicy(context.Background(), accessKey, userType, false, sys.iamUserPolicyMap) 375 // Ignore policy not mapped error 376 if err != nil && err != errNoSuchPolicy { 377 return err 378 } 379 380 return nil 381 } 382 383 // LoadServiceAccount - reloads a specific service account from backend disks or etcd. 384 func (sys *IAMSys) LoadServiceAccount(accessKey string) error { 385 if !sys.Initialized() { 386 return errServerNotInitialized 387 } 388 389 sys.store.lock() 390 defer sys.store.unlock() 391 392 err := sys.store.loadUser(context.Background(), accessKey, srvAccUser, sys.iamUsersMap) 393 if err != nil { 394 return err 395 } 396 397 return nil 398 } 399 400 // Perform IAM configuration migration. 401 func (sys *IAMSys) doIAMConfigMigration(ctx context.Context) error { 402 return sys.store.migrateBackendFormat(ctx) 403 } 404 405 // InitStore initializes IAM stores 406 func (sys *IAMSys) InitStore(objAPI ObjectLayer) { 407 sys.Lock() 408 defer sys.Unlock() 409 410 sys.store = newIAMObjectStore(objAPI) 411 412 if globalLDAPConfig.Enabled { 413 sys.EnableLDAPSys() 414 } 415 } 416 417 // Initialized check if IAM is initialized 418 func (sys *IAMSys) Initialized() bool { 419 if sys == nil { 420 return false 421 } 422 sys.Lock() 423 defer sys.Unlock() 424 return sys.store != nil 425 } 426 427 // Load - loads all credentials 428 func (sys *IAMSys) Load(ctx context.Context, store IAMStorageAPI) error { 429 iamUsersMap := make(map[string]auth.Credentials) 430 iamGroupsMap := make(map[string]GroupInfo) 431 iamUserPolicyMap := make(map[string]MappedPolicy) 432 iamGroupPolicyMap := make(map[string]MappedPolicy) 433 iamPolicyDocsMap := make(map[string]iampolicy.Policy) 434 435 store.rlock() 436 isMinIOUsersSys := sys.usersSysType == MinIOUsersSysType 437 store.runlock() 438 439 if err := store.loadPolicyDocs(ctx, iamPolicyDocsMap); err != nil { 440 return err 441 } 442 443 // Sets default canned policies, if none are set. 444 setDefaultCannedPolicies(iamPolicyDocsMap) 445 446 if isMinIOUsersSys { 447 if err := store.loadUsers(ctx, regularUser, iamUsersMap); err != nil { 448 return err 449 } 450 if err := store.loadGroups(ctx, iamGroupsMap); err != nil { 451 return err 452 } 453 } 454 455 // load polices mapped to users 456 if err := store.loadMappedPolicies(ctx, regularUser, false, iamUserPolicyMap); err != nil { 457 return err 458 } 459 460 // load policies mapped to groups 461 if err := store.loadMappedPolicies(ctx, regularUser, true, iamGroupPolicyMap); err != nil { 462 return err 463 } 464 465 if err := store.loadUsers(ctx, srvAccUser, iamUsersMap); err != nil { 466 return err 467 } 468 469 // load STS temp users 470 if err := store.loadUsers(ctx, stsUser, iamUsersMap); err != nil { 471 return err 472 } 473 474 // load STS policy mappings 475 if err := store.loadMappedPolicies(ctx, stsUser, false, iamUserPolicyMap); err != nil { 476 return err 477 } 478 479 store.lock() 480 defer store.unlock() 481 482 for k, v := range iamPolicyDocsMap { 483 sys.iamPolicyDocsMap[k] = v 484 } 485 486 // Merge the new reloaded entries into global map. 487 // See issue https://github.com/minio/minio/issues/9651 488 // where the present list of entries on disk are not yet 489 // latest, there is a small window where this can make 490 // valid users invalid. 491 for k, v := range iamUsersMap { 492 sys.iamUsersMap[k] = v 493 } 494 495 for k, v := range iamUserPolicyMap { 496 sys.iamUserPolicyMap[k] = v 497 } 498 499 // purge any expired entries which became expired now. 500 var expiredEntries []string 501 for k, v := range sys.iamUsersMap { 502 if v.IsExpired() { 503 delete(sys.iamUsersMap, k) 504 delete(sys.iamUserPolicyMap, k) 505 expiredEntries = append(expiredEntries, k) 506 // Deleting on the disk is taken care of in the next cycle 507 } 508 } 509 510 for _, v := range sys.iamUsersMap { 511 if v.IsServiceAccount() { 512 for _, accessKey := range expiredEntries { 513 if v.ParentUser == accessKey { 514 _ = store.deleteUserIdentity(ctx, v.AccessKey, srvAccUser) 515 delete(sys.iamUsersMap, v.AccessKey) 516 } 517 } 518 } 519 } 520 521 // purge any expired entries which became expired now. 522 for k, v := range sys.iamUsersMap { 523 if v.IsExpired() { 524 delete(sys.iamUsersMap, k) 525 delete(sys.iamUserPolicyMap, k) 526 // Deleting on the etcd is taken care of in the next cycle 527 } 528 } 529 530 for k, v := range iamGroupPolicyMap { 531 sys.iamGroupPolicyMap[k] = v 532 } 533 534 for k, v := range iamGroupsMap { 535 sys.iamGroupsMap[k] = v 536 } 537 538 sys.buildUserGroupMemberships() 539 select { 540 case <-sys.configLoaded: 541 default: 542 close(sys.configLoaded) 543 } 544 return nil 545 } 546 547 // Init - initializes config system by reading entries from config/iam 548 func (sys *IAMSys) Init(ctx context.Context, objAPI ObjectLayer) { 549 // Initialize IAM store 550 sys.InitStore(objAPI) 551 552 retryCtx, cancel := context.WithCancel(ctx) 553 554 // Indicate to our routine to exit cleanly upon return. 555 defer cancel() 556 557 // Hold the lock for migration only. 558 txnLk := objAPI.NewNSLock(minioMetaBucket, minioConfigPrefix+"/iam.lock") 559 560 // allocate dynamic timeout once before the loop 561 iamLockTimeout := newDynamicTimeout(5*time.Second, 3*time.Second) 562 563 r := rand.New(rand.NewSource(time.Now().UnixNano())) 564 565 for { 566 // let one of the server acquire the lock, if not let them timeout. 567 // which shall be retried again by this loop. 568 if _, err := txnLk.GetLock(retryCtx, iamLockTimeout); err != nil { 569 logger.Info("Waiting for all MinIO IAM sub-system to be initialized.. trying to acquire lock") 570 time.Sleep(time.Duration(r.Float64() * float64(5*time.Second))) 571 continue 572 } 573 574 // These messages only meant primarily for distributed setup, so only log during distributed setup. 575 if globalIsDistErasure { 576 logger.Info("Waiting for all MinIO IAM sub-system to be initialized.. lock acquired") 577 } 578 579 // Migrate IAM configuration, if necessary. 580 if err := sys.doIAMConfigMigration(ctx); err != nil { 581 txnLk.Unlock() 582 if configRetriableErrors(err) { 583 logger.Info("Waiting for all MinIO IAM sub-system to be initialized.. possible cause (%v)", err) 584 continue 585 } 586 logger.LogIf(ctx, fmt.Errorf("Unable to migrate IAM users and policies to new format: %w", err)) 587 logger.LogIf(ctx, errors.New("IAM sub-system is partially initialized, some users may not be available")) 588 return 589 } 590 591 // Successfully migrated, proceed to load the users. 592 txnLk.Unlock() 593 break 594 } 595 596 for { 597 if err := sys.store.loadAll(ctx, sys); err != nil { 598 if configRetriableErrors(err) { 599 logger.Info("Waiting for all MinIO IAM sub-system to be initialized.. possible cause (%v)", err) 600 time.Sleep(time.Duration(r.Float64() * float64(5*time.Second))) 601 continue 602 } 603 if err != nil { 604 logger.LogIf(ctx, fmt.Errorf("Unable to initialize IAM sub-system, some users may not be available %w", err)) 605 } 606 } 607 break 608 } 609 610 // Invalidate the old cred always, even upon error to avoid any leakage. 611 globalOldCred = auth.Credentials{} 612 go sys.store.watch(ctx, sys) 613 614 logger.Info("IAM initialization complete") 615 } 616 617 // DeletePolicy - deletes a canned policy from backend or etcd. 618 func (sys *IAMSys) DeletePolicy(policyName string) error { 619 if !sys.Initialized() { 620 return errServerNotInitialized 621 } 622 623 if policyName == "" { 624 return errInvalidArgument 625 } 626 627 sys.store.lock() 628 defer sys.store.unlock() 629 630 err := sys.store.deletePolicyDoc(context.Background(), policyName) 631 if err == errNoSuchPolicy { 632 // Ignore error if policy is already deleted. 633 err = nil 634 } 635 636 delete(sys.iamPolicyDocsMap, policyName) 637 638 // Delete user-policy mappings that will no longer apply 639 for u, mp := range sys.iamUserPolicyMap { 640 pset := mp.policySet() 641 if pset.Contains(policyName) { 642 cr, ok := sys.iamUsersMap[u] 643 if !ok { 644 // This case can happen when an temporary account 645 // is deleted or expired, removed it from userPolicyMap. 646 delete(sys.iamUserPolicyMap, u) 647 continue 648 } 649 pset.Remove(policyName) 650 // User is from STS if the cred are temporary 651 if cr.IsTemp() { 652 sys.policyDBSet(u, strings.Join(pset.ToSlice(), ","), stsUser, false) 653 } else { 654 sys.policyDBSet(u, strings.Join(pset.ToSlice(), ","), regularUser, false) 655 } 656 } 657 } 658 659 // Delete group-policy mappings that will no longer apply 660 for g, mp := range sys.iamGroupPolicyMap { 661 pset := mp.policySet() 662 if pset.Contains(policyName) { 663 pset.Remove(policyName) 664 sys.policyDBSet(g, strings.Join(pset.ToSlice(), ","), regularUser, true) 665 } 666 } 667 668 return err 669 } 670 671 // InfoPolicy - expands the canned policy into its JSON structure. 672 func (sys *IAMSys) InfoPolicy(policyName string) (iampolicy.Policy, error) { 673 if !sys.Initialized() { 674 return iampolicy.Policy{}, errServerNotInitialized 675 } 676 677 sys.store.rlock() 678 defer sys.store.runlock() 679 680 v, ok := sys.iamPolicyDocsMap[policyName] 681 if !ok { 682 return iampolicy.Policy{}, errNoSuchPolicy 683 } 684 685 return v, nil 686 } 687 688 // ListPolicies - lists all canned policies. 689 func (sys *IAMSys) ListPolicies() (map[string]iampolicy.Policy, error) { 690 if !sys.Initialized() { 691 return nil, errServerNotInitialized 692 } 693 694 <-sys.configLoaded 695 696 sys.store.rlock() 697 defer sys.store.runlock() 698 699 policyDocsMap := make(map[string]iampolicy.Policy, len(sys.iamPolicyDocsMap)) 700 for k, v := range sys.iamPolicyDocsMap { 701 policyDocsMap[k] = v 702 } 703 704 return policyDocsMap, nil 705 } 706 707 // SetPolicy - sets a new name policy. 708 func (sys *IAMSys) SetPolicy(policyName string, p iampolicy.Policy) error { 709 if !sys.Initialized() { 710 return errServerNotInitialized 711 } 712 713 if p.IsEmpty() || policyName == "" { 714 return errInvalidArgument 715 } 716 717 sys.store.lock() 718 defer sys.store.unlock() 719 720 if err := sys.store.savePolicyDoc(context.Background(), policyName, p); err != nil { 721 return err 722 } 723 724 sys.iamPolicyDocsMap[policyName] = p 725 return nil 726 } 727 728 // DeleteUser - delete user (only for long-term users not STS users). 729 func (sys *IAMSys) DeleteUser(accessKey string) error { 730 if !sys.Initialized() { 731 return errServerNotInitialized 732 } 733 734 if sys.usersSysType != MinIOUsersSysType { 735 return errIAMActionNotAllowed 736 } 737 738 // First we remove the user from their groups. 739 userInfo, getErr := sys.GetUserInfo(context.Background(), accessKey) 740 if getErr != nil { 741 return getErr 742 } 743 744 for _, group := range userInfo.MemberOf { 745 removeErr := sys.RemoveUsersFromGroup(group, []string{accessKey}) 746 if removeErr != nil { 747 return removeErr 748 } 749 } 750 751 // Next we can remove the user from memory and IAM store 752 sys.store.lock() 753 defer sys.store.unlock() 754 755 for _, u := range sys.iamUsersMap { 756 // Delete any service accounts if any first. 757 if u.IsServiceAccount() { 758 if u.ParentUser == accessKey { 759 _ = sys.store.deleteUserIdentity(context.Background(), u.AccessKey, srvAccUser) 760 delete(sys.iamUsersMap, u.AccessKey) 761 } 762 } 763 // Delete any associated STS users. 764 if u.IsTemp() { 765 if u.ParentUser == accessKey { 766 _ = sys.store.deleteUserIdentity(context.Background(), u.AccessKey, stsUser) 767 delete(sys.iamUsersMap, u.AccessKey) 768 } 769 } 770 } 771 772 // It is ok to ignore deletion error on the mapped policy 773 sys.store.deleteMappedPolicy(context.Background(), accessKey, regularUser, false) 774 err := sys.store.deleteUserIdentity(context.Background(), accessKey, regularUser) 775 if err == errNoSuchUser { 776 // ignore if user is already deleted. 777 err = nil 778 } 779 780 delete(sys.iamUsersMap, accessKey) 781 delete(sys.iamUserPolicyMap, accessKey) 782 783 return err 784 } 785 786 // CurrentPolicies - returns comma separated policy string, from 787 // an input policy after validating if there are any current 788 // policies which exist on MinIO corresponding to the input. 789 func (sys *IAMSys) CurrentPolicies(policyName string) string { 790 if !sys.Initialized() { 791 return "" 792 } 793 794 sys.store.rlock() 795 defer sys.store.runlock() 796 797 var policies []string 798 mp := newMappedPolicy(policyName) 799 for _, policy := range mp.toSlice() { 800 _, found := sys.iamPolicyDocsMap[policy] 801 if found { 802 policies = append(policies, policy) 803 } 804 } 805 return strings.Join(policies, ",") 806 } 807 808 // SetTempUser - set temporary user credentials, these credentials have an expiry. 809 func (sys *IAMSys) SetTempUser(accessKey string, cred auth.Credentials, policyName string) error { 810 if !sys.Initialized() { 811 return errServerNotInitialized 812 } 813 814 ttl := int64(cred.Expiration.Sub(UTCNow()).Seconds()) 815 816 // If OPA is not set we honor any policy claims for this 817 // temporary user which match with pre-configured canned 818 // policies for this server. 819 if GlobalPolicyOPA == nil && policyName != "" { 820 mp := newMappedPolicy(policyName) 821 combinedPolicy := sys.GetCombinedPolicy(mp.toSlice()...) 822 823 if combinedPolicy.IsEmpty() { 824 return fmt.Errorf("specified policy %s, not found %w", policyName, errNoSuchPolicy) 825 } 826 827 sys.store.lock() 828 defer sys.store.unlock() 829 830 if err := sys.store.saveMappedPolicy(context.Background(), accessKey, stsUser, false, mp, options{ttl: ttl}); err != nil { 831 sys.store.unlock() 832 return err 833 } 834 835 sys.iamUserPolicyMap[accessKey] = mp 836 } else { 837 sys.store.lock() 838 defer sys.store.unlock() 839 } 840 841 u := newUserIdentity(cred) 842 if err := sys.store.saveUserIdentity(context.Background(), accessKey, stsUser, u, options{ttl: ttl}); err != nil { 843 return err 844 } 845 846 sys.iamUsersMap[accessKey] = cred 847 return nil 848 } 849 850 // ListUsers - list all users. 851 func (sys *IAMSys) ListUsers() (map[string]madmin.UserInfo, error) { 852 if !sys.Initialized() { 853 return nil, errServerNotInitialized 854 } 855 856 if sys.usersSysType != MinIOUsersSysType { 857 return nil, errIAMActionNotAllowed 858 } 859 860 <-sys.configLoaded 861 862 sys.store.rlock() 863 defer sys.store.runlock() 864 865 var users = make(map[string]madmin.UserInfo) 866 867 for k, v := range sys.iamUsersMap { 868 if !v.IsTemp() && !v.IsServiceAccount() { 869 users[k] = madmin.UserInfo{ 870 PolicyName: sys.iamUserPolicyMap[k].Policies, 871 Status: func() madmin.AccountStatus { 872 if v.IsValid() { 873 return madmin.AccountEnabled 874 } 875 return madmin.AccountDisabled 876 }(), 877 } 878 } 879 } 880 881 return users, nil 882 } 883 884 // IsTempUser - returns if given key is a temporary user. 885 func (sys *IAMSys) IsTempUser(name string) (bool, string, error) { 886 if !sys.Initialized() { 887 return false, "", errServerNotInitialized 888 } 889 890 sys.store.rlock() 891 defer sys.store.runlock() 892 893 cred, found := sys.iamUsersMap[name] 894 if !found { 895 return false, "", errNoSuchUser 896 } 897 898 if cred.IsTemp() { 899 return true, cred.ParentUser, nil 900 } 901 902 return false, "", nil 903 } 904 905 // IsServiceAccount - returns if given key is a service account 906 func (sys *IAMSys) IsServiceAccount(name string) (bool, string, error) { 907 if !sys.Initialized() { 908 return false, "", errServerNotInitialized 909 } 910 911 sys.store.rlock() 912 defer sys.store.runlock() 913 914 cred, found := sys.iamUsersMap[name] 915 if !found { 916 return false, "", errNoSuchUser 917 } 918 919 if cred.IsServiceAccount() { 920 return true, cred.ParentUser, nil 921 } 922 923 return false, "", nil 924 } 925 926 // GetUserInfo - get info on a user. 927 func (sys *IAMSys) GetUserInfo(ctx context.Context, name string) (u madmin.UserInfo, err error) { 928 if !sys.Initialized() { 929 return u, errServerNotInitialized 930 } 931 932 select { 933 case <-sys.configLoaded: 934 default: 935 sys.loadUserFromStore(ctx, name) 936 } 937 938 if sys.usersSysType != MinIOUsersSysType { 939 sys.store.rlock() 940 // If the user has a mapped policy or is a member of a group, we 941 // return that info. Otherwise we return error. 942 mappedPolicy, ok1 := sys.iamUserPolicyMap[name] 943 memberships, ok2 := sys.iamUserGroupMemberships[name] 944 sys.store.runlock() 945 if !ok1 && !ok2 { 946 return u, errNoSuchUser 947 } 948 return madmin.UserInfo{ 949 PolicyName: mappedPolicy.Policies, 950 MemberOf: memberships.ToSlice(), 951 }, nil 952 } 953 954 sys.store.rlock() 955 defer sys.store.runlock() 956 957 cred, found := sys.iamUsersMap[name] 958 if !found { 959 return u, errNoSuchUser 960 } 961 962 if cred.IsTemp() || cred.IsServiceAccount() { 963 return u, errIAMActionNotAllowed 964 } 965 966 return madmin.UserInfo{ 967 PolicyName: sys.iamUserPolicyMap[name].Policies, 968 Status: func() madmin.AccountStatus { 969 if cred.IsValid() { 970 return madmin.AccountEnabled 971 } 972 return madmin.AccountDisabled 973 }(), 974 MemberOf: sys.iamUserGroupMemberships[name].ToSlice(), 975 }, nil 976 977 } 978 979 // SetUserStatus - sets current user status, supports disabled or enabled. 980 func (sys *IAMSys) SetUserStatus(accessKey string, status madmin.AccountStatus) error { 981 if !sys.Initialized() { 982 return errServerNotInitialized 983 } 984 985 if sys.usersSysType != MinIOUsersSysType { 986 return errIAMActionNotAllowed 987 } 988 989 if status != madmin.AccountEnabled && status != madmin.AccountDisabled { 990 return errInvalidArgument 991 } 992 993 sys.store.lock() 994 defer sys.store.unlock() 995 996 cred, ok := sys.iamUsersMap[accessKey] 997 if !ok { 998 return errNoSuchUser 999 } 1000 1001 if cred.IsTemp() || cred.IsServiceAccount() { 1002 return errIAMActionNotAllowed 1003 } 1004 1005 uinfo := newUserIdentity(auth.Credentials{ 1006 AccessKey: accessKey, 1007 SecretKey: cred.SecretKey, 1008 Status: func() string { 1009 if status == madmin.AccountEnabled { 1010 return auth.AccountOn 1011 } 1012 return auth.AccountOff 1013 }(), 1014 }) 1015 1016 if err := sys.store.saveUserIdentity(context.Background(), accessKey, regularUser, uinfo); err != nil { 1017 return err 1018 } 1019 1020 sys.iamUsersMap[accessKey] = uinfo.Credentials 1021 return nil 1022 } 1023 1024 type newServiceAccountOpts struct { 1025 sessionPolicy *iampolicy.Policy 1026 accessKey string 1027 secretKey string 1028 } 1029 1030 // NewServiceAccount - create a new service account 1031 func (sys *IAMSys) NewServiceAccount(ctx context.Context, parentUser string, groups []string, opts newServiceAccountOpts) (auth.Credentials, error) { 1032 if !sys.Initialized() { 1033 return auth.Credentials{}, errServerNotInitialized 1034 } 1035 1036 var policyBuf []byte 1037 if opts.sessionPolicy != nil { 1038 err := opts.sessionPolicy.Validate() 1039 if err != nil { 1040 return auth.Credentials{}, err 1041 } 1042 policyBuf, err = json.Marshal(opts.sessionPolicy) 1043 if err != nil { 1044 return auth.Credentials{}, err 1045 } 1046 if len(policyBuf) > 16*humanize.KiByte { 1047 return auth.Credentials{}, fmt.Errorf("Session policy should not exceed 16 KiB characters") 1048 } 1049 } 1050 1051 sys.store.lock() 1052 defer sys.store.unlock() 1053 1054 if parentUser == globalActiveCred.AccessKey { 1055 return auth.Credentials{}, errIAMActionNotAllowed 1056 } 1057 1058 cr, ok := sys.iamUsersMap[parentUser] 1059 if !ok { 1060 // For LDAP users we would need this fallback 1061 if sys.usersSysType != MinIOUsersSysType { 1062 _, ok = sys.iamUserPolicyMap[parentUser] 1063 if !ok { 1064 var found bool 1065 for _, group := range groups { 1066 _, ok = sys.iamGroupPolicyMap[group] 1067 if !ok { 1068 continue 1069 } 1070 found = true 1071 break 1072 } 1073 if !found { 1074 return auth.Credentials{}, errNoSuchUser 1075 } 1076 } 1077 } 1078 } 1079 1080 // Disallow service accounts to further create more service accounts. 1081 if cr.IsServiceAccount() { 1082 return auth.Credentials{}, errIAMActionNotAllowed 1083 } 1084 1085 m := make(map[string]interface{}) 1086 m[parentClaim] = parentUser 1087 1088 if len(policyBuf) > 0 { 1089 m[iampolicy.SessionPolicyName] = base64.StdEncoding.EncodeToString(policyBuf) 1090 m[iamPolicyClaimNameSA()] = "embedded-policy" 1091 } else { 1092 m[iamPolicyClaimNameSA()] = "inherited-policy" 1093 } 1094 1095 var ( 1096 cred auth.Credentials 1097 err error 1098 ) 1099 1100 if len(opts.accessKey) > 0 { 1101 cred, err = auth.CreateNewCredentialsWithMetadata(opts.accessKey, opts.secretKey, m, globalActiveCred.SecretKey) 1102 } else { 1103 cred, err = auth.GetNewCredentialsWithMetadata(m, globalActiveCred.SecretKey) 1104 } 1105 if err != nil { 1106 return auth.Credentials{}, err 1107 } 1108 cred.ParentUser = parentUser 1109 cred.Groups = groups 1110 cred.Status = string(auth.AccountOn) 1111 1112 u := newUserIdentity(cred) 1113 1114 if err := sys.store.saveUserIdentity(context.Background(), u.Credentials.AccessKey, srvAccUser, u); err != nil { 1115 return auth.Credentials{}, err 1116 } 1117 1118 sys.iamUsersMap[u.Credentials.AccessKey] = u.Credentials 1119 1120 return cred, nil 1121 } 1122 1123 type updateServiceAccountOpts struct { 1124 sessionPolicy *iampolicy.Policy 1125 secretKey string 1126 status string 1127 } 1128 1129 // UpdateServiceAccount - edit a service account 1130 func (sys *IAMSys) UpdateServiceAccount(ctx context.Context, accessKey string, opts updateServiceAccountOpts) error { 1131 if !sys.Initialized() { 1132 return errServerNotInitialized 1133 } 1134 1135 sys.store.lock() 1136 defer sys.store.unlock() 1137 1138 cr, ok := sys.iamUsersMap[accessKey] 1139 if !ok || !cr.IsServiceAccount() { 1140 return errNoSuchServiceAccount 1141 } 1142 1143 if opts.secretKey != "" { 1144 cr.SecretKey = opts.secretKey 1145 } 1146 1147 if opts.status != "" { 1148 cr.Status = opts.status 1149 } 1150 1151 if opts.sessionPolicy != nil { 1152 m := make(map[string]interface{}) 1153 err := opts.sessionPolicy.Validate() 1154 if err != nil { 1155 return err 1156 } 1157 policyBuf, err := json.Marshal(opts.sessionPolicy) 1158 if err != nil { 1159 return err 1160 } 1161 if len(policyBuf) > 16*humanize.KiByte { 1162 return fmt.Errorf("Session policy should not exceed 16 KiB characters") 1163 } 1164 1165 m[iampolicy.SessionPolicyName] = base64.StdEncoding.EncodeToString(policyBuf) 1166 m[iamPolicyClaimNameSA()] = "embedded-policy" 1167 m[parentClaim] = cr.ParentUser 1168 cr.SessionToken, err = auth.JWTSignWithAccessKey(accessKey, m, globalActiveCred.SecretKey) 1169 if err != nil { 1170 return err 1171 } 1172 } 1173 1174 u := newUserIdentity(cr) 1175 if err := sys.store.saveUserIdentity(context.Background(), u.Credentials.AccessKey, srvAccUser, u); err != nil { 1176 return err 1177 } 1178 1179 sys.iamUsersMap[u.Credentials.AccessKey] = u.Credentials 1180 1181 return nil 1182 } 1183 1184 // ListServiceAccounts - lists all services accounts associated to a specific user 1185 func (sys *IAMSys) ListServiceAccounts(ctx context.Context, accessKey string) ([]auth.Credentials, error) { 1186 if !sys.Initialized() { 1187 return nil, errServerNotInitialized 1188 } 1189 1190 <-sys.configLoaded 1191 1192 sys.store.rlock() 1193 defer sys.store.runlock() 1194 1195 var serviceAccounts []auth.Credentials 1196 for _, v := range sys.iamUsersMap { 1197 if v.IsServiceAccount() && v.ParentUser == accessKey { 1198 // Hide secret key & session key here 1199 v.SecretKey = "" 1200 v.SessionToken = "" 1201 serviceAccounts = append(serviceAccounts, v) 1202 } 1203 } 1204 1205 return serviceAccounts, nil 1206 } 1207 1208 // GetServiceAccount - gets information about a service account 1209 func (sys *IAMSys) GetServiceAccount(ctx context.Context, accessKey string) (auth.Credentials, *iampolicy.Policy, error) { 1210 if !sys.Initialized() { 1211 return auth.Credentials{}, nil, errServerNotInitialized 1212 } 1213 1214 sys.store.rlock() 1215 defer sys.store.runlock() 1216 1217 sa, ok := sys.iamUsersMap[accessKey] 1218 if !ok || !sa.IsServiceAccount() { 1219 return auth.Credentials{}, nil, errNoSuchServiceAccount 1220 } 1221 1222 var embeddedPolicy *iampolicy.Policy 1223 1224 jwtClaims, err := auth.ExtractClaims(sa.SessionToken, globalActiveCred.SecretKey) 1225 if err == nil { 1226 pt, ptok := jwtClaims.Lookup(iamPolicyClaimNameSA()) 1227 sp, spok := jwtClaims.Lookup(iampolicy.SessionPolicyName) 1228 if ptok && spok && pt == "embedded-policy" { 1229 policyBytes, err := base64.StdEncoding.DecodeString(sp) 1230 if err == nil { 1231 p, err := iampolicy.ParseConfig(bytes.NewReader(policyBytes)) 1232 if err == nil { 1233 policy := iampolicy.Policy{}.Merge(*p) 1234 embeddedPolicy = &policy 1235 } 1236 } 1237 } 1238 } 1239 1240 // Hide secret & session keys 1241 sa.SecretKey = "" 1242 sa.SessionToken = "" 1243 1244 return sa, embeddedPolicy, nil 1245 } 1246 1247 // DeleteServiceAccount - delete a service account 1248 func (sys *IAMSys) DeleteServiceAccount(ctx context.Context, accessKey string) error { 1249 if !sys.Initialized() { 1250 return errServerNotInitialized 1251 } 1252 1253 sys.store.lock() 1254 defer sys.store.unlock() 1255 1256 sa, ok := sys.iamUsersMap[accessKey] 1257 if !ok || !sa.IsServiceAccount() { 1258 return nil 1259 } 1260 1261 // It is ok to ignore deletion error on the mapped policy 1262 err := sys.store.deleteUserIdentity(context.Background(), accessKey, srvAccUser) 1263 if err != nil { 1264 // ignore if user is already deleted. 1265 if err == errNoSuchUser { 1266 return nil 1267 } 1268 return err 1269 } 1270 1271 delete(sys.iamUsersMap, accessKey) 1272 return nil 1273 } 1274 1275 // CreateUser - create new user credentials and policy, if user already exists 1276 // they shall be rewritten with new inputs. 1277 func (sys *IAMSys) CreateUser(accessKey string, uinfo madmin.UserInfo) error { 1278 if !sys.Initialized() { 1279 return errServerNotInitialized 1280 } 1281 1282 if sys.usersSysType != MinIOUsersSysType { 1283 return errIAMActionNotAllowed 1284 } 1285 1286 sys.store.lock() 1287 defer sys.store.unlock() 1288 1289 cr, ok := sys.iamUsersMap[accessKey] 1290 if cr.IsTemp() && ok { 1291 return errIAMActionNotAllowed 1292 } 1293 1294 u := newUserIdentity(auth.Credentials{ 1295 AccessKey: accessKey, 1296 SecretKey: uinfo.SecretKey, 1297 Status: func() string { 1298 if uinfo.Status == madmin.AccountEnabled { 1299 return auth.AccountOn 1300 } 1301 return auth.AccountOff 1302 }(), 1303 }) 1304 1305 if err := sys.store.saveUserIdentity(context.Background(), accessKey, regularUser, u); err != nil { 1306 return err 1307 } 1308 1309 sys.iamUsersMap[accessKey] = u.Credentials 1310 1311 // Set policy if specified. 1312 if uinfo.PolicyName != "" { 1313 return sys.policyDBSet(accessKey, uinfo.PolicyName, regularUser, false) 1314 } 1315 return nil 1316 } 1317 1318 // SetUserSecretKey - sets user secret key 1319 func (sys *IAMSys) SetUserSecretKey(accessKey string, secretKey string) error { 1320 if !sys.Initialized() { 1321 return errServerNotInitialized 1322 } 1323 1324 if sys.usersSysType != MinIOUsersSysType { 1325 return errIAMActionNotAllowed 1326 } 1327 1328 sys.store.lock() 1329 defer sys.store.unlock() 1330 1331 cred, ok := sys.iamUsersMap[accessKey] 1332 if !ok { 1333 return errNoSuchUser 1334 } 1335 1336 cred.SecretKey = secretKey 1337 u := newUserIdentity(cred) 1338 if err := sys.store.saveUserIdentity(context.Background(), accessKey, regularUser, u); err != nil { 1339 return err 1340 } 1341 1342 sys.iamUsersMap[accessKey] = cred 1343 return nil 1344 } 1345 1346 func (sys *IAMSys) loadUserFromStore(ctx context.Context, accessKey string) { 1347 sys.store.lock() 1348 // If user is already found proceed. 1349 if _, found := sys.iamUsersMap[accessKey]; !found { 1350 sys.store.loadUser(ctx, accessKey, regularUser, sys.iamUsersMap) 1351 if _, found = sys.iamUsersMap[accessKey]; found { 1352 // found user, load its mapped policies 1353 sys.store.loadMappedPolicy(ctx, accessKey, regularUser, false, sys.iamUserPolicyMap) 1354 } else { 1355 sys.store.loadUser(ctx, accessKey, srvAccUser, sys.iamUsersMap) 1356 if svc, found := sys.iamUsersMap[accessKey]; found { 1357 // Found service account, load its parent user and its mapped policies. 1358 if sys.usersSysType == MinIOUsersSysType { 1359 sys.store.loadUser(ctx, svc.ParentUser, regularUser, sys.iamUsersMap) 1360 } 1361 sys.store.loadMappedPolicy(ctx, svc.ParentUser, regularUser, false, sys.iamUserPolicyMap) 1362 } else { 1363 // None found fall back to STS users. 1364 sys.store.loadUser(ctx, accessKey, stsUser, sys.iamUsersMap) 1365 if _, found = sys.iamUsersMap[accessKey]; found { 1366 // STS user found, load its mapped policy. 1367 sys.store.loadMappedPolicy(context.Background(), accessKey, stsUser, false, sys.iamUserPolicyMap) 1368 } 1369 } 1370 } 1371 } 1372 1373 // Load associated policies if any. 1374 for _, policy := range sys.iamUserPolicyMap[accessKey].toSlice() { 1375 if _, found := sys.iamPolicyDocsMap[policy]; !found { 1376 sys.store.loadPolicyDoc(context.Background(), policy, sys.iamPolicyDocsMap) 1377 } 1378 } 1379 1380 sys.buildUserGroupMemberships() 1381 sys.store.unlock() 1382 } 1383 1384 // GetUser - get user credentials 1385 func (sys *IAMSys) GetUser(ctx context.Context, accessKey string) (cred auth.Credentials, ok bool) { 1386 if !sys.Initialized() { 1387 return cred, false 1388 } 1389 1390 fallback := false 1391 select { 1392 case <-sys.configLoaded: 1393 default: 1394 sys.loadUserFromStore(ctx, accessKey) 1395 fallback = true 1396 } 1397 1398 sys.store.rlock() 1399 cred, ok = sys.iamUsersMap[accessKey] 1400 if !ok && !fallback { 1401 sys.store.runlock() 1402 // accessKey not found, also 1403 // IAM store is not in fallback mode 1404 // we can try to reload again from 1405 // the IAM store and see if credential 1406 // exists now. If it doesn't proceed to 1407 // fail. 1408 sys.loadUserFromStore(ctx, accessKey) 1409 1410 sys.store.rlock() 1411 cred, ok = sys.iamUsersMap[accessKey] 1412 } 1413 defer sys.store.runlock() 1414 1415 if ok && cred.IsValid() { 1416 if cred.ParentUser != "" && sys.usersSysType == MinIOUsersSysType { 1417 _, ok = sys.iamUsersMap[cred.ParentUser] 1418 } 1419 // for LDAP service accounts with ParentUser set 1420 // we have no way to validate, either because user 1421 // doesn't need an explicit policy as it can come 1422 // automatically from a group. We are safe to ignore 1423 // this and continue as policies would fail eventually 1424 // the policies are missing or not configured. 1425 } 1426 return cred, ok && cred.IsValid() 1427 } 1428 1429 // AddUsersToGroup - adds users to a group, creating the group if 1430 // needed. No error if user(s) already are in the group. 1431 func (sys *IAMSys) AddUsersToGroup(group string, members []string) error { 1432 if !sys.Initialized() { 1433 return errServerNotInitialized 1434 } 1435 1436 if group == "" { 1437 return errInvalidArgument 1438 } 1439 1440 if sys.usersSysType != MinIOUsersSysType { 1441 return errIAMActionNotAllowed 1442 } 1443 1444 sys.store.lock() 1445 defer sys.store.unlock() 1446 1447 // Validate that all members exist. 1448 for _, member := range members { 1449 cr, ok := sys.iamUsersMap[member] 1450 if !ok { 1451 return errNoSuchUser 1452 } 1453 if cr.IsTemp() { 1454 return errIAMActionNotAllowed 1455 } 1456 } 1457 1458 gi, ok := sys.iamGroupsMap[group] 1459 if !ok { 1460 // Set group as enabled by default when it doesn't 1461 // exist. 1462 gi = newGroupInfo(members) 1463 } else { 1464 mergedMembers := append(gi.Members, members...) 1465 uniqMembers := set.CreateStringSet(mergedMembers...).ToSlice() 1466 gi.Members = uniqMembers 1467 } 1468 1469 if err := sys.store.saveGroupInfo(context.Background(), group, gi); err != nil { 1470 return err 1471 } 1472 1473 sys.iamGroupsMap[group] = gi 1474 1475 // update user-group membership map 1476 for _, member := range members { 1477 gset := sys.iamUserGroupMemberships[member] 1478 if gset == nil { 1479 gset = set.CreateStringSet(group) 1480 } else { 1481 gset.Add(group) 1482 } 1483 sys.iamUserGroupMemberships[member] = gset 1484 } 1485 1486 return nil 1487 } 1488 1489 // RemoveUsersFromGroup - remove users from group. If no users are 1490 // given, and the group is empty, deletes the group as well. 1491 func (sys *IAMSys) RemoveUsersFromGroup(group string, members []string) error { 1492 if !sys.Initialized() { 1493 return errServerNotInitialized 1494 } 1495 1496 if sys.usersSysType != MinIOUsersSysType { 1497 return errIAMActionNotAllowed 1498 } 1499 1500 if group == "" { 1501 return errInvalidArgument 1502 } 1503 1504 sys.store.lock() 1505 defer sys.store.unlock() 1506 1507 // Validate that all members exist. 1508 for _, member := range members { 1509 cr, ok := sys.iamUsersMap[member] 1510 if !ok { 1511 return errNoSuchUser 1512 } 1513 if cr.IsTemp() { 1514 return errIAMActionNotAllowed 1515 } 1516 } 1517 1518 gi, ok := sys.iamGroupsMap[group] 1519 if !ok { 1520 return errNoSuchGroup 1521 } 1522 1523 // Check if attempting to delete a non-empty group. 1524 if len(members) == 0 && len(gi.Members) != 0 { 1525 return errGroupNotEmpty 1526 } 1527 1528 if len(members) == 0 { 1529 // len(gi.Members) == 0 here. 1530 1531 // Remove the group from storage. First delete the 1532 // mapped policy. No-mapped-policy case is ignored. 1533 if err := sys.store.deleteMappedPolicy(context.Background(), group, regularUser, true); err != nil && err != errNoSuchPolicy { 1534 return err 1535 } 1536 if err := sys.store.deleteGroupInfo(context.Background(), group); err != nil && err != errNoSuchGroup { 1537 return err 1538 } 1539 1540 // Delete from server memory 1541 delete(sys.iamGroupsMap, group) 1542 delete(sys.iamGroupPolicyMap, group) 1543 return nil 1544 } 1545 1546 // Only removing members. 1547 s := set.CreateStringSet(gi.Members...) 1548 d := set.CreateStringSet(members...) 1549 gi.Members = s.Difference(d).ToSlice() 1550 1551 err := sys.store.saveGroupInfo(context.Background(), group, gi) 1552 if err != nil { 1553 return err 1554 } 1555 sys.iamGroupsMap[group] = gi 1556 1557 // update user-group membership map 1558 for _, member := range members { 1559 gset := sys.iamUserGroupMemberships[member] 1560 if gset == nil { 1561 continue 1562 } 1563 gset.Remove(group) 1564 sys.iamUserGroupMemberships[member] = gset 1565 } 1566 1567 return nil 1568 } 1569 1570 // SetGroupStatus - enable/disabled a group 1571 func (sys *IAMSys) SetGroupStatus(group string, enabled bool) error { 1572 if !sys.Initialized() { 1573 return errServerNotInitialized 1574 } 1575 1576 if sys.usersSysType != MinIOUsersSysType { 1577 return errIAMActionNotAllowed 1578 } 1579 1580 sys.store.lock() 1581 defer sys.store.unlock() 1582 1583 if group == "" { 1584 return errInvalidArgument 1585 } 1586 1587 gi, ok := sys.iamGroupsMap[group] 1588 if !ok { 1589 return errNoSuchGroup 1590 } 1591 1592 if enabled { 1593 gi.Status = statusEnabled 1594 } else { 1595 gi.Status = statusDisabled 1596 } 1597 1598 if err := sys.store.saveGroupInfo(context.Background(), group, gi); err != nil { 1599 return err 1600 } 1601 sys.iamGroupsMap[group] = gi 1602 return nil 1603 } 1604 1605 // GetGroupDescription - builds up group description 1606 func (sys *IAMSys) GetGroupDescription(group string) (gd madmin.GroupDesc, err error) { 1607 if !sys.Initialized() { 1608 return gd, errServerNotInitialized 1609 } 1610 1611 ps, err := sys.PolicyDBGet(group, true) 1612 if err != nil { 1613 return gd, err 1614 } 1615 1616 policy := strings.Join(ps, ",") 1617 1618 if sys.usersSysType != MinIOUsersSysType { 1619 return madmin.GroupDesc{ 1620 Name: group, 1621 Policy: policy, 1622 }, nil 1623 } 1624 1625 sys.store.rlock() 1626 defer sys.store.runlock() 1627 1628 gi, ok := sys.iamGroupsMap[group] 1629 if !ok { 1630 return gd, errNoSuchGroup 1631 } 1632 1633 return madmin.GroupDesc{ 1634 Name: group, 1635 Status: gi.Status, 1636 Members: gi.Members, 1637 Policy: policy, 1638 }, nil 1639 } 1640 1641 // ListGroups - lists groups. 1642 func (sys *IAMSys) ListGroups() (r []string, err error) { 1643 if !sys.Initialized() { 1644 return r, errServerNotInitialized 1645 } 1646 1647 if sys.usersSysType != MinIOUsersSysType { 1648 return nil, errIAMActionNotAllowed 1649 } 1650 1651 <-sys.configLoaded 1652 1653 sys.store.rlock() 1654 defer sys.store.runlock() 1655 1656 r = make([]string, 0, len(sys.iamGroupsMap)) 1657 for k := range sys.iamGroupsMap { 1658 r = append(r, k) 1659 } 1660 1661 return r, nil 1662 } 1663 1664 // PolicyDBSet - sets a policy for a user or group in the PolicyDB. 1665 func (sys *IAMSys) PolicyDBSet(name, policy string, isGroup bool) error { 1666 if !sys.Initialized() { 1667 return errServerNotInitialized 1668 } 1669 1670 sys.store.lock() 1671 defer sys.store.unlock() 1672 1673 if sys.usersSysType == LDAPUsersSysType { 1674 return sys.policyDBSet(name, policy, stsUser, isGroup) 1675 } 1676 1677 return sys.policyDBSet(name, policy, regularUser, isGroup) 1678 } 1679 1680 // policyDBSet - sets a policy for user in the policy db. Assumes that caller 1681 // has sys.Lock(). If policy == "", then policy mapping is removed. 1682 func (sys *IAMSys) policyDBSet(name, policyName string, userType IAMUserType, isGroup bool) error { 1683 if name == "" { 1684 return errInvalidArgument 1685 } 1686 1687 if sys.usersSysType == MinIOUsersSysType { 1688 if !isGroup { 1689 if _, ok := sys.iamUsersMap[name]; !ok { 1690 return errNoSuchUser 1691 } 1692 } else { 1693 if _, ok := sys.iamGroupsMap[name]; !ok { 1694 return errNoSuchGroup 1695 } 1696 } 1697 } 1698 1699 // Handle policy mapping removal 1700 if policyName == "" { 1701 if sys.usersSysType == LDAPUsersSysType { 1702 // Add a fallback removal towards previous content that may come back 1703 // as a ghost user due to lack of delete, this change occurred 1704 // introduced in PR #11840 1705 sys.store.deleteMappedPolicy(context.Background(), name, regularUser, false) 1706 } 1707 err := sys.store.deleteMappedPolicy(context.Background(), name, userType, isGroup) 1708 if err != nil && err != errNoSuchPolicy { 1709 return err 1710 } 1711 if !isGroup { 1712 delete(sys.iamUserPolicyMap, name) 1713 } else { 1714 delete(sys.iamGroupPolicyMap, name) 1715 } 1716 return nil 1717 } 1718 1719 mp := newMappedPolicy(policyName) 1720 for _, policy := range mp.toSlice() { 1721 if _, found := sys.iamPolicyDocsMap[policy]; !found { 1722 logger.LogIf(GlobalContext, fmt.Errorf("%w: (%s)", errNoSuchPolicy, policy)) 1723 return errNoSuchPolicy 1724 } 1725 } 1726 1727 // Handle policy mapping set/update 1728 if err := sys.store.saveMappedPolicy(context.Background(), name, userType, isGroup, mp); err != nil { 1729 return err 1730 } 1731 if !isGroup { 1732 sys.iamUserPolicyMap[name] = mp 1733 } else { 1734 sys.iamGroupPolicyMap[name] = mp 1735 } 1736 return nil 1737 } 1738 1739 // PolicyDBGet - gets policy set on a user or group. If a list of groups is 1740 // given, policies associated with them are included as well. 1741 func (sys *IAMSys) PolicyDBGet(name string, isGroup bool, groups ...string) ([]string, error) { 1742 if !sys.Initialized() { 1743 return nil, errServerNotInitialized 1744 } 1745 1746 if name == "" { 1747 return nil, errInvalidArgument 1748 } 1749 1750 sys.store.rlock() 1751 defer sys.store.runlock() 1752 1753 policies, err := sys.policyDBGet(name, isGroup) 1754 if err != nil { 1755 return nil, err 1756 } 1757 1758 if !isGroup { 1759 for _, group := range groups { 1760 ps, err := sys.policyDBGet(group, true) 1761 if err != nil { 1762 return nil, err 1763 } 1764 policies = append(policies, ps...) 1765 } 1766 } 1767 1768 return policies, nil 1769 } 1770 1771 // This call assumes that caller has the sys.RLock(). 1772 // 1773 // If a group is passed, it returns policies associated with the group. 1774 // 1775 // If a user is passed, it returns policies of the user along with any groups 1776 // that the server knows the user is a member of. 1777 // 1778 // In LDAP users mode, the server does not store any group membership 1779 // information in IAM (i.e sys.iam*Map) - this info is stored only in the STS 1780 // generated credentials. Thus we skip looking up group memberships, user map, 1781 // and group map and check the appropriate policy maps directly. 1782 func (sys *IAMSys) policyDBGet(name string, isGroup bool) (policies []string, err error) { 1783 if isGroup { 1784 if sys.usersSysType == MinIOUsersSysType { 1785 g, ok := sys.iamGroupsMap[name] 1786 if !ok { 1787 return nil, errNoSuchGroup 1788 } 1789 1790 // Group is disabled, so we return no policy - this 1791 // ensures the request is denied. 1792 if g.Status == statusDisabled { 1793 return nil, nil 1794 } 1795 } 1796 1797 return sys.iamGroupPolicyMap[name].toSlice(), nil 1798 } 1799 1800 var u auth.Credentials 1801 var ok bool 1802 if sys.usersSysType == MinIOUsersSysType { 1803 // When looking for a user's policies, we also check if the user 1804 // and the groups they are member of are enabled. 1805 1806 u, ok = sys.iamUsersMap[name] 1807 if !ok { 1808 return nil, errNoSuchUser 1809 } 1810 if !u.IsValid() { 1811 return nil, nil 1812 } 1813 } 1814 1815 mp, ok := sys.iamUserPolicyMap[name] 1816 if !ok { 1817 if u.ParentUser != "" { 1818 mp = sys.iamUserPolicyMap[u.ParentUser] 1819 } 1820 } 1821 1822 // returned policy could be empty 1823 policies = append(policies, mp.toSlice()...) 1824 1825 for _, group := range sys.iamUserGroupMemberships[name].ToSlice() { 1826 // Skip missing or disabled groups 1827 gi, ok := sys.iamGroupsMap[group] 1828 if !ok || gi.Status == statusDisabled { 1829 continue 1830 } 1831 1832 policies = append(policies, sys.iamGroupPolicyMap[group].toSlice()...) 1833 } 1834 1835 return policies, nil 1836 } 1837 1838 // IsAllowedServiceAccount - checks if the given service account is allowed to perform 1839 // actions. The permission of the parent user is checked first 1840 func (sys *IAMSys) IsAllowedServiceAccount(args iampolicy.Args, parent string) bool { 1841 // Now check if we have a subject claim 1842 p, ok := args.Claims[parentClaim] 1843 if ok { 1844 parentInClaim, ok := p.(string) 1845 if !ok { 1846 // Reject malformed/malicious requests. 1847 return false 1848 } 1849 // The parent claim in the session token should be equal 1850 // to the parent detected in the backend 1851 if parentInClaim != parent { 1852 return false 1853 } 1854 } else { 1855 // This is needed so a malicious user cannot 1856 // use a leaked session key of another user 1857 // to widen its privileges. 1858 return false 1859 } 1860 1861 // Check policy for this service account. 1862 svcPolicies, err := sys.PolicyDBGet(parent, false, args.Groups...) 1863 if err != nil { 1864 logger.LogIf(GlobalContext, err) 1865 return false 1866 } 1867 1868 if len(svcPolicies) == 0 { 1869 return false 1870 } 1871 1872 var availablePolicies []iampolicy.Policy 1873 1874 // Policies were found, evaluate all of them. 1875 sys.store.rlock() 1876 for _, pname := range svcPolicies { 1877 p, found := sys.iamPolicyDocsMap[pname] 1878 if found { 1879 availablePolicies = append(availablePolicies, p) 1880 } 1881 } 1882 sys.store.runlock() 1883 1884 if len(availablePolicies) == 0 { 1885 return false 1886 } 1887 1888 combinedPolicy := availablePolicies[0] 1889 for i := 1; i < len(availablePolicies); i++ { 1890 combinedPolicy.Statements = append(combinedPolicy.Statements, 1891 availablePolicies[i].Statements...) 1892 } 1893 1894 parentArgs := args 1895 parentArgs.AccountName = parent 1896 1897 saPolicyClaim, ok := args.Claims[iamPolicyClaimNameSA()] 1898 if !ok { 1899 return false 1900 } 1901 1902 saPolicyClaimStr, ok := saPolicyClaim.(string) 1903 if !ok { 1904 // Sub policy if set, should be a string reject 1905 // malformed/malicious requests. 1906 return false 1907 } 1908 1909 if saPolicyClaimStr == "inherited-policy" { 1910 return combinedPolicy.IsAllowed(parentArgs) 1911 } 1912 1913 // Now check if we have a sessionPolicy. 1914 spolicy, ok := args.Claims[iampolicy.SessionPolicyName] 1915 if !ok { 1916 return false 1917 } 1918 1919 spolicyStr, ok := spolicy.(string) 1920 if !ok { 1921 // Sub policy if set, should be a string reject 1922 // malformed/malicious requests. 1923 return false 1924 } 1925 1926 // Check if policy is parseable. 1927 subPolicy, err := iampolicy.ParseConfig(bytes.NewReader([]byte(spolicyStr))) 1928 if err != nil { 1929 // Log any error in input session policy config. 1930 logger.LogIf(GlobalContext, err) 1931 return false 1932 } 1933 1934 // Policy without Version string value reject it. 1935 if subPolicy.Version == "" { 1936 return false 1937 } 1938 1939 return combinedPolicy.IsAllowed(parentArgs) && subPolicy.IsAllowed(parentArgs) 1940 } 1941 1942 // IsAllowedLDAPSTS - checks for LDAP specific claims and values 1943 func (sys *IAMSys) IsAllowedLDAPSTS(args iampolicy.Args, parentUser string) bool { 1944 parentInClaimIface, ok := args.Claims[ldapUser] 1945 if ok { 1946 parentInClaim, ok := parentInClaimIface.(string) 1947 if !ok { 1948 // ldap parentInClaim name is not a string reject it. 1949 return false 1950 } 1951 1952 if parentInClaim != parentUser { 1953 // ldap claim has been modified maliciously reject it. 1954 return false 1955 } 1956 } else { 1957 // no ldap parentInClaim claim present reject it. 1958 return false 1959 } 1960 1961 // Check policy for this LDAP user. 1962 ldapPolicies, err := sys.PolicyDBGet(parentUser, false, args.Groups...) 1963 if err != nil { 1964 return false 1965 } 1966 1967 if len(ldapPolicies) == 0 { 1968 return false 1969 } 1970 1971 var availablePolicies []iampolicy.Policy 1972 1973 // Policies were found, evaluate all of them. 1974 sys.store.rlock() 1975 for _, pname := range ldapPolicies { 1976 p, found := sys.iamPolicyDocsMap[pname] 1977 if found { 1978 availablePolicies = append(availablePolicies, p) 1979 } 1980 } 1981 sys.store.runlock() 1982 1983 if len(availablePolicies) == 0 { 1984 return false 1985 } 1986 1987 combinedPolicy := availablePolicies[0] 1988 for i := 1; i < len(availablePolicies); i++ { 1989 combinedPolicy.Statements = 1990 append(combinedPolicy.Statements, 1991 availablePolicies[i].Statements...) 1992 } 1993 1994 return combinedPolicy.IsAllowed(args) 1995 } 1996 1997 // IsAllowedSTS is meant for STS based temporary credentials, 1998 // which implements claims validation and verification other than 1999 // applying policies. 2000 func (sys *IAMSys) IsAllowedSTS(args iampolicy.Args, parentUser string) bool { 2001 // If it is an LDAP request, check that user and group 2002 // policies allow the request. 2003 if sys.usersSysType == LDAPUsersSysType { 2004 return sys.IsAllowedLDAPSTS(args, parentUser) 2005 } 2006 2007 policies, ok := args.GetPolicies(iamPolicyClaimNameOpenID()) 2008 if !ok { 2009 // When claims are set, it should have a policy claim field. 2010 return false 2011 } 2012 2013 // When claims are set, it should have policies as claim. 2014 if policies.IsEmpty() { 2015 // No policy, no access! 2016 return false 2017 } 2018 2019 sys.store.rlock() 2020 defer sys.store.runlock() 2021 2022 // If policy is available for given user, check the policy. 2023 mp, ok := sys.iamUserPolicyMap[args.AccountName] 2024 if !ok { 2025 // No policy set for the user that we can find, no access! 2026 return false 2027 } 2028 2029 if !policies.Equals(mp.policySet()) { 2030 // When claims has a policy, it should match the 2031 // policy of args.AccountName which server remembers. 2032 // if not reject such requests. 2033 return false 2034 } 2035 2036 var availablePolicies []iampolicy.Policy 2037 for pname := range policies { 2038 p, found := sys.iamPolicyDocsMap[pname] 2039 if !found { 2040 // all policies presented in the claim should exist 2041 logger.LogIf(GlobalContext, fmt.Errorf("expected policy (%s) missing from the JWT claim %s, rejecting the request", pname, iamPolicyClaimNameOpenID())) 2042 return false 2043 } 2044 availablePolicies = append(availablePolicies, p) 2045 } 2046 2047 combinedPolicy := availablePolicies[0] 2048 for i := 1; i < len(availablePolicies); i++ { 2049 combinedPolicy.Statements = append(combinedPolicy.Statements, 2050 availablePolicies[i].Statements...) 2051 } 2052 2053 // Now check if we have a sessionPolicy. 2054 spolicy, ok := args.Claims[iampolicy.SessionPolicyName] 2055 if ok { 2056 spolicyStr, ok := spolicy.(string) 2057 if !ok { 2058 // Sub policy if set, should be a string reject 2059 // malformed/malicious requests. 2060 return false 2061 } 2062 2063 // Check if policy is parseable. 2064 subPolicy, err := iampolicy.ParseConfig(bytes.NewReader([]byte(spolicyStr))) 2065 if err != nil { 2066 // Log any error in input session policy config. 2067 logger.LogIf(GlobalContext, err) 2068 return false 2069 } 2070 2071 // Policy without Version string value reject it. 2072 if subPolicy.Version == "" { 2073 return false 2074 } 2075 2076 // Sub policy is set and valid. 2077 return combinedPolicy.IsAllowed(args) && subPolicy.IsAllowed(args) 2078 } 2079 2080 // Sub policy not set, this is most common since subPolicy 2081 // is optional, use the inherited policies. 2082 return combinedPolicy.IsAllowed(args) 2083 } 2084 2085 // GetCombinedPolicy returns a combined policy combining all policies 2086 func (sys *IAMSys) GetCombinedPolicy(policies ...string) iampolicy.Policy { 2087 // Policies were found, evaluate all of them. 2088 sys.store.rlock() 2089 defer sys.store.runlock() 2090 2091 var availablePolicies []iampolicy.Policy 2092 for _, pname := range policies { 2093 p, found := sys.iamPolicyDocsMap[pname] 2094 if found { 2095 availablePolicies = append(availablePolicies, p) 2096 } 2097 } 2098 2099 if len(availablePolicies) == 0 { 2100 return iampolicy.Policy{} 2101 } 2102 2103 combinedPolicy := availablePolicies[0] 2104 for i := 1; i < len(availablePolicies); i++ { 2105 combinedPolicy.Statements = append(combinedPolicy.Statements, 2106 availablePolicies[i].Statements...) 2107 } 2108 2109 return combinedPolicy 2110 } 2111 2112 // IsAllowed - checks given policy args is allowed to continue the Rest API. 2113 func (sys *IAMSys) IsAllowed(args iampolicy.Args) bool { 2114 // If opa is configured, use OPA always. 2115 if GlobalPolicyOPA != nil { 2116 ok, err := GlobalPolicyOPA.IsAllowed(args) 2117 if err != nil { 2118 logger.LogIf(GlobalContext, err) 2119 } 2120 return ok 2121 } 2122 2123 // Policies don't apply to the owner. 2124 if args.IsOwner { 2125 return true 2126 } 2127 2128 // If the credential is temporary, perform STS related checks. 2129 ok, parentUser, err := sys.IsTempUser(args.AccountName) 2130 if err != nil { 2131 return false 2132 } 2133 if ok { 2134 return sys.IsAllowedSTS(args, parentUser) 2135 } 2136 2137 // If the credential is for a service account, perform related check 2138 ok, parentUser, err = sys.IsServiceAccount(args.AccountName) 2139 if err != nil { 2140 return false 2141 } 2142 if ok { 2143 return sys.IsAllowedServiceAccount(args, parentUser) 2144 } 2145 2146 // Continue with the assumption of a regular user 2147 policies, err := sys.PolicyDBGet(args.AccountName, false, args.Groups...) 2148 if err != nil { 2149 return false 2150 } 2151 2152 if len(policies) == 0 { 2153 // No policy found. 2154 return false 2155 } 2156 2157 // Policies were found, evaluate all of them. 2158 return sys.GetCombinedPolicy(policies...).IsAllowed(args) 2159 } 2160 2161 // Set default canned policies only if not already overridden by users. 2162 func setDefaultCannedPolicies(policies map[string]iampolicy.Policy) { 2163 _, ok := policies["writeonly"] 2164 if !ok { 2165 policies["writeonly"] = iampolicy.WriteOnly 2166 } 2167 _, ok = policies["readonly"] 2168 if !ok { 2169 policies["readonly"] = iampolicy.ReadOnly 2170 } 2171 _, ok = policies["readwrite"] 2172 if !ok { 2173 policies["readwrite"] = iampolicy.ReadWrite 2174 } 2175 _, ok = policies["diagnostics"] 2176 if !ok { 2177 policies["diagnostics"] = iampolicy.AdminDiagnostics 2178 } 2179 _, ok = policies["consoleAdmin"] 2180 if !ok { 2181 policies["consoleAdmin"] = iampolicy.Admin 2182 } 2183 } 2184 2185 // buildUserGroupMemberships - builds the memberships map. IMPORTANT: 2186 // Assumes that sys.Lock is held by caller. 2187 func (sys *IAMSys) buildUserGroupMemberships() { 2188 for group, gi := range sys.iamGroupsMap { 2189 sys.updateGroupMembershipsMap(group, &gi) 2190 } 2191 } 2192 2193 // updateGroupMembershipsMap - updates the memberships map for a 2194 // group. IMPORTANT: Assumes sys.Lock() is held by caller. 2195 func (sys *IAMSys) updateGroupMembershipsMap(group string, gi *GroupInfo) { 2196 if gi == nil { 2197 return 2198 } 2199 for _, member := range gi.Members { 2200 v := sys.iamUserGroupMemberships[member] 2201 if v == nil { 2202 v = set.CreateStringSet(group) 2203 } else { 2204 v.Add(group) 2205 } 2206 sys.iamUserGroupMemberships[member] = v 2207 } 2208 } 2209 2210 // removeGroupFromMembershipsMap - removes the group from every member 2211 // in the cache. IMPORTANT: Assumes sys.Lock() is held by caller. 2212 func (sys *IAMSys) removeGroupFromMembershipsMap(group string) { 2213 for member, groups := range sys.iamUserGroupMemberships { 2214 if !groups.Contains(group) { 2215 continue 2216 } 2217 groups.Remove(group) 2218 sys.iamUserGroupMemberships[member] = groups 2219 } 2220 } 2221 2222 // EnableLDAPSys - enable ldap system users type. 2223 func (sys *IAMSys) EnableLDAPSys() { 2224 sys.usersSysType = LDAPUsersSysType 2225 } 2226 2227 // NewIAMSys - creates new config system object. 2228 func NewIAMSys() *IAMSys { 2229 return &IAMSys{ 2230 usersSysType: MinIOUsersSysType, 2231 iamUsersMap: make(map[string]auth.Credentials), 2232 iamPolicyDocsMap: make(map[string]iampolicy.Policy), 2233 iamUserPolicyMap: make(map[string]MappedPolicy), 2234 iamGroupPolicyMap: make(map[string]MappedPolicy), 2235 iamGroupsMap: make(map[string]GroupInfo), 2236 iamUserGroupMemberships: make(map[string]set.StringSet), 2237 configLoaded: make(chan struct{}), 2238 } 2239 }