loadUser() if not able to load() credential return error (#19931)

This commit is contained in:
Harshavardhana 2024-06-13 15:26:38 -07:00 committed by GitHub
parent 62e6dc950d
commit ba39ed9af7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 71 additions and 29 deletions

View File

@ -445,6 +445,8 @@ const (
ErrAdminNoAccessKey
ErrAdminNoSecretKey
ErrIAMNotInitialized
apiErrCodeEnd // This is used only for the testing code
)
@ -1305,6 +1307,11 @@ var errorCodes = errorCodeMap{
Description: "Server not initialized yet, please try again.",
HTTPStatusCode: http.StatusServiceUnavailable,
},
ErrIAMNotInitialized: {
Code: "XMinioIAMNotInitialized",
Description: "IAM sub-system not initialized yet, please try again.",
HTTPStatusCode: http.StatusServiceUnavailable,
},
ErrBucketMetadataNotInitialized: {
Code: "XMinioBucketMetadataNotInitialized",
Description: "Bucket metadata not initialized yet, please try again.",

File diff suppressed because one or more lines are too long

View File

@ -236,9 +236,8 @@ func (ies *IAMEtcdStore) addUser(ctx context.Context, user string, userType IAMU
// for the expiring credentials.
deleteKeyEtcd(ctx, ies.client, getUserIdentityPath(user, userType))
deleteKeyEtcd(ctx, ies.client, getMappedPolicyPath(user, userType, false))
return nil
}
return err
return nil
}
u.Credentials.Claims = jwtClaims.Map()
}

View File

@ -254,9 +254,8 @@ func (iamOS *IAMObjectStore) loadUser(ctx context.Context, user string, userType
// for the expiring credentials.
iamOS.deleteIAMConfig(ctx, getUserIdentityPath(user, userType))
iamOS.deleteIAMConfig(ctx, getMappedPolicyPath(user, userType, false))
return nil
}
return err
return nil
}
u.Credentials.Claims = jwtClaims.Map()

View File

