diff --git a/keystone/identity/core.py b/keystone/identity/core.py index cb268f72ca..a95024ecf1 100644 --- a/keystone/identity/core.py +++ b/keystone/identity/core.py @@ -14,6 +14,7 @@ """Main entry point into the Identity service.""" +import copy import functools import itertools import operator @@ -1025,12 +1026,23 @@ class Manager(manager.Manager): raise exception.InvalidOperatorError(op) return hints - def _handle_federated_attributes_in_hints(self, driver, hints): + def _handle_shadow_and_local_users(self, driver, hints): federated_attributes = ['idp_id', 'protocol_id', 'unique_id'] for filter_ in hints.filters: if filter_['name'] in federated_attributes: return PROVIDERS.shadow_users_api.get_federated_users(hints) - return driver.list_users(hints) + fed_hints = copy.deepcopy(hints) + res = driver.list_users(hints) + + # Note: If the filters contain 'name', we should get the user from both + # local user and shadow user backend. + for filter_ in fed_hints.filters: + if filter_['name'] == 'name': + fed_res = PROVIDERS.shadow_users_api.get_federated_users( + fed_hints) + res += fed_res + break + return res @domains_configured @exception_translated('user') @@ -1047,7 +1059,7 @@ class Manager(manager.Manager): # driver selection, so remove any such filter. self._mark_domain_id_filter_satisfied(hints) hints = self._translate_expired_password_hints(hints) - ref_list = self._handle_federated_attributes_in_hints(driver, hints) + ref_list = self._handle_shadow_and_local_users(driver, hints) return self._set_domain_id_and_mapping( ref_list, domain_scope, driver, mapping.EntityType.USER) diff --git a/keystone/identity/shadow_backends/sql.py b/keystone/identity/shadow_backends/sql.py index ccf420cbc7..fe62517601 100644 --- a/keystone/identity/shadow_backends/sql.py +++ b/keystone/identity/shadow_backends/sql.py @@ -67,9 +67,20 @@ class ShadowUsers(base.ShadowUsersDriverBase): def get_federated_users(self, hints): with sql.session_for_read() as session: - query = session.query(model.User).outerjoin(model.LocalUser) + query = session.query(model.User).outerjoin( + model.LocalUser).outerjoin(model.FederatedUser) query = query.filter(model.User.id == model.FederatedUser.user_id) query = self._update_query_with_federated_statements(hints, query) + name_filter = None + for filter_ in hints.filters: + if filter_['name'] == 'name': + name_filter = filter_ + query = query.filter( + model.FederatedUser.display_name == name_filter[ + 'value']) + break + if name_filter: + hints.filters.remove(name_filter) user_refs = sql.filter_limit_query(model.User, query, hints) return [identity_base.filter_user(x.to_dict()) for x in user_refs] diff --git a/keystone/tests/unit/identity/test_backends.py b/keystone/tests/unit/identity/test_backends.py index 64b2826df3..3e0388215b 100644 --- a/keystone/tests/unit/identity/test_backends.py +++ b/keystone/tests/unit/identity/test_backends.py @@ -529,6 +529,29 @@ class IdentityTests(object): filters = ['unique_id', 'idp_id', 'protocol_id'] self._test_list_users_with_attribute(filters, federated_dict) + def test_list_users_with_name(self): + federated_dict = unit.new_federated_user_ref( + display_name='test@federation.org') + domain = self._get_domain_fixture() + + hints = driver_hints.Hints() + hints.add_filter('name', 'test@federation.org') + users = self.identity_api.list_users(hints=hints) + self.assertEqual(0, len(users)) + + self.shadow_users_api.create_federated_user(domain['id'], + federated_dict) + hints = driver_hints.Hints() + hints.add_filter('name', 'test@federation.org') + users = self.identity_api.list_users(hints=hints) + self.assertEqual(1, len(users)) + + hints = driver_hints.Hints() + hints.add_filter('name', 'test@federation.org') + hints.add_filter('idp_id', 'ORG_IDP') + users = self.identity_api.list_users(hints=hints) + self.assertEqual(1, len(users)) + def test_list_groups(self): group1 = unit.new_group_ref(domain_id=CONF.identity.default_domain_id) group2 = unit.new_group_ref(domain_id=CONF.identity.default_domain_id) diff --git a/releasenotes/notes/bug-1738895-342864cd0285bc42.yaml b/releasenotes/notes/bug-1738895-342864cd0285bc42.yaml new file mode 100644 index 0000000000..ae40a4c5e8 --- /dev/null +++ b/releasenotes/notes/bug-1738895-342864cd0285bc42.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + [`bug 1738895 `_] + Fixed the bug that federated users can't be listed by `name` filter. Now + when list users by `name`, Keystone will query both local user backend and + shadow user backend.