mirror of
				https://github.com/minio/minio.git
				synced 2025-10-29 15:55:00 -04:00 
			
		
		
		
	site healing: Skip stale iam asset updates from peer. (#15203)
Allow healing to apply IAM change only when peer gave the most recent update.
This commit is contained in:
		
							parent
							
								
									63ac260bd5
								
							
						
					
					
						commit
						0ea5c9d8e8
					
				| @ -160,7 +160,7 @@ func (a adminAPIHandlers) SRPeerReplicateIAMItem(w http.ResponseWriter, r *http. | |||||||
| 		err = errSRInvalidRequest(errInvalidArgument) | 		err = errSRInvalidRequest(errInvalidArgument) | ||||||
| 	case madmin.SRIAMItemPolicy: | 	case madmin.SRIAMItemPolicy: | ||||||
| 		if item.Policy == nil { | 		if item.Policy == nil { | ||||||
| 			err = globalSiteReplicationSys.PeerAddPolicyHandler(ctx, item.Name, nil) | 			err = globalSiteReplicationSys.PeerAddPolicyHandler(ctx, item.Name, nil, item.UpdatedAt) | ||||||
| 		} else { | 		} else { | ||||||
| 			policy, perr := iampolicy.ParseConfig(bytes.NewReader(item.Policy)) | 			policy, perr := iampolicy.ParseConfig(bytes.NewReader(item.Policy)) | ||||||
| 			if perr != nil { | 			if perr != nil { | ||||||
| @ -168,21 +168,21 @@ func (a adminAPIHandlers) SRPeerReplicateIAMItem(w http.ResponseWriter, r *http. | |||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 			if policy.IsEmpty() { | 			if policy.IsEmpty() { | ||||||
| 				err = globalSiteReplicationSys.PeerAddPolicyHandler(ctx, item.Name, nil) | 				err = globalSiteReplicationSys.PeerAddPolicyHandler(ctx, item.Name, nil, item.UpdatedAt) | ||||||
| 			} else { | 			} else { | ||||||
| 				err = globalSiteReplicationSys.PeerAddPolicyHandler(ctx, item.Name, policy) | 				err = globalSiteReplicationSys.PeerAddPolicyHandler(ctx, item.Name, policy, item.UpdatedAt) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	case madmin.SRIAMItemSvcAcc: | 	case madmin.SRIAMItemSvcAcc: | ||||||
| 		err = globalSiteReplicationSys.PeerSvcAccChangeHandler(ctx, item.SvcAccChange) | 		err = globalSiteReplicationSys.PeerSvcAccChangeHandler(ctx, item.SvcAccChange, item.UpdatedAt) | ||||||
| 	case madmin.SRIAMItemPolicyMapping: | 	case madmin.SRIAMItemPolicyMapping: | ||||||
| 		err = globalSiteReplicationSys.PeerPolicyMappingHandler(ctx, item.PolicyMapping) | 		err = globalSiteReplicationSys.PeerPolicyMappingHandler(ctx, item.PolicyMapping, item.UpdatedAt) | ||||||
| 	case madmin.SRIAMItemSTSAcc: | 	case madmin.SRIAMItemSTSAcc: | ||||||
| 		err = globalSiteReplicationSys.PeerSTSAccHandler(ctx, item.STSCredential) | 		err = globalSiteReplicationSys.PeerSTSAccHandler(ctx, item.STSCredential, item.UpdatedAt) | ||||||
| 	case madmin.SRIAMItemIAMUser: | 	case madmin.SRIAMItemIAMUser: | ||||||
| 		err = globalSiteReplicationSys.PeerIAMUserChangeHandler(ctx, item.IAMUser) | 		err = globalSiteReplicationSys.PeerIAMUserChangeHandler(ctx, item.IAMUser, item.UpdatedAt) | ||||||
| 	case madmin.SRIAMItemGroupInfo: | 	case madmin.SRIAMItemGroupInfo: | ||||||
| 		err = globalSiteReplicationSys.PeerGroupInfoChangeHandler(ctx, item.GroupInfo) | 		err = globalSiteReplicationSys.PeerGroupInfoChangeHandler(ctx, item.GroupInfo, item.UpdatedAt) | ||||||
| 	} | 	} | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		logger.LogIf(ctx, err) | 		logger.LogIf(ctx, err) | ||||||
|  | |||||||
| @ -72,6 +72,7 @@ func (a adminAPIHandlers) RemoveUser(w http.ResponseWriter, r *http.Request) { | |||||||
| 			AccessKey:   accessKey, | 			AccessKey:   accessKey, | ||||||
| 			IsDeleteReq: true, | 			IsDeleteReq: true, | ||||||
| 		}, | 		}, | ||||||
|  | 		UpdatedAt: UTCNow(), | ||||||
| 	}); err != nil { | 	}); err != nil { | ||||||
| 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | ||||||
| 		return | 		return | ||||||
| @ -240,9 +241,9 @@ func (a adminAPIHandlers) UpdateGroupMembers(w http.ResponseWriter, r *http.Requ | |||||||
| 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL) | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 	var updatedAt time.Time | ||||||
| 	if updReq.IsRemove { | 	if updReq.IsRemove { | ||||||
| 		err = globalIAMSys.RemoveUsersFromGroup(ctx, updReq.Group, updReq.Members) | 		updatedAt, err = globalIAMSys.RemoveUsersFromGroup(ctx, updReq.Group, updReq.Members) | ||||||
| 	} else { | 	} else { | ||||||
| 		// Check if group already exists | 		// Check if group already exists | ||||||
| 		if _, gerr := globalIAMSys.GetGroupDescription(updReq.Group); gerr != nil { | 		if _, gerr := globalIAMSys.GetGroupDescription(updReq.Group); gerr != nil { | ||||||
| @ -253,7 +254,7 @@ func (a adminAPIHandlers) UpdateGroupMembers(w http.ResponseWriter, r *http.Requ | |||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		err = globalIAMSys.AddUsersToGroup(ctx, updReq.Group, updReq.Members) | 		updatedAt, err = globalIAMSys.AddUsersToGroup(ctx, updReq.Group, updReq.Members) | ||||||
| 	} | 	} | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | ||||||
| @ -265,6 +266,7 @@ func (a adminAPIHandlers) UpdateGroupMembers(w http.ResponseWriter, r *http.Requ | |||||||
| 		GroupInfo: &madmin.SRGroupInfo{ | 		GroupInfo: &madmin.SRGroupInfo{ | ||||||
| 			UpdateReq: updReq, | 			UpdateReq: updReq, | ||||||
| 		}, | 		}, | ||||||
|  | 		UpdatedAt: updatedAt, | ||||||
| 	}); err != nil { | 	}); err != nil { | ||||||
| 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | ||||||
| 		return | 		return | ||||||
| @ -341,12 +343,15 @@ func (a adminAPIHandlers) SetGroupStatus(w http.ResponseWriter, r *http.Request) | |||||||
| 	group := vars["group"] | 	group := vars["group"] | ||||||
| 	status := vars["status"] | 	status := vars["status"] | ||||||
| 
 | 
 | ||||||
| 	var err error | 	var ( | ||||||
|  | 		err       error | ||||||
|  | 		updatedAt time.Time | ||||||
|  | 	) | ||||||
| 	switch status { | 	switch status { | ||||||
| 	case statusEnabled: | 	case statusEnabled: | ||||||
| 		err = globalIAMSys.SetGroupStatus(ctx, group, true) | 		updatedAt, err = globalIAMSys.SetGroupStatus(ctx, group, true) | ||||||
| 	case statusDisabled: | 	case statusDisabled: | ||||||
| 		err = globalIAMSys.SetGroupStatus(ctx, group, false) | 		updatedAt, err = globalIAMSys.SetGroupStatus(ctx, group, false) | ||||||
| 	default: | 	default: | ||||||
| 		err = errInvalidArgument | 		err = errInvalidArgument | ||||||
| 	} | 	} | ||||||
| @ -364,6 +369,7 @@ func (a adminAPIHandlers) SetGroupStatus(w http.ResponseWriter, r *http.Request) | |||||||
| 				IsRemove: false, | 				IsRemove: false, | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
|  | 		UpdatedAt: updatedAt, | ||||||
| 	}); err != nil { | 	}); err != nil { | ||||||
| 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | ||||||
| 		return | 		return | ||||||
| @ -391,7 +397,8 @@ func (a adminAPIHandlers) SetUserStatus(w http.ResponseWriter, r *http.Request) | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err := globalIAMSys.SetUserStatus(ctx, accessKey, madmin.AccountStatus(status)); err != nil { | 	updatedAt, err := globalIAMSys.SetUserStatus(ctx, accessKey, madmin.AccountStatus(status)) | ||||||
|  | 	if err != nil { | ||||||
| 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| @ -405,6 +412,7 @@ func (a adminAPIHandlers) SetUserStatus(w http.ResponseWriter, r *http.Request) | |||||||
| 				Status: madmin.AccountStatus(status), | 				Status: madmin.AccountStatus(status), | ||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
|  | 		UpdatedAt: updatedAt, | ||||||
| 	}); err != nil { | 	}); err != nil { | ||||||
| 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | ||||||
| 		return | 		return | ||||||
| @ -439,8 +447,8 @@ func (a adminAPIHandlers) AddUser(w http.ResponseWriter, r *http.Request) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	userCred, exists := globalIAMSys.GetUser(ctx, accessKey) | 	user, exists := globalIAMSys.GetUser(ctx, accessKey) | ||||||
| 	if exists && (userCred.IsTemp() || userCred.IsServiceAccount()) { | 	if exists && (user.Credentials.IsTemp() || user.Credentials.IsServiceAccount()) { | ||||||
| 		// Updating STS credential is not allowed, and this API does not | 		// Updating STS credential is not allowed, and this API does not | ||||||
| 		// support updating service accounts. | 		// support updating service accounts. | ||||||
| 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAddUserInvalidArgument), r.URL) | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAddUserInvalidArgument), r.URL) | ||||||
| @ -501,7 +509,8 @@ func (a adminAPIHandlers) AddUser(w http.ResponseWriter, r *http.Request) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err = globalIAMSys.CreateUser(ctx, accessKey, ureq); err != nil { | 	updatedAt, err := globalIAMSys.CreateUser(ctx, accessKey, ureq) | ||||||
|  | 	if err != nil { | ||||||
| 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| @ -513,6 +522,7 @@ func (a adminAPIHandlers) AddUser(w http.ResponseWriter, r *http.Request) { | |||||||
| 			IsDeleteReq: false, | 			IsDeleteReq: false, | ||||||
| 			UserReq:     &ureq, | 			UserReq:     &ureq, | ||||||
| 		}, | 		}, | ||||||
|  | 		UpdatedAt: updatedAt, | ||||||
| 	}); err != nil { | 	}); err != nil { | ||||||
| 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | ||||||
| 		return | 		return | ||||||
| @ -674,7 +684,7 @@ func (a adminAPIHandlers) AddServiceAccount(w http.ResponseWriter, r *http.Reque | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	opts.sessionPolicy = sp | 	opts.sessionPolicy = sp | ||||||
| 	newCred, err := globalIAMSys.NewServiceAccount(ctx, targetUser, targetGroups, opts) | 	newCred, updatedAt, err := globalIAMSys.NewServiceAccount(ctx, targetUser, targetGroups, opts) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | ||||||
| 		return | 		return | ||||||
| @ -717,6 +727,7 @@ func (a adminAPIHandlers) AddServiceAccount(w http.ResponseWriter, r *http.Reque | |||||||
| 					Status:        auth.AccountOn, | 					Status:        auth.AccountOn, | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
|  | 			UpdatedAt: updatedAt, | ||||||
| 		}) | 		}) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			logger.LogIf(ctx, err) | 			logger.LogIf(ctx, err) | ||||||
| @ -800,7 +811,7 @@ func (a adminAPIHandlers) UpdateServiceAccount(w http.ResponseWriter, r *http.Re | |||||||
| 		status:        updateReq.NewStatus, | 		status:        updateReq.NewStatus, | ||||||
| 		sessionPolicy: sp, | 		sessionPolicy: sp, | ||||||
| 	} | 	} | ||||||
| 	err = globalIAMSys.UpdateServiceAccount(ctx, accessKey, opts) | 	updatedAt, err := globalIAMSys.UpdateServiceAccount(ctx, accessKey, opts) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | ||||||
| 		return | 		return | ||||||
| @ -818,6 +829,7 @@ func (a adminAPIHandlers) UpdateServiceAccount(w http.ResponseWriter, r *http.Re | |||||||
| 					SessionPolicy: updateReq.NewPolicy, | 					SessionPolicy: updateReq.NewPolicy, | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
|  | 			UpdatedAt: updatedAt, | ||||||
| 		}) | 		}) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 			writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | ||||||
| @ -1057,6 +1069,7 @@ func (a adminAPIHandlers) DeleteServiceAccount(w http.ResponseWriter, r *http.Re | |||||||
| 					AccessKey: serviceAccount, | 					AccessKey: serviceAccount, | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
|  | 			UpdatedAt: UTCNow(), | ||||||
| 		}); err != nil { | 		}); err != nil { | ||||||
| 			writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 			writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | ||||||
| 			return | 			return | ||||||
| @ -1397,6 +1410,7 @@ func (a adminAPIHandlers) RemoveCannedPolicy(w http.ResponseWriter, r *http.Requ | |||||||
| 	if err := globalSiteReplicationSys.IAMChangeHook(ctx, madmin.SRIAMItem{ | 	if err := globalSiteReplicationSys.IAMChangeHook(ctx, madmin.SRIAMItem{ | ||||||
| 		Type:      madmin.SRIAMItemPolicy, | 		Type:      madmin.SRIAMItemPolicy, | ||||||
| 		Name:      policyName, | 		Name:      policyName, | ||||||
|  | 		UpdatedAt: UTCNow(), | ||||||
| 	}); err != nil { | 	}); err != nil { | ||||||
| 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | ||||||
| 		return | 		return | ||||||
| @ -1453,7 +1467,8 @@ func (a adminAPIHandlers) AddCannedPolicy(w http.ResponseWriter, r *http.Request | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err = globalIAMSys.SetPolicy(ctx, policyName, *iamPolicy); err != nil { | 	updatedAt, err := globalIAMSys.SetPolicy(ctx, policyName, *iamPolicy) | ||||||
|  | 	if err != nil { | ||||||
| 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| @ -1464,6 +1479,7 @@ func (a adminAPIHandlers) AddCannedPolicy(w http.ResponseWriter, r *http.Request | |||||||
| 		Type:      madmin.SRIAMItemPolicy, | 		Type:      madmin.SRIAMItemPolicy, | ||||||
| 		Name:      policyName, | 		Name:      policyName, | ||||||
| 		Policy:    iamPolicyBytes, | 		Policy:    iamPolicyBytes, | ||||||
|  | 		UpdatedAt: updatedAt, | ||||||
| 	}); err != nil { | 	}); err != nil { | ||||||
| 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | ||||||
| 		return | 		return | ||||||
| @ -1498,7 +1514,8 @@ func (a adminAPIHandlers) SetPolicyForUserOrGroup(w http.ResponseWriter, r *http | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err := globalIAMSys.PolicyDBSet(ctx, entityName, policyName, isGroup); err != nil { | 	updatedAt, err := globalIAMSys.PolicyDBSet(ctx, entityName, policyName, isGroup) | ||||||
|  | 	if err != nil { | ||||||
| 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| @ -1510,6 +1527,7 @@ func (a adminAPIHandlers) SetPolicyForUserOrGroup(w http.ResponseWriter, r *http | |||||||
| 			IsGroup:     isGroup, | 			IsGroup:     isGroup, | ||||||
| 			Policy:      policyName, | 			Policy:      policyName, | ||||||
| 		}, | 		}, | ||||||
|  | 		UpdatedAt: updatedAt, | ||||||
| 	}); err != nil { | 	}); err != nil { | ||||||
| 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | ||||||
| 		return | 		return | ||||||
| @ -1597,22 +1615,22 @@ func (a adminAPIHandlers) ExportIAM(w http.ResponseWriter, r *http.Request) { | |||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 		case allUsersFile: | 		case allUsersFile: | ||||||
| 			userCreds := make(map[string]auth.Credentials) | 			userIdentities := make(map[string]UserIdentity) | ||||||
| 			globalIAMSys.store.rlock() | 			globalIAMSys.store.rlock() | ||||||
| 			err := globalIAMSys.store.loadUsers(ctx, regUser, userCreds) | 			err := globalIAMSys.store.loadUsers(ctx, regUser, userIdentities) | ||||||
| 			globalIAMSys.store.runlock() | 			globalIAMSys.store.runlock() | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				writeErrorResponse(ctx, w, exportError(ctx, err, iamFile, ""), r.URL) | 				writeErrorResponse(ctx, w, exportError(ctx, err, iamFile, ""), r.URL) | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 			userAccounts := make(map[string]madmin.AddOrUpdateUserReq) | 			userAccounts := make(map[string]madmin.AddOrUpdateUserReq) | ||||||
| 			for u, cred := range userCreds { | 			for u, uid := range userIdentities { | ||||||
| 				status := madmin.AccountDisabled | 				status := madmin.AccountDisabled | ||||||
| 				if cred.IsValid() { | 				if uid.Credentials.IsValid() { | ||||||
| 					status = madmin.AccountEnabled | 					status = madmin.AccountEnabled | ||||||
| 				} | 				} | ||||||
| 				userAccounts[u] = madmin.AddOrUpdateUserReq{ | 				userAccounts[u] = madmin.AddOrUpdateUserReq{ | ||||||
| 					SecretKey: cred.SecretKey, | 					SecretKey: uid.Credentials.SecretKey, | ||||||
| 					Status:    status, | 					Status:    status, | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| @ -1646,7 +1664,7 @@ func (a adminAPIHandlers) ExportIAM(w http.ResponseWriter, r *http.Request) { | |||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 		case allSvcAcctsFile: | 		case allSvcAcctsFile: | ||||||
| 			serviceAccounts := make(map[string]auth.Credentials) | 			serviceAccounts := make(map[string]UserIdentity) | ||||||
| 			globalIAMSys.store.rlock() | 			globalIAMSys.store.rlock() | ||||||
| 			err := globalIAMSys.store.loadUsers(ctx, svcUser, serviceAccounts) | 			err := globalIAMSys.store.loadUsers(ctx, svcUser, serviceAccounts) | ||||||
| 			globalIAMSys.store.runlock() | 			globalIAMSys.store.runlock() | ||||||
| @ -1661,12 +1679,12 @@ func (a adminAPIHandlers) ExportIAM(w http.ResponseWriter, r *http.Request) { | |||||||
| 					// site replication is enabled. | 					// site replication is enabled. | ||||||
| 					continue | 					continue | ||||||
| 				} | 				} | ||||||
| 				claims, err := globalIAMSys.GetClaimsForSvcAcc(ctx, acc.AccessKey) | 				claims, err := globalIAMSys.GetClaimsForSvcAcc(ctx, acc.Credentials.AccessKey) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					writeErrorResponse(ctx, w, exportError(ctx, err, iamFile, ""), r.URL) | 					writeErrorResponse(ctx, w, exportError(ctx, err, iamFile, ""), r.URL) | ||||||
| 					return | 					return | ||||||
| 				} | 				} | ||||||
| 				_, policy, err := globalIAMSys.GetServiceAccount(ctx, acc.AccessKey) | 				_, policy, err := globalIAMSys.GetServiceAccount(ctx, acc.Credentials.AccessKey) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					writeErrorResponse(ctx, w, exportError(ctx, err, iamFile, ""), r.URL) | 					writeErrorResponse(ctx, w, exportError(ctx, err, iamFile, ""), r.URL) | ||||||
| 					return | 					return | ||||||
| @ -1681,13 +1699,13 @@ func (a adminAPIHandlers) ExportIAM(w http.ResponseWriter, r *http.Request) { | |||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
| 				svcAccts[user] = madmin.SRSvcAccCreate{ | 				svcAccts[user] = madmin.SRSvcAccCreate{ | ||||||
| 					Parent:        acc.ParentUser, | 					Parent:        acc.Credentials.ParentUser, | ||||||
| 					AccessKey:     user, | 					AccessKey:     user, | ||||||
| 					SecretKey:     acc.SecretKey, | 					SecretKey:     acc.Credentials.SecretKey, | ||||||
| 					Groups:        acc.Groups, | 					Groups:        acc.Credentials.Groups, | ||||||
| 					Claims:        claims, | 					Claims:        claims, | ||||||
| 					SessionPolicy: json.RawMessage(policyJSON), | 					SessionPolicy: json.RawMessage(policyJSON), | ||||||
| 					Status:        acc.Status, | 					Status:        acc.Credentials.Status, | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| @ -1832,7 +1850,7 @@ func (a adminAPIHandlers) ImportIAM(w http.ResponseWriter, r *http.Request) { | |||||||
| 				if policy.IsEmpty() { | 				if policy.IsEmpty() { | ||||||
| 					err = globalIAMSys.DeletePolicy(ctx, policyName, true) | 					err = globalIAMSys.DeletePolicy(ctx, policyName, true) | ||||||
| 				} else { | 				} else { | ||||||
| 					err = globalIAMSys.SetPolicy(ctx, policyName, policy) | 					_, err = globalIAMSys.SetPolicy(ctx, policyName, policy) | ||||||
| 				} | 				} | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					writeErrorResponseJSON(ctx, w, importError(ctx, err, allPoliciesFile, policyName), r.URL) | 					writeErrorResponseJSON(ctx, w, importError(ctx, err, allPoliciesFile, policyName), r.URL) | ||||||
| @ -1870,8 +1888,8 @@ func (a adminAPIHandlers) ImportIAM(w http.ResponseWriter, r *http.Request) { | |||||||
| 					return | 					return | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				userCred, exists := globalIAMSys.GetUser(ctx, accessKey) | 				user, exists := globalIAMSys.GetUser(ctx, accessKey) | ||||||
| 				if exists && (userCred.IsTemp() || userCred.IsServiceAccount()) { | 				if exists && (user.Credentials.IsTemp() || user.Credentials.IsServiceAccount()) { | ||||||
| 					// Updating STS credential is not allowed, and this API does not | 					// Updating STS credential is not allowed, and this API does not | ||||||
| 					// support updating service accounts. | 					// support updating service accounts. | ||||||
| 					writeErrorResponseJSON(ctx, w, importErrorWithAPIErr(ctx, ErrAddUserInvalidArgument, err, allUsersFile, accessKey), r.URL) | 					writeErrorResponseJSON(ctx, w, importErrorWithAPIErr(ctx, ErrAddUserInvalidArgument, err, allUsersFile, accessKey), r.URL) | ||||||
| @ -1910,7 +1928,7 @@ func (a adminAPIHandlers) ImportIAM(w http.ResponseWriter, r *http.Request) { | |||||||
| 					writeErrorResponseJSON(ctx, w, importErrorWithAPIErr(ctx, ErrAccessDenied, err, allUsersFile, accessKey), r.URL) | 					writeErrorResponseJSON(ctx, w, importErrorWithAPIErr(ctx, ErrAccessDenied, err, allUsersFile, accessKey), r.URL) | ||||||
| 					return | 					return | ||||||
| 				} | 				} | ||||||
| 				if err = globalIAMSys.CreateUser(ctx, accessKey, ureq); err != nil { | 				if _, err = globalIAMSys.CreateUser(ctx, accessKey, ureq); err != nil { | ||||||
| 					writeErrorResponseJSON(ctx, w, importErrorWithAPIErr(ctx, toAdminAPIErrCode(ctx, err), err, allUsersFile, accessKey), r.URL) | 					writeErrorResponseJSON(ctx, w, importErrorWithAPIErr(ctx, toAdminAPIErrCode(ctx, err), err, allUsersFile, accessKey), r.URL) | ||||||
| 					return | 					return | ||||||
| 				} | 				} | ||||||
| @ -1949,7 +1967,7 @@ func (a adminAPIHandlers) ImportIAM(w http.ResponseWriter, r *http.Request) { | |||||||
| 						return | 						return | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
| 				if gerr := globalIAMSys.AddUsersToGroup(ctx, group, grpInfo.Members); gerr != nil { | 				if _, gerr := globalIAMSys.AddUsersToGroup(ctx, group, grpInfo.Members); gerr != nil { | ||||||
| 					writeErrorResponseJSON(ctx, w, importError(ctx, err, allGroupsFile, group), r.URL) | 					writeErrorResponseJSON(ctx, w, importError(ctx, err, allGroupsFile, group), r.URL) | ||||||
| 					return | 					return | ||||||
| 				} | 				} | ||||||
| @ -2018,7 +2036,7 @@ func (a adminAPIHandlers) ImportIAM(w http.ResponseWriter, r *http.Request) { | |||||||
| 						status:        svcAcctReq.Status, | 						status:        svcAcctReq.Status, | ||||||
| 						sessionPolicy: sp, | 						sessionPolicy: sp, | ||||||
| 					} | 					} | ||||||
| 					err = globalIAMSys.UpdateServiceAccount(ctx, svcAcctReq.AccessKey, opts) | 					_, err = globalIAMSys.UpdateServiceAccount(ctx, svcAcctReq.AccessKey, opts) | ||||||
| 					if err != nil { | 					if err != nil { | ||||||
| 						writeErrorResponseJSON(ctx, w, importError(ctx, err, allSvcAcctsFile, user), r.URL) | 						writeErrorResponseJSON(ctx, w, importError(ctx, err, allSvcAcctsFile, user), r.URL) | ||||||
| 						return | 						return | ||||||
| @ -2044,7 +2062,7 @@ func (a adminAPIHandlers) ImportIAM(w http.ResponseWriter, r *http.Request) { | |||||||
| 					opts.claims[ldapUser] = targetUser // username DN | 					opts.claims[ldapUser] = targetUser // username DN | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				if _, err = globalIAMSys.NewServiceAccount(ctx, svcAcctReq.Parent, svcAcctReq.Groups, opts); err != nil { | 				if _, _, err = globalIAMSys.NewServiceAccount(ctx, svcAcctReq.Parent, svcAcctReq.Groups, opts); err != nil { | ||||||
| 					writeErrorResponseJSON(ctx, w, importError(ctx, err, allSvcAcctsFile, user), r.URL) | 					writeErrorResponseJSON(ctx, w, importError(ctx, err, allSvcAcctsFile, user), r.URL) | ||||||
| 					return | 					return | ||||||
| 				} | 				} | ||||||
| @ -2084,7 +2102,7 @@ func (a adminAPIHandlers) ImportIAM(w http.ResponseWriter, r *http.Request) { | |||||||
| 					writeErrorResponseJSON(ctx, w, importError(ctx, errIAMActionNotAllowed, userPolicyMappingsFile, u), r.URL) | 					writeErrorResponseJSON(ctx, w, importError(ctx, errIAMActionNotAllowed, userPolicyMappingsFile, u), r.URL) | ||||||
| 					return | 					return | ||||||
| 				} | 				} | ||||||
| 				if err := globalIAMSys.PolicyDBSet(ctx, u, pm.Policies, false); err != nil { | 				if _, err := globalIAMSys.PolicyDBSet(ctx, u, pm.Policies, false); err != nil { | ||||||
| 					writeErrorResponseJSON(ctx, w, importError(ctx, err, userPolicyMappingsFile, u), r.URL) | 					writeErrorResponseJSON(ctx, w, importError(ctx, err, userPolicyMappingsFile, u), r.URL) | ||||||
| 					return | 					return | ||||||
| 				} | 				} | ||||||
| @ -2113,7 +2131,7 @@ func (a adminAPIHandlers) ImportIAM(w http.ResponseWriter, r *http.Request) { | |||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 			for g, pm := range grpPolicyMap { | 			for g, pm := range grpPolicyMap { | ||||||
| 				if err := globalIAMSys.PolicyDBSet(ctx, g, pm.Policies, true); err != nil { | 				if _, err := globalIAMSys.PolicyDBSet(ctx, g, pm.Policies, true); err != nil { | ||||||
| 					writeErrorResponseJSON(ctx, w, importError(ctx, err, groupPolicyMappingsFile, g), r.URL) | 					writeErrorResponseJSON(ctx, w, importError(ctx, err, groupPolicyMappingsFile, g), r.URL) | ||||||
| 					return | 					return | ||||||
| 				} | 				} | ||||||
| @ -2152,7 +2170,7 @@ func (a adminAPIHandlers) ImportIAM(w http.ResponseWriter, r *http.Request) { | |||||||
| 					writeErrorResponseJSON(ctx, w, importError(ctx, errIAMActionNotAllowed, stsUserPolicyMappingsFile, u), r.URL) | 					writeErrorResponseJSON(ctx, w, importError(ctx, errIAMActionNotAllowed, stsUserPolicyMappingsFile, u), r.URL) | ||||||
| 					return | 					return | ||||||
| 				} | 				} | ||||||
| 				if err := globalIAMSys.PolicyDBSet(ctx, u, pm.Policies, false); err != nil { | 				if _, err := globalIAMSys.PolicyDBSet(ctx, u, pm.Policies, false); err != nil { | ||||||
| 					writeErrorResponseJSON(ctx, w, importError(ctx, err, stsUserPolicyMappingsFile, u), r.URL) | 					writeErrorResponseJSON(ctx, w, importError(ctx, err, stsUserPolicyMappingsFile, u), r.URL) | ||||||
| 					return | 					return | ||||||
| 				} | 				} | ||||||
| @ -2181,7 +2199,7 @@ func (a adminAPIHandlers) ImportIAM(w http.ResponseWriter, r *http.Request) { | |||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 			for g, pm := range grpPolicyMap { | 			for g, pm := range grpPolicyMap { | ||||||
| 				if err := globalIAMSys.PolicyDBSet(ctx, g, pm.Policies, true); err != nil { | 				if _, err := globalIAMSys.PolicyDBSet(ctx, g, pm.Policies, true); err != nil { | ||||||
| 					writeErrorResponseJSON(ctx, w, importError(ctx, err, stsGroupPolicyMappingsFile, g), r.URL) | 					writeErrorResponseJSON(ctx, w, importError(ctx, err, stsGroupPolicyMappingsFile, g), r.URL) | ||||||
| 					return | 					return | ||||||
| 				} | 				} | ||||||
|  | |||||||
| @ -20,8 +20,6 @@ package cmd | |||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"sync" | 	"sync" | ||||||
| 
 |  | ||||||
| 	"github.com/minio/minio/internal/auth" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type iamDummyStore struct { | type iamDummyStore struct { | ||||||
| @ -79,7 +77,7 @@ func (ids *iamDummyStore) loadPolicyDocs(ctx context.Context, m map[string]Polic | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (ids *iamDummyStore) loadUser(ctx context.Context, user string, userType IAMUserType, m map[string]auth.Credentials) error { | func (ids *iamDummyStore) loadUser(ctx context.Context, user string, userType IAMUserType, m map[string]UserIdentity) error { | ||||||
| 	u, ok := ids.iamUsersMap[user] | 	u, ok := ids.iamUsersMap[user] | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		return errNoSuchUser | 		return errNoSuchUser | ||||||
| @ -88,7 +86,7 @@ func (ids *iamDummyStore) loadUser(ctx context.Context, user string, userType IA | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (ids *iamDummyStore) loadUsers(ctx context.Context, userType IAMUserType, m map[string]auth.Credentials) error { | func (ids *iamDummyStore) loadUsers(ctx context.Context, userType IAMUserType, m map[string]UserIdentity) error { | ||||||
| 	for k, v := range ids.iamUsersMap { | 	for k, v := range ids.iamUsersMap { | ||||||
| 		m[k] = v | 		m[k] = v | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -336,7 +336,7 @@ func (ies *IAMEtcdStore) loadPolicyDocs(ctx context.Context, m map[string]Policy | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (ies *IAMEtcdStore) getUserKV(ctx context.Context, userkv *mvccpb.KeyValue, userType IAMUserType, m map[string]auth.Credentials, basePrefix string) error { | func (ies *IAMEtcdStore) getUserKV(ctx context.Context, userkv *mvccpb.KeyValue, userType IAMUserType, m map[string]UserIdentity, basePrefix string) error { | ||||||
| 	var u UserIdentity | 	var u UserIdentity | ||||||
| 	err := getIAMConfig(&u, userkv.Value, string(userkv.Key)) | 	err := getIAMConfig(&u, userkv.Value, string(userkv.Key)) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @ -349,7 +349,7 @@ func (ies *IAMEtcdStore) getUserKV(ctx context.Context, userkv *mvccpb.KeyValue, | |||||||
| 	return ies.addUser(ctx, user, userType, u, m) | 	return ies.addUser(ctx, user, userType, u, m) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (ies *IAMEtcdStore) addUser(ctx context.Context, user string, userType IAMUserType, u UserIdentity, m map[string]auth.Credentials) error { | func (ies *IAMEtcdStore) addUser(ctx context.Context, user string, userType IAMUserType, u UserIdentity, m map[string]UserIdentity) error { | ||||||
| 	if u.Credentials.IsExpired() { | 	if u.Credentials.IsExpired() { | ||||||
| 		// Delete expired identity. | 		// Delete expired identity. | ||||||
| 		deleteKeyEtcd(ctx, ies.client, getUserIdentityPath(user, userType)) | 		deleteKeyEtcd(ctx, ies.client, getUserIdentityPath(user, userType)) | ||||||
| @ -359,11 +359,11 @@ func (ies *IAMEtcdStore) addUser(ctx context.Context, user string, userType IAMU | |||||||
| 	if u.Credentials.AccessKey == "" { | 	if u.Credentials.AccessKey == "" { | ||||||
| 		u.Credentials.AccessKey = user | 		u.Credentials.AccessKey = user | ||||||
| 	} | 	} | ||||||
| 	m[user] = u.Credentials | 	m[user] = u | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (ies *IAMEtcdStore) loadUser(ctx context.Context, user string, userType IAMUserType, m map[string]auth.Credentials) error { | func (ies *IAMEtcdStore) loadUser(ctx context.Context, user string, userType IAMUserType, m map[string]UserIdentity) error { | ||||||
| 	var u UserIdentity | 	var u UserIdentity | ||||||
| 	err := ies.loadIAMConfig(ctx, &u, getUserIdentityPath(user, userType)) | 	err := ies.loadIAMConfig(ctx, &u, getUserIdentityPath(user, userType)) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @ -375,7 +375,7 @@ func (ies *IAMEtcdStore) loadUser(ctx context.Context, user string, userType IAM | |||||||
| 	return ies.addUser(ctx, user, userType, u, m) | 	return ies.addUser(ctx, user, userType, u, m) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (ies *IAMEtcdStore) loadUsers(ctx context.Context, userType IAMUserType, m map[string]auth.Credentials) error { | func (ies *IAMEtcdStore) loadUsers(ctx context.Context, userType IAMUserType, m map[string]UserIdentity) error { | ||||||
| 	var basePrefix string | 	var basePrefix string | ||||||
| 	switch userType { | 	switch userType { | ||||||
| 	case svcUser: | 	case svcUser: | ||||||
|  | |||||||
| @ -287,7 +287,7 @@ func (iamOS *IAMObjectStore) loadPolicyDocs(ctx context.Context, m map[string]Po | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (iamOS *IAMObjectStore) loadUser(ctx context.Context, user string, userType IAMUserType, m map[string]auth.Credentials) error { | func (iamOS *IAMObjectStore) loadUser(ctx context.Context, user string, userType IAMUserType, m map[string]UserIdentity) error { | ||||||
| 	var u UserIdentity | 	var u UserIdentity | ||||||
| 	err := iamOS.loadIAMConfig(ctx, &u, getUserIdentityPath(user, userType)) | 	err := iamOS.loadIAMConfig(ctx, &u, getUserIdentityPath(user, userType)) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @ -308,11 +308,11 @@ func (iamOS *IAMObjectStore) loadUser(ctx context.Context, user string, userType | |||||||
| 		u.Credentials.AccessKey = user | 		u.Credentials.AccessKey = user | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	m[user] = u.Credentials | 	m[user] = u | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (iamOS *IAMObjectStore) loadUsers(ctx context.Context, userType IAMUserType, m map[string]auth.Credentials) error { | func (iamOS *IAMObjectStore) loadUsers(ctx context.Context, userType IAMUserType, m map[string]UserIdentity) error { | ||||||
| 	var basePrefix string | 	var basePrefix string | ||||||
| 	switch userType { | 	switch userType { | ||||||
| 	case svcUser: | 	case svcUser: | ||||||
|  | |||||||
							
								
								
									
										282
									
								
								cmd/iam-store.go
									
									
									
									
									
								
							
							
						
						
									
										282
									
								
								cmd/iam-store.go
									
									
									
									
									
								
							| @ -249,7 +249,7 @@ type iamCache struct { | |||||||
| 	// map of policy names to policy definitions | 	// map of policy names to policy definitions | ||||||
| 	iamPolicyDocsMap map[string]PolicyDoc | 	iamPolicyDocsMap map[string]PolicyDoc | ||||||
| 	// map of usernames to credentials | 	// map of usernames to credentials | ||||||
| 	iamUsersMap map[string]auth.Credentials | 	iamUsersMap map[string]UserIdentity | ||||||
| 	// map of group names to group info | 	// map of group names to group info | ||||||
| 	iamGroupsMap map[string]GroupInfo | 	iamGroupsMap map[string]GroupInfo | ||||||
| 	// map of user names to groups they are a member of | 	// map of user names to groups they are a member of | ||||||
| @ -263,7 +263,7 @@ type iamCache struct { | |||||||
| func newIamCache() *iamCache { | func newIamCache() *iamCache { | ||||||
| 	return &iamCache{ | 	return &iamCache{ | ||||||
| 		iamPolicyDocsMap:        map[string]PolicyDoc{}, | 		iamPolicyDocsMap:        map[string]PolicyDoc{}, | ||||||
| 		iamUsersMap:             map[string]auth.Credentials{}, | 		iamUsersMap:             map[string]UserIdentity{}, | ||||||
| 		iamGroupsMap:            map[string]GroupInfo{}, | 		iamGroupsMap:            map[string]GroupInfo{}, | ||||||
| 		iamUserGroupMemberships: map[string]set.StringSet{}, | 		iamUserGroupMemberships: map[string]set.StringSet{}, | ||||||
| 		iamUserPolicyMap:        map[string]MappedPolicy{}, | 		iamUserPolicyMap:        map[string]MappedPolicy{}, | ||||||
| @ -346,16 +346,16 @@ func (c *iamCache) policyDBGet(mode UsersSysType, name string, isGroup bool) ([] | |||||||
| 	var parentName string | 	var parentName string | ||||||
| 	u, ok := c.iamUsersMap[name] | 	u, ok := c.iamUsersMap[name] | ||||||
| 	if ok { | 	if ok { | ||||||
| 		if !u.IsValid() { | 		if !u.Credentials.IsValid() { | ||||||
| 			return nil, time.Time{}, nil | 			return nil, time.Time{}, nil | ||||||
| 		} | 		} | ||||||
| 		parentName = u.ParentUser | 		parentName = u.Credentials.ParentUser | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	mp, ok := c.iamUserPolicyMap[name] | 	mp, ok := c.iamUserPolicyMap[name] | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		// Service accounts with root credentials, inherit parent permissions | 		// Service accounts with root credentials, inherit parent permissions | ||||||
| 		if parentName == globalActiveCred.AccessKey && u.IsServiceAccount() { | 		if parentName == globalActiveCred.AccessKey && u.Credentials.IsServiceAccount() { | ||||||
| 			// even if this is set, the claims present in the service | 			// even if this is set, the claims present in the service | ||||||
| 			// accounts apply the final permissions if any. | 			// accounts apply the final permissions if any. | ||||||
| 			return []string{"consoleAdmin"}, mp.UpdatedAt, nil | 			return []string{"consoleAdmin"}, mp.UpdatedAt, nil | ||||||
| @ -395,8 +395,8 @@ type IAMStorageAPI interface { | |||||||
| 	getUsersSysType() UsersSysType | 	getUsersSysType() UsersSysType | ||||||
| 	loadPolicyDoc(ctx context.Context, policy string, m map[string]PolicyDoc) error | 	loadPolicyDoc(ctx context.Context, policy string, m map[string]PolicyDoc) error | ||||||
| 	loadPolicyDocs(ctx context.Context, m map[string]PolicyDoc) error | 	loadPolicyDocs(ctx context.Context, m map[string]PolicyDoc) error | ||||||
| 	loadUser(ctx context.Context, user string, userType IAMUserType, m map[string]auth.Credentials) error | 	loadUser(ctx context.Context, user string, userType IAMUserType, m map[string]UserIdentity) error | ||||||
| 	loadUsers(ctx context.Context, userType IAMUserType, m map[string]auth.Credentials) error | 	loadUsers(ctx context.Context, userType IAMUserType, m map[string]UserIdentity) error | ||||||
| 	loadGroup(ctx context.Context, group string, m map[string]GroupInfo) error | 	loadGroup(ctx context.Context, group string, m map[string]GroupInfo) error | ||||||
| 	loadGroups(ctx context.Context, m map[string]GroupInfo) error | 	loadGroups(ctx context.Context, m map[string]GroupInfo) error | ||||||
| 	loadMappedPolicy(ctx context.Context, name string, userType IAMUserType, isGroup bool, m map[string]MappedPolicy) error | 	loadMappedPolicy(ctx context.Context, name string, userType IAMUserType, isGroup bool, m map[string]MappedPolicy) error | ||||||
| @ -526,12 +526,12 @@ func (store *IAMStoreSys) HasWatcher() bool { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // GetUser - fetches credential from memory. | // GetUser - fetches credential from memory. | ||||||
| func (store *IAMStoreSys) GetUser(user string) (auth.Credentials, bool) { | func (store *IAMStoreSys) GetUser(user string) (UserIdentity, bool) { | ||||||
| 	cache := store.rlock() | 	cache := store.rlock() | ||||||
| 	defer store.runlock() | 	defer store.runlock() | ||||||
| 
 | 
 | ||||||
| 	c, ok := cache.iamUsersMap[user] | 	u, ok := cache.iamUsersMap[user] | ||||||
| 	return c, ok | 	return u, ok | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // GetMappedPolicy - fetches mapped policy from memory. | // GetMappedPolicy - fetches mapped policy from memory. | ||||||
| @ -614,9 +614,9 @@ func (store *IAMStoreSys) PolicyDBGet(name string, isGroup bool, groups ...strin | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // AddUsersToGroup - adds users to group, creating the group if needed. | // AddUsersToGroup - adds users to group, creating the group if needed. | ||||||
| func (store *IAMStoreSys) AddUsersToGroup(ctx context.Context, group string, members []string) error { | func (store *IAMStoreSys) AddUsersToGroup(ctx context.Context, group string, members []string) (updatedAt time.Time, err error) { | ||||||
| 	if group == "" { | 	if group == "" { | ||||||
| 		return errInvalidArgument | 		return updatedAt, errInvalidArgument | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	cache := store.lock() | 	cache := store.lock() | ||||||
| @ -624,12 +624,13 @@ func (store *IAMStoreSys) AddUsersToGroup(ctx context.Context, group string, mem | |||||||
| 
 | 
 | ||||||
| 	// Validate that all members exist. | 	// Validate that all members exist. | ||||||
| 	for _, member := range members { | 	for _, member := range members { | ||||||
| 		cr, ok := cache.iamUsersMap[member] | 		u, ok := cache.iamUsersMap[member] | ||||||
| 		if !ok { | 		if !ok { | ||||||
| 			return errNoSuchUser | 			return updatedAt, errNoSuchUser | ||||||
| 		} | 		} | ||||||
|  | 		cr := u.Credentials | ||||||
| 		if cr.IsTemp() || cr.IsServiceAccount() { | 		if cr.IsTemp() || cr.IsServiceAccount() { | ||||||
| 			return errIAMActionNotAllowed | 			return updatedAt, errIAMActionNotAllowed | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -640,10 +641,11 @@ func (store *IAMStoreSys) AddUsersToGroup(ctx context.Context, group string, mem | |||||||
| 		gi = newGroupInfo(members) | 		gi = newGroupInfo(members) | ||||||
| 	} else { | 	} else { | ||||||
| 		gi.Members = set.CreateStringSet(append(gi.Members, members...)...).ToSlice() | 		gi.Members = set.CreateStringSet(append(gi.Members, members...)...).ToSlice() | ||||||
|  | 		gi.UpdatedAt = UTCNow() | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err := store.saveGroupInfo(ctx, group, gi); err != nil { | 	if err := store.saveGroupInfo(ctx, group, gi); err != nil { | ||||||
| 		return err | 		return updatedAt, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	cache.iamGroupsMap[group] = gi | 	cache.iamGroupsMap[group] = gi | ||||||
| @ -660,16 +662,15 @@ func (store *IAMStoreSys) AddUsersToGroup(ctx context.Context, group string, mem | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	cache.updatedAt = time.Now() | 	cache.updatedAt = time.Now() | ||||||
| 
 | 	return gi.UpdatedAt, nil | ||||||
| 	return nil |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // helper function - does not take any locks. Updates only cache if | // helper function - does not take any locks. Updates only cache if | ||||||
| // updateCacheOnly is set. | // updateCacheOnly is set. | ||||||
| func removeMembersFromGroup(ctx context.Context, store *IAMStoreSys, cache *iamCache, group string, members []string, updateCacheOnly bool) error { | func removeMembersFromGroup(ctx context.Context, store *IAMStoreSys, cache *iamCache, group string, members []string, updateCacheOnly bool) (updatedAt time.Time, err error) { | ||||||
| 	gi, ok := cache.iamGroupsMap[group] | 	gi, ok := cache.iamGroupsMap[group] | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		return errNoSuchGroup | 		return updatedAt, errNoSuchGroup | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	s := set.CreateStringSet(gi.Members...) | 	s := set.CreateStringSet(gi.Members...) | ||||||
| @ -679,9 +680,10 @@ func removeMembersFromGroup(ctx context.Context, store *IAMStoreSys, cache *iamC | |||||||
| 	if !updateCacheOnly { | 	if !updateCacheOnly { | ||||||
| 		err := store.saveGroupInfo(ctx, group, gi) | 		err := store.saveGroupInfo(ctx, group, gi) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return updatedAt, err | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | 	gi.UpdatedAt = UTCNow() | ||||||
| 	cache.iamGroupsMap[group] = gi | 	cache.iamGroupsMap[group] = gi | ||||||
| 
 | 
 | ||||||
| 	// update user-group membership map | 	// update user-group membership map | ||||||
| @ -695,13 +697,13 @@ func removeMembersFromGroup(ctx context.Context, store *IAMStoreSys, cache *iamC | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	cache.updatedAt = time.Now() | 	cache.updatedAt = time.Now() | ||||||
| 	return nil | 	return gi.UpdatedAt, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // RemoveUsersFromGroup - removes users from group, deleting it if it is empty. | // RemoveUsersFromGroup - removes users from group, deleting it if it is empty. | ||||||
| func (store *IAMStoreSys) RemoveUsersFromGroup(ctx context.Context, group string, members []string) error { | func (store *IAMStoreSys) RemoveUsersFromGroup(ctx context.Context, group string, members []string) (updatedAt time.Time, err error) { | ||||||
| 	if group == "" { | 	if group == "" { | ||||||
| 		return errInvalidArgument | 		return updatedAt, errInvalidArgument | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	cache := store.lock() | 	cache := store.lock() | ||||||
| @ -709,23 +711,24 @@ func (store *IAMStoreSys) RemoveUsersFromGroup(ctx context.Context, group string | |||||||
| 
 | 
 | ||||||
| 	// Validate that all members exist. | 	// Validate that all members exist. | ||||||
| 	for _, member := range members { | 	for _, member := range members { | ||||||
| 		cr, ok := cache.iamUsersMap[member] | 		u, ok := cache.iamUsersMap[member] | ||||||
| 		if !ok { | 		if !ok { | ||||||
| 			return errNoSuchUser | 			return updatedAt, errNoSuchUser | ||||||
| 		} | 		} | ||||||
|  | 		cr := u.Credentials | ||||||
| 		if cr.IsTemp() || cr.IsServiceAccount() { | 		if cr.IsTemp() || cr.IsServiceAccount() { | ||||||
| 			return errIAMActionNotAllowed | 			return updatedAt, errIAMActionNotAllowed | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	gi, ok := cache.iamGroupsMap[group] | 	gi, ok := cache.iamGroupsMap[group] | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		return errNoSuchGroup | 		return updatedAt, errNoSuchGroup | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Check if attempting to delete a non-empty group. | 	// Check if attempting to delete a non-empty group. | ||||||
| 	if len(members) == 0 && len(gi.Members) != 0 { | 	if len(members) == 0 && len(gi.Members) != 0 { | ||||||
| 		return errGroupNotEmpty | 		return updatedAt, errGroupNotEmpty | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if len(members) == 0 { | 	if len(members) == 0 { | ||||||
| @ -734,26 +737,26 @@ func (store *IAMStoreSys) RemoveUsersFromGroup(ctx context.Context, group string | |||||||
| 		// Remove the group from storage. First delete the | 		// Remove the group from storage. First delete the | ||||||
| 		// mapped policy. No-mapped-policy case is ignored. | 		// mapped policy. No-mapped-policy case is ignored. | ||||||
| 		if err := store.deleteMappedPolicy(ctx, group, regUser, true); err != nil && err != errNoSuchPolicy { | 		if err := store.deleteMappedPolicy(ctx, group, regUser, true); err != nil && err != errNoSuchPolicy { | ||||||
| 			return err | 			return updatedAt, err | ||||||
| 		} | 		} | ||||||
| 		if err := store.deleteGroupInfo(ctx, group); err != nil && err != errNoSuchGroup { | 		if err := store.deleteGroupInfo(ctx, group); err != nil && err != errNoSuchGroup { | ||||||
| 			return err | 			return updatedAt, err | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// Delete from server memory | 		// Delete from server memory | ||||||
| 		delete(cache.iamGroupsMap, group) | 		delete(cache.iamGroupsMap, group) | ||||||
| 		delete(cache.iamGroupPolicyMap, group) | 		delete(cache.iamGroupPolicyMap, group) | ||||||
| 		cache.updatedAt = time.Now() | 		cache.updatedAt = time.Now() | ||||||
| 		return nil | 		return cache.updatedAt, nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return removeMembersFromGroup(ctx, store, cache, group, members, false) | 	return removeMembersFromGroup(ctx, store, cache, group, members, false) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // SetGroupStatus - updates group status | // SetGroupStatus - updates group status | ||||||
| func (store *IAMStoreSys) SetGroupStatus(ctx context.Context, group string, enabled bool) error { | func (store *IAMStoreSys) SetGroupStatus(ctx context.Context, group string, enabled bool) (updatedAt time.Time, err error) { | ||||||
| 	if group == "" { | 	if group == "" { | ||||||
| 		return errInvalidArgument | 		return updatedAt, errInvalidArgument | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	cache := store.lock() | 	cache := store.lock() | ||||||
| @ -761,7 +764,7 @@ func (store *IAMStoreSys) SetGroupStatus(ctx context.Context, group string, enab | |||||||
| 
 | 
 | ||||||
| 	gi, ok := cache.iamGroupsMap[group] | 	gi, ok := cache.iamGroupsMap[group] | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		return errNoSuchGroup | 		return updatedAt, errNoSuchGroup | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if enabled { | 	if enabled { | ||||||
| @ -769,15 +772,15 @@ func (store *IAMStoreSys) SetGroupStatus(ctx context.Context, group string, enab | |||||||
| 	} else { | 	} else { | ||||||
| 		gi.Status = statusDisabled | 		gi.Status = statusDisabled | ||||||
| 	} | 	} | ||||||
| 
 | 	gi.UpdatedAt = UTCNow() | ||||||
| 	if err := store.saveGroupInfo(ctx, group, gi); err != nil { | 	if err := store.saveGroupInfo(ctx, group, gi); err != nil { | ||||||
| 		return err | 		return gi.UpdatedAt, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	cache.iamGroupsMap[group] = gi | 	cache.iamGroupsMap[group] = gi | ||||||
| 	cache.updatedAt = time.Now() | 	cache.updatedAt = time.Now() | ||||||
| 
 | 
 | ||||||
| 	return nil | 	return gi.UpdatedAt, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // GetGroupDescription - builds up group description | // GetGroupDescription - builds up group description | ||||||
| @ -851,9 +854,9 @@ func (store *IAMStoreSys) ListGroups(ctx context.Context) (res []string, err err | |||||||
| 
 | 
 | ||||||
| // PolicyDBSet - update the policy mapping for the given user or group in | // PolicyDBSet - update the policy mapping for the given user or group in | ||||||
| // storage and in cache. | // storage and in cache. | ||||||
| func (store *IAMStoreSys) PolicyDBSet(ctx context.Context, name, policy string, userType IAMUserType, isGroup bool) error { | func (store *IAMStoreSys) PolicyDBSet(ctx context.Context, name, policy string, userType IAMUserType, isGroup bool) (updatedAt time.Time, err error) { | ||||||
| 	if name == "" { | 	if name == "" { | ||||||
| 		return errInvalidArgument | 		return updatedAt, errInvalidArgument | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	cache := store.lock() | 	cache := store.lock() | ||||||
| @ -863,11 +866,11 @@ func (store *IAMStoreSys) PolicyDBSet(ctx context.Context, name, policy string, | |||||||
| 	if store.getUsersSysType() == MinIOUsersSysType { | 	if store.getUsersSysType() == MinIOUsersSysType { | ||||||
| 		if !isGroup { | 		if !isGroup { | ||||||
| 			if _, ok := cache.iamUsersMap[name]; !ok { | 			if _, ok := cache.iamUsersMap[name]; !ok { | ||||||
| 				return errNoSuchUser | 				return updatedAt, errNoSuchUser | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
| 			if _, ok := cache.iamGroupsMap[name]; !ok { | 			if _, ok := cache.iamGroupsMap[name]; !ok { | ||||||
| 				return errNoSuchGroup | 				return updatedAt, errNoSuchGroup | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @ -882,7 +885,7 @@ func (store *IAMStoreSys) PolicyDBSet(ctx context.Context, name, policy string, | |||||||
| 		} | 		} | ||||||
| 		err := store.deleteMappedPolicy(ctx, name, userType, isGroup) | 		err := store.deleteMappedPolicy(ctx, name, userType, isGroup) | ||||||
| 		if err != nil && err != errNoSuchPolicy { | 		if err != nil && err != errNoSuchPolicy { | ||||||
| 			return err | 			return updatedAt, err | ||||||
| 		} | 		} | ||||||
| 		if !isGroup { | 		if !isGroup { | ||||||
| 			delete(cache.iamUserPolicyMap, name) | 			delete(cache.iamUserPolicyMap, name) | ||||||
| @ -890,8 +893,7 @@ func (store *IAMStoreSys) PolicyDBSet(ctx context.Context, name, policy string, | |||||||
| 			delete(cache.iamGroupPolicyMap, name) | 			delete(cache.iamGroupPolicyMap, name) | ||||||
| 		} | 		} | ||||||
| 		cache.updatedAt = time.Now() | 		cache.updatedAt = time.Now() | ||||||
| 
 | 		return cache.updatedAt, nil | ||||||
| 		return nil |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Handle policy mapping set/update | 	// Handle policy mapping set/update | ||||||
| @ -899,12 +901,12 @@ func (store *IAMStoreSys) PolicyDBSet(ctx context.Context, name, policy string, | |||||||
| 	for _, p := range mp.toSlice() { | 	for _, p := range mp.toSlice() { | ||||||
| 		if _, found := cache.iamPolicyDocsMap[p]; !found { | 		if _, found := cache.iamPolicyDocsMap[p]; !found { | ||||||
| 			logger.LogIf(GlobalContext, fmt.Errorf("%w: (%s)", errNoSuchPolicy, p)) | 			logger.LogIf(GlobalContext, fmt.Errorf("%w: (%s)", errNoSuchPolicy, p)) | ||||||
| 			return errNoSuchPolicy | 			return updatedAt, errNoSuchPolicy | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err := store.saveMappedPolicy(ctx, name, userType, isGroup, mp); err != nil { | 	if err := store.saveMappedPolicy(ctx, name, userType, isGroup, mp); err != nil { | ||||||
| 		return err | 		return updatedAt, err | ||||||
| 	} | 	} | ||||||
| 	if !isGroup { | 	if !isGroup { | ||||||
| 		cache.iamUserPolicyMap[name] = mp | 		cache.iamUserPolicyMap[name] = mp | ||||||
| @ -912,7 +914,7 @@ func (store *IAMStoreSys) PolicyDBSet(ctx context.Context, name, policy string, | |||||||
| 		cache.iamGroupPolicyMap[name] = mp | 		cache.iamGroupPolicyMap[name] = mp | ||||||
| 	} | 	} | ||||||
| 	cache.updatedAt = time.Now() | 	cache.updatedAt = time.Now() | ||||||
| 	return nil | 	return mp.UpdatedAt, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // PolicyNotificationHandler - loads given policy from storage. If not present, | // PolicyNotificationHandler - loads given policy from storage. If not present, | ||||||
| @ -1068,9 +1070,9 @@ func (store *IAMStoreSys) GetPolicyDoc(name string) (r PolicyDoc, err error) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // SetPolicy - creates a policy with name. | // SetPolicy - creates a policy with name. | ||||||
| func (store *IAMStoreSys) SetPolicy(ctx context.Context, name string, policy iampolicy.Policy) error { | func (store *IAMStoreSys) SetPolicy(ctx context.Context, name string, policy iampolicy.Policy) (time.Time, error) { | ||||||
| 	if policy.IsEmpty() || name == "" { | 	if policy.IsEmpty() || name == "" { | ||||||
| 		return errInvalidArgument | 		return time.Time{}, errInvalidArgument | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	cache := store.lock() | 	cache := store.lock() | ||||||
| @ -1087,13 +1089,13 @@ func (store *IAMStoreSys) SetPolicy(ctx context.Context, name string, policy iam | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err := store.savePolicyDoc(ctx, name, d); err != nil { | 	if err := store.savePolicyDoc(ctx, name, d); err != nil { | ||||||
| 		return err | 		return d.UpdateDate, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	cache.iamPolicyDocsMap[name] = d | 	cache.iamPolicyDocsMap[name] = d | ||||||
| 	cache.updatedAt = time.Now() | 	cache.updatedAt = time.Now() | ||||||
| 
 | 
 | ||||||
| 	return nil | 	return d.UpdateDate, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ListPolicies - fetches all policies from storage and updates cache as well. | // ListPolicies - fetches all policies from storage and updates cache as well. | ||||||
| @ -1198,7 +1200,8 @@ func (store *IAMStoreSys) GetBucketUsers(bucket string) (map[string]madmin.UserI | |||||||
| 
 | 
 | ||||||
| 	result := map[string]madmin.UserInfo{} | 	result := map[string]madmin.UserInfo{} | ||||||
| 	for k, v := range cache.iamUsersMap { | 	for k, v := range cache.iamUsersMap { | ||||||
| 		if v.IsTemp() || v.IsServiceAccount() { | 		c := v.Credentials | ||||||
|  | 		if c.IsTemp() || c.IsServiceAccount() { | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| 		var policies []string | 		var policies []string | ||||||
| @ -1216,7 +1219,7 @@ func (store *IAMStoreSys) GetBucketUsers(bucket string) (map[string]madmin.UserI | |||||||
| 			result[k] = madmin.UserInfo{ | 			result[k] = madmin.UserInfo{ | ||||||
| 				PolicyName: matchedPolicies, | 				PolicyName: matchedPolicies, | ||||||
| 				Status: func() madmin.AccountStatus { | 				Status: func() madmin.AccountStatus { | ||||||
| 					if v.IsValid() { | 					if c.IsValid() { | ||||||
| 						return madmin.AccountEnabled | 						return madmin.AccountEnabled | ||||||
| 					} | 					} | ||||||
| 					return madmin.AccountDisabled | 					return madmin.AccountDisabled | ||||||
| @ -1235,7 +1238,9 @@ func (store *IAMStoreSys) GetUsers() map[string]madmin.UserInfo { | |||||||
| 	defer store.runlock() | 	defer store.runlock() | ||||||
| 
 | 
 | ||||||
| 	result := map[string]madmin.UserInfo{} | 	result := map[string]madmin.UserInfo{} | ||||||
| 	for k, v := range cache.iamUsersMap { | 	for k, u := range cache.iamUsersMap { | ||||||
|  | 		v := u.Credentials | ||||||
|  | 
 | ||||||
| 		if v.IsTemp() || v.IsServiceAccount() { | 		if v.IsTemp() || v.IsServiceAccount() { | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| @ -1281,8 +1286,8 @@ func (store *IAMStoreSys) GetUserInfo(name string) (u madmin.UserInfo, err error | |||||||
| 		// return that info. Otherwise we return error. | 		// return that info. Otherwise we return error. | ||||||
| 		var groups []string | 		var groups []string | ||||||
| 		for _, v := range cache.iamUsersMap { | 		for _, v := range cache.iamUsersMap { | ||||||
| 			if v.ParentUser == name { | 			if v.Credentials.ParentUser == name { | ||||||
| 				groups = v.Groups | 				groups = v.Credentials.Groups | ||||||
| 				break | 				break | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| @ -1297,11 +1302,11 @@ func (store *IAMStoreSys) GetUserInfo(name string) (u madmin.UserInfo, err error | |||||||
| 		}, nil | 		}, nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	cred, found := cache.iamUsersMap[name] | 	ui, found := cache.iamUsersMap[name] | ||||||
| 	if !found { | 	if !found { | ||||||
| 		return u, errNoSuchUser | 		return u, errNoSuchUser | ||||||
| 	} | 	} | ||||||
| 
 | 	cred := ui.Credentials | ||||||
| 	if cred.IsTemp() || cred.IsServiceAccount() { | 	if cred.IsTemp() || cred.IsServiceAccount() { | ||||||
| 		return u, errIAMActionNotAllowed | 		return u, errIAMActionNotAllowed | ||||||
| 	} | 	} | ||||||
| @ -1363,7 +1368,7 @@ func (store *IAMStoreSys) UserNotificationHandler(ctx context.Context, accessKey | |||||||
| 		if store.getUsersSysType() == MinIOUsersSysType { | 		if store.getUsersSysType() == MinIOUsersSysType { | ||||||
| 			memberOf := cache.iamUserGroupMemberships[accessKey].ToSlice() | 			memberOf := cache.iamUserGroupMemberships[accessKey].ToSlice() | ||||||
| 			for _, group := range memberOf { | 			for _, group := range memberOf { | ||||||
| 				removeErr := removeMembersFromGroup(ctx, store, cache, group, []string{accessKey}, true) | 				_, removeErr := removeMembersFromGroup(ctx, store, cache, group, []string{accessKey}, true) | ||||||
| 				if removeErr == errNoSuchGroup { | 				if removeErr == errNoSuchGroup { | ||||||
| 					removeErr = nil | 					removeErr = nil | ||||||
| 				} | 				} | ||||||
| @ -1376,11 +1381,11 @@ func (store *IAMStoreSys) UserNotificationHandler(ctx context.Context, accessKey | |||||||
| 		// 2. Remove any derived credentials from memory | 		// 2. Remove any derived credentials from memory | ||||||
| 		if userType == regUser { | 		if userType == regUser { | ||||||
| 			for _, u := range cache.iamUsersMap { | 			for _, u := range cache.iamUsersMap { | ||||||
| 				if u.IsServiceAccount() && u.ParentUser == accessKey { | 				if u.Credentials.IsServiceAccount() && u.Credentials.ParentUser == accessKey { | ||||||
| 					delete(cache.iamUsersMap, u.AccessKey) | 					delete(cache.iamUsersMap, u.Credentials.AccessKey) | ||||||
| 				} | 				} | ||||||
| 				if u.IsTemp() && u.ParentUser == accessKey { | 				if u.Credentials.IsTemp() && u.Credentials.ParentUser == accessKey { | ||||||
| 					delete(cache.iamUsersMap, u.AccessKey) | 					delete(cache.iamUsersMap, u.Credentials.AccessKey) | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| @ -1412,8 +1417,9 @@ func (store *IAMStoreSys) UserNotificationHandler(ctx context.Context, accessKey | |||||||
| 	// This mapping is necessary to ensure that valid credentials | 	// This mapping is necessary to ensure that valid credentials | ||||||
| 	// have necessary ParentUser present - this is mainly for only | 	// have necessary ParentUser present - this is mainly for only | ||||||
| 	// webIdentity based STS tokens. | 	// webIdentity based STS tokens. | ||||||
| 	cred, ok := cache.iamUsersMap[accessKey] | 	u, ok := cache.iamUsersMap[accessKey] | ||||||
| 	if ok { | 	if ok { | ||||||
|  | 		cred := u.Credentials | ||||||
| 		if cred.IsTemp() && cred.ParentUser != "" && cred.ParentUser != globalActiveCred.AccessKey { | 		if cred.IsTemp() && cred.ParentUser != "" && cred.ParentUser != globalActiveCred.AccessKey { | ||||||
| 			if _, ok := cache.iamUserPolicyMap[cred.ParentUser]; !ok { | 			if _, ok := cache.iamUserPolicyMap[cred.ParentUser]; !ok { | ||||||
| 				cache.iamUserPolicyMap[cred.ParentUser] = cache.iamUserPolicyMap[accessKey] | 				cache.iamUserPolicyMap[cred.ParentUser] = cache.iamUserPolicyMap[accessKey] | ||||||
| @ -1439,7 +1445,7 @@ func (store *IAMStoreSys) DeleteUser(ctx context.Context, accessKey string, user | |||||||
| 	if store.getUsersSysType() == MinIOUsersSysType && userType == regUser { | 	if store.getUsersSysType() == MinIOUsersSysType && userType == regUser { | ||||||
| 		memberOf := cache.iamUserGroupMemberships[accessKey].ToSlice() | 		memberOf := cache.iamUserGroupMemberships[accessKey].ToSlice() | ||||||
| 		for _, group := range memberOf { | 		for _, group := range memberOf { | ||||||
| 			removeErr := removeMembersFromGroup(ctx, store, cache, group, []string{accessKey}, false) | 			_, removeErr := removeMembersFromGroup(ctx, store, cache, group, []string{accessKey}, false) | ||||||
| 			if removeErr != nil { | 			if removeErr != nil { | ||||||
| 				return removeErr | 				return removeErr | ||||||
| 			} | 			} | ||||||
| @ -1451,7 +1457,8 @@ func (store *IAMStoreSys) DeleteUser(ctx context.Context, accessKey string, user | |||||||
| 	// Delete any STS and service account derived from this credential | 	// Delete any STS and service account derived from this credential | ||||||
| 	// first. | 	// first. | ||||||
| 	if userType == regUser { | 	if userType == regUser { | ||||||
| 		for _, u := range cache.iamUsersMap { | 		for _, ui := range cache.iamUsersMap { | ||||||
|  | 			u := ui.Credentials | ||||||
| 			if u.IsServiceAccount() && u.ParentUser == accessKey { | 			if u.IsServiceAccount() && u.ParentUser == accessKey { | ||||||
| 				_ = store.deleteUserIdentity(ctx, u.AccessKey, svcUser) | 				_ = store.deleteUserIdentity(ctx, u.AccessKey, svcUser) | ||||||
| 				delete(cache.iamUsersMap, u.AccessKey) | 				delete(cache.iamUsersMap, u.AccessKey) | ||||||
| @ -1483,9 +1490,9 @@ func (store *IAMStoreSys) DeleteUser(ctx context.Context, accessKey string, user | |||||||
| // SetTempUser - saves temporary (STS) credential to storage and cache. If a | // SetTempUser - saves temporary (STS) credential to storage and cache. If a | ||||||
| // policy name is given, it is associated with the parent user specified in the | // policy name is given, it is associated with the parent user specified in the | ||||||
| // credential. | // credential. | ||||||
| func (store *IAMStoreSys) SetTempUser(ctx context.Context, accessKey string, cred auth.Credentials, policyName string) error { | func (store *IAMStoreSys) SetTempUser(ctx context.Context, accessKey string, cred auth.Credentials, policyName string) (time.Time, error) { | ||||||
| 	if accessKey == "" || !cred.IsTemp() || cred.IsExpired() || cred.ParentUser == "" { | 	if accessKey == "" || !cred.IsTemp() || cred.IsExpired() || cred.ParentUser == "" { | ||||||
| 		return errInvalidArgument | 		return time.Time{}, errInvalidArgument | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	ttl := int64(cred.Expiration.Sub(UTCNow()).Seconds()) | 	ttl := int64(cred.Expiration.Sub(UTCNow()).Seconds()) | ||||||
| @ -1498,12 +1505,12 @@ func (store *IAMStoreSys) SetTempUser(ctx context.Context, accessKey string, cre | |||||||
| 		_, combinedPolicyStmt := filterPolicies(cache, mp.Policies, "") | 		_, combinedPolicyStmt := filterPolicies(cache, mp.Policies, "") | ||||||
| 
 | 
 | ||||||
| 		if combinedPolicyStmt.IsEmpty() { | 		if combinedPolicyStmt.IsEmpty() { | ||||||
| 			return fmt.Errorf("specified policy %s, not found %w", policyName, errNoSuchPolicy) | 			return time.Time{}, fmt.Errorf("specified policy %s, not found %w", policyName, errNoSuchPolicy) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		err := store.saveMappedPolicy(ctx, cred.ParentUser, stsUser, false, mp, options{ttl: ttl}) | 		err := store.saveMappedPolicy(ctx, cred.ParentUser, stsUser, false, mp, options{ttl: ttl}) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return time.Time{}, err | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		cache.iamUserPolicyMap[cred.ParentUser] = mp | 		cache.iamUserPolicyMap[cred.ParentUser] = mp | ||||||
| @ -1512,14 +1519,14 @@ func (store *IAMStoreSys) SetTempUser(ctx context.Context, accessKey string, cre | |||||||
| 	u := newUserIdentity(cred) | 	u := newUserIdentity(cred) | ||||||
| 	err := store.saveUserIdentity(ctx, accessKey, stsUser, u, options{ttl: ttl}) | 	err := store.saveUserIdentity(ctx, accessKey, stsUser, u, options{ttl: ttl}) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return time.Time{}, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	cache.iamUsersMap[accessKey] = cred | 	cache.iamUsersMap[accessKey] = u | ||||||
| 
 | 
 | ||||||
| 	cache.updatedAt = time.Now() | 	cache.updatedAt = time.Now() | ||||||
| 
 | 
 | ||||||
| 	return nil | 	return u.UpdatedAt, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // DeleteUsers - given a set of users or access keys, deletes them along with | // DeleteUsers - given a set of users or access keys, deletes them along with | ||||||
| @ -1531,8 +1538,10 @@ func (store *IAMStoreSys) DeleteUsers(ctx context.Context, users []string) error | |||||||
| 
 | 
 | ||||||
| 	var deleted bool | 	var deleted bool | ||||||
| 	usersToDelete := set.CreateStringSet(users...) | 	usersToDelete := set.CreateStringSet(users...) | ||||||
| 	for user, cred := range cache.iamUsersMap { | 	for user, ui := range cache.iamUsersMap { | ||||||
| 		userType := regUser | 		userType := regUser | ||||||
|  | 		cred := ui.Credentials | ||||||
|  | 
 | ||||||
| 		if cred.IsServiceAccount() { | 		if cred.IsServiceAccount() { | ||||||
| 			userType = svcUser | 			userType = svcUser | ||||||
| 		} else if cred.IsTemp() { | 		} else if cred.IsTemp() { | ||||||
| @ -1575,7 +1584,8 @@ func (store *IAMStoreSys) GetAllParentUsers() map[string]ParentUserInfo { | |||||||
| 	defer store.runlock() | 	defer store.runlock() | ||||||
| 
 | 
 | ||||||
| 	res := map[string]ParentUserInfo{} | 	res := map[string]ParentUserInfo{} | ||||||
| 	for _, cred := range cache.iamUsersMap { | 	for _, ui := range cache.iamUsersMap { | ||||||
|  | 		cred := ui.Credentials | ||||||
| 		// Only consider service account or STS credentials with | 		// Only consider service account or STS credentials with | ||||||
| 		// non-empty session tokens. | 		// non-empty session tokens. | ||||||
| 		if !(cred.IsServiceAccount() || cred.IsTemp()) || | 		if !(cred.IsServiceAccount() || cred.IsTemp()) || | ||||||
| @ -1633,21 +1643,22 @@ func (store *IAMStoreSys) GetAllParentUsers() map[string]ParentUserInfo { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // SetUserStatus - sets current user status. | // SetUserStatus - sets current user status. | ||||||
| func (store *IAMStoreSys) SetUserStatus(ctx context.Context, accessKey string, status madmin.AccountStatus) error { | func (store *IAMStoreSys) SetUserStatus(ctx context.Context, accessKey string, status madmin.AccountStatus) (updatedAt time.Time, err error) { | ||||||
| 	if accessKey != "" && status != madmin.AccountEnabled && status != madmin.AccountDisabled { | 	if accessKey != "" && status != madmin.AccountEnabled && status != madmin.AccountDisabled { | ||||||
| 		return errInvalidArgument | 		return updatedAt, errInvalidArgument | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	cache := store.lock() | 	cache := store.lock() | ||||||
| 	defer store.unlock() | 	defer store.unlock() | ||||||
| 
 | 
 | ||||||
| 	cred, ok := cache.iamUsersMap[accessKey] | 	ui, ok := cache.iamUsersMap[accessKey] | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		return errNoSuchUser | 		return updatedAt, errNoSuchUser | ||||||
| 	} | 	} | ||||||
|  | 	cred := ui.Credentials | ||||||
| 
 | 
 | ||||||
| 	if cred.IsTemp() || cred.IsServiceAccount() { | 	if cred.IsTemp() || cred.IsServiceAccount() { | ||||||
| 		return errIAMActionNotAllowed | 		return updatedAt, errIAMActionNotAllowed | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	uinfo := newUserIdentity(auth.Credentials{ | 	uinfo := newUserIdentity(auth.Credentials{ | ||||||
| @ -1663,17 +1674,17 @@ func (store *IAMStoreSys) SetUserStatus(ctx context.Context, accessKey string, s | |||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	if err := store.saveUserIdentity(ctx, accessKey, regUser, uinfo); err != nil { | 	if err := store.saveUserIdentity(ctx, accessKey, regUser, uinfo); err != nil { | ||||||
| 		return err | 		return updatedAt, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	cache.iamUsersMap[accessKey] = uinfo.Credentials | 	cache.iamUsersMap[accessKey] = uinfo | ||||||
| 	cache.updatedAt = time.Now() | 	cache.updatedAt = time.Now() | ||||||
| 
 | 
 | ||||||
| 	return nil | 	return uinfo.UpdatedAt, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // AddServiceAccount - add a new service account | // AddServiceAccount - add a new service account | ||||||
| func (store *IAMStoreSys) AddServiceAccount(ctx context.Context, cred auth.Credentials) error { | func (store *IAMStoreSys) AddServiceAccount(ctx context.Context, cred auth.Credentials) (updatedAt time.Time, err error) { | ||||||
| 	cache := store.lock() | 	cache := store.lock() | ||||||
| 	defer store.unlock() | 	defer store.unlock() | ||||||
| 
 | 
 | ||||||
| @ -1683,44 +1694,45 @@ func (store *IAMStoreSys) AddServiceAccount(ctx context.Context, cred auth.Crede | |||||||
| 	// Found newly requested service account, to be an existing account - | 	// Found newly requested service account, to be an existing account - | ||||||
| 	// reject such operation (updates to the service account are handled in | 	// reject such operation (updates to the service account are handled in | ||||||
| 	// a different API). | 	// a different API). | ||||||
| 	if scred, found := cache.iamUsersMap[accessKey]; found { | 	if su, found := cache.iamUsersMap[accessKey]; found { | ||||||
|  | 		scred := su.Credentials | ||||||
| 		if scred.ParentUser != parentUser { | 		if scred.ParentUser != parentUser { | ||||||
| 			return errIAMServiceAccountUsed | 			return updatedAt, errIAMServiceAccountUsed | ||||||
| 		} | 		} | ||||||
| 		return errIAMServiceAccount | 		return updatedAt, errIAMServiceAccount | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Parent user must not be a service account. | 	// Parent user must not be a service account. | ||||||
| 	if cr, found := cache.iamUsersMap[parentUser]; found && cr.IsServiceAccount() { | 	if u, found := cache.iamUsersMap[parentUser]; found && u.Credentials.IsServiceAccount() { | ||||||
| 		return errIAMServiceAccount | 		return updatedAt, errIAMServiceAccount | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	u := newUserIdentity(cred) | 	u := newUserIdentity(cred) | ||||||
| 	err := store.saveUserIdentity(ctx, u.Credentials.AccessKey, svcUser, u) | 	err = store.saveUserIdentity(ctx, u.Credentials.AccessKey, svcUser, u) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return updatedAt, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	cache.iamUsersMap[u.Credentials.AccessKey] = u.Credentials | 	cache.iamUsersMap[u.Credentials.AccessKey] = u | ||||||
| 	cache.updatedAt = time.Now() | 	cache.updatedAt = time.Now() | ||||||
| 
 | 
 | ||||||
| 	return nil | 	return u.UpdatedAt, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // UpdateServiceAccount - updates a service account on storage. | // UpdateServiceAccount - updates a service account on storage. | ||||||
| func (store *IAMStoreSys) UpdateServiceAccount(ctx context.Context, accessKey string, opts updateServiceAccountOpts) error { | func (store *IAMStoreSys) UpdateServiceAccount(ctx context.Context, accessKey string, opts updateServiceAccountOpts) (updatedAt time.Time, err error) { | ||||||
| 	cache := store.lock() | 	cache := store.lock() | ||||||
| 	defer store.unlock() | 	defer store.unlock() | ||||||
| 
 | 
 | ||||||
| 	cr, ok := cache.iamUsersMap[accessKey] | 	ui, ok := cache.iamUsersMap[accessKey] | ||||||
| 	if !ok || !cr.IsServiceAccount() { | 	if !ok || !ui.Credentials.IsServiceAccount() { | ||||||
| 		return errNoSuchServiceAccount | 		return updatedAt, errNoSuchServiceAccount | ||||||
| 	} | 	} | ||||||
| 
 | 	cr := ui.Credentials | ||||||
| 	currentSecretKey := cr.SecretKey | 	currentSecretKey := cr.SecretKey | ||||||
| 	if opts.secretKey != "" { | 	if opts.secretKey != "" { | ||||||
| 		if !auth.IsSecretKeyValid(opts.secretKey) { | 		if !auth.IsSecretKeyValid(opts.secretKey) { | ||||||
| 			return auth.ErrInvalidSecretKeyLength | 			return updatedAt, auth.ErrInvalidSecretKeyLength | ||||||
| 		} | 		} | ||||||
| 		cr.SecretKey = opts.secretKey | 		cr.SecretKey = opts.secretKey | ||||||
| 	} | 	} | ||||||
| @ -1736,12 +1748,12 @@ func (store *IAMStoreSys) UpdateServiceAccount(ctx context.Context, accessKey st | |||||||
| 	case auth.AccountOn, auth.AccountOff: | 	case auth.AccountOn, auth.AccountOff: | ||||||
| 		cr.Status = opts.status | 		cr.Status = opts.status | ||||||
| 	default: | 	default: | ||||||
| 		return errors.New("unknown account status value") | 		return updatedAt, errors.New("unknown account status value") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	m, err := getClaimsFromTokenWithSecret(cr.SessionToken, currentSecretKey) | 	m, err := getClaimsFromTokenWithSecret(cr.SessionToken, currentSecretKey) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return fmt.Errorf("unable to get svc acc claims: %v", err) | 		return updatedAt, fmt.Errorf("unable to get svc acc claims: %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Extracted session policy name string can be removed as its not useful | 	// Extracted session policy name string can be removed as its not useful | ||||||
| @ -1757,16 +1769,16 @@ func (store *IAMStoreSys) UpdateServiceAccount(ctx context.Context, accessKey st | |||||||
| 
 | 
 | ||||||
| 	if opts.sessionPolicy != nil { | 	if opts.sessionPolicy != nil { | ||||||
| 		if err := opts.sessionPolicy.Validate(); err != nil { | 		if err := opts.sessionPolicy.Validate(); err != nil { | ||||||
| 			return err | 			return updatedAt, err | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		policyBuf, err := json.Marshal(opts.sessionPolicy) | 		policyBuf, err := json.Marshal(opts.sessionPolicy) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return updatedAt, err | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if len(policyBuf) > 16*humanize.KiByte { | 		if len(policyBuf) > 16*humanize.KiByte { | ||||||
| 			return fmt.Errorf("Session policy should not exceed 16 KiB characters") | 			return updatedAt, fmt.Errorf("Session policy should not exceed 16 KiB characters") | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// Overwrite session policy claims. | 		// Overwrite session policy claims. | ||||||
| @ -1776,41 +1788,41 @@ func (store *IAMStoreSys) UpdateServiceAccount(ctx context.Context, accessKey st | |||||||
| 
 | 
 | ||||||
| 	cr.SessionToken, err = auth.JWTSignWithAccessKey(accessKey, m, cr.SecretKey) | 	cr.SessionToken, err = auth.JWTSignWithAccessKey(accessKey, m, cr.SecretKey) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return updatedAt, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	u := newUserIdentity(cr) | 	u := newUserIdentity(cr) | ||||||
| 	if err := store.saveUserIdentity(ctx, u.Credentials.AccessKey, svcUser, u); err != nil { | 	if err := store.saveUserIdentity(ctx, u.Credentials.AccessKey, svcUser, u); err != nil { | ||||||
| 		return err | 		return updatedAt, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	cache.iamUsersMap[u.Credentials.AccessKey] = u.Credentials | 	cache.iamUsersMap[u.Credentials.AccessKey] = u | ||||||
| 	cache.updatedAt = time.Now() | 	cache.updatedAt = time.Now() | ||||||
| 
 | 
 | ||||||
| 	return nil | 	return u.UpdatedAt, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ListTempAccounts - lists only temporary accounts from the cache. | // ListTempAccounts - lists only temporary accounts from the cache. | ||||||
| func (store *IAMStoreSys) ListTempAccounts(ctx context.Context, accessKey string) ([]auth.Credentials, error) { | func (store *IAMStoreSys) ListTempAccounts(ctx context.Context, accessKey string) ([]UserIdentity, error) { | ||||||
| 	cache := store.rlock() | 	cache := store.rlock() | ||||||
| 	defer store.runlock() | 	defer store.runlock() | ||||||
| 
 | 
 | ||||||
| 	userExists := false | 	userExists := false | ||||||
| 	var tempAccounts []auth.Credentials | 	var tempAccounts []UserIdentity | ||||||
| 	for _, v := range cache.iamUsersMap { | 	for _, v := range cache.iamUsersMap { | ||||||
| 		isDerived := false | 		isDerived := false | ||||||
| 		if v.IsServiceAccount() || v.IsTemp() { | 		if v.Credentials.IsServiceAccount() || v.Credentials.IsTemp() { | ||||||
| 			isDerived = true | 			isDerived = true | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if !isDerived && v.AccessKey == accessKey { | 		if !isDerived && v.Credentials.AccessKey == accessKey { | ||||||
| 			userExists = true | 			userExists = true | ||||||
| 		} else if isDerived && v.ParentUser == accessKey { | 		} else if isDerived && v.Credentials.ParentUser == accessKey { | ||||||
| 			userExists = true | 			userExists = true | ||||||
| 			if v.IsTemp() { | 			if v.Credentials.IsTemp() { | ||||||
| 				// Hide secret key & session key here | 				// Hide secret key & session key here | ||||||
| 				v.SecretKey = "" | 				v.Credentials.SecretKey = "" | ||||||
| 				v.SessionToken = "" | 				v.Credentials.SessionToken = "" | ||||||
| 				tempAccounts = append(tempAccounts, v) | 				tempAccounts = append(tempAccounts, v) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| @ -1830,8 +1842,9 @@ func (store *IAMStoreSys) ListServiceAccounts(ctx context.Context, accessKey str | |||||||
| 
 | 
 | ||||||
| 	userExists := false | 	userExists := false | ||||||
| 	var serviceAccounts []auth.Credentials | 	var serviceAccounts []auth.Credentials | ||||||
| 	for _, v := range cache.iamUsersMap { | 	for _, u := range cache.iamUsersMap { | ||||||
| 		isDerived := false | 		isDerived := false | ||||||
|  | 		v := u.Credentials | ||||||
| 		if v.IsServiceAccount() || v.IsTemp() { | 		if v.IsServiceAccount() || v.IsTemp() { | ||||||
| 			isDerived = true | 			isDerived = true | ||||||
| 		} | 		} | ||||||
| @ -1857,17 +1870,17 @@ func (store *IAMStoreSys) ListServiceAccounts(ctx context.Context, accessKey str | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // AddUser - adds/updates long term user account to storage. | // AddUser - adds/updates long term user account to storage. | ||||||
| func (store *IAMStoreSys) AddUser(ctx context.Context, accessKey string, ureq madmin.AddOrUpdateUserReq) error { | func (store *IAMStoreSys) AddUser(ctx context.Context, accessKey string, ureq madmin.AddOrUpdateUserReq) (updatedAt time.Time, err error) { | ||||||
| 	cache := store.lock() | 	cache := store.lock() | ||||||
| 	defer store.unlock() | 	defer store.unlock() | ||||||
| 
 | 
 | ||||||
| 	cache.updatedAt = time.Now() | 	cache.updatedAt = time.Now() | ||||||
| 
 | 
 | ||||||
| 	cr, ok := cache.iamUsersMap[accessKey] | 	ui, ok := cache.iamUsersMap[accessKey] | ||||||
| 
 | 
 | ||||||
| 	// It is not possible to update an STS account. | 	// It is not possible to update an STS account. | ||||||
| 	if ok && cr.IsTemp() { | 	if ok && ui.Credentials.IsTemp() { | ||||||
| 		return errIAMActionNotAllowed | 		return updatedAt, errIAMActionNotAllowed | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	u := newUserIdentity(auth.Credentials{ | 	u := newUserIdentity(auth.Credentials{ | ||||||
| @ -1883,12 +1896,12 @@ func (store *IAMStoreSys) AddUser(ctx context.Context, accessKey string, ureq ma | |||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	if err := store.saveUserIdentity(ctx, accessKey, regUser, u); err != nil { | 	if err := store.saveUserIdentity(ctx, accessKey, regUser, u); err != nil { | ||||||
| 		return err | 		return updatedAt, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	cache.iamUsersMap[accessKey] = u.Credentials | 	cache.iamUsersMap[accessKey] = u | ||||||
| 
 | 
 | ||||||
| 	return nil | 	return u.UpdatedAt, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // UpdateUserSecretKey - sets user secret key to storage. | // UpdateUserSecretKey - sets user secret key to storage. | ||||||
| @ -1898,18 +1911,18 @@ func (store *IAMStoreSys) UpdateUserSecretKey(ctx context.Context, accessKey, se | |||||||
| 
 | 
 | ||||||
| 	cache.updatedAt = time.Now() | 	cache.updatedAt = time.Now() | ||||||
| 
 | 
 | ||||||
| 	cred, ok := cache.iamUsersMap[accessKey] | 	ui, ok := cache.iamUsersMap[accessKey] | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		return errNoSuchUser | 		return errNoSuchUser | ||||||
| 	} | 	} | ||||||
| 
 | 	cred := ui.Credentials | ||||||
| 	cred.SecretKey = secretKey | 	cred.SecretKey = secretKey | ||||||
| 	u := newUserIdentity(cred) | 	u := newUserIdentity(cred) | ||||||
| 	if err := store.saveUserIdentity(ctx, accessKey, regUser, u); err != nil { | 	if err := store.saveUserIdentity(ctx, accessKey, regUser, u); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	cache.iamUsersMap[accessKey] = cred | 	cache.iamUsersMap[accessKey] = u | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -1919,7 +1932,8 @@ func (store *IAMStoreSys) GetSTSAndServiceAccounts() []auth.Credentials { | |||||||
| 	defer store.runlock() | 	defer store.runlock() | ||||||
| 
 | 
 | ||||||
| 	var res []auth.Credentials | 	var res []auth.Credentials | ||||||
| 	for _, cred := range cache.iamUsersMap { | 	for _, u := range cache.iamUsersMap { | ||||||
|  | 		cred := u.Credentials | ||||||
| 		if cred.IsTemp() || cred.IsServiceAccount() { | 		if cred.IsTemp() || cred.IsServiceAccount() { | ||||||
| 			res = append(res, cred) | 			res = append(res, cred) | ||||||
| 		} | 		} | ||||||
| @ -1940,13 +1954,13 @@ func (store *IAMStoreSys) UpdateUserIdentity(ctx context.Context, cred auth.Cred | |||||||
| 	} else if cred.IsTemp() { | 	} else if cred.IsTemp() { | ||||||
| 		userType = stsUser | 		userType = stsUser | ||||||
| 	} | 	} | ||||||
| 
 | 	ui := newUserIdentity(cred) | ||||||
| 	// Overwrite the user identity here. As store should be | 	// Overwrite the user identity here. As store should be | ||||||
| 	// atomic, it shouldn't cause any corruption. | 	// atomic, it shouldn't cause any corruption. | ||||||
| 	if err := store.saveUserIdentity(ctx, cred.AccessKey, userType, newUserIdentity(cred)); err != nil { | 	if err := store.saveUserIdentity(ctx, cred.AccessKey, userType, ui); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	cache.iamUsersMap[cred.AccessKey] = cred | 	cache.iamUsersMap[cred.AccessKey] = ui | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -1969,9 +1983,9 @@ func (store *IAMStoreSys) LoadUser(ctx context.Context, accessKey string) { | |||||||
| 			if svc, found := cache.iamUsersMap[accessKey]; found { | 			if svc, found := cache.iamUsersMap[accessKey]; found { | ||||||
| 				// Load parent user and mapped policies. | 				// Load parent user and mapped policies. | ||||||
| 				if store.getUsersSysType() == MinIOUsersSysType { | 				if store.getUsersSysType() == MinIOUsersSysType { | ||||||
| 					store.loadUser(ctx, svc.ParentUser, regUser, cache.iamUsersMap) | 					store.loadUser(ctx, svc.Credentials.ParentUser, regUser, cache.iamUsersMap) | ||||||
| 				} | 				} | ||||||
| 				store.loadMappedPolicy(ctx, svc.ParentUser, regUser, false, cache.iamUserPolicyMap) | 				store.loadMappedPolicy(ctx, svc.Credentials.ParentUser, regUser, false, cache.iamUserPolicyMap) | ||||||
| 			} else { | 			} else { | ||||||
| 				// check for STS account | 				// check for STS account | ||||||
| 				store.loadUser(ctx, accessKey, stsUser, cache.iamUsersMap) | 				store.loadUser(ctx, accessKey, stsUser, cache.iamUsersMap) | ||||||
|  | |||||||
							
								
								
									
										182
									
								
								cmd/iam.go
									
									
									
									
									
								
							
							
						
						
									
										182
									
								
								cmd/iam.go
									
									
									
									
									
								
							| @ -599,14 +599,14 @@ func (sys *IAMSys) ListPolicyDocs(ctx context.Context, bucketName string) (map[s | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // SetPolicy - sets a new named policy. | // SetPolicy - sets a new named policy. | ||||||
| func (sys *IAMSys) SetPolicy(ctx context.Context, policyName string, p iampolicy.Policy) error { | func (sys *IAMSys) SetPolicy(ctx context.Context, policyName string, p iampolicy.Policy) (time.Time, error) { | ||||||
| 	if !sys.Initialized() { | 	if !sys.Initialized() { | ||||||
| 		return errServerNotInitialized | 		return time.Time{}, errServerNotInitialized | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err := sys.store.SetPolicy(ctx, policyName, p) | 	updatedAt, err := sys.store.SetPolicy(ctx, policyName, p) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return updatedAt, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if !sys.HasWatcher() { | 	if !sys.HasWatcher() { | ||||||
| @ -618,7 +618,7 @@ func (sys *IAMSys) SetPolicy(ctx context.Context, policyName string, p iampolicy | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return nil | 	return updatedAt, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // DeleteUser - delete user (only for long-term users not STS users). | // DeleteUser - delete user (only for long-term users not STS users). | ||||||
| @ -707,9 +707,9 @@ func (sys *IAMSys) notifyForUser(ctx context.Context, accessKey string, isTemp b | |||||||
| // elsewhere), the AssumeRole case (because the parent user is real and their | // elsewhere), the AssumeRole case (because the parent user is real and their | ||||||
| // policy is associated via policy-set API) and the AssumeRoleWithLDAP case | // policy is associated via policy-set API) and the AssumeRoleWithLDAP case | ||||||
| // (because the policy association is made via policy-set API). | // (because the policy association is made via policy-set API). | ||||||
| func (sys *IAMSys) SetTempUser(ctx context.Context, accessKey string, cred auth.Credentials, policyName string) error { | func (sys *IAMSys) SetTempUser(ctx context.Context, accessKey string, cred auth.Credentials, policyName string) (time.Time, error) { | ||||||
| 	if !sys.Initialized() { | 	if !sys.Initialized() { | ||||||
| 		return errServerNotInitialized | 		return time.Time{}, errServerNotInitialized | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if newGlobalAuthZPluginFn() != nil { | 	if newGlobalAuthZPluginFn() != nil { | ||||||
| @ -717,14 +717,14 @@ func (sys *IAMSys) SetTempUser(ctx context.Context, accessKey string, cred auth. | |||||||
| 		policyName = "" | 		policyName = "" | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err := sys.store.SetTempUser(ctx, accessKey, cred, policyName) | 	updatedAt, err := sys.store.SetTempUser(ctx, accessKey, cred, policyName) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return time.Time{}, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	sys.notifyForUser(ctx, cred.AccessKey, true) | 	sys.notifyForUser(ctx, cred.AccessKey, true) | ||||||
| 
 | 
 | ||||||
| 	return nil | 	return updatedAt, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ListBucketUsers - list all users who can access this 'bucket' | // ListBucketUsers - list all users who can access this 'bucket' | ||||||
| @ -782,11 +782,11 @@ func (sys *IAMSys) IsTempUser(name string) (bool, string, error) { | |||||||
| 		return false, "", errServerNotInitialized | 		return false, "", errServerNotInitialized | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	cred, found := sys.store.GetUser(name) | 	u, found := sys.store.GetUser(name) | ||||||
| 	if !found { | 	if !found { | ||||||
| 		return false, "", errNoSuchUser | 		return false, "", errNoSuchUser | ||||||
| 	} | 	} | ||||||
| 
 | 	cred := u.Credentials | ||||||
| 	if cred.IsTemp() { | 	if cred.IsTemp() { | ||||||
| 		return true, cred.ParentUser, nil | 		return true, cred.ParentUser, nil | ||||||
| 	} | 	} | ||||||
| @ -800,11 +800,11 @@ func (sys *IAMSys) IsServiceAccount(name string) (bool, string, error) { | |||||||
| 		return false, "", errServerNotInitialized | 		return false, "", errServerNotInitialized | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	cred, found := sys.store.GetUser(name) | 	u, found := sys.store.GetUser(name) | ||||||
| 	if !found { | 	if !found { | ||||||
| 		return false, "", errNoSuchUser | 		return false, "", errNoSuchUser | ||||||
| 	} | 	} | ||||||
| 
 | 	cred := u.Credentials | ||||||
| 	if cred.IsServiceAccount() { | 	if cred.IsServiceAccount() { | ||||||
| 		return true, cred.ParentUser, nil | 		return true, cred.ParentUser, nil | ||||||
| 	} | 	} | ||||||
| @ -828,22 +828,22 @@ func (sys *IAMSys) GetUserInfo(ctx context.Context, name string) (u madmin.UserI | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // SetUserStatus - sets current user status, supports disabled or enabled. | // SetUserStatus - sets current user status, supports disabled or enabled. | ||||||
| func (sys *IAMSys) SetUserStatus(ctx context.Context, accessKey string, status madmin.AccountStatus) error { | func (sys *IAMSys) SetUserStatus(ctx context.Context, accessKey string, status madmin.AccountStatus) (updatedAt time.Time, err error) { | ||||||
| 	if !sys.Initialized() { | 	if !sys.Initialized() { | ||||||
| 		return errServerNotInitialized | 		return updatedAt, errServerNotInitialized | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if sys.usersSysType != MinIOUsersSysType { | 	if sys.usersSysType != MinIOUsersSysType { | ||||||
| 		return errIAMActionNotAllowed | 		return updatedAt, errIAMActionNotAllowed | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err := sys.store.SetUserStatus(ctx, accessKey, status) | 	updatedAt, err = sys.store.SetUserStatus(ctx, accessKey, status) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	sys.notifyForUser(ctx, accessKey, false) | 	sys.notifyForUser(ctx, accessKey, false) | ||||||
| 	return nil | 	return updatedAt, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (sys *IAMSys) notifyForServiceAccount(ctx context.Context, accessKey string) { | func (sys *IAMSys) notifyForServiceAccount(ctx context.Context, accessKey string) { | ||||||
| @ -867,34 +867,34 @@ type newServiceAccountOpts struct { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // NewServiceAccount - create a new service account | // NewServiceAccount - create a new service account | ||||||
| func (sys *IAMSys) NewServiceAccount(ctx context.Context, parentUser string, groups []string, opts newServiceAccountOpts) (auth.Credentials, error) { | func (sys *IAMSys) NewServiceAccount(ctx context.Context, parentUser string, groups []string, opts newServiceAccountOpts) (auth.Credentials, time.Time, error) { | ||||||
| 	if !sys.Initialized() { | 	if !sys.Initialized() { | ||||||
| 		return auth.Credentials{}, errServerNotInitialized | 		return auth.Credentials{}, time.Time{}, errServerNotInitialized | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if parentUser == "" { | 	if parentUser == "" { | ||||||
| 		return auth.Credentials{}, errInvalidArgument | 		return auth.Credentials{}, time.Time{}, errInvalidArgument | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	var policyBuf []byte | 	var policyBuf []byte | ||||||
| 	if opts.sessionPolicy != nil { | 	if opts.sessionPolicy != nil { | ||||||
| 		err := opts.sessionPolicy.Validate() | 		err := opts.sessionPolicy.Validate() | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return auth.Credentials{}, err | 			return auth.Credentials{}, time.Time{}, err | ||||||
| 		} | 		} | ||||||
| 		policyBuf, err = json.Marshal(opts.sessionPolicy) | 		policyBuf, err = json.Marshal(opts.sessionPolicy) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return auth.Credentials{}, err | 			return auth.Credentials{}, time.Time{}, err | ||||||
| 		} | 		} | ||||||
| 		if len(policyBuf) > 16*humanize.KiByte { | 		if len(policyBuf) > 16*humanize.KiByte { | ||||||
| 			return auth.Credentials{}, fmt.Errorf("Session policy should not exceed 16 KiB characters") | 			return auth.Credentials{}, time.Time{}, fmt.Errorf("Session policy should not exceed 16 KiB characters") | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// found newly requested service account, to be same as | 	// found newly requested service account, to be same as | ||||||
| 	// parentUser, reject such operations. | 	// parentUser, reject such operations. | ||||||
| 	if parentUser == opts.accessKey { | 	if parentUser == opts.accessKey { | ||||||
| 		return auth.Credentials{}, errIAMActionNotAllowed | 		return auth.Credentials{}, time.Time{}, errIAMActionNotAllowed | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	m := make(map[string]interface{}) | 	m := make(map[string]interface{}) | ||||||
| @ -922,24 +922,24 @@ func (sys *IAMSys) NewServiceAccount(ctx context.Context, parentUser string, gro | |||||||
| 	} else { | 	} else { | ||||||
| 		accessKey, secretKey, err = auth.GenerateCredentials() | 		accessKey, secretKey, err = auth.GenerateCredentials() | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return auth.Credentials{}, err | 			return auth.Credentials{}, time.Time{}, err | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	cred, err := auth.CreateNewCredentialsWithMetadata(accessKey, secretKey, m, secretKey) | 	cred, err := auth.CreateNewCredentialsWithMetadata(accessKey, secretKey, m, secretKey) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return auth.Credentials{}, err | 		return auth.Credentials{}, time.Time{}, err | ||||||
| 	} | 	} | ||||||
| 	cred.ParentUser = parentUser | 	cred.ParentUser = parentUser | ||||||
| 	cred.Groups = groups | 	cred.Groups = groups | ||||||
| 	cred.Status = string(auth.AccountOn) | 	cred.Status = string(auth.AccountOn) | ||||||
| 
 | 
 | ||||||
| 	err = sys.store.AddServiceAccount(ctx, cred) | 	updatedAt, err := sys.store.AddServiceAccount(ctx, cred) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return auth.Credentials{}, err | 		return auth.Credentials{}, time.Time{}, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	sys.notifyForServiceAccount(ctx, cred.AccessKey) | 	sys.notifyForServiceAccount(ctx, cred.AccessKey) | ||||||
| 	return cred, nil | 	return cred, updatedAt, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type updateServiceAccountOpts struct { | type updateServiceAccountOpts struct { | ||||||
| @ -949,18 +949,18 @@ type updateServiceAccountOpts struct { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // UpdateServiceAccount - edit a service account | // UpdateServiceAccount - edit a service account | ||||||
| func (sys *IAMSys) UpdateServiceAccount(ctx context.Context, accessKey string, opts updateServiceAccountOpts) error { | func (sys *IAMSys) UpdateServiceAccount(ctx context.Context, accessKey string, opts updateServiceAccountOpts) (updatedAt time.Time, err error) { | ||||||
| 	if !sys.Initialized() { | 	if !sys.Initialized() { | ||||||
| 		return errServerNotInitialized | 		return updatedAt, errServerNotInitialized | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err := sys.store.UpdateServiceAccount(ctx, accessKey, opts) | 	updatedAt, err = sys.store.UpdateServiceAccount(ctx, accessKey, opts) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return updatedAt, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	sys.notifyForServiceAccount(ctx, accessKey) | 	sys.notifyForServiceAccount(ctx, accessKey) | ||||||
| 	return nil | 	return updatedAt, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ListServiceAccounts - lists all services accounts associated to a specific user | // ListServiceAccounts - lists all services accounts associated to a specific user | ||||||
| @ -978,7 +978,7 @@ func (sys *IAMSys) ListServiceAccounts(ctx context.Context, accessKey string) ([ | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ListTempAccounts - lists all services accounts associated to a specific user | // ListTempAccounts - lists all services accounts associated to a specific user | ||||||
| func (sys *IAMSys) ListTempAccounts(ctx context.Context, accessKey string) ([]auth.Credentials, error) { | func (sys *IAMSys) ListTempAccounts(ctx context.Context, accessKey string) ([]UserIdentity, error) { | ||||||
| 	if !sys.Initialized() { | 	if !sys.Initialized() { | ||||||
| 		return nil, errServerNotInitialized | 		return nil, errServerNotInitialized | ||||||
| 	} | 	} | ||||||
| @ -995,32 +995,32 @@ func (sys *IAMSys) ListTempAccounts(ctx context.Context, accessKey string) ([]au | |||||||
| func (sys *IAMSys) GetServiceAccount(ctx context.Context, accessKey string) (auth.Credentials, *iampolicy.Policy, error) { | func (sys *IAMSys) GetServiceAccount(ctx context.Context, accessKey string) (auth.Credentials, *iampolicy.Policy, error) { | ||||||
| 	sa, embeddedPolicy, err := sys.getServiceAccount(ctx, accessKey) | 	sa, embeddedPolicy, err := sys.getServiceAccount(ctx, accessKey) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return sa, embeddedPolicy, err | 		return auth.Credentials{}, embeddedPolicy, err | ||||||
| 	} | 	} | ||||||
| 	// Hide secret & session keys | 	// Hide secret & session keys | ||||||
| 	sa.SecretKey = "" | 	sa.Credentials.SecretKey = "" | ||||||
| 	sa.SessionToken = "" | 	sa.Credentials.SessionToken = "" | ||||||
| 	return sa, embeddedPolicy, nil | 	return sa.Credentials, embeddedPolicy, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // getServiceAccount - gets information about a service account | // getServiceAccount - gets information about a service account | ||||||
| func (sys *IAMSys) getServiceAccount(ctx context.Context, accessKey string) (auth.Credentials, *iampolicy.Policy, error) { | func (sys *IAMSys) getServiceAccount(ctx context.Context, accessKey string) (u UserIdentity, p *iampolicy.Policy, err error) { | ||||||
| 	if !sys.Initialized() { | 	if !sys.Initialized() { | ||||||
| 		return auth.Credentials{}, nil, errServerNotInitialized | 		return u, nil, errServerNotInitialized | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	sa, ok := sys.store.GetUser(accessKey) | 	sa, ok := sys.store.GetUser(accessKey) | ||||||
| 	if !ok || !sa.IsServiceAccount() { | 	if !ok || !sa.Credentials.IsServiceAccount() { | ||||||
| 		return auth.Credentials{}, nil, errNoSuchServiceAccount | 		return u, nil, errNoSuchServiceAccount | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	var embeddedPolicy *iampolicy.Policy | 	var embeddedPolicy *iampolicy.Policy | ||||||
| 
 | 
 | ||||||
| 	jwtClaims, err := auth.ExtractClaims(sa.SessionToken, sa.SecretKey) | 	jwtClaims, err := auth.ExtractClaims(sa.Credentials.SessionToken, sa.Credentials.SecretKey) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		jwtClaims, err = auth.ExtractClaims(sa.SessionToken, globalActiveCred.SecretKey) | 		jwtClaims, err = auth.ExtractClaims(sa.Credentials.SessionToken, globalActiveCred.SecretKey) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return auth.Credentials{}, nil, err | 			return u, nil, err | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	pt, ptok := jwtClaims.Lookup(iamPolicyClaimNameSA()) | 	pt, ptok := jwtClaims.Lookup(iamPolicyClaimNameSA()) | ||||||
| @ -1028,11 +1028,11 @@ func (sys *IAMSys) getServiceAccount(ctx context.Context, accessKey string) (aut | |||||||
| 	if ptok && spok && pt == embeddedPolicyType { | 	if ptok && spok && pt == embeddedPolicyType { | ||||||
| 		policyBytes, err := base64.StdEncoding.DecodeString(sp) | 		policyBytes, err := base64.StdEncoding.DecodeString(sp) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return auth.Credentials{}, nil, err | 			return u, nil, err | ||||||
| 		} | 		} | ||||||
| 		embeddedPolicy, err = iampolicy.ParseConfig(bytes.NewReader(policyBytes)) | 		embeddedPolicy, err = iampolicy.ParseConfig(bytes.NewReader(policyBytes)) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return auth.Credentials{}, nil, err | 			return u, nil, err | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -1050,13 +1050,13 @@ func (sys *IAMSys) GetClaimsForSvcAcc(ctx context.Context, accessKey string) (ma | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	sa, ok := sys.store.GetUser(accessKey) | 	sa, ok := sys.store.GetUser(accessKey) | ||||||
| 	if !ok || !sa.IsServiceAccount() { | 	if !ok || !sa.Credentials.IsServiceAccount() { | ||||||
| 		return nil, errNoSuchServiceAccount | 		return nil, errNoSuchServiceAccount | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	jwtClaims, err := auth.ExtractClaims(sa.SessionToken, sa.SecretKey) | 	jwtClaims, err := auth.ExtractClaims(sa.Credentials.SessionToken, sa.Credentials.SecretKey) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		jwtClaims, err = auth.ExtractClaims(sa.SessionToken, globalActiveCred.SecretKey) | 		jwtClaims, err = auth.ExtractClaims(sa.Credentials.SessionToken, globalActiveCred.SecretKey) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return nil, err | 			return nil, err | ||||||
| 		} | 		} | ||||||
| @ -1071,7 +1071,7 @@ func (sys *IAMSys) DeleteServiceAccount(ctx context.Context, accessKey string, n | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	sa, ok := sys.store.GetUser(accessKey) | 	sa, ok := sys.store.GetUser(accessKey) | ||||||
| 	if !ok || !sa.IsServiceAccount() { | 	if !ok || !sa.Credentials.IsServiceAccount() { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -1093,30 +1093,30 @@ func (sys *IAMSys) DeleteServiceAccount(ctx context.Context, accessKey string, n | |||||||
| 
 | 
 | ||||||
| // CreateUser - create new user credentials and policy, if user already exists | // CreateUser - create new user credentials and policy, if user already exists | ||||||
| // they shall be rewritten with new inputs. | // they shall be rewritten with new inputs. | ||||||
| func (sys *IAMSys) CreateUser(ctx context.Context, accessKey string, ureq madmin.AddOrUpdateUserReq) error { | func (sys *IAMSys) CreateUser(ctx context.Context, accessKey string, ureq madmin.AddOrUpdateUserReq) (updatedAt time.Time, err error) { | ||||||
| 	if !sys.Initialized() { | 	if !sys.Initialized() { | ||||||
| 		return errServerNotInitialized | 		return updatedAt, errServerNotInitialized | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if sys.usersSysType != MinIOUsersSysType { | 	if sys.usersSysType != MinIOUsersSysType { | ||||||
| 		return errIAMActionNotAllowed | 		return updatedAt, errIAMActionNotAllowed | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if !auth.IsAccessKeyValid(accessKey) { | 	if !auth.IsAccessKeyValid(accessKey) { | ||||||
| 		return auth.ErrInvalidAccessKeyLength | 		return updatedAt, auth.ErrInvalidAccessKeyLength | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if !auth.IsSecretKeyValid(ureq.SecretKey) { | 	if !auth.IsSecretKeyValid(ureq.SecretKey) { | ||||||
| 		return auth.ErrInvalidSecretKeyLength | 		return updatedAt, auth.ErrInvalidSecretKeyLength | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err := sys.store.AddUser(ctx, accessKey, ureq) | 	updatedAt, err = sys.store.AddUser(ctx, accessKey, ureq) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return updatedAt, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	sys.notifyForUser(ctx, accessKey, false) | 	sys.notifyForUser(ctx, accessKey, false) | ||||||
| 	return nil | 	return updatedAt, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // SetUserSecretKey - sets user secret key | // SetUserSecretKey - sets user secret key | ||||||
| @ -1291,9 +1291,9 @@ func (sys *IAMSys) updateGroupMembershipsForLDAP(ctx context.Context) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // GetUser - get user credentials | // GetUser - get user credentials | ||||||
| func (sys *IAMSys) GetUser(ctx context.Context, accessKey string) (cred auth.Credentials, ok bool) { | func (sys *IAMSys) GetUser(ctx context.Context, accessKey string) (u UserIdentity, ok bool) { | ||||||
| 	if !sys.Initialized() { | 	if !sys.Initialized() { | ||||||
| 		return cred, false | 		return u, false | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	fallback := false | 	fallback := false | ||||||
| @ -1304,7 +1304,7 @@ func (sys *IAMSys) GetUser(ctx context.Context, accessKey string) (cred auth.Cre | |||||||
| 		fallback = true | 		fallback = true | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	cred, ok = sys.store.GetUser(accessKey) | 	u, ok = sys.store.GetUser(accessKey) | ||||||
| 	if !ok && !fallback { | 	if !ok && !fallback { | ||||||
| 		// accessKey not found, also | 		// accessKey not found, also | ||||||
| 		// IAM store is not in fallback mode | 		// IAM store is not in fallback mode | ||||||
| @ -1313,10 +1313,10 @@ func (sys *IAMSys) GetUser(ctx context.Context, accessKey string) (cred auth.Cre | |||||||
| 		// exists now. If it doesn't proceed to | 		// exists now. If it doesn't proceed to | ||||||
| 		// fail. | 		// fail. | ||||||
| 		sys.store.LoadUser(ctx, accessKey) | 		sys.store.LoadUser(ctx, accessKey) | ||||||
| 		cred, ok = sys.store.GetUser(accessKey) | 		u, ok = sys.store.GetUser(accessKey) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return cred, ok && cred.IsValid() | 	return u, ok && u.Credentials.IsValid() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Notify all other MinIO peers to load group. | // Notify all other MinIO peers to load group. | ||||||
| @ -1333,61 +1333,61 @@ func (sys *IAMSys) notifyForGroup(ctx context.Context, group string) { | |||||||
| 
 | 
 | ||||||
| // AddUsersToGroup - adds users to a group, creating the group if | // AddUsersToGroup - adds users to a group, creating the group if | ||||||
| // needed. No error if user(s) already are in the group. | // needed. No error if user(s) already are in the group. | ||||||
| func (sys *IAMSys) AddUsersToGroup(ctx context.Context, group string, members []string) error { | func (sys *IAMSys) AddUsersToGroup(ctx context.Context, group string, members []string) (updatedAt time.Time, err error) { | ||||||
| 	if !sys.Initialized() { | 	if !sys.Initialized() { | ||||||
| 		return errServerNotInitialized | 		return updatedAt, errServerNotInitialized | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if sys.usersSysType != MinIOUsersSysType { | 	if sys.usersSysType != MinIOUsersSysType { | ||||||
| 		return errIAMActionNotAllowed | 		return updatedAt, errIAMActionNotAllowed | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err := sys.store.AddUsersToGroup(ctx, group, members) | 	updatedAt, err = sys.store.AddUsersToGroup(ctx, group, members) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return updatedAt, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	sys.notifyForGroup(ctx, group) | 	sys.notifyForGroup(ctx, group) | ||||||
| 	return nil | 	return updatedAt, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // RemoveUsersFromGroup - remove users from group. If no users are | // RemoveUsersFromGroup - remove users from group. If no users are | ||||||
| // given, and the group is empty, deletes the group as well. | // given, and the group is empty, deletes the group as well. | ||||||
| func (sys *IAMSys) RemoveUsersFromGroup(ctx context.Context, group string, members []string) error { | func (sys *IAMSys) RemoveUsersFromGroup(ctx context.Context, group string, members []string) (updatedAt time.Time, err error) { | ||||||
| 	if !sys.Initialized() { | 	if !sys.Initialized() { | ||||||
| 		return errServerNotInitialized | 		return updatedAt, errServerNotInitialized | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if sys.usersSysType != MinIOUsersSysType { | 	if sys.usersSysType != MinIOUsersSysType { | ||||||
| 		return errIAMActionNotAllowed | 		return updatedAt, errIAMActionNotAllowed | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err := sys.store.RemoveUsersFromGroup(ctx, group, members) | 	updatedAt, err = sys.store.RemoveUsersFromGroup(ctx, group, members) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return updatedAt, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	sys.notifyForGroup(ctx, group) | 	sys.notifyForGroup(ctx, group) | ||||||
| 	return nil | 	return updatedAt, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // SetGroupStatus - enable/disabled a group | // SetGroupStatus - enable/disabled a group | ||||||
| func (sys *IAMSys) SetGroupStatus(ctx context.Context, group string, enabled bool) error { | func (sys *IAMSys) SetGroupStatus(ctx context.Context, group string, enabled bool) (updatedAt time.Time, err error) { | ||||||
| 	if !sys.Initialized() { | 	if !sys.Initialized() { | ||||||
| 		return errServerNotInitialized | 		return updatedAt, errServerNotInitialized | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if sys.usersSysType != MinIOUsersSysType { | 	if sys.usersSysType != MinIOUsersSysType { | ||||||
| 		return errIAMActionNotAllowed | 		return updatedAt, errIAMActionNotAllowed | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err := sys.store.SetGroupStatus(ctx, group, enabled) | 	updatedAt, err = sys.store.SetGroupStatus(ctx, group, enabled) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return updatedAt, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	sys.notifyForGroup(ctx, group) | 	sys.notifyForGroup(ctx, group) | ||||||
| 	return nil | 	return updatedAt, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // GetGroupDescription - builds up group description | // GetGroupDescription - builds up group description | ||||||
| @ -1414,9 +1414,9 @@ func (sys *IAMSys) ListGroups(ctx context.Context) (r []string, err error) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // PolicyDBSet - sets a policy for a user or group in the PolicyDB. | // PolicyDBSet - sets a policy for a user or group in the PolicyDB. | ||||||
| func (sys *IAMSys) PolicyDBSet(ctx context.Context, name, policy string, isGroup bool) error { | func (sys *IAMSys) PolicyDBSet(ctx context.Context, name, policy string, isGroup bool) (updatedAt time.Time, err error) { | ||||||
| 	if !sys.Initialized() { | 	if !sys.Initialized() { | ||||||
| 		return errServerNotInitialized | 		return updatedAt, errServerNotInitialized | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Determine user-type based on IDP mode. | 	// Determine user-type based on IDP mode. | ||||||
| @ -1425,9 +1425,9 @@ func (sys *IAMSys) PolicyDBSet(ctx context.Context, name, policy string, isGroup | |||||||
| 		userType = stsUser | 		userType = stsUser | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err := sys.store.PolicyDBSet(ctx, name, policy, userType, isGroup) | 	updatedAt, err = sys.store.PolicyDBSet(ctx, name, policy, userType, isGroup) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Notify all other MinIO peers to reload policy | 	// Notify all other MinIO peers to reload policy | ||||||
| @ -1440,7 +1440,7 @@ func (sys *IAMSys) PolicyDBSet(ctx context.Context, name, policy string, isGroup | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return nil | 	return updatedAt, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // PolicyDBGet - gets policy set on a user or group. If a list of groups is | // PolicyDBGet - gets policy set on a user or group. If a list of groups is | ||||||
|  | |||||||
							
								
								
									
										11
									
								
								cmd/jwt.go
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								cmd/jwt.go
									
									
									
									
									
								
							| @ -63,11 +63,11 @@ func authenticateJWTUsers(accessKey, secretKey string, expiry time.Duration) (st | |||||||
| func authenticateJWTUsersWithCredentials(credentials auth.Credentials, expiresAt time.Time) (string, error) { | func authenticateJWTUsersWithCredentials(credentials auth.Credentials, expiresAt time.Time) (string, error) { | ||||||
| 	serverCred := globalActiveCred | 	serverCred := globalActiveCred | ||||||
| 	if serverCred.AccessKey != credentials.AccessKey { | 	if serverCred.AccessKey != credentials.AccessKey { | ||||||
| 		var ok bool | 		u, ok := globalIAMSys.GetUser(context.TODO(), credentials.AccessKey) | ||||||
| 		serverCred, ok = globalIAMSys.GetUser(context.TODO(), credentials.AccessKey) |  | ||||||
| 		if !ok { | 		if !ok { | ||||||
| 			return "", errInvalidAccessKeyID | 			return "", errInvalidAccessKeyID | ||||||
| 		} | 		} | ||||||
|  | 		serverCred = u.Credentials | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if !serverCred.Equal(credentials) { | 	if !serverCred.Equal(credentials) { | ||||||
| @ -145,10 +145,11 @@ func metricsRequestAuthenticate(req *http.Request) (*xjwt.MapClaims, []string, b | |||||||
| 		if claims.AccessKey == globalActiveCred.AccessKey { | 		if claims.AccessKey == globalActiveCred.AccessKey { | ||||||
| 			return []byte(globalActiveCred.SecretKey), nil | 			return []byte(globalActiveCred.SecretKey), nil | ||||||
| 		} | 		} | ||||||
| 		cred, ok := globalIAMSys.GetUser(req.Context(), claims.AccessKey) | 		u, ok := globalIAMSys.GetUser(req.Context(), claims.AccessKey) | ||||||
| 		if !ok { | 		if !ok { | ||||||
| 			return nil, errInvalidAccessKeyID | 			return nil, errInvalidAccessKeyID | ||||||
| 		} | 		} | ||||||
|  | 		cred := u.Credentials | ||||||
| 		return []byte(cred.SecretKey), nil | 		return []byte(cred.SecretKey), nil | ||||||
| 	}); err != nil { | 	}); err != nil { | ||||||
| 		return claims, nil, false, errAuthentication | 		return claims, nil, false, errAuthentication | ||||||
| @ -157,11 +158,11 @@ func metricsRequestAuthenticate(req *http.Request) (*xjwt.MapClaims, []string, b | |||||||
| 	var groups []string | 	var groups []string | ||||||
| 	if globalActiveCred.AccessKey != claims.AccessKey { | 	if globalActiveCred.AccessKey != claims.AccessKey { | ||||||
| 		// Check if the access key is part of users credentials. | 		// Check if the access key is part of users credentials. | ||||||
| 		ucred, ok := globalIAMSys.GetUser(req.Context(), claims.AccessKey) | 		u, ok := globalIAMSys.GetUser(req.Context(), claims.AccessKey) | ||||||
| 		if !ok { | 		if !ok { | ||||||
| 			return nil, nil, false, errInvalidAccessKeyID | 			return nil, nil, false, errInvalidAccessKeyID | ||||||
| 		} | 		} | ||||||
| 
 | 		ucred := u.Credentials | ||||||
| 		// get embedded claims | 		// get embedded claims | ||||||
| 		eclaims, s3Err := checkClaimsFromToken(req, ucred) | 		eclaims, s3Err := checkClaimsFromToken(req, ucred) | ||||||
| 		if s3Err != ErrNone { | 		if s3Err != ErrNone { | ||||||
|  | |||||||
| @ -152,16 +152,16 @@ func checkKeyValid(r *http.Request, accessKey string) (auth.Credentials, bool, A | |||||||
| 	cred := globalActiveCred | 	cred := globalActiveCred | ||||||
| 	if cred.AccessKey != accessKey { | 	if cred.AccessKey != accessKey { | ||||||
| 		// Check if the access key is part of users credentials. | 		// Check if the access key is part of users credentials. | ||||||
| 		ucred, ok := globalIAMSys.GetUser(r.Context(), accessKey) | 		u, ok := globalIAMSys.GetUser(r.Context(), accessKey) | ||||||
| 		if !ok { | 		if !ok { | ||||||
| 			// Credentials will be invalid but and disabled | 			// Credentials will be invalid but and disabled | ||||||
| 			// return a different error in such a scenario. | 			// return a different error in such a scenario. | ||||||
| 			if ucred.Status == auth.AccountOff { | 			if u.Credentials.Status == auth.AccountOff { | ||||||
| 				return cred, false, ErrAccessKeyDisabled | 				return cred, false, ErrAccessKeyDisabled | ||||||
| 			} | 			} | ||||||
| 			return cred, false, ErrInvalidAccessKeyID | 			return cred, false, ErrInvalidAccessKeyID | ||||||
| 		} | 		} | ||||||
| 		cred = ucred | 		cred = u.Credentials | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	claims, s3Err := checkClaimsFromToken(r, cred) | 	claims, s3Err := checkClaimsFromToken(r, cred) | ||||||
|  | |||||||
| @ -403,14 +403,15 @@ func (c *SiteReplicationSys) AddPeerClusters(ctx context.Context, psites []madmi | |||||||
| 
 | 
 | ||||||
| 	// Generate a secret key for the service account if not created already. | 	// Generate a secret key for the service account if not created already. | ||||||
| 	var secretKey string | 	var secretKey string | ||||||
| 	svcCred, _, err := globalIAMSys.getServiceAccount(ctx, siteReplicatorSvcAcc) | 	var svcCred auth.Credentials | ||||||
|  | 	sa, _, err := globalIAMSys.getServiceAccount(ctx, siteReplicatorSvcAcc) | ||||||
| 	switch { | 	switch { | ||||||
| 	case err == errNoSuchServiceAccount: | 	case err == errNoSuchServiceAccount: | ||||||
| 		_, secretKey, err = auth.GenerateCredentials() | 		_, secretKey, err = auth.GenerateCredentials() | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return madmin.ReplicateAddStatus{}, errSRServiceAccount(fmt.Errorf("unable to create local service account: %w", err)) | 			return madmin.ReplicateAddStatus{}, errSRServiceAccount(fmt.Errorf("unable to create local service account: %w", err)) | ||||||
| 		} | 		} | ||||||
| 		svcCred, err = globalIAMSys.NewServiceAccount(ctx, sites[selfIdx].AccessKey, nil, newServiceAccountOpts{ | 		svcCred, _, err = globalIAMSys.NewServiceAccount(ctx, sites[selfIdx].AccessKey, nil, newServiceAccountOpts{ | ||||||
| 			accessKey: siteReplicatorSvcAcc, | 			accessKey: siteReplicatorSvcAcc, | ||||||
| 			secretKey: secretKey, | 			secretKey: secretKey, | ||||||
| 		}) | 		}) | ||||||
| @ -418,6 +419,7 @@ func (c *SiteReplicationSys) AddPeerClusters(ctx context.Context, psites []madmi | |||||||
| 			return madmin.ReplicateAddStatus{}, errSRServiceAccount(fmt.Errorf("unable to create local service account: %w", err)) | 			return madmin.ReplicateAddStatus{}, errSRServiceAccount(fmt.Errorf("unable to create local service account: %w", err)) | ||||||
| 		} | 		} | ||||||
| 	case err == nil: | 	case err == nil: | ||||||
|  | 		svcCred = sa.Credentials | ||||||
| 		secretKey = svcCred.SecretKey | 		secretKey = svcCred.SecretKey | ||||||
| 	default: | 	default: | ||||||
| 		return madmin.ReplicateAddStatus{}, errSRBackendIssue(err) | 		return madmin.ReplicateAddStatus{}, errSRBackendIssue(err) | ||||||
| @ -525,7 +527,7 @@ func (c *SiteReplicationSys) PeerJoinReq(ctx context.Context, arg madmin.SRPeerJ | |||||||
| 
 | 
 | ||||||
| 	_, _, err := globalIAMSys.GetServiceAccount(ctx, arg.SvcAcctAccessKey) | 	_, _, err := globalIAMSys.GetServiceAccount(ctx, arg.SvcAcctAccessKey) | ||||||
| 	if err == errNoSuchServiceAccount { | 	if err == errNoSuchServiceAccount { | ||||||
| 		_, err = globalIAMSys.NewServiceAccount(ctx, arg.SvcAcctParent, nil, newServiceAccountOpts{ | 		_, _, err = globalIAMSys.NewServiceAccount(ctx, arg.SvcAcctParent, nil, newServiceAccountOpts{ | ||||||
| 			accessKey: arg.SvcAcctAccessKey, | 			accessKey: arg.SvcAcctAccessKey, | ||||||
| 			secretKey: arg.SvcAcctSecretKey, | 			secretKey: arg.SvcAcctSecretKey, | ||||||
| 		}) | 		}) | ||||||
| @ -1022,12 +1024,18 @@ func (c *SiteReplicationSys) IAMChangeHook(ctx context.Context, item madmin.SRIA | |||||||
| 
 | 
 | ||||||
| // PeerAddPolicyHandler - copies IAM policy to local. A nil policy argument, | // PeerAddPolicyHandler - copies IAM policy to local. A nil policy argument, | ||||||
| // causes the named policy to be deleted. | // causes the named policy to be deleted. | ||||||
| func (c *SiteReplicationSys) PeerAddPolicyHandler(ctx context.Context, policyName string, p *iampolicy.Policy) error { | func (c *SiteReplicationSys) PeerAddPolicyHandler(ctx context.Context, policyName string, p *iampolicy.Policy, updatedAt time.Time) error { | ||||||
| 	var err error | 	var err error | ||||||
|  | 	// skip overwrite of local update if peer sent stale info | ||||||
|  | 	if !updatedAt.IsZero() { | ||||||
|  | 		if p, err := globalIAMSys.store.GetPolicyDoc(policyName); err == nil && p.UpdateDate.After(updatedAt) { | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 	if p == nil { | 	if p == nil { | ||||||
| 		err = globalIAMSys.DeletePolicy(ctx, policyName, true) | 		err = globalIAMSys.DeletePolicy(ctx, policyName, true) | ||||||
| 	} else { | 	} else { | ||||||
| 		err = globalIAMSys.SetPolicy(ctx, policyName, *p) | 		_, err = globalIAMSys.SetPolicy(ctx, policyName, *p) | ||||||
| 	} | 	} | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return wrapSRErr(err) | 		return wrapSRErr(err) | ||||||
| @ -1036,10 +1044,17 @@ func (c *SiteReplicationSys) PeerAddPolicyHandler(ctx context.Context, policyNam | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // PeerIAMUserChangeHandler - copies IAM user to local. | // PeerIAMUserChangeHandler - copies IAM user to local. | ||||||
| func (c *SiteReplicationSys) PeerIAMUserChangeHandler(ctx context.Context, change *madmin.SRIAMUser) error { | func (c *SiteReplicationSys) PeerIAMUserChangeHandler(ctx context.Context, change *madmin.SRIAMUser, updatedAt time.Time) error { | ||||||
| 	if change == nil { | 	if change == nil { | ||||||
| 		return errSRInvalidRequest(errInvalidArgument) | 		return errSRInvalidRequest(errInvalidArgument) | ||||||
| 	} | 	} | ||||||
|  | 	// skip overwrite of local update if peer sent stale info | ||||||
|  | 	if !updatedAt.IsZero() { | ||||||
|  | 		if ui, err := globalIAMSys.GetUserInfo(ctx, change.AccessKey); err == nil && ui.UpdatedAt.After(updatedAt) { | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	var err error | 	var err error | ||||||
| 	if change.IsDeleteReq { | 	if change.IsDeleteReq { | ||||||
| 		err = globalIAMSys.DeleteUser(ctx, change.AccessKey, true) | 		err = globalIAMSys.DeleteUser(ctx, change.AccessKey, true) | ||||||
| @ -1051,9 +1066,9 @@ func (c *SiteReplicationSys) PeerIAMUserChangeHandler(ctx context.Context, chang | |||||||
| 		if userReq.Status != "" && userReq.SecretKey == "" { | 		if userReq.Status != "" && userReq.SecretKey == "" { | ||||||
| 			// Status is set without secretKey updates means we are | 			// Status is set without secretKey updates means we are | ||||||
| 			// only changing the account status. | 			// only changing the account status. | ||||||
| 			err = globalIAMSys.SetUserStatus(ctx, change.AccessKey, userReq.Status) | 			_, err = globalIAMSys.SetUserStatus(ctx, change.AccessKey, userReq.Status) | ||||||
| 		} else { | 		} else { | ||||||
| 			err = globalIAMSys.CreateUser(ctx, change.AccessKey, userReq) | 			_, err = globalIAMSys.CreateUser(ctx, change.AccessKey, userReq) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @ -1063,19 +1078,27 @@ func (c *SiteReplicationSys) PeerIAMUserChangeHandler(ctx context.Context, chang | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // PeerGroupInfoChangeHandler - copies group changes to local. | // PeerGroupInfoChangeHandler - copies group changes to local. | ||||||
| func (c *SiteReplicationSys) PeerGroupInfoChangeHandler(ctx context.Context, change *madmin.SRGroupInfo) error { | func (c *SiteReplicationSys) PeerGroupInfoChangeHandler(ctx context.Context, change *madmin.SRGroupInfo, updatedAt time.Time) error { | ||||||
| 	if change == nil { | 	if change == nil { | ||||||
| 		return errSRInvalidRequest(errInvalidArgument) | 		return errSRInvalidRequest(errInvalidArgument) | ||||||
| 	} | 	} | ||||||
| 	updReq := change.UpdateReq | 	updReq := change.UpdateReq | ||||||
| 	var err error | 	var err error | ||||||
|  | 
 | ||||||
|  | 	// skip overwrite of local update if peer sent stale info | ||||||
|  | 	if !updatedAt.IsZero() { | ||||||
|  | 		if gd, err := globalIAMSys.GetGroupDescription(updReq.Group); err == nil && gd.UpdatedAt.After(updatedAt) { | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if updReq.IsRemove { | 	if updReq.IsRemove { | ||||||
| 		err = globalIAMSys.RemoveUsersFromGroup(ctx, updReq.Group, updReq.Members) | 		_, err = globalIAMSys.RemoveUsersFromGroup(ctx, updReq.Group, updReq.Members) | ||||||
| 	} else { | 	} else { | ||||||
| 		if updReq.Status != "" && len(updReq.Members) == 0 { | 		if updReq.Status != "" && len(updReq.Members) == 0 { | ||||||
| 			err = globalIAMSys.SetGroupStatus(ctx, updReq.Group, updReq.Status == madmin.GroupEnabled) | 			_, err = globalIAMSys.SetGroupStatus(ctx, updReq.Group, updReq.Status == madmin.GroupEnabled) | ||||||
| 		} else { | 		} else { | ||||||
| 			err = globalIAMSys.AddUsersToGroup(ctx, updReq.Group, updReq.Members) | 			_, err = globalIAMSys.AddUsersToGroup(ctx, updReq.Group, updReq.Members) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @ -1085,7 +1108,7 @@ func (c *SiteReplicationSys) PeerGroupInfoChangeHandler(ctx context.Context, cha | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // PeerSvcAccChangeHandler - copies service-account change to local. | // PeerSvcAccChangeHandler - copies service-account change to local. | ||||||
| func (c *SiteReplicationSys) PeerSvcAccChangeHandler(ctx context.Context, change *madmin.SRSvcAccChange) error { | func (c *SiteReplicationSys) PeerSvcAccChangeHandler(ctx context.Context, change *madmin.SRSvcAccChange, updatedAt time.Time) error { | ||||||
| 	if change == nil { | 	if change == nil { | ||||||
| 		return errSRInvalidRequest(errInvalidArgument) | 		return errSRInvalidRequest(errInvalidArgument) | ||||||
| 	} | 	} | ||||||
| @ -1099,14 +1122,19 @@ func (c *SiteReplicationSys) PeerSvcAccChangeHandler(ctx context.Context, change | |||||||
| 				return wrapSRErr(err) | 				return wrapSRErr(err) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 		// skip overwrite of local update if peer sent stale info | ||||||
|  | 		if !updatedAt.IsZero() && change.Create.AccessKey != "" { | ||||||
|  | 			if sa, _, err := globalIAMSys.getServiceAccount(ctx, change.Create.AccessKey); err == nil && sa.UpdatedAt.After(updatedAt) { | ||||||
|  | 				return nil | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
| 		opts := newServiceAccountOpts{ | 		opts := newServiceAccountOpts{ | ||||||
| 			accessKey:     change.Create.AccessKey, | 			accessKey:     change.Create.AccessKey, | ||||||
| 			secretKey:     change.Create.SecretKey, | 			secretKey:     change.Create.SecretKey, | ||||||
| 			sessionPolicy: sp, | 			sessionPolicy: sp, | ||||||
| 			claims:        change.Create.Claims, | 			claims:        change.Create.Claims, | ||||||
| 		} | 		} | ||||||
| 		_, err = globalIAMSys.NewServiceAccount(ctx, change.Create.Parent, change.Create.Groups, opts) | 		_, _, err = globalIAMSys.NewServiceAccount(ctx, change.Create.Parent, change.Create.Groups, opts) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return wrapSRErr(err) | 			return wrapSRErr(err) | ||||||
| 		} | 		} | ||||||
| @ -1120,20 +1148,31 @@ func (c *SiteReplicationSys) PeerSvcAccChangeHandler(ctx context.Context, change | |||||||
| 				return wrapSRErr(err) | 				return wrapSRErr(err) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 		// skip overwrite of local update if peer sent stale info | ||||||
|  | 		if !updatedAt.IsZero() { | ||||||
|  | 			if sa, _, err := globalIAMSys.getServiceAccount(ctx, change.Update.AccessKey); err == nil && sa.UpdatedAt.After(updatedAt) { | ||||||
|  | 				return nil | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
| 		opts := updateServiceAccountOpts{ | 		opts := updateServiceAccountOpts{ | ||||||
| 			secretKey:     change.Update.SecretKey, | 			secretKey:     change.Update.SecretKey, | ||||||
| 			status:        change.Update.Status, | 			status:        change.Update.Status, | ||||||
| 			sessionPolicy: sp, | 			sessionPolicy: sp, | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		err = globalIAMSys.UpdateServiceAccount(ctx, change.Update.AccessKey, opts) | 		_, err = globalIAMSys.UpdateServiceAccount(ctx, change.Update.AccessKey, opts) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return wrapSRErr(err) | 			return wrapSRErr(err) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 	case change.Delete != nil: | 	case change.Delete != nil: | ||||||
| 		err := globalIAMSys.DeleteServiceAccount(ctx, change.Delete.AccessKey, true) | 		// skip overwrite of local update if peer sent stale info | ||||||
| 		if err != nil { | 		if !updatedAt.IsZero() { | ||||||
|  | 			if sa, _, err := globalIAMSys.getServiceAccount(ctx, change.Delete.AccessKey); err == nil && sa.UpdatedAt.After(updatedAt) { | ||||||
|  | 				return nil | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if err := globalIAMSys.DeleteServiceAccount(ctx, change.Delete.AccessKey, true); err != nil { | ||||||
| 			return wrapSRErr(err) | 			return wrapSRErr(err) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| @ -1143,11 +1182,19 @@ func (c *SiteReplicationSys) PeerSvcAccChangeHandler(ctx context.Context, change | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // PeerPolicyMappingHandler - copies policy mapping to local. | // PeerPolicyMappingHandler - copies policy mapping to local. | ||||||
| func (c *SiteReplicationSys) PeerPolicyMappingHandler(ctx context.Context, mapping *madmin.SRPolicyMapping) error { | func (c *SiteReplicationSys) PeerPolicyMappingHandler(ctx context.Context, mapping *madmin.SRPolicyMapping, updatedAt time.Time) error { | ||||||
| 	if mapping == nil { | 	if mapping == nil { | ||||||
| 		return errSRInvalidRequest(errInvalidArgument) | 		return errSRInvalidRequest(errInvalidArgument) | ||||||
| 	} | 	} | ||||||
| 	err := globalIAMSys.PolicyDBSet(ctx, mapping.UserOrGroup, mapping.Policy, mapping.IsGroup) | 	// skip overwrite of local update if peer sent stale info | ||||||
|  | 	if !updatedAt.IsZero() { | ||||||
|  | 		mp, ok := globalIAMSys.store.GetMappedPolicy(mapping.Policy, mapping.IsGroup) | ||||||
|  | 		if ok && mp.UpdatedAt.After(updatedAt) { | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	_, err := globalIAMSys.PolicyDBSet(ctx, mapping.UserOrGroup, mapping.Policy, mapping.IsGroup) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return wrapSRErr(err) | 		return wrapSRErr(err) | ||||||
| 	} | 	} | ||||||
| @ -1155,10 +1202,19 @@ func (c *SiteReplicationSys) PeerPolicyMappingHandler(ctx context.Context, mappi | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // PeerSTSAccHandler - replicates STS credential locally. | // PeerSTSAccHandler - replicates STS credential locally. | ||||||
| func (c *SiteReplicationSys) PeerSTSAccHandler(ctx context.Context, stsCred *madmin.SRSTSCredential) error { | func (c *SiteReplicationSys) PeerSTSAccHandler(ctx context.Context, stsCred *madmin.SRSTSCredential, updatedAt time.Time) error { | ||||||
| 	if stsCred == nil { | 	if stsCred == nil { | ||||||
| 		return errSRInvalidRequest(errInvalidArgument) | 		return errSRInvalidRequest(errInvalidArgument) | ||||||
| 	} | 	} | ||||||
|  | 	// skip overwrite of local update if peer sent stale info | ||||||
|  | 	if !updatedAt.IsZero() { | ||||||
|  | 		if u, err := globalIAMSys.GetUserInfo(ctx, stsCred.AccessKey); err == nil { | ||||||
|  | 			ok, _, _ := globalIAMSys.IsTempUser(stsCred.AccessKey) | ||||||
|  | 			if ok && u.UpdatedAt.After(updatedAt) { | ||||||
|  | 				return nil | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	// Verify the session token of the stsCred | 	// Verify the session token of the stsCred | ||||||
| 	claims, err := auth.ExtractClaims(stsCred.SessionToken, globalActiveCred.SecretKey) | 	claims, err := auth.ExtractClaims(stsCred.SessionToken, globalActiveCred.SecretKey) | ||||||
| @ -1195,7 +1251,7 @@ func (c *SiteReplicationSys) PeerSTSAccHandler(ctx context.Context, stsCred *mad | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Set these credentials to IAM. | 	// Set these credentials to IAM. | ||||||
| 	if err := globalIAMSys.SetTempUser(ctx, cred.AccessKey, cred, stsCred.ParentPolicyMapping); err != nil { | 	if _, err := globalIAMSys.SetTempUser(ctx, cred.AccessKey, cred, stsCred.ParentPolicyMapping); err != nil { | ||||||
| 		return fmt.Errorf("unable to save STS credential and/or parent policy mapping: %w", err) | 		return fmt.Errorf("unable to save STS credential and/or parent policy mapping: %w", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -1426,11 +1482,11 @@ func (c *SiteReplicationSys) getAdminClientWithEndpoint(ctx context.Context, dep | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (c *SiteReplicationSys) getPeerCreds() (*auth.Credentials, error) { | func (c *SiteReplicationSys) getPeerCreds() (*auth.Credentials, error) { | ||||||
| 	creds, ok := globalIAMSys.store.GetUser(c.state.ServiceAccountAccessKey) | 	u, ok := globalIAMSys.store.GetUser(c.state.ServiceAccountAccessKey) | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		return nil, errors.New("site replication service account not found") | 		return nil, errors.New("site replication service account not found") | ||||||
| 	} | 	} | ||||||
| 	return &creds, nil | 	return &u.Credentials, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // listBuckets returns a consistent common view of latest unique buckets across | // listBuckets returns a consistent common view of latest unique buckets across | ||||||
| @ -1606,13 +1662,13 @@ func (c *SiteReplicationSys) syncToAllPeers(ctx context.Context) error { | |||||||
| 	// Policies should be synced first. | 	// Policies should be synced first. | ||||||
| 	{ | 	{ | ||||||
| 		// Replicate IAM policies on local to all peers. | 		// Replicate IAM policies on local to all peers. | ||||||
| 		allPolicies, err := globalIAMSys.ListPolicies(ctx, "") | 		allPolicyDocs, err := globalIAMSys.ListPolicyDocs(ctx, "") | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return errSRBackendIssue(err) | 			return errSRBackendIssue(err) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		for pname, policy := range allPolicies { | 		for pname, pdoc := range allPolicyDocs { | ||||||
| 			policyJSON, err := json.Marshal(policy) | 			policyJSON, err := json.Marshal(pdoc.Policy) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return wrapSRErr(err) | 				return wrapSRErr(err) | ||||||
| 			} | 			} | ||||||
| @ -1620,6 +1676,7 @@ func (c *SiteReplicationSys) syncToAllPeers(ctx context.Context) error { | |||||||
| 				Type:      madmin.SRIAMItemPolicy, | 				Type:      madmin.SRIAMItemPolicy, | ||||||
| 				Name:      pname, | 				Name:      pname, | ||||||
| 				Policy:    policyJSON, | 				Policy:    policyJSON, | ||||||
|  | 				UpdatedAt: pdoc.UpdateDate, | ||||||
| 			}) | 			}) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return errSRIAMError(err) | 				return errSRIAMError(err) | ||||||
| @ -1630,7 +1687,7 @@ func (c *SiteReplicationSys) syncToAllPeers(ctx context.Context) error { | |||||||
| 	// Next should be userAccounts those are local users, OIDC and LDAP will not | 	// Next should be userAccounts those are local users, OIDC and LDAP will not | ||||||
| 	// may not have any local users. | 	// may not have any local users. | ||||||
| 	{ | 	{ | ||||||
| 		userAccounts := make(map[string]auth.Credentials) | 		userAccounts := make(map[string]UserIdentity) | ||||||
| 		globalIAMSys.store.rlock() | 		globalIAMSys.store.rlock() | ||||||
| 		err := globalIAMSys.store.loadUsers(ctx, regUser, userAccounts) | 		err := globalIAMSys.store.loadUsers(ctx, regUser, userAccounts) | ||||||
| 		globalIAMSys.store.runlock() | 		globalIAMSys.store.runlock() | ||||||
| @ -1642,13 +1699,14 @@ func (c *SiteReplicationSys) syncToAllPeers(ctx context.Context) error { | |||||||
| 			if err := c.IAMChangeHook(ctx, madmin.SRIAMItem{ | 			if err := c.IAMChangeHook(ctx, madmin.SRIAMItem{ | ||||||
| 				Type: madmin.SRIAMItemIAMUser, | 				Type: madmin.SRIAMItemIAMUser, | ||||||
| 				IAMUser: &madmin.SRIAMUser{ | 				IAMUser: &madmin.SRIAMUser{ | ||||||
| 					AccessKey:   acc.AccessKey, | 					AccessKey:   acc.Credentials.AccessKey, | ||||||
| 					IsDeleteReq: false, | 					IsDeleteReq: false, | ||||||
| 					UserReq: &madmin.AddOrUpdateUserReq{ | 					UserReq: &madmin.AddOrUpdateUserReq{ | ||||||
| 						SecretKey: acc.SecretKey, | 						SecretKey: acc.Credentials.SecretKey, | ||||||
| 						Status:    madmin.AccountStatus(acc.Status), | 						Status:    madmin.AccountStatus(acc.Credentials.Status), | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
|  | 				UpdatedAt: acc.UpdatedAt, | ||||||
| 			}); err != nil { | 			}); err != nil { | ||||||
| 				return errSRIAMError(err) | 				return errSRIAMError(err) | ||||||
| 			} | 			} | ||||||
| @ -1678,6 +1736,7 @@ func (c *SiteReplicationSys) syncToAllPeers(ctx context.Context) error { | |||||||
| 						IsRemove: false, | 						IsRemove: false, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
|  | 				UpdatedAt: group.UpdatedAt, | ||||||
| 			}); err != nil { | 			}); err != nil { | ||||||
| 				return errSRIAMError(err) | 				return errSRIAMError(err) | ||||||
| 			} | 			} | ||||||
| @ -1687,7 +1746,7 @@ func (c *SiteReplicationSys) syncToAllPeers(ctx context.Context) error { | |||||||
| 	// Service accounts are the static accounts that should be synced with | 	// Service accounts are the static accounts that should be synced with | ||||||
| 	// valid claims. | 	// valid claims. | ||||||
| 	{ | 	{ | ||||||
| 		serviceAccounts := make(map[string]auth.Credentials) | 		serviceAccounts := make(map[string]UserIdentity) | ||||||
| 		globalIAMSys.store.rlock() | 		globalIAMSys.store.rlock() | ||||||
| 		err := globalIAMSys.store.loadUsers(ctx, svcUser, serviceAccounts) | 		err := globalIAMSys.store.loadUsers(ctx, svcUser, serviceAccounts) | ||||||
| 		globalIAMSys.store.runlock() | 		globalIAMSys.store.runlock() | ||||||
| @ -1702,12 +1761,12 @@ func (c *SiteReplicationSys) syncToAllPeers(ctx context.Context) error { | |||||||
| 				continue | 				continue | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			claims, err := globalIAMSys.GetClaimsForSvcAcc(ctx, acc.AccessKey) | 			claims, err := globalIAMSys.GetClaimsForSvcAcc(ctx, acc.Credentials.AccessKey) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return errSRBackendIssue(err) | 				return errSRBackendIssue(err) | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			_, policy, err := globalIAMSys.GetServiceAccount(ctx, acc.AccessKey) | 			_, policy, err := globalIAMSys.GetServiceAccount(ctx, acc.Credentials.AccessKey) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return errSRBackendIssue(err) | 				return errSRBackendIssue(err) | ||||||
| 			} | 			} | ||||||
| @ -1724,15 +1783,16 @@ func (c *SiteReplicationSys) syncToAllPeers(ctx context.Context) error { | |||||||
| 				Type: madmin.SRIAMItemSvcAcc, | 				Type: madmin.SRIAMItemSvcAcc, | ||||||
| 				SvcAccChange: &madmin.SRSvcAccChange{ | 				SvcAccChange: &madmin.SRSvcAccChange{ | ||||||
| 					Create: &madmin.SRSvcAccCreate{ | 					Create: &madmin.SRSvcAccCreate{ | ||||||
| 						Parent:        acc.ParentUser, | 						Parent:        acc.Credentials.ParentUser, | ||||||
| 						AccessKey:     user, | 						AccessKey:     user, | ||||||
| 						SecretKey:     acc.SecretKey, | 						SecretKey:     acc.Credentials.SecretKey, | ||||||
| 						Groups:        acc.Groups, | 						Groups:        acc.Credentials.Groups, | ||||||
| 						Claims:        claims, | 						Claims:        claims, | ||||||
| 						SessionPolicy: json.RawMessage(policyJSON), | 						SessionPolicy: json.RawMessage(policyJSON), | ||||||
| 						Status:        acc.Status, | 						Status:        acc.Credentials.Status, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
|  | 				UpdatedAt: acc.UpdatedAt, | ||||||
| 			}) | 			}) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return errSRIAMError(err) | 				return errSRIAMError(err) | ||||||
| @ -1761,6 +1821,7 @@ func (c *SiteReplicationSys) syncToAllPeers(ctx context.Context) error { | |||||||
| 					IsGroup:     false, | 					IsGroup:     false, | ||||||
| 					Policy:      mp.Policies, | 					Policy:      mp.Policies, | ||||||
| 				}, | 				}, | ||||||
|  | 				UpdatedAt: mp.UpdatedAt, | ||||||
| 			}) | 			}) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return errSRIAMError(err) | 				return errSRIAMError(err) | ||||||
| @ -1779,6 +1840,7 @@ func (c *SiteReplicationSys) syncToAllPeers(ctx context.Context) error { | |||||||
| 					IsGroup:     true, | 					IsGroup:     true, | ||||||
| 					Policy:      mp.Policies, | 					Policy:      mp.Policies, | ||||||
| 				}, | 				}, | ||||||
|  | 				UpdatedAt: mp.UpdatedAt, | ||||||
| 			}) | 			}) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return errSRIAMError(err) | 				return errSRIAMError(err) | ||||||
| @ -1807,6 +1869,7 @@ func (c *SiteReplicationSys) syncToAllPeers(ctx context.Context) error { | |||||||
| 					IsGroup:     false, | 					IsGroup:     false, | ||||||
| 					Policy:      mp.Policies, | 					Policy:      mp.Policies, | ||||||
| 				}, | 				}, | ||||||
|  | 				UpdatedAt: mp.UpdatedAt, | ||||||
| 			}) | 			}) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return errSRIAMError(err) | 				return errSRIAMError(err) | ||||||
| @ -1825,6 +1888,7 @@ func (c *SiteReplicationSys) syncToAllPeers(ctx context.Context) error { | |||||||
| 					IsGroup:     true, | 					IsGroup:     true, | ||||||
| 					Policy:      mp.Policies, | 					Policy:      mp.Policies, | ||||||
| 				}, | 				}, | ||||||
|  | 				UpdatedAt: mp.UpdatedAt, | ||||||
| 			}) | 			}) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return errSRIAMError(err) | 				return errSRIAMError(err) | ||||||
| @ -3244,8 +3308,8 @@ func (c *SiteReplicationSys) SiteReplicationMetaInfo(ctx context.Context, objAPI | |||||||
| 					return info, errSRBackendIssue(err) | 					return info, errSRBackendIssue(err) | ||||||
| 				} | 				} | ||||||
| 				for _, tempAcct := range tempAccts { | 				for _, tempAcct := range tempAccts { | ||||||
| 					info.UserInfoMap[tempAcct.AccessKey] = madmin.UserInfo{ | 					info.UserInfoMap[tempAcct.Credentials.AccessKey] = madmin.UserInfo{ | ||||||
| 						Status: madmin.AccountStatus(tempAcct.Status), | 						Status: madmin.AccountStatus(tempAcct.Credentials.Status), | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| @ -4166,6 +4230,7 @@ func (c *SiteReplicationSys) healPolicies(ctx context.Context, objAPI ObjectLaye | |||||||
| 			Type:      madmin.SRIAMItemPolicy, | 			Type:      madmin.SRIAMItemPolicy, | ||||||
| 			Name:      policy, | 			Name:      policy, | ||||||
| 			Policy:    latestPolicyStat.policy.Policy, | 			Policy:    latestPolicyStat.policy.Policy, | ||||||
|  | 			UpdatedAt: lastUpdate, | ||||||
| 		}) | 		}) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			logger.LogIf(ctx, fmt.Errorf("Error healing IAM policy %s from peer site %s -> site %s : %w", policy, latestPeerName, peerName, err)) | 			logger.LogIf(ctx, fmt.Errorf("Error healing IAM policy %s from peer site %s -> site %s : %w", policy, latestPeerName, peerName, err)) | ||||||
| @ -4225,6 +4290,7 @@ func (c *SiteReplicationSys) healUserPolicies(ctx context.Context, objAPI Object | |||||||
| 				IsGroup:     false, | 				IsGroup:     false, | ||||||
| 				Policy:      latestUserStat.userPolicy.Policy, | 				Policy:      latestUserStat.userPolicy.Policy, | ||||||
| 			}, | 			}, | ||||||
|  | 			UpdatedAt: lastUpdate, | ||||||
| 		}) | 		}) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			logger.LogIf(ctx, fmt.Errorf("Error healing IAM user policy mapping for %s from peer site %s -> site %s : %w", user, latestPeerName, peerName, err)) | 			logger.LogIf(ctx, fmt.Errorf("Error healing IAM user policy mapping for %s from peer site %s -> site %s : %w", user, latestPeerName, peerName, err)) | ||||||
| @ -4286,6 +4352,7 @@ func (c *SiteReplicationSys) healGroupPolicies(ctx context.Context, objAPI Objec | |||||||
| 				IsGroup:     true, | 				IsGroup:     true, | ||||||
| 				Policy:      latestGroupStat.groupPolicy.Policy, | 				Policy:      latestGroupStat.groupPolicy.Policy, | ||||||
| 			}, | 			}, | ||||||
|  | 			UpdatedAt: lastUpdate, | ||||||
| 		}) | 		}) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			logger.LogIf(ctx, fmt.Errorf("Error healing IAM group policy mapping for %s from peer site %s -> site %s : %w", group, latestPeerName, peerName, err)) | 			logger.LogIf(ctx, fmt.Errorf("Error healing IAM group policy mapping for %s from peer site %s -> site %s : %w", group, latestPeerName, peerName, err)) | ||||||
| @ -4340,11 +4407,11 @@ func (c *SiteReplicationSys) healUsers(ctx context.Context, objAPI ObjectLayer, | |||||||
| 
 | 
 | ||||||
| 		peerName := info.Sites[dID].Name | 		peerName := info.Sites[dID].Name | ||||||
| 
 | 
 | ||||||
| 		creds, ok := globalIAMSys.GetUser(ctx, user) | 		u, ok := globalIAMSys.GetUser(ctx, user) | ||||||
| 		if !ok { | 		if !ok { | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| 
 | 		creds := u.Credentials | ||||||
| 		// heal only the user accounts that are local users | 		// heal only the user accounts that are local users | ||||||
| 		if creds.IsServiceAccount() { | 		if creds.IsServiceAccount() { | ||||||
| 			claims, err := globalIAMSys.GetClaimsForSvcAcc(ctx, creds.AccessKey) | 			claims, err := globalIAMSys.GetClaimsForSvcAcc(ctx, creds.AccessKey) | ||||||
| @ -4381,6 +4448,7 @@ func (c *SiteReplicationSys) healUsers(ctx context.Context, objAPI ObjectLayer, | |||||||
| 						Status:        creds.Status, | 						Status:        creds.Status, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
|  | 				UpdatedAt: lastUpdate, | ||||||
| 			}); err != nil { | 			}); err != nil { | ||||||
| 				logger.LogIf(ctx, fmt.Errorf("Error healing service account %s from peer site %s -> %s : %w", user, latestPeerName, peerName, err)) | 				logger.LogIf(ctx, fmt.Errorf("Error healing service account %s from peer site %s -> %s : %w", user, latestPeerName, peerName, err)) | ||||||
| 			} | 			} | ||||||
| @ -4402,6 +4470,7 @@ func (c *SiteReplicationSys) healUsers(ctx context.Context, objAPI ObjectLayer, | |||||||
| 					ParentUser:          creds.ParentUser, | 					ParentUser:          creds.ParentUser, | ||||||
| 					ParentPolicyMapping: u.PolicyName, | 					ParentPolicyMapping: u.PolicyName, | ||||||
| 				}, | 				}, | ||||||
|  | 				UpdatedAt: lastUpdate, | ||||||
| 			}); err != nil { | 			}); err != nil { | ||||||
| 				logger.LogIf(ctx, fmt.Errorf("Error healing temporary credentials %s from peer site %s -> %s : %w", user, latestPeerName, peerName, err)) | 				logger.LogIf(ctx, fmt.Errorf("Error healing temporary credentials %s from peer site %s -> %s : %w", user, latestPeerName, peerName, err)) | ||||||
| 			} | 			} | ||||||
| @ -4417,6 +4486,7 @@ func (c *SiteReplicationSys) healUsers(ctx context.Context, objAPI ObjectLayer, | |||||||
| 					Status:    latestUserStat.userInfo.Status, | 					Status:    latestUserStat.userInfo.Status, | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
|  | 			UpdatedAt: lastUpdate, | ||||||
| 		}); err != nil { | 		}); err != nil { | ||||||
| 			logger.LogIf(ctx, fmt.Errorf("Error healing user %s from peer site %s -> %s : %w", user, latestPeerName, peerName, err)) | 			logger.LogIf(ctx, fmt.Errorf("Error healing user %s from peer site %s -> %s : %w", user, latestPeerName, peerName, err)) | ||||||
| 		} | 		} | ||||||
| @ -4481,6 +4551,7 @@ func (c *SiteReplicationSys) healGroups(ctx context.Context, objAPI ObjectLayer, | |||||||
| 					IsRemove: false, | 					IsRemove: false, | ||||||
| 				}, | 				}, | ||||||
| 			}, | 			}, | ||||||
|  | 			UpdatedAt: lastUpdate, | ||||||
| 		}); err != nil { | 		}); err != nil { | ||||||
| 			logger.LogIf(ctx, fmt.Errorf("Error healing group %s from peer site %s -> site %s : %w", group, latestPeerName, peerName, err)) | 			logger.LogIf(ctx, fmt.Errorf("Error healing group %s from peer site %s -> site %s : %w", group, latestPeerName, peerName, err)) | ||||||
| 		} | 		} | ||||||
|  | |||||||
| @ -275,7 +275,8 @@ func (sts *stsAPIHandlers) AssumeRole(w http.ResponseWriter, r *http.Request) { | |||||||
| 	cred.ParentUser = user.AccessKey | 	cred.ParentUser = user.AccessKey | ||||||
| 
 | 
 | ||||||
| 	// Set the newly generated credentials. | 	// Set the newly generated credentials. | ||||||
| 	if err = globalIAMSys.SetTempUser(ctx, cred.AccessKey, cred, ""); err != nil { | 	updatedAt, err := globalIAMSys.SetTempUser(ctx, cred.AccessKey, cred, "") | ||||||
|  | 	if err != nil { | ||||||
| 		writeSTSErrorResponse(ctx, w, true, ErrSTSInternalError, err) | 		writeSTSErrorResponse(ctx, w, true, ErrSTSInternalError, err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| @ -290,6 +291,7 @@ func (sts *stsAPIHandlers) AssumeRole(w http.ResponseWriter, r *http.Request) { | |||||||
| 				SessionToken: cred.SessionToken, | 				SessionToken: cred.SessionToken, | ||||||
| 				ParentUser:   cred.ParentUser, | 				ParentUser:   cred.ParentUser, | ||||||
| 			}, | 			}, | ||||||
|  | 			UpdatedAt: updatedAt, | ||||||
| 		}); err != nil { | 		}); err != nil { | ||||||
| 			logger.LogIf(ctx, err) | 			logger.LogIf(ctx, err) | ||||||
| 		} | 		} | ||||||
| @ -469,7 +471,8 @@ func (sts *stsAPIHandlers) AssumeRoleWithSSO(w http.ResponseWriter, r *http.Requ | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Set the newly generated credentials. | 	// Set the newly generated credentials. | ||||||
| 	if err = globalIAMSys.SetTempUser(ctx, cred.AccessKey, cred, policyName); err != nil { | 	updatedAt, err := globalIAMSys.SetTempUser(ctx, cred.AccessKey, cred, policyName) | ||||||
|  | 	if err != nil { | ||||||
| 		writeSTSErrorResponse(ctx, w, true, ErrSTSInternalError, err) | 		writeSTSErrorResponse(ctx, w, true, ErrSTSInternalError, err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| @ -484,6 +487,7 @@ func (sts *stsAPIHandlers) AssumeRoleWithSSO(w http.ResponseWriter, r *http.Requ | |||||||
| 			ParentUser:          cred.ParentUser, | 			ParentUser:          cred.ParentUser, | ||||||
| 			ParentPolicyMapping: policyName, | 			ParentPolicyMapping: policyName, | ||||||
| 		}, | 		}, | ||||||
|  | 		UpdatedAt: updatedAt, | ||||||
| 	}); err != nil { | 	}); err != nil { | ||||||
| 		logger.LogIf(ctx, err) | 		logger.LogIf(ctx, err) | ||||||
| 	} | 	} | ||||||
| @ -639,7 +643,8 @@ func (sts *stsAPIHandlers) AssumeRoleWithLDAPIdentity(w http.ResponseWriter, r * | |||||||
| 	// Set the newly generated credentials, policyName is empty on purpose | 	// Set the newly generated credentials, policyName is empty on purpose | ||||||
| 	// LDAP policies are applied automatically using their ldapUser, ldapGroups | 	// LDAP policies are applied automatically using their ldapUser, ldapGroups | ||||||
| 	// mapping. | 	// mapping. | ||||||
| 	if err = globalIAMSys.SetTempUser(ctx, cred.AccessKey, cred, ""); err != nil { | 	updatedAt, err := globalIAMSys.SetTempUser(ctx, cred.AccessKey, cred, "") | ||||||
|  | 	if err != nil { | ||||||
| 		writeSTSErrorResponse(ctx, w, true, ErrSTSInternalError, err) | 		writeSTSErrorResponse(ctx, w, true, ErrSTSInternalError, err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| @ -653,6 +658,7 @@ func (sts *stsAPIHandlers) AssumeRoleWithLDAPIdentity(w http.ResponseWriter, r * | |||||||
| 			SessionToken: cred.SessionToken, | 			SessionToken: cred.SessionToken, | ||||||
| 			ParentUser:   cred.ParentUser, | 			ParentUser:   cred.ParentUser, | ||||||
| 		}, | 		}, | ||||||
|  | 		UpdatedAt: updatedAt, | ||||||
| 	}); err != nil { | 	}); err != nil { | ||||||
| 		logger.LogIf(ctx, err) | 		logger.LogIf(ctx, err) | ||||||
| 	} | 	} | ||||||
| @ -797,7 +803,7 @@ func (sts *stsAPIHandlers) AssumeRoleWithCertificate(w http.ResponseWriter, r *h | |||||||
| 
 | 
 | ||||||
| 	tmpCredentials.ParentUser = parentUser | 	tmpCredentials.ParentUser = parentUser | ||||||
| 	policyName := certificate.Subject.CommonName | 	policyName := certificate.Subject.CommonName | ||||||
| 	err = globalIAMSys.SetTempUser(ctx, tmpCredentials.AccessKey, tmpCredentials, policyName) | 	updatedAt, err := globalIAMSys.SetTempUser(ctx, tmpCredentials.AccessKey, tmpCredentials, policyName) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		writeSTSErrorResponse(ctx, w, true, ErrSTSInternalError, err) | 		writeSTSErrorResponse(ctx, w, true, ErrSTSInternalError, err) | ||||||
| 		return | 		return | ||||||
| @ -813,6 +819,7 @@ func (sts *stsAPIHandlers) AssumeRoleWithCertificate(w http.ResponseWriter, r *h | |||||||
| 			ParentUser:          tmpCredentials.ParentUser, | 			ParentUser:          tmpCredentials.ParentUser, | ||||||
| 			ParentPolicyMapping: policyName, | 			ParentPolicyMapping: policyName, | ||||||
| 		}, | 		}, | ||||||
|  | 		UpdatedAt: updatedAt, | ||||||
| 	}); err != nil { | 	}); err != nil { | ||||||
| 		logger.LogIf(ctx, err) | 		logger.LogIf(ctx, err) | ||||||
| 	} | 	} | ||||||
| @ -918,7 +925,7 @@ func (sts *stsAPIHandlers) AssumeRoleWithCustomToken(w http.ResponseWriter, r *h | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	tmpCredentials.ParentUser = parentUser | 	tmpCredentials.ParentUser = parentUser | ||||||
| 	err = globalIAMSys.SetTempUser(ctx, tmpCredentials.AccessKey, tmpCredentials, "") | 	updatedAt, err := globalIAMSys.SetTempUser(ctx, tmpCredentials.AccessKey, tmpCredentials, "") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		writeSTSErrorResponse(ctx, w, true, ErrSTSInternalError, err) | 		writeSTSErrorResponse(ctx, w, true, ErrSTSInternalError, err) | ||||||
| 		return | 		return | ||||||
| @ -933,6 +940,7 @@ func (sts *stsAPIHandlers) AssumeRoleWithCustomToken(w http.ResponseWriter, r *h | |||||||
| 			SessionToken: tmpCredentials.SessionToken, | 			SessionToken: tmpCredentials.SessionToken, | ||||||
| 			ParentUser:   tmpCredentials.ParentUser, | 			ParentUser:   tmpCredentials.ParentUser, | ||||||
| 		}, | 		}, | ||||||
|  | 		UpdatedAt: updatedAt, | ||||||
| 	}); err != nil { | 	}); err != nil { | ||||||
| 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | ||||||
| 		return | 		return | ||||||
|  | |||||||
| @ -285,13 +285,43 @@ if [ "${val}" != "val1" ]; then | |||||||
|     echo "expected bucket tag to have replicated, exiting..." |     echo "expected bucket tag to have replicated, exiting..." | ||||||
|     exit_1; |     exit_1; | ||||||
| fi | fi | ||||||
|  | # Create user with policy consoleAdmin on minio1 | ||||||
|  | ./mc admin user add minio1 foobarx foobar123 | ||||||
|  | if [ $? -ne 0 ]; then | ||||||
|  |     echo "adding user failed, exiting.." | ||||||
|  |     exit_1; | ||||||
|  | fi | ||||||
|  | ./mc admin policy set minio1 consoleAdmin user=foobarx | ||||||
|  | if [ $? -ne 0 ]; then | ||||||
|  |     echo "adding policy mapping failed, exiting.." | ||||||
|  |     exit_1; | ||||||
|  | fi | ||||||
|  | sleep 10 | ||||||
|  | 
 | ||||||
|  | # unset policy for foobarx in minio2 | ||||||
|  | ./mc admin policy unset minio2 consoleAdmin user=foobarx | ||||||
|  | if [ $? -ne 0 ]; then | ||||||
|  |     echo "unset policy mapping failed, exiting.." | ||||||
|  |     exit_1; | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | sleep 10 | ||||||
|  | 
 | ||||||
|  | # Test whether policy unset replicated to minio1 | ||||||
|  | policy=$(./mc admin user info minio1 foobarx --json | jq -r .policyName) | ||||||
|  | if [ "${policy}" != "null" ]; then | ||||||
|  |     echo "expected policy unset to have replicated, exiting..." | ||||||
|  |     exit_1; | ||||||
|  | fi | ||||||
| 
 | 
 | ||||||
| kill -9 ${site1_pid} | kill -9 ${site1_pid} | ||||||
| # Update tag on minio2/newbucket when minio1 is down | # Update tag on minio2/newbucket when minio1 is down | ||||||
| ./mc tag set minio2/newbucket "key=val2" | ./mc tag set minio2/newbucket "key=val2" | ||||||
|  | 
 | ||||||
| # Restart minio1 instance | # Restart minio1 instance | ||||||
| minio server --config-dir /tmp/minio-internal --address ":9001" /tmp/minio-internal-idp1/{1...4} >/tmp/minio1_1.log 2>&1 & | minio server --config-dir /tmp/minio-internal --address ":9001" /tmp/minio-internal-idp1/{1...4} >/tmp/minio1_1.log 2>&1 & | ||||||
| sleep 10 | sleep 15 | ||||||
|  | 
 | ||||||
| # Test whether most recent tag update on minio2 is replicated to minio1 | # Test whether most recent tag update on minio2 is replicated to minio1 | ||||||
| val=$(./mc tag list minio1/newbucket --json | jq -r .tagset | jq -r .key ) | val=$(./mc tag list minio1/newbucket --json | jq -r .tagset | jq -r .key ) | ||||||
| if [ "${val}" != "val2" ]; then | if [ "${val}" != "val2" ]; then | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user