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:
Poorna 2022-07-01 13:19:13 -07:00 committed by GitHub
parent 63ac260bd5
commit 0ea5c9d8e8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 489 additions and 349 deletions

View file

@ -160,7 +160,7 @@ func (a adminAPIHandlers) SRPeerReplicateIAMItem(w http.ResponseWriter, r *http.
err = errSRInvalidRequest(errInvalidArgument)
case madmin.SRIAMItemPolicy:
if item.Policy == nil {
err = globalSiteReplicationSys.PeerAddPolicyHandler(ctx, item.Name, nil)
err = globalSiteReplicationSys.PeerAddPolicyHandler(ctx, item.Name, nil, item.UpdatedAt)
} else {
policy, perr := iampolicy.ParseConfig(bytes.NewReader(item.Policy))
if perr != nil {
@ -168,21 +168,21 @@ func (a adminAPIHandlers) SRPeerReplicateIAMItem(w http.ResponseWriter, r *http.
return
}
if policy.IsEmpty() {
err = globalSiteReplicationSys.PeerAddPolicyHandler(ctx, item.Name, nil)
err = globalSiteReplicationSys.PeerAddPolicyHandler(ctx, item.Name, nil, item.UpdatedAt)
} else {
err = globalSiteReplicationSys.PeerAddPolicyHandler(ctx, item.Name, policy)
err = globalSiteReplicationSys.PeerAddPolicyHandler(ctx, item.Name, policy, item.UpdatedAt)
}
}
case madmin.SRIAMItemSvcAcc:
err = globalSiteReplicationSys.PeerSvcAccChangeHandler(ctx, item.SvcAccChange)
err = globalSiteReplicationSys.PeerSvcAccChangeHandler(ctx, item.SvcAccChange, item.UpdatedAt)
case madmin.SRIAMItemPolicyMapping:
err = globalSiteReplicationSys.PeerPolicyMappingHandler(ctx, item.PolicyMapping)
err = globalSiteReplicationSys.PeerPolicyMappingHandler(ctx, item.PolicyMapping, item.UpdatedAt)
case madmin.SRIAMItemSTSAcc:
err = globalSiteReplicationSys.PeerSTSAccHandler(ctx, item.STSCredential)
err = globalSiteReplicationSys.PeerSTSAccHandler(ctx, item.STSCredential, item.UpdatedAt)
case madmin.SRIAMItemIAMUser:
err = globalSiteReplicationSys.PeerIAMUserChangeHandler(ctx, item.IAMUser)
err = globalSiteReplicationSys.PeerIAMUserChangeHandler(ctx, item.IAMUser, item.UpdatedAt)
case madmin.SRIAMItemGroupInfo:
err = globalSiteReplicationSys.PeerGroupInfoChangeHandler(ctx, item.GroupInfo)
err = globalSiteReplicationSys.PeerGroupInfoChangeHandler(ctx, item.GroupInfo, item.UpdatedAt)
}
if err != nil {
logger.LogIf(ctx, err)

View file

@ -72,6 +72,7 @@ func (a adminAPIHandlers) RemoveUser(w http.ResponseWriter, r *http.Request) {
AccessKey: accessKey,
IsDeleteReq: true,
},
UpdatedAt: UTCNow(),
}); err != nil {
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
return
@ -240,9 +241,9 @@ func (a adminAPIHandlers) UpdateGroupMembers(w http.ResponseWriter, r *http.Requ
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL)
return
}
var updatedAt time.Time
if updReq.IsRemove {
err = globalIAMSys.RemoveUsersFromGroup(ctx, updReq.Group, updReq.Members)
updatedAt, err = globalIAMSys.RemoveUsersFromGroup(ctx, updReq.Group, updReq.Members)
} else {
// Check if group already exists
if _, gerr := globalIAMSys.GetGroupDescription(updReq.Group); gerr != nil {
@ -253,7 +254,7 @@ func (a adminAPIHandlers) UpdateGroupMembers(w http.ResponseWriter, r *http.Requ
return
}
}
err = globalIAMSys.AddUsersToGroup(ctx, updReq.Group, updReq.Members)
updatedAt, err = globalIAMSys.AddUsersToGroup(ctx, updReq.Group, updReq.Members)
}
if err != nil {
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{
UpdateReq: updReq,
},
UpdatedAt: updatedAt,
}); err != nil {
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
return
@ -341,12 +343,15 @@ func (a adminAPIHandlers) SetGroupStatus(w http.ResponseWriter, r *http.Request)
group := vars["group"]
status := vars["status"]
var err error
var (
err error
updatedAt time.Time
)
switch status {
case statusEnabled:
err = globalIAMSys.SetGroupStatus(ctx, group, true)
updatedAt, err = globalIAMSys.SetGroupStatus(ctx, group, true)
case statusDisabled:
err = globalIAMSys.SetGroupStatus(ctx, group, false)
updatedAt, err = globalIAMSys.SetGroupStatus(ctx, group, false)
default:
err = errInvalidArgument
}
@ -364,6 +369,7 @@ func (a adminAPIHandlers) SetGroupStatus(w http.ResponseWriter, r *http.Request)
IsRemove: false,
},
},
UpdatedAt: updatedAt,
}); err != nil {
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
return
@ -391,7 +397,8 @@ func (a adminAPIHandlers) SetUserStatus(w http.ResponseWriter, r *http.Request)
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)
return
}
@ -405,6 +412,7 @@ func (a adminAPIHandlers) SetUserStatus(w http.ResponseWriter, r *http.Request)
Status: madmin.AccountStatus(status),
},
},
UpdatedAt: updatedAt,
}); err != nil {
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
return
@ -439,8 +447,8 @@ func (a adminAPIHandlers) AddUser(w http.ResponseWriter, r *http.Request) {
return
}
userCred, exists := globalIAMSys.GetUser(ctx, accessKey)
if exists && (userCred.IsTemp() || userCred.IsServiceAccount()) {
user, exists := globalIAMSys.GetUser(ctx, accessKey)
if exists && (user.Credentials.IsTemp() || user.Credentials.IsServiceAccount()) {
// Updating STS credential is not allowed, and this API does not
// support updating service accounts.
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAddUserInvalidArgument), r.URL)
@ -501,7 +509,8 @@ func (a adminAPIHandlers) AddUser(w http.ResponseWriter, r *http.Request) {
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)
return
}
@ -513,6 +522,7 @@ func (a adminAPIHandlers) AddUser(w http.ResponseWriter, r *http.Request) {
IsDeleteReq: false,
UserReq: &ureq,
},
UpdatedAt: updatedAt,
}); err != nil {
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
return
@ -674,7 +684,7 @@ func (a adminAPIHandlers) AddServiceAccount(w http.ResponseWriter, r *http.Reque
}
opts.sessionPolicy = sp
newCred, err := globalIAMSys.NewServiceAccount(ctx, targetUser, targetGroups, opts)
newCred, updatedAt, err := globalIAMSys.NewServiceAccount(ctx, targetUser, targetGroups, opts)
if err != nil {
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
return
@ -717,6 +727,7 @@ func (a adminAPIHandlers) AddServiceAccount(w http.ResponseWriter, r *http.Reque
Status: auth.AccountOn,
},
},
UpdatedAt: updatedAt,
})
if err != nil {
logger.LogIf(ctx, err)
@ -800,7 +811,7 @@ func (a adminAPIHandlers) UpdateServiceAccount(w http.ResponseWriter, r *http.Re
status: updateReq.NewStatus,
sessionPolicy: sp,
}
err = globalIAMSys.UpdateServiceAccount(ctx, accessKey, opts)
updatedAt, err := globalIAMSys.UpdateServiceAccount(ctx, accessKey, opts)
if err != nil {
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
return
@ -818,6 +829,7 @@ func (a adminAPIHandlers) UpdateServiceAccount(w http.ResponseWriter, r *http.Re
SessionPolicy: updateReq.NewPolicy,
},
},
UpdatedAt: updatedAt,
})
if err != nil {
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
@ -1057,6 +1069,7 @@ func (a adminAPIHandlers) DeleteServiceAccount(w http.ResponseWriter, r *http.Re
AccessKey: serviceAccount,
},
},
UpdatedAt: UTCNow(),
}); err != nil {
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
return
@ -1395,8 +1408,9 @@ func (a adminAPIHandlers) RemoveCannedPolicy(w http.ResponseWriter, r *http.Requ
// Call cluster-replication policy creation hook to replicate policy deletion to
// other minio clusters.
if err := globalSiteReplicationSys.IAMChangeHook(ctx, madmin.SRIAMItem{
Type: madmin.SRIAMItemPolicy,
Name: policyName,
Type: madmin.SRIAMItemPolicy,
Name: policyName,
UpdatedAt: UTCNow(),
}); err != nil {
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
return
@ -1453,7 +1467,8 @@ func (a adminAPIHandlers) AddCannedPolicy(w http.ResponseWriter, r *http.Request
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)
return
}
@ -1461,9 +1476,10 @@ func (a adminAPIHandlers) AddCannedPolicy(w http.ResponseWriter, r *http.Request
// Call cluster-replication policy creation hook to replicate policy to
// other minio clusters.
if err := globalSiteReplicationSys.IAMChangeHook(ctx, madmin.SRIAMItem{
Type: madmin.SRIAMItemPolicy,
Name: policyName,
Policy: iamPolicyBytes,
Type: madmin.SRIAMItemPolicy,
Name: policyName,
Policy: iamPolicyBytes,
UpdatedAt: updatedAt,
}); err != nil {
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
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)
return
}
@ -1510,6 +1527,7 @@ func (a adminAPIHandlers) SetPolicyForUserOrGroup(w http.ResponseWriter, r *http
IsGroup: isGroup,
Policy: policyName,
},
UpdatedAt: updatedAt,
}); err != nil {
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
return
@ -1597,22 +1615,22 @@ func (a adminAPIHandlers) ExportIAM(w http.ResponseWriter, r *http.Request) {
return
}
case allUsersFile:
userCreds := make(map[string]auth.Credentials)
userIdentities := make(map[string]UserIdentity)
globalIAMSys.store.rlock()
err := globalIAMSys.store.loadUsers(ctx, regUser, userCreds)
err := globalIAMSys.store.loadUsers(ctx, regUser, userIdentities)
globalIAMSys.store.runlock()
if err != nil {
writeErrorResponse(ctx, w, exportError(ctx, err, iamFile, ""), r.URL)
return
}
userAccounts := make(map[string]madmin.AddOrUpdateUserReq)
for u, cred := range userCreds {
for u, uid := range userIdentities {
status := madmin.AccountDisabled
if cred.IsValid() {
if uid.Credentials.IsValid() {
status = madmin.AccountEnabled
}
userAccounts[u] = madmin.AddOrUpdateUserReq{
SecretKey: cred.SecretKey,
SecretKey: uid.Credentials.SecretKey,
Status: status,
}
}
@ -1646,7 +1664,7 @@ func (a adminAPIHandlers) ExportIAM(w http.ResponseWriter, r *http.Request) {
return
}
case allSvcAcctsFile:
serviceAccounts := make(map[string]auth.Credentials)
serviceAccounts := make(map[string]UserIdentity)
globalIAMSys.store.rlock()
err := globalIAMSys.store.loadUsers(ctx, svcUser, serviceAccounts)
globalIAMSys.store.runlock()
@ -1661,12 +1679,12 @@ func (a adminAPIHandlers) ExportIAM(w http.ResponseWriter, r *http.Request) {
// site replication is enabled.
continue
}
claims, err := globalIAMSys.GetClaimsForSvcAcc(ctx, acc.AccessKey)
claims, err := globalIAMSys.GetClaimsForSvcAcc(ctx, acc.Credentials.AccessKey)
if err != nil {
writeErrorResponse(ctx, w, exportError(ctx, err, iamFile, ""), r.URL)
return
}
_, policy, err := globalIAMSys.GetServiceAccount(ctx, acc.AccessKey)
_, policy, err := globalIAMSys.GetServiceAccount(ctx, acc.Credentials.AccessKey)
if err != nil {
writeErrorResponse(ctx, w, exportError(ctx, err, iamFile, ""), r.URL)
return
@ -1681,13 +1699,13 @@ func (a adminAPIHandlers) ExportIAM(w http.ResponseWriter, r *http.Request) {
}
}
svcAccts[user] = madmin.SRSvcAccCreate{
Parent: acc.ParentUser,
Parent: acc.Credentials.ParentUser,
AccessKey: user,
SecretKey: acc.SecretKey,
Groups: acc.Groups,
SecretKey: acc.Credentials.SecretKey,
Groups: acc.Credentials.Groups,
Claims: claims,
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() {
err = globalIAMSys.DeletePolicy(ctx, policyName, true)
} else {
err = globalIAMSys.SetPolicy(ctx, policyName, policy)
_, err = globalIAMSys.SetPolicy(ctx, policyName, policy)
}
if err != nil {
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
}
userCred, exists := globalIAMSys.GetUser(ctx, accessKey)
if exists && (userCred.IsTemp() || userCred.IsServiceAccount()) {
user, exists := globalIAMSys.GetUser(ctx, accessKey)
if exists && (user.Credentials.IsTemp() || user.Credentials.IsServiceAccount()) {
// Updating STS credential is not allowed, and this API does not
// support updating service accounts.
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)
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)
return
}
@ -1949,7 +1967,7 @@ func (a adminAPIHandlers) ImportIAM(w http.ResponseWriter, r *http.Request) {
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)
return
}
@ -2018,7 +2036,7 @@ func (a adminAPIHandlers) ImportIAM(w http.ResponseWriter, r *http.Request) {
status: svcAcctReq.Status,
sessionPolicy: sp,
}
err = globalIAMSys.UpdateServiceAccount(ctx, svcAcctReq.AccessKey, opts)
_, err = globalIAMSys.UpdateServiceAccount(ctx, svcAcctReq.AccessKey, opts)
if err != nil {
writeErrorResponseJSON(ctx, w, importError(ctx, err, allSvcAcctsFile, user), r.URL)
return
@ -2044,7 +2062,7 @@ func (a adminAPIHandlers) ImportIAM(w http.ResponseWriter, r *http.Request) {
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)
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)
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)
return
}
@ -2113,7 +2131,7 @@ func (a adminAPIHandlers) ImportIAM(w http.ResponseWriter, r *http.Request) {
return
}
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)
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)
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)
return
}
@ -2181,7 +2199,7 @@ func (a adminAPIHandlers) ImportIAM(w http.ResponseWriter, r *http.Request) {
return
}
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)
return
}

