diff --git a/releasenotes/notes/list-auth-domains-v3-endpoint-9ec60c7d3011c397.yaml b/releasenotes/notes/list-auth-domains-v3-endpoint-9ec60c7d3011c397.yaml new file mode 100644 index 0000000000..0f104cf530 --- /dev/null +++ b/releasenotes/notes/list-auth-domains-v3-endpoint-9ec60c7d3011c397.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Add ``list_auth_domains`` API endpoint to the identity v3 client. This + allows the possibility of listing all domains a user has access to + via role assignments. diff --git a/tempest/api/identity/admin/v3/test_tokens.py b/tempest/api/identity/admin/v3/test_tokens.py index 6343ea84df..084540709d 100644 --- a/tempest/api/identity/admin/v3/test_tokens.py +++ b/tempest/api/identity/admin/v3/test_tokens.py @@ -26,6 +26,8 @@ CONF = config.CONF class TokensV3TestJSON(base.BaseIdentityV3AdminTest): + credentials = ['primary', 'admin', 'alt'] + @decorators.idempotent_id('0f9f5a5f-d5cd-4a86-8a5b-c5ded151f212') def test_tokens(self): # Valid user's token is authenticated @@ -163,12 +165,78 @@ class TokensV3TestJSON(base.BaseIdentityV3AdminTest): # Get available project scopes available_projects = self.client.list_auth_projects()['projects'] - # create list to save fetched project's id + # Create list to save fetched project IDs fetched_project_ids = [i['id'] for i in available_projects] # verifying the project ids in list missing_project_ids = \ [p for p in assigned_project_ids if p not in fetched_project_ids] self.assertEmpty(missing_project_ids, - "Failed to find project_id %s in fetched list" % + "Failed to find project_ids %s in fetched list" % ', '.join(missing_project_ids)) + + @decorators.idempotent_id('ec5ecb05-af64-4c04-ac86-4d9f6f12f185') + def test_get_available_domain_scopes(self): + # Test for verifying that listing domain scopes for a user works if + # the user has a domain role or belongs to a group that has a domain + # role. For this test, admin client is used to add roles to alt user, + # which performs API calls, to avoid 401 Unauthorized errors. + alt_user_id = self.os_alt.credentials.user_id + + def _create_user_domain_role_for_alt_user(): + domain_id = self.setup_test_domain()['id'] + role_id = self.setup_test_role()['id'] + + # Create a role association between the user and domain. + self.roles_client.create_user_role_on_domain( + domain_id, alt_user_id, role_id) + self.addCleanup( + self.roles_client.delete_role_from_user_on_domain, + domain_id, alt_user_id, role_id) + + return domain_id + + def _create_group_domain_role_for_alt_user(): + domain_id = self.setup_test_domain()['id'] + role_id = self.setup_test_role()['id'] + + # Create a group. + group_name = data_utils.rand_name('Group') + group_id = self.groups_client.create_group( + name=group_name, domain_id=domain_id)['group']['id'] + self.addCleanup(self.groups_client.delete_group, group_id) + + # Add the alt user to the group. + self.groups_client.add_group_user(group_id, alt_user_id) + self.addCleanup(self.groups_client.delete_group_user, + group_id, alt_user_id) + + # Create a role association between the group and domain. + self.roles_client.create_group_role_on_domain( + domain_id, group_id, role_id) + self.addCleanup( + self.roles_client.delete_role_from_group_on_domain, + domain_id, group_id, role_id) + + return domain_id + + # Add the alt user to 2 random domains and 2 random groups + # with randomized domains and roles. + assigned_domain_ids = [] + for _ in range(2): + domain_id = _create_user_domain_role_for_alt_user() + assigned_domain_ids.append(domain_id) + domain_id = _create_group_domain_role_for_alt_user() + assigned_domain_ids.append(domain_id) + + # Get available domain scopes for the alt user. + available_domains = self.os_alt.identity_v3_client.list_auth_domains()[ + 'domains'] + fetched_domain_ids = [i['id'] for i in available_domains] + + # Verify the expected domain IDs are in the list. + missing_domain_ids = \ + [p for p in assigned_domain_ids if p not in fetched_domain_ids] + self.assertEmpty(missing_domain_ids, + "Failed to find domain_ids %s in fetched list" + % ", ".join(missing_domain_ids)) diff --git a/tempest/lib/services/identity/v3/identity_client.py b/tempest/lib/services/identity/v3/identity_client.py index 2512a3edc2..ad770bf9f4 100644 --- a/tempest/lib/services/identity/v3/identity_client.py +++ b/tempest/lib/services/identity/v3/identity_client.py @@ -57,3 +57,10 @@ class IdentityClient(rest_client.RestClient): self.expected_success(200, resp.status) body = json.loads(body) return rest_client.ResponseBody(resp, body) + + def list_auth_domains(self): + """Get available domain scopes.""" + resp, body = self.get("auth/domains") + self.expected_success(200, resp.status) + body = json.loads(body) + return rest_client.ResponseBody(resp, body) diff --git a/tempest/tests/lib/services/identity/v3/test_identity_client.py b/tempest/tests/lib/services/identity/v3/test_identity_client.py index 6572947fe7..3739fe6e20 100644 --- a/tempest/tests/lib/services/identity/v3/test_identity_client.py +++ b/tempest/tests/lib/services/identity/v3/test_identity_client.py @@ -60,6 +60,34 @@ class TestIdentityClient(base.BaseServiceTest): } } + FAKE_AUTH_DOMAINS = { + "domains": [ + { + "description": "my domain description", + "enabled": True, + "id": "1789d1", + "links": { + "self": "https://example.com/identity/v3/domains/1789d1" + }, + "name": "my domain" + }, + { + "description": "description of my other domain", + "enabled": True, + "id": "43e8da", + "links": { + "self": "https://example.com/identity/v3/domains/43e8da" + }, + "name": "another domain" + } + ], + "links": { + "self": "https://example.com/identity/v3/auth/domains", + "previous": None, + "next": None + } + } + def setUp(self): super(TestIdentityClient, self).setUp() fake_auth = fake_auth_provider.FakeAuthProvider() @@ -89,6 +117,13 @@ class TestIdentityClient(base.BaseServiceTest): self.FAKE_AUTH_PROJECTS, bytes_body) + def _test_list_auth_domains(self, bytes_body=False): + self.check_service_client_function( + self.client.list_auth_domains, + 'tempest.lib.common.rest_client.RestClient.get', + self.FAKE_AUTH_DOMAINS, + bytes_body) + def test_show_api_description_with_str_body(self): self._test_show_api_description() @@ -122,3 +157,9 @@ class TestIdentityClient(base.BaseServiceTest): def test_list_auth_projects_with_bytes_body(self): self._test_list_auth_projects(bytes_body=True) + + def test_list_auth_domains_with_str_body(self): + self._test_list_auth_domains() + + def test_list_auth_domains_with_bytes_body(self): + self._test_list_auth_domains(bytes_body=True)