mirror of https://github.com/minio/minio.git
loadUser() if not able to load() credential return error (#19931)
This commit is contained in:
parent
62e6dc950d
commit
ba39ed9af7
|
@ -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
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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) {
|
||||
|
|
29
cmd/iam.go
29
cmd/iam.go
|
@ -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.
|
||||
|
|
|
@ -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...)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue