From ba210d3e788eb7e6880ddbdcd1898349f43f9bfb Mon Sep 17 00:00:00 2001 From: Jorge Merlino Date: Wed, 4 Jun 2025 13:58:17 -0300 Subject: [PATCH] Fix AD nested groups issues The implementation of AD nested groups searches works fine when listing the groups a user belongs to, but fails when listing all members of a group. This function of listing all members is also used to check if a user belongs to a group which also fails. This patch fixes the query for getting all users in a group. Closes-Bug: #2112477 Depends-on: https://review.opendev.org/c/openstack/devstack/+/960683 Depends-on: https://review.opendev.org/c/openstack/devstack/+/960684 Change-Id: I9707e1a9bc4a334902933d6251888144f8c3bc19 Signed-off-by: Jorge Merlino (cherry picked from commit f8338be43073f23f3db64fa4ba658c3e1f554aa7) --- keystone/identity/backends/ldap/common.py | 15 +++++++++++++-- keystone/identity/backends/ldap/core.py | 13 ++++++++----- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/keystone/identity/backends/ldap/common.py b/keystone/identity/backends/ldap/common.py index 2212543872..00e495c1d5 100644 --- a/keystone/identity/backends/ldap/common.py +++ b/keystone/identity/backends/ldap/common.py @@ -1417,6 +1417,12 @@ class BaseLdap: self.auth_pool_conn_lifetime = conf.ldap.auth_pool_connection_lifetime if self.options_name is not None: + self.user_tree_dn = ( + conf.ldap.user_tree_dn + or f'{self.DEFAULT_OU},{conf.ldap.suffix}' + ) + self.user_objectclass = conf.ldap.user_objectclass + self.tree_dn = ( getattr(conf.ldap, f'{self.options_name}_tree_dn') or f'{self.DEFAULT_OU},{conf.ldap.suffix}' @@ -1854,9 +1860,14 @@ class BaseLdap: return self._filter_ldap_result_by_attr(res, 'name') def _ldap_get_list( - self, search_base, scope, query_params=None, attrlist=None + self, + search_base, + scope, + query_params=None, + attrlist=None, + object_class=None, ): - query = f'(objectClass={self.object_class})' + query = f'(objectClass={object_class or self.object_class})' if query_params: def calc_filter(attrname, value): diff --git a/keystone/identity/backends/ldap/core.py b/keystone/identity/backends/ldap/core.py index 75251234ba..5ddf14d064 100644 --- a/keystone/identity/backends/ldap/core.py +++ b/keystone/identity/backends/ldap/core.py @@ -434,23 +434,26 @@ class GroupApi(common_ldap.BaseLdap): """Return a list of user dns which are members of a group.""" group_ref = self.get(group_id) group_dn = group_ref['dn'] + attribute = self.member_attribute try: if self.group_ad_nesting: # NOTE(ayoung): LDAP_SCOPE is used here instead of hard- # coding to SCOPE_SUBTREE to get through the unit tests. # However, it is also probably more correct. + attribute = "distinguishedName" attrs = self._ldap_get_list( - self.tree_dn, + self.user_tree_dn, self.LDAP_SCOPE, query_params={ - f"member:{LDAP_MATCHING_RULE_IN_CHAIN}:": group_dn + f"memberOf:{LDAP_MATCHING_RULE_IN_CHAIN}:": group_dn }, - attrlist=[self.member_attribute], + attrlist=[attribute], + object_class=self.user_objectclass, ) else: attrs = self._ldap_get_list( - group_dn, ldap.SCOPE_BASE, attrlist=[self.member_attribute] + group_dn, ldap.SCOPE_BASE, attrlist=[attribute] ) except ldap.NO_SUCH_OBJECT: @@ -458,7 +461,7 @@ class GroupApi(common_ldap.BaseLdap): users = [] for dn, member in attrs: - user_dns = member.get(self.member_attribute, []) + user_dns = member.get(attribute, []) for user_dn in user_dns: users.append(user_dn) return users