Repository: minio/minio (language: go)

Issue:
Inconsistent results when querying ldap entities
## Expected Behavior
When querying LDAP policy attachments from minio i expect it to return all attached policies to a user every time.


## Current Behavior
when you get a mapping either via "mc idp ldap policy entities -u" USER TARGET cli command or via the Administer > Identitiy > LDAP > entities section in the webui console. Both with randomly return a subset or all policies attached to a user. Sometimes multiple sets of results will be returned for the same user. This number of returned results depends on number of nodes in the cluster. With 3 nodes at least one set of results is always correct. With 4 nodes this is not the case.

Multiple sets of results are returned until the minio service is restarted by the k8s operator. I presume this is doing some reload of applied policies on all nodes.

❯ mc idp ldap policy entities -u "cn=LDAP_USER" TARGET
Query time: 2024-01-08T16:17:04Z
User -> Policy Mappings:
  User: LDAP_USER
    consoleAdmin
    tmp1
    tmp2
    tmp3
    tmp4
  User: LDAP_USER
    consoleAdmin
    tmp2
    tmp3
    tmp4


## Steps to Reproduce (for bugs)
add ldap idp config to minio k8s deployment.
add 2 policies to a user. query policies attached to a user and see only some be returned.


## Context
we are on a previous binary deployment of minio in production and are testing upgrading to k8s minio or a newer version and noticed this bug which would cause consistency issues.

## Your Environment
* running cluster minio deployment using minio operator in k8s. 4 node deployment. different by similar issues on 3 node cluster.
* MinIO version: minio/minio:RELEASE.2024-01-05T22-17-24Z
* when deploying this version with a single docker container connecting to ldap we do not see this issue. So i presume it has to do with multi node deployments.



Discussion / hints:
Hi Is there any update with this issue? Were you able to replicate the issue?
I've not been able to replicate this so far. In your environment, what is the response to `mc admin user list TARGET`? (This is not the intended way of getting user info for LDAP so don't worry if its output is wrong. Just looking for more information)
Without passing --json it would not list all policies, it swaps back and forth between the following. One policy is missing in one of the results.

`
{
 "status": "success",
 "accessKey": "xxxxxxxxxxxxxxxx",
 "policyName": "consoleAdmin",
 "userStatus": "enabled"
}
{
 "status": "success",
 "accessKey": "cn=xxxxxxxx,cn=xxxx,dc=xxxx,dc=xxxxx",
 "policyName": "consoleAdmin,tmp2,tmp3,tmp4,tmp5",
 "userStatus": "enabled"
}
`

`
{
 "status": "success",
 "accessKey": "xxxxxxxxxxxxxxxxxxxx",
 "policyName": "consoleAdmin",
 "userStatus": "enabled"
}
{
 "status": "success",
 "accessKey": "cn=xxxxxxxxx,cn=xxxxxx,dc=xxxx,dc=xxx",
 "policyName": "consoleAdmin,tmp2,tmp3,tmp4,tmp5,tmp6",
 "userStatus": "enabled"
}
`
Hi 
Just checking if there is any update on this?
We would like to know if this is an issue before we update our production cluster to the new version and continue using the Ldap connection.
> We would like to know if this is an issue before we update our production cluster to the new version and continue using the Ldap connection.

Production cluster guidance from our end requires a subscription. Otherwise, you can read the release notes and ask questions in our community Slack.

However, there has been many changes since 

> MinIO version: minio/minio:RELEASE.2024-01-05T22-17-24Z

Please upgrade to the latest release which will be done today. 


Fix patch:
diff --git a/cmd/iam-store.go b/cmd/iam-store.go
index 6d3dc38e921ba..175fdbd42c07e 100644
--- a/cmd/iam-store.go
+++ b/cmd/iam-store.go
@@ -1634,6 +1634,8 @@ func (store *IAMStoreSys) PolicyMappingNotificationHandler(ctx context.Context,
 	switch {
 	case isGroup:
 		m = cache.iamGroupPolicyMap
+	case userType == stsUser:
+		m = cache.iamSTSPolicyMap
 	default:
 		m = cache.iamUserPolicyMap
 	}
@@ -2108,6 +2110,32 @@ func (store *IAMStoreSys) listPolicyMappings(cache *iamCache, policies []string,
 			}
 		}
 	}
+	if iamOS, ok := store.IAMStorageAPI.(*IAMEtcdStore); ok {
+		m := xsync.NewMapOf[string, MappedPolicy]()
+		err := iamOS.loadMappedPolicies(context.Background(), stsUser, false, m)
+		if err == nil {
+			m.Range(func(user string, mappedPolicy MappedPolicy) bool {
+				if userPredicate != nil && !userPredicate(user) {
+					return true
+				}
+
+				commonPolicySet := mappedPolicy.policySet()
+				if !queryPolSet.IsEmpty() {
+					commonPolicySet = commonPolicySet.Intersection(queryPolSet)
+				}
+				for _, policy := range commonPolicySet.ToSlice() {
+					s, ok := policyToUsersMap[policy]
+					if !ok {
+						policyToUsersMap[policy] = set.CreateStringSet(user)
+					} else {
+						s.Add(user)
+						policyToUsersMap[policy] = s
+					}
+				}
+				return true
+			})
+		}
+	}
 
 	policyToGroupsMap := make(map[string]set.StringSet)
 	cache.iamGroupPolicyMap.Range(func(group string, mappedPolicy MappedPolicy) bool {


Test patch: