Merge "Fixing dn_to_id function for cases were id is not in the DN" into stable/rocky
This commit is contained in:
commit
10a3517042
|
@ -1293,9 +1293,38 @@ class BaseLdap(object):
|
||||||
else:
|
else:
|
||||||
return self._id_to_dn_string(object_id)
|
return self._id_to_dn_string(object_id)
|
||||||
|
|
||||||
@staticmethod
|
def _dn_to_id(self, dn):
|
||||||
def _dn_to_id(dn):
|
# Check if the naming attribute in the DN is the same as keystone's
|
||||||
return ldap.dn.str2dn(dn)[0][0][1]
|
# configured 'id' attribute'. If so, extract the ID value from the DN
|
||||||
|
if self.id_attr == utf8_decode(
|
||||||
|
ldap.dn.str2dn(utf8_encode(dn))[0][0][0].lower()):
|
||||||
|
return utf8_decode(ldap.dn.str2dn(utf8_encode(dn))[0][0][1])
|
||||||
|
else:
|
||||||
|
# The 'ID' attribute is NOT in the DN, so we need to perform an
|
||||||
|
# LDAP search to look it up from the user entry itself.
|
||||||
|
with self.get_connection() as conn:
|
||||||
|
search_result = conn.search_s(dn, ldap.SCOPE_BASE)
|
||||||
|
|
||||||
|
if search_result:
|
||||||
|
try:
|
||||||
|
id_list = search_result[0][1][self.id_attr]
|
||||||
|
except KeyError:
|
||||||
|
message = ('ID attribute %(id_attr)s not found in LDAP '
|
||||||
|
'object %(dn)s.') % ({'id_attr': self.id_attr,
|
||||||
|
'dn': search_result})
|
||||||
|
LOG.warning(message)
|
||||||
|
raise exception.NotFound(message=message)
|
||||||
|
if len(id_list) > 1:
|
||||||
|
message = ('In order to keep backward compatibility, in '
|
||||||
|
'the case of multivalued ids, we are '
|
||||||
|
'returning the first id %(id_attr) in the '
|
||||||
|
'DN.') % ({'id_attr': id_list[0]})
|
||||||
|
LOG.warning(message)
|
||||||
|
return id_list[0]
|
||||||
|
else:
|
||||||
|
message = _('DN attribute %(dn)s not found in LDAP') % (
|
||||||
|
{'dn': dn})
|
||||||
|
raise exception.NotFound(message=message)
|
||||||
|
|
||||||
def _ldap_res_to_model(self, res):
|
def _ldap_res_to_model(self, res):
|
||||||
# LDAP attribute names may be returned in a different case than
|
# LDAP attribute names may be returned in a different case than
|
||||||
|
|
|
@ -311,8 +311,11 @@ class UserApi(common_ldap.EnabledEmuMixIn, common_ldap.BaseLdap):
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
def get_filtered(self, user_id):
|
def get_filtered(self, user_id):
|
||||||
user = self.get(user_id)
|
try:
|
||||||
return self.filter_attributes(user)
|
user = self.get(user_id)
|
||||||
|
return self.filter_attributes(user)
|
||||||
|
except ldap.NO_SUCH_OBJECT:
|
||||||
|
raise self.NotFound(user_id=user_id)
|
||||||
|
|
||||||
def get_all(self, ldap_filter=None, hints=None):
|
def get_all(self, ldap_filter=None, hints=None):
|
||||||
objs = super(UserApi, self).get_all(ldap_filter=ldap_filter,
|
objs = super(UserApi, self).get_all(ldap_filter=ldap_filter,
|
||||||
|
|
|
@ -1816,7 +1816,33 @@ class LDAPIdentity(BaseLDAPIdentity):
|
||||||
self.assertEqual(self.user_foo['email'], user_ref['id'])
|
self.assertEqual(self.user_foo['email'], user_ref['id'])
|
||||||
|
|
||||||
@mock.patch.object(common_ldap.BaseLdap, '_ldap_get')
|
@mock.patch.object(common_ldap.BaseLdap, '_ldap_get')
|
||||||
def test_get_id_from_dn_for_multivalued_attribute_id(self, mock_ldap_get):
|
def test_get_multivalued_attribute_id_from_dn(self,
|
||||||
|
mock_ldap_get):
|
||||||
|
driver = PROVIDERS.identity_api._select_identity_driver(
|
||||||
|
CONF.identity.default_domain_id)
|
||||||
|
driver.user.id_attr = 'mail'
|
||||||
|
|
||||||
|
# make 'email' multivalued so we can test the error condition
|
||||||
|
email1 = uuid.uuid4().hex
|
||||||
|
email2 = uuid.uuid4().hex
|
||||||
|
# Mock the ldap search results to return user entries with
|
||||||
|
# user_name_attribute('sn') value has emptyspaces, emptystring
|
||||||
|
# and attibute itself is not set.
|
||||||
|
mock_ldap_get.return_value = (
|
||||||
|
'cn=users,dc=example,dc=com',
|
||||||
|
{
|
||||||
|
'mail': [email1, email2],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# This is not a valid scenario, since we do not support multiple value
|
||||||
|
# attribute id on DN.
|
||||||
|
self.assertRaises(exception.NotFound,
|
||||||
|
PROVIDERS.identity_api.get_user, email1)
|
||||||
|
|
||||||
|
@mock.patch.object(common_ldap.BaseLdap, '_ldap_get')
|
||||||
|
def test_raise_not_found_dn_for_multivalued_attribute_id(self,
|
||||||
|
mock_ldap_get):
|
||||||
driver = PROVIDERS.identity_api._select_identity_driver(
|
driver = PROVIDERS.identity_api._select_identity_driver(
|
||||||
CONF.identity.default_domain_id)
|
CONF.identity.default_domain_id)
|
||||||
driver.user.id_attr = 'mail'
|
driver.user.id_attr = 'mail'
|
||||||
|
@ -1833,10 +1859,29 @@ class LDAPIdentity(BaseLDAPIdentity):
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
user_ref = PROVIDERS.identity_api.get_user(email1)
|
# This is not a valid scenario, since we do not support multiple value
|
||||||
# make sure we get the ID from DN (old behavior) if the ID attribute
|
# attribute id on DN.
|
||||||
# has multiple values
|
self.assertRaises(exception.NotFound,
|
||||||
self.assertEqual('nobodycares', user_ref['id'])
|
PROVIDERS.identity_api.get_user, email1)
|
||||||
|
|
||||||
|
@mock.patch.object(common_ldap.BaseLdap, '_ldap_get')
|
||||||
|
def test_get_id_not_in_dn(self,
|
||||||
|
mock_ldap_get):
|
||||||
|
driver = PROVIDERS.identity_api._select_identity_driver(
|
||||||
|
CONF.identity.default_domain_id)
|
||||||
|
driver.user.id_attr = 'sAMAccountName'
|
||||||
|
|
||||||
|
user_id = uuid.uuid4().hex
|
||||||
|
mock_ldap_get.return_value = (
|
||||||
|
'cn=someuser,dc=example,dc=com',
|
||||||
|
{
|
||||||
|
'cn': 'someuser',
|
||||||
|
'sn': [uuid.uuid4().hex],
|
||||||
|
'sAMAccountName': [user_id],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
user_ref = PROVIDERS.identity_api.get_user(user_id)
|
||||||
|
self.assertEqual(user_id, user_ref['id'])
|
||||||
|
|
||||||
@mock.patch.object(common_ldap.BaseLdap, '_ldap_get')
|
@mock.patch.object(common_ldap.BaseLdap, '_ldap_get')
|
||||||
def test_id_attribute_not_found(self, mock_ldap_get):
|
def test_id_attribute_not_found(self, mock_ldap_get):
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
---
|
||||||
|
fixes:
|
||||||
|
- |
|
||||||
|
[`bug 1782922 <https://bugs.launchpad.net/keystone/+bug/1782922>`_]
|
||||||
|
Fixed the problem where Keystone indiscriminately return the first RDN
|
||||||
|
as the user ID, regardless whether it matches the configured
|
||||||
|
'user_id_attribute' or not. This will break deployments where
|
||||||
|
'group_members_are_ids' are set to False and 'user_id_attribute' is not
|
||||||
|
in the DN. This patch will perform a lookup by DN if the first RND does
|
||||||
|
not match the configured 'user_id_attribute'.
|
Loading…
Reference in New Issue