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
|
ErrAdminNoAccessKey
|
||||||
ErrAdminNoSecretKey
|
ErrAdminNoSecretKey
|
||||||
|
|
||||||
|
ErrIAMNotInitialized
|
||||||
|
|
||||||
apiErrCodeEnd // This is used only for the testing code
|
apiErrCodeEnd // This is used only for the testing code
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1305,6 +1307,11 @@ var errorCodes = errorCodeMap{
|
||||||
Description: "Server not initialized yet, please try again.",
|
Description: "Server not initialized yet, please try again.",
|
||||||
HTTPStatusCode: http.StatusServiceUnavailable,
|
HTTPStatusCode: http.StatusServiceUnavailable,
|
||||||
},
|
},
|
||||||
|
ErrIAMNotInitialized: {
|
||||||
|
Code: "XMinioIAMNotInitialized",
|
||||||
|
Description: "IAM sub-system not initialized yet, please try again.",
|
||||||
|
HTTPStatusCode: http.StatusServiceUnavailable,
|
||||||
|
},
|
||||||
ErrBucketMetadataNotInitialized: {
|
ErrBucketMetadataNotInitialized: {
|
||||||
Code: "XMinioBucketMetadataNotInitialized",
|
Code: "XMinioBucketMetadataNotInitialized",
|
||||||
Description: "Bucket metadata not initialized yet, please try again.",
|
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.
|
// for the expiring credentials.
|
||||||
deleteKeyEtcd(ctx, ies.client, getUserIdentityPath(user, userType))
|
deleteKeyEtcd(ctx, ies.client, getUserIdentityPath(user, userType))
|
||||||
deleteKeyEtcd(ctx, ies.client, getMappedPolicyPath(user, userType, false))
|
deleteKeyEtcd(ctx, ies.client, getMappedPolicyPath(user, userType, false))
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
return err
|
return nil
|
||||||
}
|
}
|
||||||
u.Credentials.Claims = jwtClaims.Map()
|
u.Credentials.Claims = jwtClaims.Map()
|
||||||
}
|
}
|
||||||
|
|
|
@ -254,9 +254,8 @@ func (iamOS *IAMObjectStore) loadUser(ctx context.Context, user string, userType
|
||||||
// for the expiring credentials.
|
// for the expiring credentials.
|
||||||
iamOS.deleteIAMConfig(ctx, getUserIdentityPath(user, userType))
|
iamOS.deleteIAMConfig(ctx, getUserIdentityPath(user, userType))
|
||||||
iamOS.deleteIAMConfig(ctx, getMappedPolicyPath(user, userType, false))
|
iamOS.deleteIAMConfig(ctx, getMappedPolicyPath(user, userType, false))
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
return err
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
u.Credentials.Claims = jwtClaims.Map()
|
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.
|
// 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
|
// We use singleflight to de-duplicate requests when server
|
||||||
// is coming up and loading accessKey and its associated assets
|
// 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()
|
cache := store.lock()
|
||||||
defer func() {
|
defer func() {
|
||||||
cache.updatedAt = time.Now()
|
cache.updatedAt = time.Now()
|
||||||
|
@ -2582,27 +2582,29 @@ func (store *IAMStoreSys) LoadUser(ctx context.Context, accessKey string) {
|
||||||
|
|
||||||
// Check for regular user access key
|
// Check for regular user access key
|
||||||
if !found {
|
if !found {
|
||||||
store.loadUser(ctx, accessKey, regUser, cache.iamUsersMap)
|
err = store.loadUser(ctx, accessKey, regUser, cache.iamUsersMap)
|
||||||
if _, found = cache.iamUsersMap[accessKey]; found {
|
if _, found = cache.iamUsersMap[accessKey]; found {
|
||||||
// load mapped policies
|
// 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
|
// Check for service account
|
||||||
if !found {
|
if !found {
|
||||||
store.loadUser(ctx, accessKey, svcUser, cache.iamUsersMap)
|
err = store.loadUser(ctx, accessKey, svcUser, cache.iamUsersMap)
|
||||||
var svc UserIdentity
|
var svc UserIdentity
|
||||||
svc, found = cache.iamUsersMap[accessKey]
|
svc, found = cache.iamUsersMap[accessKey]
|
||||||
if found {
|
if found {
|
||||||
// Load parent user and mapped policies.
|
// Load parent user and mapped policies.
|
||||||
if store.getUsersSysType() == MinIOUsersSysType {
|
if store.getUsersSysType() == MinIOUsersSysType {
|
||||||
store.loadUser(ctx, svc.Credentials.ParentUser, regUser, cache.iamUsersMap)
|
err = store.loadUser(ctx, svc.Credentials.ParentUser, regUser, cache.iamUsersMap)
|
||||||
store.loadMappedPolicyWithRetry(ctx, svc.Credentials.ParentUser, regUser, false, cache.iamUserPolicyMap, 3)
|
if err == nil {
|
||||||
|
err = store.loadMappedPolicyWithRetry(ctx, svc.Credentials.ParentUser, regUser, false, cache.iamUserPolicyMap, 3)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// In case of LDAP the parent user's policy mapping needs to be
|
// In case of LDAP the parent user's policy mapping needs to be
|
||||||
// loaded into sts map
|
// 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
|
stsAccountFound := false
|
||||||
var stsUserCred UserIdentity
|
var stsUserCred UserIdentity
|
||||||
if !found {
|
if !found {
|
||||||
store.loadUser(ctx, accessKey, stsUser, cache.iamSTSAccountsMap)
|
err = store.loadUser(ctx, accessKey, stsUser, cache.iamSTSAccountsMap)
|
||||||
if stsUserCred, found = cache.iamSTSAccountsMap[accessKey]; found {
|
if stsUserCred, found = cache.iamSTSAccountsMap[accessKey]; found {
|
||||||
// Load mapped policy
|
// 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
|
stsAccountFound = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2624,24 +2626,30 @@ func (store *IAMStoreSys) LoadUser(ctx context.Context, accessKey string) {
|
||||||
pols, _ := cache.iamUserPolicyMap.Load(accessKey)
|
pols, _ := cache.iamUserPolicyMap.Load(accessKey)
|
||||||
for _, policy := range pols.toSlice() {
|
for _, policy := range pols.toSlice() {
|
||||||
if _, found = cache.iamPolicyDocsMap[policy]; !found {
|
if _, found = cache.iamPolicyDocsMap[policy]; !found {
|
||||||
store.loadPolicyDocWithRetry(ctx, policy, cache.iamPolicyDocsMap, 3)
|
err = store.loadPolicyDocWithRetry(ctx, policy, cache.iamPolicyDocsMap, 3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pols, _ := cache.iamSTSPolicyMap.Load(stsUserCred.Credentials.AccessKey)
|
pols, _ := cache.iamSTSPolicyMap.Load(stsUserCred.Credentials.AccessKey)
|
||||||
for _, policy := range pols.toSlice() {
|
for _, policy := range pols.toSlice() {
|
||||||
if _, found = cache.iamPolicyDocsMap[policy]; !found {
|
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 {
|
if serverDebugLog {
|
||||||
console.Debugln("loadUser: loading shared", val, err, shared)
|
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) {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUser - get user credentials
|
// CheckKey validates the incoming accessKey
|
||||||
func (sys *IAMSys) GetUser(ctx context.Context, accessKey string) (u UserIdentity, ok bool) {
|
func (sys *IAMSys) CheckKey(ctx context.Context, accessKey string) (u UserIdentity, ok bool, err error) {
|
||||||
if !sys.Initialized() {
|
if !sys.Initialized() {
|
||||||
return u, false
|
return u, false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if accessKey == globalActiveCred.AccessKey {
|
if accessKey == globalActiveCred.AccessKey {
|
||||||
return newUserIdentity(globalActiveCred), true
|
return newUserIdentity(globalActiveCred), true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
loadUserCalled := false
|
loadUserCalled := false
|
||||||
select {
|
select {
|
||||||
case <-sys.configLoaded:
|
case <-sys.configLoaded:
|
||||||
default:
|
default:
|
||||||
sys.store.LoadUser(ctx, accessKey)
|
err = sys.store.LoadUser(ctx, accessKey)
|
||||||
loadUserCalled = true
|
loadUserCalled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
u, ok = sys.store.GetUser(accessKey)
|
u, ok = sys.store.GetUser(accessKey)
|
||||||
if !ok && !loadUserCalled {
|
if !ok && !loadUserCalled {
|
||||||
sys.store.LoadUser(ctx, accessKey)
|
err = sys.store.LoadUser(ctx, accessKey)
|
||||||
|
loadUserCalled = true
|
||||||
|
|
||||||
u, ok = sys.store.GetUser(accessKey)
|
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.
|
// 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...)
|
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{}) {
|
func iamLogIf(ctx context.Context, err error, errKind ...interface{}) {
|
||||||
if !errors.Is(err, grid.ErrDisconnected) {
|
if !errors.Is(err, grid.ErrDisconnected) {
|
||||||
logger.LogIf(ctx, "iam", err, errKind...)
|
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
|
// Check if server has initialized, then only proceed
|
||||||
// to check for IAM users otherwise its okay for clients
|
// to check for IAM users otherwise its okay for clients
|
||||||
// to retry with 503 errors when server is coming up.
|
// 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.
|
// 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 {
|
if !ok {
|
||||||
// Credentials could be valid but disabled - return a different
|
// Credentials could be valid but disabled - return a different
|
||||||
// error in such a scenario.
|
// error in such a scenario.
|
||||||
|
|
|
@ -37,6 +37,12 @@ func niceError(code APIErrorCode) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDoesPolicySignatureMatch(t *testing.T) {
|
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"
|
credentialTemplate := "%s/%s/%s/s3/aws4_request"
|
||||||
now := UTCNow()
|
now := UTCNow()
|
||||||
accessKey := globalActiveCred.AccessKey
|
accessKey := globalActiveCred.AccessKey
|
||||||
|
|
Loading…
Reference in New Issue