@ -2568,10 +2568,10 @@ func (store *IAMStoreSys) UpdateUserIdentity(ctx context.Context, cred auth.Cred
}
// LoadUser - attempts to load user info from storage and updates cache.
func (store *IAMStoreSys) LoadUser(ctx context.Context, accessKey string) {
func (store *IAMStoreSys) LoadUser(ctx context.Context, accessKey string) error {
// We use singleflight to de-duplicate requests when server
// is coming up and loading accessKey and its associated assets
val, err, shared := store.group.Do(accessKey, func() (interface{}, error) {
val, err, shared := store.group.Do(accessKey, func() (val interface{}, err error) {
cache := store.lock()
defer func() {
cache.updatedAt = time.Now()
@ -2582,27 +2582,29 @@ func (store *IAMStoreSys) LoadUser(ctx context.Context, accessKey string) {
// Check for regular user access key
if !found {
store.loadUser(ctx, accessKey, regUser, cache.iamUsersMap)
err = store.loadUser(ctx, accessKey, regUser, cache.iamUsersMap)
if _, found = cache.iamUsersMap[accessKey]; found {
// load mapped policies
store.loadMappedPolicyWithRetry(ctx, accessKey, regUser, false, cache.iamUserPolicyMap, 3)
err = store.loadMappedPolicyWithRetry(ctx, accessKey, regUser, false, cache.iamUserPolicyMap, 3)
}
}
// Check for service account
if !found {
store.loadUser(ctx, accessKey, svcUser, cache.iamUsersMap)
err = store.loadUser(ctx, accessKey, svcUser, cache.iamUsersMap)
var svc UserIdentity
svc, found = cache.iamUsersMap[accessKey]
if found {
// Load parent user and mapped policies.
if store.getUsersSysType() == MinIOUsersSysType {
store.loadUser(ctx, svc.Credentials.ParentUser, regUser, cache.iamUsersMap)
store.loadMappedPolicyWithRetry(ctx, svc.Credentials.ParentUser, regUser, false, cache.iamUserPolicyMap, 3)
err = store.loadUser(ctx, svc.Credentials.ParentUser, regUser, cache.iamUsersMap)
if err == nil {
err = store.loadMappedPolicyWithRetry(ctx, svc.Credentials.ParentUser, regUser, false, cache.iamUserPolicyMap, 3)
}
} else {
// In case of LDAP the parent user's policy mapping needs to be
// loaded into sts map
store.loadMappedPolicyWithRetry(ctx, svc.Credentials.ParentUser, stsUser, false, cache.iamSTSPolicyMap, 3)
err = store.loadMappedPolicyWithRetry(ctx, svc.Credentials.ParentUser, stsUser, false, cache.iamSTSPolicyMap, 3)
}
}
}
@ -2611,10 +2613,10 @@ func (store *IAMStoreSys) LoadUser(ctx context.Context, accessKey string) {
stsAccountFound := false
var stsUserCred UserIdentity
if !found {
store.loadUser(ctx, accessKey, stsUser, cache.iamSTSAccountsMap)
err = store.loadUser(ctx, accessKey, stsUser, cache.iamSTSAccountsMap)
if stsUserCred, found = cache.iamSTSAccountsMap[accessKey]; found {
// Load mapped policy
store.loadMappedPolicyWithRetry(ctx, stsUserCred.Credentials.ParentUser, stsUser, false, cache.iamSTSPolicyMap, 3)
err = store.loadMappedPolicyWithRetry(ctx, stsUserCred.Credentials.ParentUser, stsUser, false, cache.iamSTSPolicyMap, 3)
stsAccountFound = true
}
}
@ -2624,24 +2626,30 @@ func (store *IAMStoreSys) LoadUser(ctx context.Context, accessKey string) {
pols, _ := cache.iamUserPolicyMap.Load(accessKey)
for _, policy := range pols.toSlice() {
if _, found = cache.iamPolicyDocsMap[policy]; !found {
store.loadPolicyDocWithRetry(ctx, policy, cache.iamPolicyDocsMap, 3)
err = store.loadPolicyDocWithRetry(ctx, policy, cache.iamPolicyDocsMap, 3)
}
}
} else {
pols, _ := cache.iamSTSPolicyMap.Load(stsUserCred.Credentials.AccessKey)
for _, policy := range pols.toSlice() {
if _, found = cache.iamPolicyDocsMap[policy]; !found {
store.loadPolicyDocWithRetry(ctx, policy, cache.iamPolicyDocsMap, 3)
err = store.loadPolicyDocWithRetry(ctx, policy, cache.iamPolicyDocsMap, 3)
}
}
}
return "done", nil
return "done", err
})
if serverDebugLog {
console.Debugln("loadUser: loading shared", val, err, shared)
}
if IsErr(err, errNoSuchUser, errNoSuchPolicy, errNoSuchGroup) {
return nil
}
return err
}
func extractJWTClaims(u UserIdentity) (*jwt.MapClaims, error) {

View File

@ -1703,31 +1703,46 @@ func (sys *IAMSys) NormalizeLDAPMappingImport(ctx context.Context, isGroup bool,
return nil
}
// GetUser - get user credentials
func (sys *IAMSys) GetUser(ctx context.Context, accessKey string) (u UserIdentity, ok bool) {
// CheckKey validates the incoming accessKey
func (sys *IAMSys) CheckKey(ctx context.Context, accessKey string) (u UserIdentity, ok bool, err error) {
if !sys.Initialized() {
return u, false
return u, false, nil
}
if accessKey == globalActiveCred.AccessKey {
return newUserIdentity(globalActiveCred), true
return newUserIdentity(globalActiveCred), true, nil
}
loadUserCalled := false
select {
case <-sys.configLoaded:
default:
sys.store.LoadUser(ctx, accessKey)
err = sys.store.LoadUser(ctx, accessKey)
loadUserCalled = true
}
u, ok = sys.store.GetUser(accessKey)
if !ok && !loadUserCalled {
sys.store.LoadUser(ctx, accessKey)
err = sys.store.LoadUser(ctx, accessKey)
loadUserCalled = true
u, ok = sys.store.GetUser(accessKey)
}
return u, ok && u.Credentials.IsValid()
if !ok && loadUserCalled && err != nil {
iamLogOnceIf(ctx, err, accessKey)
// return 503 to application
return u, false, errIAMNotInitialized
}
return u, ok && u.Credentials.IsValid(), nil
}
// GetUser - get user credentials
func (sys *IAMSys) GetUser(ctx context.Context, accessKey string) (u UserIdentity, ok bool) {
u, ok, _ = sys.CheckKey(ctx, accessKey)
return u, ok
}
// Notify all other MinIO peers to load group.

View File

@ -20,6 +20,10 @@ func replLogOnceIf(ctx context.Context, err error, id string, errKind ...interfa
logger.LogOnceIf(ctx, "replication", err, id, errKind...)
}
func iamLogOnceIf(ctx context.Context, err error, id string, errKind ...interface{}) {
logger.LogOnceIf(ctx, "iam", err, id, errKind...)
}
func iamLogIf(ctx context.Context, err error, errKind ...interface{}) {
if !errors.Is(err, grid.ErrDisconnected) {
logger.LogIf(ctx, "iam", err, errKind...)

View File

@ -152,11 +152,14 @@ func checkKeyValid(r *http.Request, accessKey string) (auth.Credentials, bool, A
// Check if server has initialized, then only proceed
// to check for IAM users otherwise its okay for clients
// to retry with 503 errors when server is coming up.
return auth.Credentials{}, false, ErrServerNotInitialized
return auth.Credentials{}, false, ErrIAMNotInitialized
}
// Check if the access key is part of users credentials.
u, ok := globalIAMSys.GetUser(r.Context(), accessKey)
u, ok, err := globalIAMSys.CheckKey(r.Context(), accessKey)
if err != nil {
return auth.Credentials{}, false, ErrIAMNotInitialized
}
if !ok {
// Credentials could be valid but disabled - return a different
// error in such a scenario.

View File

@ -37,6 +37,12 @@ func niceError(code APIErrorCode) string {
}
func TestDoesPolicySignatureMatch(t *testing.T) {
_, fsDir, err := prepareFS(context.Background())
if err != nil {
t.Fatal(err)
}
defer removeRoots([]string{fsDir})
credentialTemplate := "%s/%s/%s/s3/aws4_request"
now := UTCNow()
accessKey := globalActiveCred.AccessKey