View file

@ -20,8 +20,6 @@ package cmd
import (
"context"
"sync"
"github.com/minio/minio/internal/auth"
)
type iamDummyStore struct {
@ -79,7 +77,7 @@ func (ids *iamDummyStore) loadPolicyDocs(ctx context.Context, m map[string]Polic
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]
if !ok {
return errNoSuchUser
@ -88,7 +86,7 @@ func (ids *iamDummyStore) loadUser(ctx context.Context, user string, userType IA
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 {
m[k] = v
}

View file

@ -336,7 +336,7 @@ func (ies *IAMEtcdStore) loadPolicyDocs(ctx context.Context, m map[string]Policy
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
err := getIAMConfig(&u, userkv.Value, string(userkv.Key))
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)
}
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() {
// Delete expired identity.
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 == "" {
u.Credentials.AccessKey = user
}
m[user] = u.Credentials
m[user] = u
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
err := ies.loadIAMConfig(ctx, &u, getUserIdentityPath(user, userType))
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)
}
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
switch userType {
case svcUser:

View file

@ -287,7 +287,7 @@ func (iamOS *IAMObjectStore) loadPolicyDocs(ctx context.Context, m map[string]Po
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
err := iamOS.loadIAMConfig(ctx, &u, getUserIdentityPath(user, userType))
if err != nil {
@ -308,11 +308,11 @@ func (iamOS *IAMObjectStore) loadUser(ctx context.Context, user string, userType
u.Credentials.AccessKey = user
}
m[user] = u.Credentials
m[user] = u
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
switch userType {
case svcUser:

View file

@ -249,7 +249,7 @@ type iamCache struct {
// map of policy names to policy definitions
iamPolicyDocsMap map[string]PolicyDoc
// map of usernames to credentials
iamUsersMap map[string]auth.Credentials
iamUsersMap map[string]UserIdentity
// map of group names to group info
iamGroupsMap map[string]GroupInfo
// map of user names to groups they are a member of
@ -263,7 +263,7 @@ type iamCache struct {
func newIamCache() *iamCache {
return &iamCache{
iamPolicyDocsMap: map[string]PolicyDoc{},
iamUsersMap: map[string]auth.Credentials{},
iamUsersMap: map[string]UserIdentity{},
iamGroupsMap: map[string]GroupInfo{},
iamUserGroupMemberships: map[string]set.StringSet{},
iamUserPolicyMap: map[string]MappedPolicy{},
@ -346,16 +346,16 @@ func (c *iamCache) policyDBGet(mode UsersSysType, name string, isGroup bool) ([]
var parentName string
u, ok := c.iamUsersMap[name]
if ok {
if !u.IsValid() {
if !u.Credentials.IsValid() {
return nil, time.Time{}, nil
}
parentName = u.ParentUser
parentName = u.Credentials.ParentUser
}
mp, ok := c.iamUserPolicyMap[name]
if !ok {
// 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
// accounts apply the final permissions if any.
return []string{"consoleAdmin"}, mp.UpdatedAt, nil
@ -395,8 +395,8 @@ type IAMStorageAPI interface {
getUsersSysType() UsersSysType
loadPolicyDoc(ctx context.Context, policy string, 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
loadUsers(ctx context.Context, 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]UserIdentity) error
loadGroup(ctx context.Context, group string, 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
@ -526,12 +526,12 @@ func (store *IAMStoreSys) HasWatcher() bool {
}
// 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()
defer store.runlock()
c, ok := cache.iamUsersMap[user]
return c, ok
u, ok := cache.iamUsersMap[user]
return u, ok
}
// 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.
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 == "" {
return errInvalidArgument
return updatedAt, errInvalidArgument
}
cache := store.lock()
@ -624,12 +624,13 @@ func (store *IAMStoreSys) AddUsersToGroup(ctx context.Context, group string, mem
// Validate that all members exist.
for _, member := range members {
cr, ok := cache.iamUsersMap[member]
u, ok := cache.iamUsersMap[member]
if !ok {
return errNoSuchUser
return updatedAt, errNoSuchUser
}
cr := u.Credentials
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)
} else {
gi.Members = set.CreateStringSet(append(gi.Members, members...)...).ToSlice()
gi.UpdatedAt = UTCNow()
}
if err := store.saveGroupInfo(ctx, group, gi); err != nil {
return err
return updatedAt, err
}
cache.iamGroupsMap[group] = gi
@ -660,16 +662,15 @@ func (store *IAMStoreSys) AddUsersToGroup(ctx context.Context, group string, mem
}
cache.updatedAt = time.Now()
return nil
return gi.UpdatedAt, nil
}
// helper function - does not take any locks. Updates only cache if
// 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]
if !ok {
return errNoSuchGroup
return updatedAt, errNoSuchGroup
}
s := set.CreateStringSet(gi.Members...)
@ -679,9 +680,10 @@ func removeMembersFromGroup(ctx context.Context, store *IAMStoreSys, cache *iamC
if !updateCacheOnly {
err := store.saveGroupInfo(ctx, group, gi)
if err != nil {
return err
return updatedAt, err
}
}
gi.UpdatedAt = UTCNow()
cache.iamGroupsMap[group] = gi
// update user-group membership map
@ -695,13 +697,13 @@ func removeMembersFromGroup(ctx context.Context, store *IAMStoreSys, cache *iamC
}
cache.updatedAt = time.Now()
return nil
return gi.UpdatedAt, nil
}
// 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 == "" {
return errInvalidArgument
return updatedAt, errInvalidArgument
}
cache := store.lock()
@ -709,23 +711,24 @@ func (store *IAMStoreSys) RemoveUsersFromGroup(ctx context.Context, group string
// Validate that all members exist.
for _, member := range members {
cr, ok := cache.iamUsersMap[member]
u, ok := cache.iamUsersMap[member]
if !ok {
return errNoSuchUser
return updatedAt, errNoSuchUser
}
cr := u.Credentials
if cr.IsTemp() || cr.IsServiceAccount() {
return errIAMActionNotAllowed
return updatedAt, errIAMActionNotAllowed
}
}
gi, ok := cache.iamGroupsMap[group]
if !ok {
return errNoSuchGroup
return updatedAt, errNoSuchGroup
}
// Check if attempting to delete a non-empty group.
if len(members) == 0 && len(gi.Members) != 0 {
return errGroupNotEmpty
return updatedAt, errGroupNotEmpty
}
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
// mapped policy. No-mapped-policy case is ignored.
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 {
return err
return updatedAt, err
}
// Delete from server memory
delete(cache.iamGroupsMap, group)
delete(cache.iamGroupPolicyMap, group)
cache.updatedAt = time.Now()
return nil
return cache.updatedAt, nil
}
return removeMembersFromGroup(ctx, store, cache, group, members, false)
}
// 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 == "" {
return errInvalidArgument
return updatedAt, errInvalidArgument
}
cache := store.lock()
@ -761,7 +764,7 @@ func (store *IAMStoreSys) SetGroupStatus(ctx context.Context, group string, enab
gi, ok := cache.iamGroupsMap[group]
if !ok {
return errNoSuchGroup
return updatedAt, errNoSuchGroup
}
if enabled {
@ -769,15 +772,15 @@ func (store *IAMStoreSys) SetGroupStatus(ctx context.Context, group string, enab
} else {
gi.Status = statusDisabled
}
gi.UpdatedAt = UTCNow()
if err := store.saveGroupInfo(ctx, group, gi); err != nil {
return err
return gi.UpdatedAt, err
}
cache.iamGroupsMap[group] = gi
cache.updatedAt = time.Now()
return nil
return gi.UpdatedAt, nil
}
// 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
// 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 == "" {
return errInvalidArgument
return updatedAt, errInvalidArgument
}
cache := store.lock()
@ -863,11 +866,11 @@ func (store *IAMStoreSys) PolicyDBSet(ctx context.Context, name, policy string,
if store.getUsersSysType() == MinIOUsersSysType {
if !isGroup {
if _, ok := cache.iamUsersMap[name]; !ok {
return errNoSuchUser
return updatedAt, errNoSuchUser
}
} else {
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)
if err != nil && err != errNoSuchPolicy {
return err
return updatedAt, err
}
if !isGroup {
delete(cache.iamUserPolicyMap, name)
@ -890,8 +893,7 @@ func (store *IAMStoreSys) PolicyDBSet(ctx context.Context, name, policy string,
delete(cache.iamGroupPolicyMap, name)
}
cache.updatedAt = time.Now()
return nil
return cache.updatedAt, nil
}
// Handle policy mapping set/update
@ -899,12 +901,12 @@ func (store *IAMStoreSys) PolicyDBSet(ctx context.Context, name, policy string,
for _, p := range mp.toSlice() {
if _, found := cache.iamPolicyDocsMap[p]; !found {
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 {
return err
return updatedAt, err
}
if !isGroup {
cache.iamUserPolicyMap[name] = mp
@ -912,7 +914,7 @@ func (store *IAMStoreSys) PolicyDBSet(ctx context.Context, name, policy string,
cache.iamGroupPolicyMap[name] = mp
}
cache.updatedAt = time.Now()
return nil
return mp.UpdatedAt, nil
}
// 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.
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 == "" {
return errInvalidArgument
return time.Time{}, errInvalidArgument
}
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 {
return err
return d.UpdateDate, err
}
cache.iamPolicyDocsMap[name] = d
cache.updatedAt = time.Now()
return nil
return d.UpdateDate, nil
}
// 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{}
for k, v := range cache.iamUsersMap {
if v.IsTemp() || v.IsServiceAccount() {
c := v.Credentials
if c.IsTemp() || c.IsServiceAccount() {
continue
}
var policies []string
@ -1216,7 +1219,7 @@ func (store *IAMStoreSys) GetBucketUsers(bucket string) (map[string]madmin.UserI
result[k] = madmin.UserInfo{
PolicyName: matchedPolicies,
Status: func() madmin.AccountStatus {
if v.IsValid() {
if c.IsValid() {
return madmin.AccountEnabled
}
return madmin.AccountDisabled
@ -1235,7 +1238,9 @@ func (store *IAMStoreSys) GetUsers() map[string]madmin.UserInfo {
defer store.runlock()
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() {
continue
}
@ -1281,8 +1286,8 @@ func (store *IAMStoreSys) GetUserInfo(name string) (u madmin.UserInfo, err error
// return that info. Otherwise we return error.
var groups []string
for _, v := range cache.iamUsersMap {
if v.ParentUser == name {
groups = v.Groups
if v.Credentials.ParentUser == name {
groups = v.Credentials.Groups
break
}
}
@ -1297,11 +1302,11 @@ func (store *IAMStoreSys) GetUserInfo(name string) (u madmin.UserInfo, err error
}, nil
}
cred, found := cache.iamUsersMap[name]
ui, found := cache.iamUsersMap[name]
if !found {
return u, errNoSuchUser
}
cred := ui.Credentials
if cred.IsTemp() || cred.IsServiceAccount() {
return u, errIAMActionNotAllowed
}
@ -1363,7 +1368,7 @@ func (store *IAMStoreSys) UserNotificationHandler(ctx context.Context, accessKey
if store.getUsersSysType() == MinIOUsersSysType {
memberOf := cache.iamUserGroupMemberships[accessKey].ToSlice()
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 {
removeErr = nil
}
@ -1376,11 +1381,11 @@ func (store *IAMStoreSys) UserNotificationHandler(ctx context.Context, accessKey
// 2. Remove any derived credentials from memory
if userType == regUser {
for _, u := range cache.iamUsersMap {
if u.IsServiceAccount() && u.ParentUser == accessKey {
delete(cache.iamUsersMap, u.AccessKey)
if u.Credentials.IsServiceAccount() && u.Credentials.ParentUser == accessKey {
delete(cache.iamUsersMap, u.Credentials.AccessKey)
}
if u.IsTemp() && u.ParentUser == accessKey {
delete(cache.iamUsersMap, u.AccessKey)
if u.Credentials.IsTemp() && u.Credentials.ParentUser == 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
// have necessary ParentUser present - this is mainly for only
// webIdentity based STS tokens.
cred, ok := cache.iamUsersMap[accessKey]
u, ok := cache.iamUsersMap[accessKey]
if ok {
cred := u.Credentials
if cred.IsTemp() && cred.ParentUser != "" && cred.ParentUser != globalActiveCred.AccessKey {
if _, ok := cache.iamUserPolicyMap[cred.ParentUser]; !ok {
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 {
memberOf := cache.iamUserGroupMemberships[accessKey].ToSlice()
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 {
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
// first.
if userType == regUser {
for _, u := range cache.iamUsersMap {
for _, ui := range cache.iamUsersMap {
u := ui.Credentials
if u.IsServiceAccount() && u.ParentUser == accessKey {
_ = store.deleteUserIdentity(ctx, u.AccessKey, svcUser)
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
// policy name is given, it is associated with the parent user specified in the
// 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 == "" {
return errInvalidArgument
return time.Time{}, errInvalidArgument
}
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, "")
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})
if err != nil {
return err
return time.Time{}, err
}
cache.iamUserPolicyMap[cred.ParentUser] = mp
@ -1512,14 +1519,14 @@ func (store *IAMStoreSys) SetTempUser(ctx context.Context, accessKey string, cre
u := newUserIdentity(cred)
err := store.saveUserIdentity(ctx, accessKey, stsUser, u, options{ttl: ttl})
if err != nil {
return err
return time.Time{}, err
}
cache.iamUsersMap[accessKey] = cred
cache.iamUsersMap[accessKey] = u
cache.updatedAt = time.Now()
return nil
return u.UpdatedAt, nil
}
// 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
usersToDelete := set.CreateStringSet(users...)
for user, cred := range cache.iamUsersMap {
for user, ui := range cache.iamUsersMap {
userType := regUser
cred := ui.Credentials
if cred.IsServiceAccount() {
userType = svcUser
} else if cred.IsTemp() {
@ -1575,7 +1584,8 @@ func (store *IAMStoreSys) GetAllParentUsers() map[string]ParentUserInfo {
defer store.runlock()
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
// non-empty session tokens.
if !(cred.IsServiceAccount() || cred.IsTemp()) ||
@ -1633,21 +1643,22 @@ func (store *IAMStoreSys) GetAllParentUsers() map[string]ParentUserInfo {
}
// 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 {
return errInvalidArgument
return updatedAt, errInvalidArgument
}
cache := store.lock()
defer store.unlock()
cred, ok := cache.iamUsersMap[accessKey]
ui, ok := cache.iamUsersMap[accessKey]
if !ok {
return errNoSuchUser
return updatedAt, errNoSuchUser
}
cred := ui.Credentials
if cred.IsTemp() || cred.IsServiceAccount() {
return errIAMActionNotAllowed
return updatedAt, errIAMActionNotAllowed
}
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 {
return err
return updatedAt, err
}
cache.iamUsersMap[accessKey] = uinfo.Credentials
cache.iamUsersMap[accessKey] = uinfo
cache.updatedAt = time.Now()
return nil
return uinfo.UpdatedAt, nil
}
// 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()
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 -
// reject such operation (updates to the service account are handled in
// a different API).
if scred, found := cache.iamUsersMap[accessKey]; found {
if su, found := cache.iamUsersMap[accessKey]; found {
scred := su.Credentials
if scred.ParentUser != parentUser {
return errIAMServiceAccountUsed
return updatedAt, errIAMServiceAccountUsed
}
return errIAMServiceAccount
return updatedAt, errIAMServiceAccount
}
// Parent user must not be a service account.
if cr, found := cache.iamUsersMap[parentUser]; found && cr.IsServiceAccount() {
return errIAMServiceAccount
if u, found := cache.iamUsersMap[parentUser]; found && u.Credentials.IsServiceAccount() {
return updatedAt, errIAMServiceAccount
}
u := newUserIdentity(cred)
err := store.saveUserIdentity(ctx, u.Credentials.AccessKey, svcUser, u)
err = store.saveUserIdentity(ctx, u.Credentials.AccessKey, svcUser, u)
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()
return nil
return u.UpdatedAt, nil
}
// 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()
defer store.unlock()
cr, ok := cache.iamUsersMap[accessKey]
if !ok || !cr.IsServiceAccount() {
return errNoSuchServiceAccount
ui, ok := cache.iamUsersMap[accessKey]
if !ok || !ui.Credentials.IsServiceAccount() {
return updatedAt, errNoSuchServiceAccount
}
cr := ui.Credentials
currentSecretKey := cr.SecretKey
if opts.secretKey != "" {
if !auth.IsSecretKeyValid(opts.secretKey) {
return auth.ErrInvalidSecretKeyLength
return updatedAt, auth.ErrInvalidSecretKeyLength
}
cr.SecretKey = opts.secretKey
}
@ -1736,12 +1748,12 @@ func (store *IAMStoreSys) UpdateServiceAccount(ctx context.Context, accessKey st
case auth.AccountOn, auth.AccountOff:
cr.Status = opts.status
default:
return errors.New("unknown account status value")
return updatedAt, errors.New("unknown account status value")
}
m, err := getClaimsFromTokenWithSecret(cr.SessionToken, currentSecretKey)
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
@ -1757,16 +1769,16 @@ func (store *IAMStoreSys) UpdateServiceAccount(ctx context.Context, accessKey st
if opts.sessionPolicy != nil {
if err := opts.sessionPolicy.Validate(); err != nil {
return err
return updatedAt, err
}
policyBuf, err := json.Marshal(opts.sessionPolicy)
if err != nil {
return err
return updatedAt, err
}
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.
@ -1776,41 +1788,41 @@ func (store *IAMStoreSys) UpdateServiceAccount(ctx context.Context, accessKey st
cr.SessionToken, err = auth.JWTSignWithAccessKey(accessKey, m, cr.SecretKey)
if err != nil {
return err
return updatedAt, err
}
u := newUserIdentity(cr)
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()
return nil
return u.UpdatedAt, nil
}
// 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()
defer store.runlock()
userExists := false
var tempAccounts []auth.Credentials
var tempAccounts []UserIdentity
for _, v := range cache.iamUsersMap {
isDerived := false
if v.IsServiceAccount() || v.IsTemp() {
if v.Credentials.IsServiceAccount() || v.Credentials.IsTemp() {
isDerived = true
}
if !isDerived && v.AccessKey == accessKey {
if !isDerived && v.Credentials.AccessKey == accessKey {
userExists = true
} else if isDerived && v.ParentUser == accessKey {
} else if isDerived && v.Credentials.ParentUser == accessKey {
userExists = true
if v.IsTemp() {
if v.Credentials.IsTemp() {
// Hide secret key & session key here
v.SecretKey = ""
v.SessionToken = ""
v.Credentials.SecretKey = ""
v.Credentials.SessionToken = ""
tempAccounts = append(tempAccounts, v)
}
}
@ -1830,8 +1842,9 @@ func (store *IAMStoreSys) ListServiceAccounts(ctx context.Context, accessKey str
userExists := false
var serviceAccounts []auth.Credentials
for _, v := range cache.iamUsersMap {
for _, u := range cache.iamUsersMap {
isDerived := false
v := u.Credentials
if v.IsServiceAccount() || v.IsTemp() {
isDerived = true
}
@ -1857,17 +1870,17 @@ func (store *IAMStoreSys) ListServiceAccounts(ctx context.Context, accessKey str
}
// 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()
defer store.unlock()
cache.updatedAt = time.Now()
cr, ok := cache.iamUsersMap[accessKey]
ui, ok := cache.iamUsersMap[accessKey]
// It is not possible to update an STS account.
if ok && cr.IsTemp() {
return errIAMActionNotAllowed
if ok && ui.Credentials.IsTemp() {
return updatedAt, errIAMActionNotAllowed
}
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 {
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.
@ -1898,18 +1911,18 @@ func (store *IAMStoreSys) UpdateUserSecretKey(ctx context.Context, accessKey, se
cache.updatedAt = time.Now()
cred, ok := cache.iamUsersMap[accessKey]
ui, ok := cache.iamUsersMap[accessKey]
if !ok {
return errNoSuchUser
}
cred := ui.Credentials
cred.SecretKey = secretKey
u := newUserIdentity(cred)
if err := store.saveUserIdentity(ctx, accessKey, regUser, u); err != nil {
return err
}
cache.iamUsersMap[accessKey] = cred
cache.iamUsersMap[accessKey] = u
return nil
}
@ -1919,7 +1932,8 @@ func (store *IAMStoreSys) GetSTSAndServiceAccounts() []auth.Credentials {
defer store.runlock()
var res []auth.Credentials
for _, cred := range cache.iamUsersMap {
for _, u := range cache.iamUsersMap {
cred := u.Credentials
if cred.IsTemp() || cred.IsServiceAccount() {
res = append(res, cred)
}
@ -1940,13 +1954,13 @@ func (store *IAMStoreSys) UpdateUserIdentity(ctx context.Context, cred auth.Cred
} else if cred.IsTemp() {
userType = stsUser
}
ui := newUserIdentity(cred)
// Overwrite the user identity here. As store should be
// 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
}
cache.iamUsersMap[cred.AccessKey] = cred
cache.iamUsersMap[cred.AccessKey] = ui
return nil
}
@ -1969,9 +1983,9 @@ func (store *IAMStoreSys) LoadUser(ctx context.Context, accessKey string) {
if svc, found := cache.iamUsersMap[accessKey]; found {
// Load parent user and mapped policies.
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 {
// check for STS account
store.loadUser(ctx, accessKey, stsUser, cache.iamUsersMap)

View file

@ -599,14 +599,14 @@ func (sys *IAMSys) ListPolicyDocs(ctx context.Context, bucketName string) (map[s
}
// 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() {
return errServerNotInitialized
return time.Time{}, errServerNotInitialized
}
err := sys.store.SetPolicy(ctx, policyName, p)
updatedAt, err := sys.store.SetPolicy(ctx, policyName, p)
if err != nil {
return err
return updatedAt, err
}
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).
@ -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
// policy is associated via policy-set API) and the AssumeRoleWithLDAP case
// (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() {
return errServerNotInitialized
return time.Time{}, errServerNotInitialized
}
if newGlobalAuthZPluginFn() != nil {
@ -717,14 +717,14 @@ func (sys *IAMSys) SetTempUser(ctx context.Context, accessKey string, cred auth.
policyName = ""
}
err := sys.store.SetTempUser(ctx, accessKey, cred, policyName)
updatedAt, err := sys.store.SetTempUser(ctx, accessKey, cred, policyName)
if err != nil {
return err
return time.Time{}, err
}
sys.notifyForUser(ctx, cred.AccessKey, true)
return nil
return updatedAt, nil
}
// 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
}
cred, found := sys.store.GetUser(name)
u, found := sys.store.GetUser(name)
if !found {
return false, "", errNoSuchUser
}
cred := u.Credentials
if cred.IsTemp() {
return true, cred.ParentUser, nil
}
@ -800,11 +800,11 @@ func (sys *IAMSys) IsServiceAccount(name string) (bool, string, error) {
return false, "", errServerNotInitialized
}
cred, found := sys.store.GetUser(name)
u, found := sys.store.GetUser(name)
if !found {
return false, "", errNoSuchUser
}
cred := u.Credentials
if cred.IsServiceAccount() {
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.
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() {
return errServerNotInitialized
return updatedAt, errServerNotInitialized
}
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 {
return err
return
}
sys.notifyForUser(ctx, accessKey, false)
return nil
return updatedAt, nil
}
func (sys *IAMSys) notifyForServiceAccount(ctx context.Context, accessKey string) {
@ -867,34 +867,34 @@ type newServiceAccountOpts struct {
}
// 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() {
return auth.Credentials{}, errServerNotInitialized
return auth.Credentials{}, time.Time{}, errServerNotInitialized
}
if parentUser == "" {
return auth.Credentials{}, errInvalidArgument
return auth.Credentials{}, time.Time{}, errInvalidArgument
}
var policyBuf []byte
if opts.sessionPolicy != nil {
err := opts.sessionPolicy.Validate()
if err != nil {
return auth.Credentials{}, err
return auth.Credentials{}, time.Time{}, err
}
policyBuf, err = json.Marshal(opts.sessionPolicy)
if err != nil {
return auth.Credentials{}, err
return auth.Credentials{}, time.Time{}, err
}
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
// parentUser, reject such operations.
if parentUser == opts.accessKey {
return auth.Credentials{}, errIAMActionNotAllowed
return auth.Credentials{}, time.Time{}, errIAMActionNotAllowed
}
m := make(map[string]interface{})
@ -922,24 +922,24 @@ func (sys *IAMSys) NewServiceAccount(ctx context.Context, parentUser string, gro
} else {
accessKey, secretKey, err = auth.GenerateCredentials()
if err != nil {
return auth.Credentials{}, err
return auth.Credentials{}, time.Time{}, err
}
}
cred, err := auth.CreateNewCredentialsWithMetadata(accessKey, secretKey, m, secretKey)
if err != nil {
return auth.Credentials{}, err
return auth.Credentials{}, time.Time{}, err
}
cred.ParentUser = parentUser
cred.Groups = groups
cred.Status = string(auth.AccountOn)
err = sys.store.AddServiceAccount(ctx, cred)
updatedAt, err := sys.store.AddServiceAccount(ctx, cred)
if err != nil {
return auth.Credentials{}, err
return auth.Credentials{}, time.Time{}, err
}
sys.notifyForServiceAccount(ctx, cred.AccessKey)
return cred, nil
return cred, updatedAt, nil
}
type updateServiceAccountOpts struct {
@ -949,18 +949,18 @@ type updateServiceAccountOpts struct {
}
// 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() {
return errServerNotInitialized
return updatedAt, errServerNotInitialized
}
err := sys.store.UpdateServiceAccount(ctx, accessKey, opts)
updatedAt, err = sys.store.UpdateServiceAccount(ctx, accessKey, opts)
if err != nil {
return err
return updatedAt, err
}
sys.notifyForServiceAccount(ctx, accessKey)
return nil
return updatedAt, nil
}
// 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
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() {
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) {
sa, embeddedPolicy, err := sys.getServiceAccount(ctx, accessKey)
if err != nil {
return sa, embeddedPolicy, err
return auth.Credentials{}, embeddedPolicy, err
}
// Hide secret & session keys
sa.SecretKey = ""
sa.SessionToken = ""
return sa, embeddedPolicy, nil
sa.Credentials.SecretKey = ""
sa.Credentials.SessionToken = ""
return sa.Credentials, embeddedPolicy, nil
}
// 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() {
return auth.Credentials{}, nil, errServerNotInitialized
return u, nil, errServerNotInitialized
}
sa, ok := sys.store.GetUser(accessKey)
if !ok || !sa.IsServiceAccount() {
return auth.Credentials{}, nil, errNoSuchServiceAccount
if !ok || !sa.Credentials.IsServiceAccount() {
return u, nil, errNoSuchServiceAccount
}
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 {
jwtClaims, err = auth.ExtractClaims(sa.SessionToken, globalActiveCred.SecretKey)
jwtClaims, err = auth.ExtractClaims(sa.Credentials.SessionToken, globalActiveCred.SecretKey)
if err != nil {
return auth.Credentials{}, nil, err
return u, nil, err
}
}
pt, ptok := jwtClaims.Lookup(iamPolicyClaimNameSA())
@ -1028,11 +1028,11 @@ func (sys *IAMSys) getServiceAccount(ctx context.Context, accessKey string) (aut
if ptok && spok && pt == embeddedPolicyType {
policyBytes, err := base64.StdEncoding.DecodeString(sp)
if err != nil {
return auth.Credentials{}, nil, err
return u, nil, err
}
embeddedPolicy, err = iampolicy.ParseConfig(bytes.NewReader(policyBytes))
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)
if !ok || !sa.IsServiceAccount() {
if !ok || !sa.Credentials.IsServiceAccount() {
return nil, errNoSuchServiceAccount
}
jwtClaims, err := auth.ExtractClaims(sa.SessionToken, sa.SecretKey)
jwtClaims, err := auth.ExtractClaims(sa.Credentials.SessionToken, sa.Credentials.SecretKey)
if err != nil {
jwtClaims, err = auth.ExtractClaims(sa.SessionToken, globalActiveCred.SecretKey)
jwtClaims, err = auth.ExtractClaims(sa.Credentials.SessionToken, globalActiveCred.SecretKey)
if err != nil {
return nil, err
}
@ -1071,7 +1071,7 @@ func (sys *IAMSys) DeleteServiceAccount(ctx context.Context, accessKey string, n
}
sa, ok := sys.store.GetUser(accessKey)
if !ok || !sa.IsServiceAccount() {
if !ok || !sa.Credentials.IsServiceAccount() {
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
// 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() {
return errServerNotInitialized
return updatedAt, errServerNotInitialized
}
if sys.usersSysType != MinIOUsersSysType {
return errIAMActionNotAllowed
return updatedAt, errIAMActionNotAllowed
}
if !auth.IsAccessKeyValid(accessKey) {
return auth.ErrInvalidAccessKeyLength
return updatedAt, auth.ErrInvalidAccessKeyLength
}
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 {
return err
return updatedAt, err
}
sys.notifyForUser(ctx, accessKey, false)
return nil
return updatedAt, nil
}
// SetUserSecretKey - sets user secret key
@ -1291,9 +1291,9 @@ func (sys *IAMSys) updateGroupMembershipsForLDAP(ctx context.Context) {
}
// 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() {
return cred, false
return u, false
}
fallback := false
@ -1304,7 +1304,7 @@ func (sys *IAMSys) GetUser(ctx context.Context, accessKey string) (cred auth.Cre
fallback = true
}
cred, ok = sys.store.GetUser(accessKey)
u, ok = sys.store.GetUser(accessKey)
if !ok && !fallback {
// accessKey not found, also
// 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
// fail.
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.
@ -1333,61 +1333,61 @@ func (sys *IAMSys) notifyForGroup(ctx context.Context, group string) {
// AddUsersToGroup - adds users to a group, creating the group if
// 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() {
return errServerNotInitialized
return updatedAt, errServerNotInitialized
}
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 {
return err
return updatedAt, err
}
sys.notifyForGroup(ctx, group)
return nil
return updatedAt, nil
}
// RemoveUsersFromGroup - remove users from group. If no users are
// 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() {
return errServerNotInitialized
return updatedAt, errServerNotInitialized
}
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 {
return err
return updatedAt, err
}
sys.notifyForGroup(ctx, group)
return nil
return updatedAt, nil
}
// 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() {
return errServerNotInitialized
return updatedAt, errServerNotInitialized
}
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 {
return err
return updatedAt, err
}
sys.notifyForGroup(ctx, group)
return nil
return updatedAt, nil
}
// 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.
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() {
return errServerNotInitialized
return updatedAt, errServerNotInitialized
}
// Determine user-type based on IDP mode.
@ -1425,9 +1425,9 @@ func (sys *IAMSys) PolicyDBSet(ctx context.Context, name, policy string, isGroup
userType = stsUser
}
err := sys.store.PolicyDBSet(ctx, name, policy, userType, isGroup)
updatedAt, err = sys.store.PolicyDBSet(ctx, name, policy, userType, isGroup)
if err != nil {
return err
return
}
// 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

View file

@ -63,11 +63,11 @@ func authenticateJWTUsers(accessKey, secretKey string, expiry time.Duration) (st
func authenticateJWTUsersWithCredentials(credentials auth.Credentials, expiresAt time.Time) (string, error) {
serverCred := globalActiveCred
if serverCred.AccessKey != credentials.AccessKey {
var ok bool
serverCred, ok = globalIAMSys.GetUser(context.TODO(), credentials.AccessKey)
u, ok := globalIAMSys.GetUser(context.TODO(), credentials.AccessKey)
if !ok {
return "", errInvalidAccessKeyID
}
serverCred = u.Credentials
}
if !serverCred.Equal(credentials) {
@ -145,10 +145,11 @@ func metricsRequestAuthenticate(req *http.Request) (*xjwt.MapClaims, []string, b
if claims.AccessKey == globalActiveCred.AccessKey {
return []byte(globalActiveCred.SecretKey), nil
}
cred, ok := globalIAMSys.GetUser(req.Context(), claims.AccessKey)
u, ok := globalIAMSys.GetUser(req.Context(), claims.AccessKey)
if !ok {
return nil, errInvalidAccessKeyID
}
cred := u.Credentials
return []byte(cred.SecretKey), nil
}); err != nil {
return claims, nil, false, errAuthentication
@ -157,11 +158,11 @@ func metricsRequestAuthenticate(req *http.Request) (*xjwt.MapClaims, []string, b
var groups []string
if globalActiveCred.AccessKey != claims.AccessKey {
// 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 {
return nil, nil, false, errInvalidAccessKeyID
}
ucred := u.Credentials
// get embedded claims
eclaims, s3Err := checkClaimsFromToken(req, ucred)
if s3Err != ErrNone {

View file

@ -152,16 +152,16 @@ func checkKeyValid(r *http.Request, accessKey string) (auth.Credentials, bool, A
cred := globalActiveCred
if cred.AccessKey != accessKey {
// 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 {
// Credentials will be invalid but and disabled
// 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, ErrInvalidAccessKeyID
}
cred = ucred
cred = u.Credentials
}
claims, s3Err := checkClaimsFromToken(r, cred)

View file

@ -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.
var secretKey string
svcCred, _, err := globalIAMSys.getServiceAccount(ctx, siteReplicatorSvcAcc)
var svcCred auth.Credentials
sa, _, err := globalIAMSys.getServiceAccount(ctx, siteReplicatorSvcAcc)
switch {
case err == errNoSuchServiceAccount:
_, secretKey, err = auth.GenerateCredentials()
if err != nil {
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,
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))
}
case err == nil:
svcCred = sa.Credentials
secretKey = svcCred.SecretKey
default:
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)
if err == errNoSuchServiceAccount {
_, err = globalIAMSys.NewServiceAccount(ctx, arg.SvcAcctParent, nil, newServiceAccountOpts{
_, _, err = globalIAMSys.NewServiceAccount(ctx, arg.SvcAcctParent, nil, newServiceAccountOpts{
accessKey: arg.SvcAcctAccessKey,
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,
// 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
// 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 {
err = globalIAMSys.DeletePolicy(ctx, policyName, true)
} else {
err = globalIAMSys.SetPolicy(ctx, policyName, *p)
_, err = globalIAMSys.SetPolicy(ctx, policyName, *p)
}
if err != nil {
return wrapSRErr(err)
@ -1036,10 +1044,17 @@ func (c *SiteReplicationSys) PeerAddPolicyHandler(ctx context.Context, policyNam
}
// 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 {
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
if change.IsDeleteReq {
err = globalIAMSys.DeleteUser(ctx, change.AccessKey, true)
@ -1051,9 +1066,9 @@ func (c *SiteReplicationSys) PeerIAMUserChangeHandler(ctx context.Context, chang
if userReq.Status != "" && userReq.SecretKey == "" {
// Status is set without secretKey updates means we are
// only changing the account status.
err = globalIAMSys.SetUserStatus(ctx, change.AccessKey, userReq.Status)
_, err = globalIAMSys.SetUserStatus(ctx, change.AccessKey, userReq.Status)
} else {
err = globalIAMSys.CreateUser(ctx, change.AccessKey, userReq)
_, err = globalIAMSys.CreateUser(ctx, change.AccessKey, userReq)
}
}
if err != nil {
@ -1063,19 +1078,27 @@ func (c *SiteReplicationSys) PeerIAMUserChangeHandler(ctx context.Context, chang
}
// 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 {
return errSRInvalidRequest(errInvalidArgument)
}
updReq := change.UpdateReq
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 {
err = globalIAMSys.RemoveUsersFromGroup(ctx, updReq.Group, updReq.Members)
_, err = globalIAMSys.RemoveUsersFromGroup(ctx, updReq.Group, updReq.Members)
} else {
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 {
err = globalIAMSys.AddUsersToGroup(ctx, updReq.Group, updReq.Members)
_, err = globalIAMSys.AddUsersToGroup(ctx, updReq.Group, updReq.Members)
}
}
if err != nil {
@ -1085,7 +1108,7 @@ func (c *SiteReplicationSys) PeerGroupInfoChangeHandler(ctx context.Context, cha
}
// 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 {
return errSRInvalidRequest(errInvalidArgument)
}
@ -1099,14 +1122,19 @@ func (c *SiteReplicationSys) PeerSvcAccChangeHandler(ctx context.Context, change
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{
accessKey: change.Create.AccessKey,
secretKey: change.Create.SecretKey,
sessionPolicy: sp,
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 {
return wrapSRErr(err)
}
@ -1120,20 +1148,31 @@ func (c *SiteReplicationSys) PeerSvcAccChangeHandler(ctx context.Context, change
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{
secretKey: change.Update.SecretKey,
status: change.Update.Status,
sessionPolicy: sp,
}
err = globalIAMSys.UpdateServiceAccount(ctx, change.Update.AccessKey, opts)
_, err = globalIAMSys.UpdateServiceAccount(ctx, change.Update.AccessKey, opts)
if err != nil {
return wrapSRErr(err)
}
case change.Delete != nil:
err := globalIAMSys.DeleteServiceAccount(ctx, change.Delete.AccessKey, true)
if err != nil {
// skip overwrite of local update if peer sent stale info
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)
}
@ -1143,11 +1182,19 @@ func (c *SiteReplicationSys) PeerSvcAccChangeHandler(ctx context.Context, change
}
// 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 {
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 {
return wrapSRErr(err)
}
@ -1155,10 +1202,19 @@ func (c *SiteReplicationSys) PeerPolicyMappingHandler(ctx context.Context, mappi
}
// 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 {
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
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.
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)
}
@ -1426,11 +1482,11 @@ func (c *SiteReplicationSys) getAdminClientWithEndpoint(ctx context.Context, dep
}
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 {
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
@ -1606,20 +1662,21 @@ func (c *SiteReplicationSys) syncToAllPeers(ctx context.Context) error {
// Policies should be synced first.
{
// Replicate IAM policies on local to all peers.
allPolicies, err := globalIAMSys.ListPolicies(ctx, "")
allPolicyDocs, err := globalIAMSys.ListPolicyDocs(ctx, "")
if err != nil {
return errSRBackendIssue(err)
}
for pname, policy := range allPolicies {
policyJSON, err := json.Marshal(policy)
for pname, pdoc := range allPolicyDocs {
policyJSON, err := json.Marshal(pdoc.Policy)
if err != nil {
return wrapSRErr(err)
}
err = c.IAMChangeHook(ctx, madmin.SRIAMItem{
Type: madmin.SRIAMItemPolicy,
Name: pname,
Policy: policyJSON,
Type: madmin.SRIAMItemPolicy,
Name: pname,
Policy: policyJSON,
UpdatedAt: pdoc.UpdateDate,
})
if err != nil {
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
// may not have any local users.
{
userAccounts := make(map[string]auth.Credentials)
userAccounts := make(map[string]UserIdentity)
globalIAMSys.store.rlock()
err := globalIAMSys.store.loadUsers(ctx, regUser, userAccounts)
globalIAMSys.store.runlock()
@ -1642,13 +1699,14 @@ func (c *SiteReplicationSys) syncToAllPeers(ctx context.Context) error {
if err := c.IAMChangeHook(ctx, madmin.SRIAMItem{
Type: madmin.SRIAMItemIAMUser,
IAMUser: &madmin.SRIAMUser{
AccessKey: acc.AccessKey,
AccessKey: acc.Credentials.AccessKey,
IsDeleteReq: false,
UserReq: &madmin.AddOrUpdateUserReq{
SecretKey: acc.SecretKey,
Status: madmin.AccountStatus(acc.Status),
SecretKey: acc.Credentials.SecretKey,
Status: madmin.AccountStatus(acc.Credentials.Status),
},
},
UpdatedAt: acc.UpdatedAt,
}); err != nil {
return errSRIAMError(err)
}
@ -1678,6 +1736,7 @@ func (c *SiteReplicationSys) syncToAllPeers(ctx context.Context) error {
IsRemove: false,
},
},
UpdatedAt: group.UpdatedAt,
}); err != nil {
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
// valid claims.
{
serviceAccounts := make(map[string]auth.Credentials)
serviceAccounts := make(map[string]UserIdentity)
globalIAMSys.store.rlock()
err := globalIAMSys.store.loadUsers(ctx, svcUser, serviceAccounts)
globalIAMSys.store.runlock()
@ -1702,12 +1761,12 @@ func (c *SiteReplicationSys) syncToAllPeers(ctx context.Context) error {
continue
}
claims, err := globalIAMSys.GetClaimsForSvcAcc(ctx, acc.AccessKey)
claims, err := globalIAMSys.GetClaimsForSvcAcc(ctx, acc.Credentials.AccessKey)
if err != nil {
return errSRBackendIssue(err)
}
_, policy, err := globalIAMSys.GetServiceAccount(ctx, acc.AccessKey)
_, policy, err := globalIAMSys.GetServiceAccount(ctx, acc.Credentials.AccessKey)
if err != nil {
return errSRBackendIssue(err)
}
@ -1724,15 +1783,16 @@ func (c *SiteReplicationSys) syncToAllPeers(ctx context.Context) error {
Type: madmin.SRIAMItemSvcAcc,
SvcAccChange: &madmin.SRSvcAccChange{
Create: &madmin.SRSvcAccCreate{
Parent: acc.ParentUser,
Parent: acc.Credentials.ParentUser,
AccessKey: user,
SecretKey: acc.SecretKey,
Groups: acc.Groups,
SecretKey: acc.Credentials.SecretKey,
Groups: acc.Credentials.Groups,
Claims: claims,
SessionPolicy: json.RawMessage(policyJSON),
Status: acc.Status,
Status: acc.Credentials.Status,
},
},
UpdatedAt: acc.UpdatedAt,
})
if err != nil {
return errSRIAMError(err)
@ -1761,6 +1821,7 @@ func (c *SiteReplicationSys) syncToAllPeers(ctx context.Context) error {
IsGroup: false,
Policy: mp.Policies,
},
UpdatedAt: mp.UpdatedAt,
})
if err != nil {
return errSRIAMError(err)
@ -1779,6 +1840,7 @@ func (c *SiteReplicationSys) syncToAllPeers(ctx context.Context) error {
IsGroup: true,
Policy: mp.Policies,
},
UpdatedAt: mp.UpdatedAt,
})
if err != nil {
return errSRIAMError(err)
@ -1807,6 +1869,7 @@ func (c *SiteReplicationSys) syncToAllPeers(ctx context.Context) error {
IsGroup: false,
Policy: mp.Policies,
},
UpdatedAt: mp.UpdatedAt,
})
if err != nil {
return errSRIAMError(err)
@ -1825,6 +1888,7 @@ func (c *SiteReplicationSys) syncToAllPeers(ctx context.Context) error {
IsGroup: true,
Policy: mp.Policies,
},
UpdatedAt: mp.UpdatedAt,
})
if err != nil {
return errSRIAMError(err)
@ -3244,8 +3308,8 @@ func (c *SiteReplicationSys) SiteReplicationMetaInfo(ctx context.Context, objAPI
return info, errSRBackendIssue(err)
}
for _, tempAcct := range tempAccts {
info.UserInfoMap[tempAcct.AccessKey] = madmin.UserInfo{
Status: madmin.AccountStatus(tempAcct.Status),
info.UserInfoMap[tempAcct.Credentials.AccessKey] = madmin.UserInfo{
Status: madmin.AccountStatus(tempAcct.Credentials.Status),
}
}
}
@ -4163,9 +4227,10 @@ func (c *SiteReplicationSys) healPolicies(ctx context.Context, objAPI ObjectLaye
}
peerName := info.Sites[dID].Name
err := c.IAMChangeHook(ctx, madmin.SRIAMItem{
Type: madmin.SRIAMItemPolicy,
Name: policy,
Policy: latestPolicyStat.policy.Policy,
Type: madmin.SRIAMItemPolicy,
Name: policy,
Policy: latestPolicyStat.policy.Policy,
UpdatedAt: lastUpdate,
})
if err != nil {
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,
Policy: latestUserStat.userPolicy.Policy,
},
UpdatedAt: lastUpdate,
})
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))
@ -4286,6 +4352,7 @@ func (c *SiteReplicationSys) healGroupPolicies(ctx context.Context, objAPI Objec
IsGroup: true,
Policy: latestGroupStat.groupPolicy.Policy,
},
UpdatedAt: lastUpdate,
})
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))
@ -4340,11 +4407,11 @@ func (c *SiteReplicationSys) healUsers(ctx context.Context, objAPI ObjectLayer,
peerName := info.Sites[dID].Name
creds, ok := globalIAMSys.GetUser(ctx, user)
u, ok := globalIAMSys.GetUser(ctx, user)
if !ok {
continue
}
creds := u.Credentials
// heal only the user accounts that are local users
if creds.IsServiceAccount() {
claims, err := globalIAMSys.GetClaimsForSvcAcc(ctx, creds.AccessKey)
@ -4381,6 +4448,7 @@ func (c *SiteReplicationSys) healUsers(ctx context.Context, objAPI ObjectLayer,
Status: creds.Status,
},
},
UpdatedAt: lastUpdate,
}); err != nil {
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,
ParentPolicyMapping: u.PolicyName,
},
UpdatedAt: lastUpdate,
}); err != nil {
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,
},
},
UpdatedAt: lastUpdate,
}); err != nil {
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,
},
},
UpdatedAt: lastUpdate,
}); err != nil {
logger.LogIf(ctx, fmt.Errorf("Error healing group %s from peer site %s -> site %s : %w", group, latestPeerName, peerName, err))
}

View file

@ -275,7 +275,8 @@ func (sts *stsAPIHandlers) AssumeRole(w http.ResponseWriter, r *http.Request) {
cred.ParentUser = user.AccessKey
// 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)
return
}
@ -290,6 +291,7 @@ func (sts *stsAPIHandlers) AssumeRole(w http.ResponseWriter, r *http.Request) {
SessionToken: cred.SessionToken,
ParentUser: cred.ParentUser,
},
UpdatedAt: updatedAt,
}); err != nil {
logger.LogIf(ctx, err)
}
@ -469,7 +471,8 @@ func (sts *stsAPIHandlers) AssumeRoleWithSSO(w http.ResponseWriter, r *http.Requ
}
// 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)
return
}
@ -484,6 +487,7 @@ func (sts *stsAPIHandlers) AssumeRoleWithSSO(w http.ResponseWriter, r *http.Requ
ParentUser: cred.ParentUser,
ParentPolicyMapping: policyName,
},
UpdatedAt: updatedAt,
}); err != nil {
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
// LDAP policies are applied automatically using their ldapUser, ldapGroups
// 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)
return
}
@ -653,6 +658,7 @@ func (sts *stsAPIHandlers) AssumeRoleWithLDAPIdentity(w http.ResponseWriter, r *
SessionToken: cred.SessionToken,
ParentUser: cred.ParentUser,
},
UpdatedAt: updatedAt,
}); err != nil {
logger.LogIf(ctx, err)
}
@ -797,7 +803,7 @@ func (sts *stsAPIHandlers) AssumeRoleWithCertificate(w http.ResponseWriter, r *h
tmpCredentials.ParentUser = parentUser
policyName := certificate.Subject.CommonName
err = globalIAMSys.SetTempUser(ctx, tmpCredentials.AccessKey, tmpCredentials, policyName)
updatedAt, err := globalIAMSys.SetTempUser(ctx, tmpCredentials.AccessKey, tmpCredentials, policyName)
if err != nil {
writeSTSErrorResponse(ctx, w, true, ErrSTSInternalError, err)
return
@ -813,6 +819,7 @@ func (sts *stsAPIHandlers) AssumeRoleWithCertificate(w http.ResponseWriter, r *h
ParentUser: tmpCredentials.ParentUser,
ParentPolicyMapping: policyName,
},
UpdatedAt: updatedAt,
}); err != nil {
logger.LogIf(ctx, err)
}
@ -918,7 +925,7 @@ func (sts *stsAPIHandlers) AssumeRoleWithCustomToken(w http.ResponseWriter, r *h
}
tmpCredentials.ParentUser = parentUser
err = globalIAMSys.SetTempUser(ctx, tmpCredentials.AccessKey, tmpCredentials, "")
updatedAt, err := globalIAMSys.SetTempUser(ctx, tmpCredentials.AccessKey, tmpCredentials, "")
if err != nil {
writeSTSErrorResponse(ctx, w, true, ErrSTSInternalError, err)
return
@ -933,6 +940,7 @@ func (sts *stsAPIHandlers) AssumeRoleWithCustomToken(w http.ResponseWriter, r *h
SessionToken: tmpCredentials.SessionToken,
ParentUser: tmpCredentials.ParentUser,
},
UpdatedAt: updatedAt,
}); err != nil {
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
return

View file

@ -285,13 +285,43 @@ if [ "${val}" != "val1" ]; then
echo "expected bucket tag to have replicated, exiting..."
exit_1;
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}
# Update tag on minio2/newbucket when minio1 is down
./mc tag set minio2/newbucket "key=val2"
# Restart minio1 instance
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
val=$(./mc tag list minio1/newbucket --json | jq -r .tagset | jq -r .key )
if [ "${val}" != "val2" ]; then