do not fallback on the drives to load groups for LDAP (#20320)

if a user policy is found, avoid reading from the drives
for missing group mappings, group mappings are not mandatory
and conditional.

This PR restores the older behavior while making sure that
if a direct user policy is not found, we would still attempt
to load from the group from the drives.
This commit is contained in:
Harshavardhana 2024-08-25 17:22:45 -07:00 committed by GitHub
parent 2d67c26794
commit af55f37b27
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -23,6 +23,7 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"path"
"sort" "sort"
"strings" "strings"
"time" "time"
@ -367,7 +368,7 @@ func (c *iamCache) removeGroupFromMembershipsMap(group string) {
// information in IAM (i.e sys.iam*Map) - this info is stored only in the STS // information in IAM (i.e sys.iam*Map) - this info is stored only in the STS
// generated credentials. Thus we skip looking up group memberships, user map, // generated credentials. Thus we skip looking up group memberships, user map,
// and group map and check the appropriate policy maps directly. // and group map and check the appropriate policy maps directly.
func (c *iamCache) policyDBGet(store *IAMStoreSys, name string, isGroup bool) ([]string, time.Time, error) { func (c *iamCache) policyDBGet(store *IAMStoreSys, name string, isGroup bool, policyPresent bool) ([]string, time.Time, error) {
if isGroup { if isGroup {
if store.getUsersSysType() == MinIOUsersSysType { if store.getUsersSysType() == MinIOUsersSysType {
g, ok := c.iamGroupsMap[name] g, ok := c.iamGroupsMap[name]
@ -392,13 +393,34 @@ func (c *iamCache) policyDBGet(store *IAMStoreSys, name string, isGroup bool) ([
if ok { if ok {
return policy.toSlice(), policy.UpdatedAt, nil return policy.toSlice(), policy.UpdatedAt, nil
} }
if err := store.loadMappedPolicyWithRetry(context.TODO(), name, regUser, true, c.iamGroupPolicyMap, 3); err != nil && !errors.Is(err, errNoSuchPolicy) { if !policyPresent {
if err := store.loadMappedPolicy(context.TODO(), name, regUser, true, c.iamGroupPolicyMap); err != nil && !errors.Is(err, errNoSuchPolicy) {
return nil, time.Time{}, err return nil, time.Time{}, err
} }
policy, _ = c.iamGroupPolicyMap.Load(name) policy, _ = c.iamGroupPolicyMap.Load(name)
return policy.toSlice(), policy.UpdatedAt, nil return policy.toSlice(), policy.UpdatedAt, nil
} }
return nil, time.Time{}, nil
}
// returned policy could be empty, we use set to de-duplicate.
var policies set.StringSet
var updatedAt time.Time
if store.getUsersSysType() == LDAPUsersSysType {
// For LDAP policy mapping is part of STS users, we only need to lookup
// those mappings.
mp, ok := c.iamSTSPolicyMap.Load(name)
if !ok {
// Attempt to load parent user mapping for STS accounts
if err := store.loadMappedPolicy(context.TODO(), name, stsUser, false, c.iamSTSPolicyMap); err != nil && !errors.Is(err, errNoSuchPolicy) {
return nil, time.Time{}, err
}
mp, _ = c.iamSTSPolicyMap.Load(name)
}
policies = set.CreateStringSet(mp.toSlice()...)
updatedAt = mp.UpdatedAt
} else {
// When looking for a user's policies, we also check if the user // When looking for a user's policies, we also check if the user
// and the groups they are member of are enabled. // and the groups they are member of are enabled.
u, ok := c.iamUsersMap[name] u, ok := c.iamUsersMap[name]
@ -413,10 +435,9 @@ func (c *iamCache) policyDBGet(store *IAMStoreSys, name string, isGroup bool) ([
// passed here and we lookup the mapping in iamSTSPolicyMap. // passed here and we lookup the mapping in iamSTSPolicyMap.
mp, ok := c.iamUserPolicyMap.Load(name) mp, ok := c.iamUserPolicyMap.Load(name)
if !ok { if !ok {
if err := store.loadMappedPolicyWithRetry(context.TODO(), name, regUser, false, c.iamUserPolicyMap, 3); err != nil && !errors.Is(err, errNoSuchPolicy) { if err := store.loadMappedPolicy(context.TODO(), name, regUser, false, c.iamUserPolicyMap); err != nil && !errors.Is(err, errNoSuchPolicy) {
return nil, time.Time{}, err return nil, time.Time{}, err
} }
mp, ok = c.iamUserPolicyMap.Load(name) mp, ok = c.iamUserPolicyMap.Load(name)
if !ok { if !ok {
// Since user "name" could be a parent user of an STS account, we look up // Since user "name" could be a parent user of an STS account, we look up
@ -424,30 +445,18 @@ func (c *iamCache) policyDBGet(store *IAMStoreSys, name string, isGroup bool) ([
mp, ok = c.iamSTSPolicyMap.Load(name) mp, ok = c.iamSTSPolicyMap.Load(name)
if !ok { if !ok {
// Attempt to load parent user mapping for STS accounts // Attempt to load parent user mapping for STS accounts
if err := store.loadMappedPolicyWithRetry(context.TODO(), name, stsUser, false, c.iamSTSPolicyMap, 3); err != nil && !errors.Is(err, errNoSuchPolicy) { if err := store.loadMappedPolicy(context.TODO(), name, stsUser, false, c.iamSTSPolicyMap); err != nil && !errors.Is(err, errNoSuchPolicy) {
return nil, time.Time{}, err return nil, time.Time{}, err
} }
mp, _ = c.iamSTSPolicyMap.Load(name) mp, _ = c.iamSTSPolicyMap.Load(name)
} }
} }
} }
policies = set.CreateStringSet(mp.toSlice()...)
// returned policy could be empty, we use set to de-duplicate.
policies := set.CreateStringSet(mp.toSlice()...)
for _, group := range u.Credentials.Groups { for _, group := range u.Credentials.Groups {
if store.getUsersSysType() == MinIOUsersSysType {
g, ok := c.iamGroupsMap[group] g, ok := c.iamGroupsMap[group]
if !ok { if ok {
if err := store.loadGroup(context.Background(), group, c.iamGroupsMap); err != nil {
return nil, time.Time{}, err
}
g, ok = c.iamGroupsMap[group]
if !ok {
return nil, time.Time{}, errNoSuchGroup
}
}
// Group is disabled, so we return no policy - this // Group is disabled, so we return no policy - this
// ensures the request is denied. // ensures the request is denied.
if g.Status == statusDisabled { if g.Status == statusDisabled {
@ -457,7 +466,7 @@ func (c *iamCache) policyDBGet(store *IAMStoreSys, name string, isGroup bool) ([
policy, ok := c.iamGroupPolicyMap.Load(group) policy, ok := c.iamGroupPolicyMap.Load(group)
if !ok { if !ok {
if err := store.loadMappedPolicyWithRetry(context.TODO(), group, regUser, true, c.iamGroupPolicyMap, 3); err != nil && !errors.Is(err, errNoSuchPolicy) { if err := store.loadMappedPolicy(context.TODO(), group, regUser, true, c.iamGroupPolicyMap); err != nil && !errors.Is(err, errNoSuchPolicy) {
return nil, time.Time{}, err return nil, time.Time{}, err
} }
policy, _ = c.iamGroupPolicyMap.Load(group) policy, _ = c.iamGroupPolicyMap.Load(group)
@ -467,30 +476,24 @@ func (c *iamCache) policyDBGet(store *IAMStoreSys, name string, isGroup bool) ([
policies.Add(p) policies.Add(p)
} }
} }
updatedAt = mp.UpdatedAt
}
for _, group := range c.iamUserGroupMemberships[name].ToSlice() { for _, group := range c.iamUserGroupMemberships[name].ToSlice() {
if store.getUsersSysType() == MinIOUsersSysType { if store.getUsersSysType() == MinIOUsersSysType {
g, ok := c.iamGroupsMap[group] g, ok := c.iamGroupsMap[group]
if !ok { if ok {
if err := store.loadGroup(context.Background(), group, c.iamGroupsMap); err != nil {
return nil, time.Time{}, err
}
g, ok = c.iamGroupsMap[group]
if !ok {
return nil, time.Time{}, errNoSuchGroup
}
}
// Group is disabled, so we return no policy - this // Group is disabled, so we return no policy - this
// ensures the request is denied. // ensures the request is denied.
if g.Status == statusDisabled { if g.Status == statusDisabled {
return nil, time.Time{}, nil return nil, time.Time{}, nil
} }
} }
}
policy, ok := c.iamGroupPolicyMap.Load(group) policy, ok := c.iamGroupPolicyMap.Load(group)
if !ok { if !ok {
if err := store.loadMappedPolicyWithRetry(context.TODO(), group, regUser, true, c.iamGroupPolicyMap, 3); err != nil && !errors.Is(err, errNoSuchPolicy) { if err := store.loadMappedPolicy(context.TODO(), group, regUser, true, c.iamGroupPolicyMap); err != nil && !errors.Is(err, errNoSuchPolicy) {
return nil, time.Time{}, err return nil, time.Time{}, err
} }
policy, _ = c.iamGroupPolicyMap.Load(group) policy, _ = c.iamGroupPolicyMap.Load(group)
@ -501,7 +504,7 @@ func (c *iamCache) policyDBGet(store *IAMStoreSys, name string, isGroup bool) ([
} }
} }
return policies.ToSlice(), mp.UpdatedAt, nil return policies.ToSlice(), updatedAt, nil
} }
func (c *iamCache) updateUserWithClaims(key string, u UserIdentity) error { func (c *iamCache) updateUserWithClaims(key string, u UserIdentity) error {
@ -754,13 +757,14 @@ func (store *IAMStoreSys) PolicyDBGet(name string, groups ...string) ([]string,
cache := store.rlock() cache := store.rlock()
defer store.runlock() defer store.runlock()
policies, _, err := cache.policyDBGet(store, name, false) policies, _, err := cache.policyDBGet(store, name, false, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }
userPolicyPresent := len(policies) > 0
for _, group := range groups { for _, group := range groups {
ps, _, err := cache.policyDBGet(store, group, true) ps, _, err := cache.policyDBGet(store, group, true, userPolicyPresent)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -945,7 +949,7 @@ func (store *IAMStoreSys) GetGroupDescription(group string) (gd madmin.GroupDesc
cache := store.rlock() cache := store.rlock()
defer store.runlock() defer store.runlock()
ps, updatedAt, err := cache.policyDBGet(store, group, true) ps, updatedAt, err := cache.policyDBGet(store, group, true, false)
if err != nil { if err != nil {
return gd, err return gd, err
} }
@ -974,33 +978,45 @@ func (store *IAMStoreSys) GetGroupDescription(group string) (gd madmin.GroupDesc
}, nil }, nil
} }
// updateGroups updates the group from the persistent store, and also related policy mapping if any.
func (store *IAMStoreSys) updateGroups(ctx context.Context, cache *iamCache) (res []string, err error) { func (store *IAMStoreSys) updateGroups(ctx context.Context, cache *iamCache) (res []string, err error) {
if store.getUsersSysType() == MinIOUsersSysType { if iamOS, ok := store.IAMStorageAPI.(*IAMObjectStore); ok {
m := map[string]GroupInfo{} listedConfigItems, err := iamOS.listAllIAMConfigItems(ctx)
err = store.loadGroups(ctx, m)
if err != nil { if err != nil {
return return nil, err
} }
cache.iamGroupsMap = m if store.getUsersSysType() == MinIOUsersSysType {
cache.updatedAt = time.Now() groupsList := listedConfigItems[groupsListKey]
for k := range cache.iamGroupsMap { for _, item := range groupsList {
res = append(res, k) group := path.Dir(item)
if err = iamOS.loadGroup(ctx, group, cache.iamGroupsMap); err != nil && !errors.Is(err, errNoSuchGroup) {
return nil, fmt.Errorf("unable to load the group: %w", err)
}
res = append(res, group)
} }
} }
if store.getUsersSysType() == LDAPUsersSysType { groupPolicyMappingsList := listedConfigItems[policyDBGroupsListKey]
m := xsync.NewMapOf[string, MappedPolicy]() for _, item := range groupPolicyMappingsList {
err = store.loadMappedPolicies(ctx, stsUser, true, m) group := strings.TrimSuffix(item, ".json")
if err != nil { if err = iamOS.loadMappedPolicy(ctx, group, regUser, true, cache.iamGroupPolicyMap); err != nil && !errors.Is(err, errNoSuchPolicy) {
return return nil, fmt.Errorf("unable to load the policy mapping for the group: %w", err)
} }
cache.iamGroupPolicyMap = m res = append(res, group)
cache.updatedAt = time.Now() }
return res, nil
}
// For etcd just return from cache.
for k := range cache.iamGroupsMap {
res = append(res, k)
}
cache.iamGroupPolicyMap.Range(func(k string, v MappedPolicy) bool { cache.iamGroupPolicyMap.Range(func(k string, v MappedPolicy) bool {
res = append(res, k) res = append(res, k)
return true return true
}) })
}
return res, nil return res, nil
} }
@ -2800,14 +2816,8 @@ func (store *IAMStoreSys) LoadUser(ctx context.Context, accessKey string) error
} }
if groupLoad { if groupLoad {
load := len(newCache.iamGroupsMap) == 0
if store.getUsersSysType() == LDAPUsersSysType && newCache.iamGroupPolicyMap.Size() == 0 {
load = true
}
if load {
// NOTE: we are not worried about loading errors from groups. // NOTE: we are not worried about loading errors from groups.
store.updateGroups(ctx, newCache) store.updateGroups(ctx, newCache)
}
newCache.buildUserGroupMemberships() newCache.buildUserGroupMemberships()
} }