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
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 f1cc530..4e01835 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 @@
         # 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 0611393..b5b1d7b 100644
--- a/tempest/api/identity/admin/v3/test_roles.py
+++ b/tempest/api/identity/admin/v3/test_roles.py
@@ -144,11 +144,11 @@
         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 5cc498f..7358ce9 100644
--- a/tempest/api/identity/admin/v3/test_tokens.py
+++ b/tempest/api/identity/admin/v3/test_tokens.py
@@ -36,7 +36,8 @@
             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 @@
                                      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 @@
 
         # 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 @@
 
         # 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 f29e72a..9d9f61c 100644
--- a/tempest/api/identity/admin/v3/test_users.py
+++ b/tempest/api/identity/admin/v3/test_users.py
@@ -79,7 +79,8 @@
         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 d7f9adb..b82b819 100644
--- a/tempest/auth.py
+++ b/tempest/auth.py
@@ -328,11 +328,17 @@
 
     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 @@
     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 @@
         - 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 @@
         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 b0824a7..3e37403 100644
--- a/tempest/services/identity/v3/json/token_client.py
+++ b/tempest/services/identity/v3/json/token_client.py
@@ -37,22 +37,30 @@
 
         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 @@
             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 @@
 
         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 48f67d2..649d51d 100644
--- a/tempest/tests/fake_credentials.py
+++ b/tempest/tests/fake_credentials.py
@@ -43,7 +43,8 @@
             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)