From 9750461e82ee690a14aa0b33986b9f74fc375814 Mon Sep 17 00:00:00 2001 From: Jamie Lennox Date: Thu, 26 Feb 2015 16:47:06 +1100 Subject: [PATCH] Allow full v3 authentication Allow authenticating with ids and fetching domain scoped tokens. Changes the acceptable parameters to more specific parameter names so that _id and _name parameters can no longer be confused. This tries to not change the config file and existing credential loading paths. Closes-Bug: #1420605 Change-Id: If5a889be5826d60bf61dbb84661a5896cb094875 --- .../admin/v3/test_default_project_id.py | 2 +- tempest/api/identity/admin/v3/test_roles.py | 8 +- tempest/api/identity/admin/v3/test_tokens.py | 13 +-- tempest/api/identity/admin/v3/test_users.py | 3 +- tempest/auth.py | 27 ++++-- .../services/identity/v3/json/token_client.py | 89 +++++++++++++------ tempest/tests/fake_credentials.py | 3 +- 7 files changed, 98 insertions(+), 47 deletions(-) diff --git a/tempest/api/identity/admin/v3/test_default_project_id.py b/tempest/api/identity/admin/v3/test_default_project_id.py index f1cc53063b..4e01835cad 100644 --- a/tempest/api/identity/admin/v3/test_default_project_id.py +++ b/tempest/api/identity/admin/v3/test_default_project_id.py @@ -76,7 +76,7 @@ class TestDefaultProjectId (base.BaseIdentityV3AdminTest): # create a new client with user's credentials (NOTE: unscoped token!) creds = auth.KeystoneV3Credentials(username=user_name, password=user_name, - domain_name=dom_name) + user_domain_name=dom_name) auth_provider = manager.get_auth_provider(creds) creds = auth_provider.fill_credentials() admin_client = clients.Manager(credentials=creds) diff --git a/tempest/api/identity/admin/v3/test_roles.py b/tempest/api/identity/admin/v3/test_roles.py index 0611393e23..b5b1d7b80e 100644 --- a/tempest/api/identity/admin/v3/test_roles.py +++ b/tempest/api/identity/admin/v3/test_roles.py @@ -144,11 +144,11 @@ class RolesV3TestJSON(base.BaseIdentityV3AdminTest): self.client.add_group_user(self.group_body['id'], self.user_body['id']) self.addCleanup(self.client.delete_group_user, self.group_body['id'], self.user_body['id']) - body = self.token.auth(user=self.user_body['id'], + body = self.token.auth(user_id=self.user_body['id'], password=self.u_password, - user_domain=self.domain['name'], - project=self.project['name'], - project_domain=self.domain['name']) + user_domain_name=self.domain['name'], + project_name=self.project['name'], + project_domain_name=self.domain['name']) roles = body['token']['roles'] self.assertEqual(len(roles), 1) self.assertEqual(roles[0]['id'], self.role['id']) diff --git a/tempest/api/identity/admin/v3/test_tokens.py b/tempest/api/identity/admin/v3/test_tokens.py index 5cc498fbe8..7358ce9af4 100644 --- a/tempest/api/identity/admin/v3/test_tokens.py +++ b/tempest/api/identity/admin/v3/test_tokens.py @@ -36,7 +36,8 @@ class TokensV3TestJSON(base.BaseIdentityV3AdminTest): email=u_email) self.addCleanup(self.client.delete_user, user['id']) # Perform Authentication - resp = self.token.auth(user['id'], u_password).response + resp = self.token.auth(user_id=user['id'], + password=u_password).response subject_token = resp['x-subject-token'] # Perform GET Token token_details = self.client.get_token(subject_token) @@ -87,7 +88,7 @@ class TokensV3TestJSON(base.BaseIdentityV3AdminTest): role['id']) # Get an unscoped token. - token_auth = self.token.auth(user=user['id'], + token_auth = self.token.auth(user_id=user['id'], password=user_password) token_id = token_auth.response['x-subject-token'] @@ -110,8 +111,8 @@ class TokensV3TestJSON(base.BaseIdentityV3AdminTest): # Use the unscoped token to get a scoped token. token_auth = self.token.auth(token=token_id, - project=project1_name, - project_domain='Default') + project_name=project1_name, + project_domain_name='Default') token1_id = token_auth.response['x-subject-token'] self.assertEqual(orig_expires_at, token_auth['token']['expires_at'], @@ -140,8 +141,8 @@ class TokensV3TestJSON(base.BaseIdentityV3AdminTest): # Now get another scoped token using the unscoped token. token_auth = self.token.auth(token=token_id, - project=project2_name, - project_domain='Default') + project_name=project2_name, + project_domain_name='Default') self.assertEqual(project2['id'], token_auth['token']['project']['id']) diff --git a/tempest/api/identity/admin/v3/test_users.py b/tempest/api/identity/admin/v3/test_users.py index f29e72ac18..9d9f61c86c 100644 --- a/tempest/api/identity/admin/v3/test_users.py +++ b/tempest/api/identity/admin/v3/test_users.py @@ -79,7 +79,8 @@ class UsersV3TestJSON(base.BaseIdentityV3AdminTest): new_password = data_utils.rand_name('pass1') self.client.update_user_password(user['id'], new_password, original_password) - resp = self.token.auth(user['id'], new_password).response + resp = self.token.auth(user_id=user['id'], + password=new_password).response subject_token = resp['x-subject-token'] # Perform GET Token to verify and confirm password is updated token_details = self.client.get_token(subject_token) diff --git a/tempest/auth.py b/tempest/auth.py index d7f9adbb35..b82b819fc6 100644 --- a/tempest/auth.py +++ b/tempest/auth.py @@ -328,11 +328,17 @@ class KeystoneV3AuthProvider(KeystoneAuthProvider): def _auth_params(self): return dict( - user=self.credentials.username, + user_id=self.credentials.user_id, + username=self.credentials.username, password=self.credentials.password, - project=self.credentials.tenant_name, - user_domain=self.credentials.user_domain_name, - project_domain=self.credentials.project_domain_name, + project_id=self.credentials.project_id, + project_name=self.credentials.project_name, + user_domain_id=self.credentials.user_domain_id, + user_domain_name=self.credentials.user_domain_name, + project_domain_id=self.credentials.project_domain_id, + project_domain_name=self.credentials.project_domain_name, + domain_id=self.credentials.domain_id, + domain_name=self.credentials.domain_name, auth_data=True) def _fill_credentials(self, auth_data_body): @@ -569,7 +575,7 @@ class KeystoneV3Credentials(Credentials): Credentials suitable for the Keystone Identity V3 API """ - ATTRIBUTES = ['domain_name', 'password', 'tenant_name', 'username', + ATTRIBUTES = ['domain_id', 'domain_name', 'password', 'username', 'project_domain_id', 'project_domain_name', 'project_id', 'project_name', 'tenant_id', 'tenant_name', 'user_domain_id', 'user_domain_name', 'user_id'] @@ -615,6 +621,8 @@ class KeystoneV3Credentials(Credentials): - None - Project id (optional domain) - Project name and its domain id/name + - Domain id + - Domain name """ valid_user_domain = any( [self.user_domain_id is not None, @@ -625,11 +633,16 @@ class KeystoneV3Credentials(Credentials): valid_user = any( [self.user_id is not None, self.username is not None and valid_user_domain]) - valid_project = any( + valid_project_scope = any( [self.project_name is None and self.project_id is None, self.project_id is not None, self.project_name is not None and valid_project_domain]) - return all([self.password is not None, valid_user, valid_project]) + valid_domain_scope = any( + [self.domain_id is None and self.domain_name is None, + self.domain_id or self.domain_name]) + return all([self.password is not None, + valid_user, + valid_project_scope and valid_domain_scope]) IDENTITY_VERSION = {'v2': (KeystoneV2Credentials, KeystoneV2AuthProvider), diff --git a/tempest/services/identity/v3/json/token_client.py b/tempest/services/identity/v3/json/token_client.py index b0824a7ded..3e3740361b 100644 --- a/tempest/services/identity/v3/json/token_client.py +++ b/tempest/services/identity/v3/json/token_client.py @@ -37,22 +37,30 @@ class V3TokenClientJSON(rest_client.RestClient): self.auth_url = auth_url - def auth(self, user=None, password=None, project=None, user_type='id', - user_domain=None, project_domain=None, token=None): + def auth(self, user_id=None, username=None, password=None, project_id=None, + project_name=None, user_domain_id=None, user_domain_name=None, + project_domain_id=None, project_domain_name=None, domain_id=None, + domain_name=None, token=None): """ - :param user: user id or name, as specified in user_type - :param user_domain: the user domain - :param project_domain: the project domain + :param user_id: user id + :param username: user name + :param user_domain_id: the user domain id + :param user_domain_name: the user domain name + :param project_domain_id: the project domain id + :param project_domain_name: the project domain name + :param domain_id: a domain id to scope to + :param domain_name: a domain name to scope to + :param project_id: a project id to scope to + :param project_name: a project name to scope to :param token: a token to re-scope. - Accepts different combinations of credentials. Restrictions: - - project and domain are only name (no id) + Accepts different combinations of credentials. Sample sample valid combinations: - token - - token, project, project_domain + - token, project_name, project_domain_id - user_id, password - - username, password, user_domain - - username, password, project, user_domain, project_domain + - username, password, user_domain_id + - username, password, project_name, user_domain_id, project_domain_id Validation is left to the server side. """ creds = { @@ -68,25 +76,45 @@ class V3TokenClientJSON(rest_client.RestClient): id_obj['token'] = { 'id': token } - if user and password: + + if (user_id or username) and password: id_obj['methods'].append('password') id_obj['password'] = { 'user': { 'password': password, } } - if user_type == 'id': - id_obj['password']['user']['id'] = user + if user_id: + id_obj['password']['user']['id'] = user_id else: - id_obj['password']['user']['name'] = user - if user_domain is not None: - _domain = dict(name=user_domain) + id_obj['password']['user']['name'] = username + + _domain = None + if user_domain_id is not None: + _domain = dict(id=user_domain_id) + elif user_domain_name is not None: + _domain = dict(name=user_domain_name) + if _domain: id_obj['password']['user']['domain'] = _domain - if project is not None: - _domain = dict(name=project_domain) - _project = dict(name=project, domain=_domain) - scope = dict(project=_project) - creds['auth']['scope'] = scope + + if (project_id or project_name): + _project = dict() + + if project_id: + _project['id'] = project_id + elif project_name: + _project['name'] = project_name + + if project_domain_id is not None: + _project['domain'] = {'id': project_domain_id} + elif project_domain_name is not None: + _project['domain'] = {'name': project_domain_name} + + creds['auth']['scope'] = dict(project=_project) + elif domain_id: + creds['auth']['scope'] = dict(domain={'id': domain_id}) + elif domain_name: + creds['auth']['scope'] = dict(domain={'name': domain_name}) body = json.dumps(creds) resp, body = self.post(self.auth_url, body=body) @@ -120,15 +148,22 @@ class V3TokenClientJSON(rest_client.RestClient): return resp, json.loads(resp_body) - def get_token(self, user, password, project=None, project_domain='Default', - user_domain='Default', auth_data=False): + def get_token(self, **kwargs): """ - :param user: username Returns (token id, token data) for supplied credentials """ - body = self.auth(user, password, project, user_type='name', - user_domain=user_domain, - project_domain=project_domain) + + auth_data = kwargs.pop('auth_data', False) + + if not (kwargs.get('user_domain_id') or + kwargs.get('user_domain_name')): + kwargs['user_domain_name'] = 'Default' + + if not (kwargs.get('project_domain_id') or + kwargs.get('project_domain_name')): + kwargs['project_domain_name'] = 'Default' + + body = self.auth(**kwargs) token = body.response.get('x-subject-token') if auth_data: diff --git a/tempest/tests/fake_credentials.py b/tempest/tests/fake_credentials.py index 48f67d2429..649d51df6c 100644 --- a/tempest/tests/fake_credentials.py +++ b/tempest/tests/fake_credentials.py @@ -43,7 +43,8 @@ class FakeKeystoneV3Credentials(auth.KeystoneV3Credentials): username='fake_username', password='fake_password', user_domain_name='fake_domain_name', - project_name='fake_tenant_name' + project_name='fake_tenant_name', + project_domain_name='fake_domain_name' ) super(FakeKeystoneV3Credentials, self).__init__(**creds)