fix: load credentials from etcd directly when possible (#11339)

under large deployments loading credentials might be
time consuming, while this is okay and we will not
respond quickly for `mc admin user list` like queries
but it is possible to support `mc admin user info`

just like how we handle authentication by fetching
the user directly from persistent store.

additionally support service accounts properly,
reloaded from etcd during watch() - this was missing

This PR is also half way remedy for #11305
master
Harshavardhana 4 years ago committed by GitHub
parent 5f51ef0b40
commit 5c6bfae4c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      cmd/admin-handlers-users.go
  2. 8
      cmd/config/etcd/etcd.go
  3. 129
      cmd/iam-etcd-store.go
  4. 105
      cmd/iam-object-store.go
  5. 263
      cmd/iam.go
  6. 1
      go.mod

@ -426,7 +426,7 @@ func (a adminAPIHandlers) AddUser(w http.ResponseWriter, r *http.Request) {
return return
} }
if err = globalIAMSys.SetUser(accessKey, uinfo); err != nil { if err = globalIAMSys.CreateUser(accessKey, uinfo); err != nil {
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
return return
} }

@ -27,6 +27,7 @@ import (
xnet "github.com/minio/minio/pkg/net" xnet "github.com/minio/minio/pkg/net"
"go.etcd.io/etcd/clientv3" "go.etcd.io/etcd/clientv3"
"go.etcd.io/etcd/clientv3/namespace" "go.etcd.io/etcd/clientv3/namespace"
"go.uber.org/zap"
) )
const ( const (
@ -144,6 +145,13 @@ func LookupConfig(kvs config.KVS, rootCAs *x509.CertPool) (Config, error) {
cfg.Enabled = true cfg.Enabled = true
cfg.DialTimeout = defaultDialTimeout cfg.DialTimeout = defaultDialTimeout
cfg.DialKeepAliveTime = defaultDialKeepAlive cfg.DialKeepAliveTime = defaultDialKeepAlive
// Disable etcd client SDK logging, etcd client
// incorrectly starts logging in unexpected data
// format.
cfg.LogConfig = &zap.Config{
Level: zap.NewAtomicLevelAt(zap.FatalLevel),
Encoding: "console",
}
cfg.Endpoints = etcdEndpoints cfg.Endpoints = etcdEndpoints
cfg.CoreDNSPath = env.Get(EnvEtcdCoreDNSPath, kvs.Get(CoreDNSPath)) cfg.CoreDNSPath = env.Get(EnvEtcdCoreDNSPath, kvs.Get(CoreDNSPath))
// Default path prefix for all keys on etcd, other than CoreDNSPath. // Default path prefix for all keys on etcd, other than CoreDNSPath.

@ -264,6 +264,9 @@ func (ies *IAMEtcdStore) loadPolicyDoc(ctx context.Context, policy string, m map
var p iampolicy.Policy var p iampolicy.Policy
err := ies.loadIAMConfig(ctx, &p, getPolicyDocPath(policy)) err := ies.loadIAMConfig(ctx, &p, getPolicyDocPath(policy))
if err != nil { if err != nil {
if err == errConfigNotFound {
return errNoSuchPolicy
}
return err return err
} }
m[policy] = p m[policy] = p
@ -282,8 +285,7 @@ func (ies *IAMEtcdStore) loadPolicyDocs(ctx context.Context, m map[string]iampol
// Reload config and policies for all policys. // Reload config and policies for all policys.
for _, policyName := range policies.ToSlice() { for _, policyName := range policies.ToSlice() {
err = ies.loadPolicyDoc(ctx, policyName, m) if err = ies.loadPolicyDoc(ctx, policyName, m); err != nil && err != errNoSuchPolicy {
if err != nil {
return err return err
} }
} }
@ -358,7 +360,7 @@ func (ies *IAMEtcdStore) loadUsers(ctx context.Context, userType IAMUserType, m
// Reload config for all users. // Reload config for all users.
for _, user := range users.ToSlice() { for _, user := range users.ToSlice() {
if err = ies.loadUser(ctx, user, userType, m); err != nil { if err = ies.loadUser(ctx, user, userType, m); err != nil && err != errNoSuchUser {
return err return err
} }
} }
@ -392,7 +394,7 @@ func (ies *IAMEtcdStore) loadGroups(ctx context.Context, m map[string]GroupInfo)
// Reload config for all groups. // Reload config for all groups.
for _, group := range groups.ToSlice() { for _, group := range groups.ToSlice() {
if err = ies.loadGroup(ctx, group, m); err != nil { if err = ies.loadGroup(ctx, group, m); err != nil && err != errNoSuchGroup {
return err return err
} }
} }
@ -440,7 +442,7 @@ func (ies *IAMEtcdStore) loadMappedPolicies(ctx context.Context, userType IAMUse
// Reload config and policies for all users. // Reload config and policies for all users.
for _, user := range users.ToSlice() { for _, user := range users.ToSlice() {
if err = ies.loadMappedPolicy(ctx, user, userType, isGroup, m); err != nil { if err = ies.loadMappedPolicy(ctx, user, userType, isGroup, m); err != nil && err != errNoSuchPolicy {
return err return err
} }
} }
@ -449,117 +451,7 @@ func (ies *IAMEtcdStore) loadMappedPolicies(ctx context.Context, userType IAMUse
} }
func (ies *IAMEtcdStore) loadAll(ctx context.Context, sys *IAMSys) error { func (ies *IAMEtcdStore) loadAll(ctx context.Context, sys *IAMSys) error {
iamUsersMap := make(map[string]auth.Credentials) return sys.Load(ctx, ies)
iamGroupsMap := make(map[string]GroupInfo)
iamUserPolicyMap := make(map[string]MappedPolicy)
iamGroupPolicyMap := make(map[string]MappedPolicy)
ies.rlock()
isMinIOUsersSys := sys.usersSysType == MinIOUsersSysType
ies.runlock()
ies.lock()
if err := ies.loadPolicyDocs(ctx, sys.iamPolicyDocsMap); err != nil {
ies.unlock()
return err
}
// Sets default canned policies, if none are set.
setDefaultCannedPolicies(sys.iamPolicyDocsMap)
ies.unlock()
if isMinIOUsersSys {
if err := ies.loadUsers(ctx, regularUser, iamUsersMap); err != nil {
return err
}
if err := ies.loadGroups(ctx, iamGroupsMap); err != nil {
return err
}
}
// load polices mapped to users
if err := ies.loadMappedPolicies(ctx, regularUser, false, iamUserPolicyMap); err != nil {
return err
}
// load policies mapped to groups
if err := ies.loadMappedPolicies(ctx, regularUser, true, iamGroupPolicyMap); err != nil {
return err
}
if err := ies.loadUsers(ctx, srvAccUser, iamUsersMap); err != nil {
return err
}
// load STS temp users
if err := ies.loadUsers(ctx, stsUser, iamUsersMap); err != nil {
return err
}
// load STS policy mappings
if err := ies.loadMappedPolicies(ctx, stsUser, false, iamUserPolicyMap); err != nil {
return err
}
ies.lock()
defer ies.Unlock()
// Merge the new reloaded entries into global map.
// See issue https://github.com/minio/minio/issues/9651
// where the present list of entries on disk are not yet
// latest, there is a small window where this can make
// valid users invalid.
for k, v := range iamUsersMap {
sys.iamUsersMap[k] = v
}
for k, v := range iamUserPolicyMap {
sys.iamUserPolicyMap[k] = v
}
// purge any expired entries which became expired now.
var expiredEntries []string
for k, v := range sys.iamUsersMap {
if v.IsExpired() {
delete(sys.iamUsersMap, k)
delete(sys.iamUserPolicyMap, k)
expiredEntries = append(expiredEntries, k)
// Deleting on the disk is taken care of in the next cycle
}
}
for _, v := range sys.iamUsersMap {
if v.IsServiceAccount() {
for _, accessKey := range expiredEntries {
if v.ParentUser == accessKey {
_ = ies.deleteUserIdentity(ctx, v.AccessKey, srvAccUser)
delete(sys.iamUsersMap, v.AccessKey)
}
}
}
}
// purge any expired entries which became expired now.
for k, v := range sys.iamUsersMap {
if v.IsExpired() {
delete(sys.iamUsersMap, k)
delete(sys.iamUserPolicyMap, k)
// Deleting on the etcd is taken care of in the next cycle
}
}
for k, v := range iamGroupPolicyMap {
sys.iamGroupPolicyMap[k] = v
}
for k, v := range iamGroupsMap {
sys.iamGroupsMap[k] = v
}
sys.buildUserGroupMemberships()
sys.storeFallback = false
return nil
} }
func (ies *IAMEtcdStore) savePolicyDoc(ctx context.Context, policyName string, p iampolicy.Policy) error { func (ies *IAMEtcdStore) savePolicyDoc(ctx context.Context, policyName string, p iampolicy.Policy) error {
@ -653,6 +545,7 @@ func (ies *IAMEtcdStore) reloadFromEvent(sys *IAMSys, event *etcd.Event) {
usersPrefix := strings.HasPrefix(string(event.Kv.Key), iamConfigUsersPrefix) usersPrefix := strings.HasPrefix(string(event.Kv.Key), iamConfigUsersPrefix)
groupsPrefix := strings.HasPrefix(string(event.Kv.Key), iamConfigGroupsPrefix) groupsPrefix := strings.HasPrefix(string(event.Kv.Key), iamConfigGroupsPrefix)
stsPrefix := strings.HasPrefix(string(event.Kv.Key), iamConfigSTSPrefix) stsPrefix := strings.HasPrefix(string(event.Kv.Key), iamConfigSTSPrefix)
svcPrefix := strings.HasPrefix(string(event.Kv.Key), iamConfigServiceAccountsPrefix)
policyPrefix := strings.HasPrefix(string(event.Kv.Key), iamConfigPoliciesPrefix) policyPrefix := strings.HasPrefix(string(event.Kv.Key), iamConfigPoliciesPrefix)
policyDBUsersPrefix := strings.HasPrefix(string(event.Kv.Key), iamConfigPolicyDBUsersPrefix) policyDBUsersPrefix := strings.HasPrefix(string(event.Kv.Key), iamConfigPolicyDBUsersPrefix)
policyDBSTSUsersPrefix := strings.HasPrefix(string(event.Kv.Key), iamConfigPolicyDBSTSUsersPrefix) policyDBSTSUsersPrefix := strings.HasPrefix(string(event.Kv.Key), iamConfigPolicyDBSTSUsersPrefix)
@ -672,6 +565,10 @@ func (ies *IAMEtcdStore) reloadFromEvent(sys *IAMSys, event *etcd.Event) {
accessKey := path.Dir(strings.TrimPrefix(string(event.Kv.Key), accessKey := path.Dir(strings.TrimPrefix(string(event.Kv.Key),
iamConfigSTSPrefix)) iamConfigSTSPrefix))
ies.loadUser(ctx, accessKey, stsUser, sys.iamUsersMap) ies.loadUser(ctx, accessKey, stsUser, sys.iamUsersMap)
case svcPrefix:
accessKey := path.Dir(strings.TrimPrefix(string(event.Kv.Key),
iamConfigServiceAccountsPrefix))
ies.loadUser(ctx, accessKey, srvAccUser, sys.iamUsersMap)
case groupsPrefix: case groupsPrefix:
group := path.Dir(strings.TrimPrefix(string(event.Kv.Key), group := path.Dir(strings.TrimPrefix(string(event.Kv.Key),
iamConfigGroupsPrefix)) iamConfigGroupsPrefix))

@ -402,110 +402,9 @@ func (iamOS *IAMObjectStore) loadMappedPolicies(ctx context.Context, userType IA
return nil return nil
} }
// Refresh IAMSys. If an object layer is passed in use that, otherwise // Refresh IAMSys. If an object layer is passed in use that, otherwise load from global.
// load from global.
func (iamOS *IAMObjectStore) loadAll(ctx context.Context, sys *IAMSys) error { func (iamOS *IAMObjectStore) loadAll(ctx context.Context, sys *IAMSys) error {
iamUsersMap := make(map[string]auth.Credentials) return sys.Load(ctx, iamOS)
iamGroupsMap := make(map[string]GroupInfo)
iamUserPolicyMap := make(map[string]MappedPolicy)
iamGroupPolicyMap := make(map[string]MappedPolicy)
iamOS.rlock()
isMinIOUsersSys := sys.usersSysType == MinIOUsersSysType
iamOS.runlock()
iamOS.lock()
if err := iamOS.loadPolicyDocs(ctx, sys.iamPolicyDocsMap); err != nil {
iamOS.unlock()
return err
}
// Sets default canned policies, if none are set.
setDefaultCannedPolicies(sys.iamPolicyDocsMap)
iamOS.unlock()
if isMinIOUsersSys {
if err := iamOS.loadUsers(ctx, regularUser, iamUsersMap); err != nil {
return err
}
if err := iamOS.loadGroups(ctx, iamGroupsMap); err != nil {
return err
}
}
// load polices mapped to users
if err := iamOS.loadMappedPolicies(ctx, regularUser, false, iamUserPolicyMap); err != nil {
return err
}
// load policies mapped to groups
if err := iamOS.loadMappedPolicies(ctx, regularUser, true, iamGroupPolicyMap); err != nil {
return err
}
if err := iamOS.loadUsers(ctx, srvAccUser, iamUsersMap); err != nil {
return err
}
// load STS temp users
if err := iamOS.loadUsers(ctx, stsUser, iamUsersMap); err != nil {
return err
}
// load STS policy mappings
if err := iamOS.loadMappedPolicies(ctx, stsUser, false, iamUserPolicyMap); err != nil {
return err
}
iamOS.lock()
defer iamOS.unlock()
// Merge the new reloaded entries into global map.
// See issue https://github.com/minio/minio/issues/9651
// where the present list of entries on disk are not yet
// latest, there is a small window where this can make
// valid users invalid.
for k, v := range iamUsersMap {
sys.iamUsersMap[k] = v
}
for k, v := range iamUserPolicyMap {
sys.iamUserPolicyMap[k] = v
}
// purge any expired entries which became expired now.
var expiredEntries []string
for k, v := range sys.iamUsersMap {
if v.IsExpired() {
delete(sys.iamUsersMap, k)
delete(sys.iamUserPolicyMap, k)
expiredEntries = append(expiredEntries, k)
// Deleting on the disk is taken care of in the next cycle
}
}
for _, v := range sys.iamUsersMap {
if v.IsServiceAccount() {
for _, accessKey := range expiredEntries {
if v.ParentUser == accessKey {
_ = iamOS.deleteUserIdentity(ctx, v.AccessKey, srvAccUser)
delete(sys.iamUsersMap, v.AccessKey)
}
}
}
}
for k, v := range iamGroupPolicyMap {
sys.iamGroupPolicyMap[k] = v
}
for k, v := range iamGroupsMap {
sys.iamGroupsMap[k] = v
}
sys.buildUserGroupMemberships()
sys.storeFallback = false
return nil
} }
func (iamOS *IAMObjectStore) savePolicyDoc(ctx context.Context, policyName string, p iampolicy.Policy) error { func (iamOS *IAMObjectStore) savePolicyDoc(ctx context.Context, policyName string, p iampolicy.Policy) error {

@ -442,6 +442,122 @@ func (sys *IAMSys) Initialized() bool {
return sys.store != nil return sys.store != nil
} }
// Load - loads all credentials
func (sys *IAMSys) Load(ctx context.Context, store IAMStorageAPI) error {
iamUsersMap := make(map[string]auth.Credentials)
iamGroupsMap := make(map[string]GroupInfo)
iamUserPolicyMap := make(map[string]MappedPolicy)
iamGroupPolicyMap := make(map[string]MappedPolicy)
iamPolicyDocsMap := make(map[string]iampolicy.Policy)
store.rlock()
isMinIOUsersSys := sys.usersSysType == MinIOUsersSysType
store.runlock()
if err := store.loadPolicyDocs(ctx, iamPolicyDocsMap); err != nil {
return err
}
// Sets default canned policies, if none are set.
setDefaultCannedPolicies(iamPolicyDocsMap)
if isMinIOUsersSys {
if err := store.loadUsers(ctx, regularUser, iamUsersMap); err != nil {
return err
}
if err := store.loadGroups(ctx, iamGroupsMap); err != nil {
return err
}
}
// load polices mapped to users
if err := store.loadMappedPolicies(ctx, regularUser, false, iamUserPolicyMap); err != nil {
return err
}
// load policies mapped to groups
if err := store.loadMappedPolicies(ctx, regularUser, true, iamGroupPolicyMap); err != nil {
return err
}
if err := store.loadUsers(ctx, srvAccUser, iamUsersMap); err != nil {
return err
}
// load STS temp users
if err := store.loadUsers(ctx, stsUser, iamUsersMap); err != nil {
return err
}
// load STS policy mappings
if err := store.loadMappedPolicies(ctx, stsUser, false, iamUserPolicyMap); err != nil {
return err
}
store.lock()
defer store.unlock()
for k, v := range iamPolicyDocsMap {
sys.iamPolicyDocsMap[k] = v
}
// Merge the new reloaded entries into global map.
// See issue https://github.com/minio/minio/issues/9651
// where the present list of entries on disk are not yet
// latest, there is a small window where this can make
// valid users invalid.
for k, v := range iamUsersMap {
sys.iamUsersMap[k] = v
}
for k, v := range iamUserPolicyMap {
sys.iamUserPolicyMap[k] = v
}
// purge any expired entries which became expired now.
var expiredEntries []string
for k, v := range sys.iamUsersMap {
if v.IsExpired() {
delete(sys.iamUsersMap, k)
delete(sys.iamUserPolicyMap, k)
expiredEntries = append(expiredEntries, k)
// Deleting on the disk is taken care of in the next cycle
}
}
for _, v := range sys.iamUsersMap {
if v.IsServiceAccount() {
for _, accessKey := range expiredEntries {
if v.ParentUser == accessKey {
_ = store.deleteUserIdentity(ctx, v.AccessKey, srvAccUser)
delete(sys.iamUsersMap, v.AccessKey)
}
}
}
}
// purge any expired entries which became expired now.
for k, v := range sys.iamUsersMap {
if v.IsExpired() {
delete(sys.iamUsersMap, k)
delete(sys.iamUserPolicyMap, k)
// Deleting on the etcd is taken care of in the next cycle
}
}
for k, v := range iamGroupPolicyMap {
sys.iamGroupPolicyMap[k] = v
}
for k, v := range iamGroupsMap {
sys.iamGroupsMap[k] = v
}
sys.buildUserGroupMemberships()
sys.storeFallback = false
return nil
}
// Init - initializes config system by reading entries from config/iam // Init - initializes config system by reading entries from config/iam
func (sys *IAMSys) Init(ctx context.Context, objAPI ObjectLayer) { func (sys *IAMSys) Init(ctx context.Context, objAPI ObjectLayer) {
// Initialize IAM store // Initialize IAM store
@ -664,6 +780,10 @@ func (sys *IAMSys) DeleteUser(accessKey string) error {
return errServerNotInitialized return errServerNotInitialized
} }
if sys.usersSysType != MinIOUsersSysType {
return errIAMActionNotAllowed
}
// First we remove the user from their groups. // First we remove the user from their groups.
userInfo, getErr := sys.GetUserInfo(accessKey) userInfo, getErr := sys.GetUserInfo(accessKey)
if getErr != nil { if getErr != nil {
@ -681,18 +801,21 @@ func (sys *IAMSys) DeleteUser(accessKey string) error {
sys.store.lock() sys.store.lock()
defer sys.store.unlock() defer sys.store.unlock()
if sys.usersSysType != MinIOUsersSysType {
return errIAMActionNotAllowed
}
// Delete any service accounts if any first.
for _, u := range sys.iamUsersMap { for _, u := range sys.iamUsersMap {
// Delete any service accounts if any first.
if u.IsServiceAccount() { if u.IsServiceAccount() {
if u.ParentUser == accessKey { if u.ParentUser == accessKey {
_ = sys.store.deleteUserIdentity(context.Background(), u.AccessKey, srvAccUser) _ = sys.store.deleteUserIdentity(context.Background(), u.AccessKey, srvAccUser)
delete(sys.iamUsersMap, u.AccessKey) delete(sys.iamUsersMap, u.AccessKey)
} }
} }
// Delete any associated STS users.
if u.IsTemp() {
if u.ParentUser == accessKey {
_ = sys.store.deleteUserIdentity(context.Background(), u.AccessKey, stsUser)
delete(sys.iamUsersMap, u.AccessKey)
}
}
} }
// It is ok to ignore deletion error on the mapped policy // It is ok to ignore deletion error on the mapped policy
@ -869,13 +992,19 @@ func (sys *IAMSys) GetUserInfo(name string) (u madmin.UserInfo, err error) {
} }
sys.store.rlock() sys.store.rlock()
defer sys.store.runlock() fallback := sys.storeFallback
sys.store.runlock()
if fallback {
sys.loadUserFromStore(name)
}
if sys.usersSysType != MinIOUsersSysType { if sys.usersSysType != MinIOUsersSysType {
sys.store.rlock()
// If the user has a mapped policy or is a member of a group, we // If the user has a mapped policy or is a member of a group, we
// return that info. Otherwise we return error. // return that info. Otherwise we return error.
mappedPolicy, ok1 := sys.iamUserPolicyMap[name] mappedPolicy, ok1 := sys.iamUserPolicyMap[name]
memberships, ok2 := sys.iamUserGroupMemberships[name] memberships, ok2 := sys.iamUserGroupMemberships[name]
sys.store.runlock()
if !ok1 && !ok2 { if !ok1 && !ok2 {
return u, errNoSuchUser return u, errNoSuchUser
} }
@ -885,6 +1014,9 @@ func (sys *IAMSys) GetUserInfo(name string) (u madmin.UserInfo, err error) {
}, nil }, nil
} }
sys.store.rlock()
defer sys.store.runlock()
cred, found := sys.iamUsersMap[name] cred, found := sys.iamUsersMap[name]
if !found { if !found {
return u, errNoSuchUser return u, errNoSuchUser
@ -894,7 +1026,7 @@ func (sys *IAMSys) GetUserInfo(name string) (u madmin.UserInfo, err error) {
return u, errIAMActionNotAllowed return u, errIAMActionNotAllowed
} }
u = madmin.UserInfo{ return madmin.UserInfo{
PolicyName: sys.iamUserPolicyMap[name].Policies, PolicyName: sys.iamUserPolicyMap[name].Policies,
Status: func() madmin.AccountStatus { Status: func() madmin.AccountStatus {
if cred.IsValid() { if cred.IsValid() {
@ -903,8 +1035,8 @@ func (sys *IAMSys) GetUserInfo(name string) (u madmin.UserInfo, err error) {
return madmin.AccountDisabled return madmin.AccountDisabled
}(), }(),
MemberOf: sys.iamUserGroupMemberships[name].ToSlice(), MemberOf: sys.iamUserGroupMemberships[name].ToSlice(),
} }, nil
return u, nil
} }
// SetUserStatus - sets current user status, supports disabled or enabled. // SetUserStatus - sets current user status, supports disabled or enabled.
@ -913,6 +1045,10 @@ func (sys *IAMSys) SetUserStatus(accessKey string, status madmin.AccountStatus)
return errServerNotInitialized return errServerNotInitialized
} }
if sys.usersSysType != MinIOUsersSysType {
return errIAMActionNotAllowed
}
if status != madmin.AccountEnabled && status != madmin.AccountDisabled { if status != madmin.AccountEnabled && status != madmin.AccountDisabled {
return errInvalidArgument return errInvalidArgument
} }
@ -920,10 +1056,6 @@ func (sys *IAMSys) SetUserStatus(accessKey string, status madmin.AccountStatus)
sys.store.lock() sys.store.lock()
defer sys.store.unlock() defer sys.store.unlock()
if sys.usersSysType != MinIOUsersSysType {
return errIAMActionNotAllowed
}
cred, ok := sys.iamUsersMap[accessKey] cred, ok := sys.iamUsersMap[accessKey]
if !ok { if !ok {
return errNoSuchUser return errNoSuchUser
@ -1091,30 +1223,31 @@ func (sys *IAMSys) DeleteServiceAccount(ctx context.Context, accessKey string) e
return nil return nil
} }
// SetUser - set user credentials and policy. // CreateUser - create new user credentials and policy, if user already exists
func (sys *IAMSys) SetUser(accessKey string, uinfo madmin.UserInfo) error { // they shall be rewritten with new inputs.
func (sys *IAMSys) CreateUser(accessKey string, uinfo madmin.UserInfo) error {
if !sys.Initialized() { if !sys.Initialized() {
return errServerNotInitialized return errServerNotInitialized
} }
u := newUserIdentity(auth.Credentials{
AccessKey: accessKey,
SecretKey: uinfo.SecretKey,
Status: string(uinfo.Status),
})
sys.store.lock()
defer sys.store.unlock()
if sys.usersSysType != MinIOUsersSysType { if sys.usersSysType != MinIOUsersSysType {
return errIAMActionNotAllowed return errIAMActionNotAllowed
} }
sys.store.lock()
defer sys.store.unlock()
cr, ok := sys.iamUsersMap[accessKey] cr, ok := sys.iamUsersMap[accessKey]
if cr.IsTemp() && ok { if cr.IsTemp() && ok {
return errIAMActionNotAllowed return errIAMActionNotAllowed
} }
u := newUserIdentity(auth.Credentials{
AccessKey: accessKey,
SecretKey: uinfo.SecretKey,
Status: string(uinfo.Status),
})
if err := sys.store.saveUserIdentity(context.Background(), accessKey, regularUser, u); err != nil { if err := sys.store.saveUserIdentity(context.Background(), accessKey, regularUser, u); err != nil {
return err return err
} }
@ -1156,55 +1289,55 @@ func (sys *IAMSys) SetUserSecretKey(accessKey string, secretKey string) error {
return nil return nil
} }
// GetUser - get user credentials func (sys *IAMSys) loadUserFromStore(accessKey string) {
func (sys *IAMSys) GetUser(accessKey string) (cred auth.Credentials, ok bool) { sys.store.lock()
if !sys.Initialized() { // If user is already found proceed.
return cred, false if _, found := sys.iamUsersMap[accessKey]; !found {
} sys.store.loadUser(context.Background(), accessKey, regularUser, sys.iamUsersMap)
if _, found = sys.iamUsersMap[accessKey]; found {
reloadUser := func() { // found user, load its mapped policies
sys.store.lock() sys.store.loadMappedPolicy(context.Background(), accessKey, regularUser, false, sys.iamUserPolicyMap)
// If user is already found proceed. } else {
if _, found := sys.iamUsersMap[accessKey]; !found { sys.store.loadUser(context.Background(), accessKey, srvAccUser, sys.iamUsersMap)
sys.store.loadUser(context.Background(), accessKey, regularUser, sys.iamUsersMap) if svc, found := sys.iamUsersMap[accessKey]; found {
if _, found = sys.iamUsersMap[accessKey]; found { // Found service account, load its parent user and its mapped policies.
// found user, load its mapped policies if sys.usersSysType == MinIOUsersSysType {
sys.store.loadMappedPolicy(context.Background(), accessKey, regularUser, false, sys.iamUserPolicyMap) sys.store.loadUser(context.Background(), svc.ParentUser, regularUser, sys.iamUsersMap)
}
sys.store.loadMappedPolicy(context.Background(), svc.ParentUser, regularUser, false, sys.iamUserPolicyMap)
} else { } else {
sys.store.loadUser(context.Background(), accessKey, srvAccUser, sys.iamUsersMap) // None found fall back to STS users.
if svc, found := sys.iamUsersMap[accessKey]; found { sys.store.loadUser(context.Background(), accessKey, stsUser, sys.iamUsersMap)
// Found service account, load its parent user and its mapped policies. if _, found = sys.iamUsersMap[accessKey]; found {
if sys.usersSysType == MinIOUsersSysType { // STS user found, load its mapped policy.
sys.store.loadUser(context.Background(), svc.ParentUser, regularUser, sys.iamUsersMap) sys.store.loadMappedPolicy(context.Background(), accessKey, stsUser, false, sys.iamUserPolicyMap)
}
sys.store.loadMappedPolicy(context.Background(), svc.ParentUser, regularUser, false, sys.iamUserPolicyMap)
} else {
// None found fall back to STS users.
sys.store.loadUser(context.Background(), accessKey, stsUser, sys.iamUsersMap)
if _, found = sys.iamUsersMap[accessKey]; found {
// STS user found, load its mapped policy.
sys.store.loadMappedPolicy(context.Background(), accessKey, stsUser, false, sys.iamUserPolicyMap)
}
} }
} }
} }
}
// Load associated policies if any. // Load associated policies if any.
for _, policy := range sys.iamUserPolicyMap[accessKey].toSlice() { for _, policy := range sys.iamUserPolicyMap[accessKey].toSlice() {
if _, found := sys.iamPolicyDocsMap[policy]; !found { if _, found := sys.iamPolicyDocsMap[policy]; !found {
sys.store.loadPolicyDoc(context.Background(), policy, sys.iamPolicyDocsMap) sys.store.loadPolicyDoc(context.Background(), policy, sys.iamPolicyDocsMap)
}
} }
}
sys.buildUserGroupMemberships()
sys.store.unlock()
}
sys.buildUserGroupMemberships() // GetUser - get user credentials
sys.store.unlock() func (sys *IAMSys) GetUser(accessKey string) (cred auth.Credentials, ok bool) {
if !sys.Initialized() {
return cred, false
} }
sys.store.rlock() sys.store.rlock()
fallback := sys.storeFallback fallback := sys.storeFallback
sys.store.runlock() sys.store.runlock()
if fallback { if fallback {
reloadUser() sys.loadUserFromStore(accessKey)
} }
sys.store.rlock() sys.store.rlock()
@ -1217,7 +1350,7 @@ func (sys *IAMSys) GetUser(accessKey string) (cred auth.Credentials, ok bool) {
// the IAM store and see if credential // the IAM store and see if credential
// exists now. If it doesn't proceed to // exists now. If it doesn't proceed to
// fail. // fail.
reloadUser() sys.loadUserFromStore(accessKey)
sys.store.rlock() sys.store.rlock()
cred, ok = sys.iamUsersMap[accessKey] cred, ok = sys.iamUsersMap[accessKey]
@ -1305,14 +1438,14 @@ func (sys *IAMSys) RemoveUsersFromGroup(group string, members []string) error {
return errServerNotInitialized return errServerNotInitialized
} }
if group == "" {
return errInvalidArgument
}
if sys.usersSysType != MinIOUsersSysType { if sys.usersSysType != MinIOUsersSysType {
return errIAMActionNotAllowed return errIAMActionNotAllowed
} }
if group == "" {
return errInvalidArgument
}
sys.store.lock() sys.store.lock()
defer sys.store.unlock() defer sys.store.unlock()

@ -81,6 +81,7 @@ require (
github.com/willf/bloom v2.0.3+incompatible github.com/willf/bloom v2.0.3+incompatible
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c
go.etcd.io/etcd v0.0.0-20201125193152-8a03d2e9614b go.etcd.io/etcd v0.0.0-20201125193152-8a03d2e9614b
go.uber.org/zap v1.13.0
golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392 golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392
golang.org/x/net v0.0.0-20201216054612-986b41b23924 golang.org/x/net v0.0.0-20201216054612-986b41b23924
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68

Loading…
Cancel
Save