blob: 3f10deed084c85ba9bfbede43e37931388345b02 [file] [log] [blame]
Jamie Lennox15350172015-08-17 10:54:25 +10001# Licensed under the Apache License, Version 2.0 (the "License"); you may
2# not use this file except in compliance with the License. You may obtain
3# a copy of the License at
4#
5# http://www.apache.org/licenses/LICENSE-2.0
6#
7# Unless required by applicable law or agreed to in writing, software
8# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10# License for the specific language governing permissions and limitations
11# under the License.
12
13import abc
14
15from oslo_log import log as logging
16import six
Jamie Lennox15350172015-08-17 10:54:25 +100017
Andrea Frittoli (andreaf)db9672e2016-02-23 14:07:24 -050018from tempest.lib import auth
19from tempest.lib import exceptions as lib_exc
Martin Kopec0cb35532016-09-02 08:32:41 +000020from tempest.lib.services.identity.v2 import identity_client as v2_identity
Jamie Lennox15350172015-08-17 10:54:25 +100021
Jamie Lennox15350172015-08-17 10:54:25 +100022LOG = logging.getLogger(__name__)
23
24
25@six.add_metaclass(abc.ABCMeta)
26class CredsClient(object):
Ken'ichi Ohmichicb67d2d2015-11-19 08:23:22 +000027 """This class is a wrapper around the identity clients
28
29 to provide a single interface for managing credentials in both v2 and v3
30 cases. It's not bound to created credentials, only to a specific set of
31 admin credentials used for generating credentials.
Jamie Lennox15350172015-08-17 10:54:25 +100032 """
33
Daniel Mellado7aea5342016-02-09 09:10:12 +000034 def __init__(self, identity_client, projects_client, users_client,
Arx Cruz24bcb882016-02-10 15:20:16 +010035 roles_client):
Jamie Lennox15350172015-08-17 10:54:25 +100036 # The client implies version and credentials
37 self.identity_client = identity_client
Daniel Mellado7aea5342016-02-09 09:10:12 +000038 self.users_client = users_client
Yaroslav Lobankov47a93ab2016-02-07 16:32:49 -060039 self.projects_client = projects_client
Arx Cruz24bcb882016-02-10 15:20:16 +010040 self.roles_client = roles_client
Jamie Lennox15350172015-08-17 10:54:25 +100041
42 def create_user(self, username, password, project, email):
ghanshyam7f817db2016-08-01 18:37:13 +090043 params = {'name': username,
44 'password': password,
45 self.project_id_param: project['id'],
46 'email': email}
ghanshyame1c6c1c2016-06-15 14:50:41 +090047 user = self.users_client.create_user(**params)
Jamie Lennox15350172015-08-17 10:54:25 +100048 if 'user' in user:
49 user = user['user']
50 return user
51
Yaroslav Lobankov47a93ab2016-02-07 16:32:49 -060052 def delete_user(self, user_id):
53 self.users_client.delete_user(user_id)
54
Jamie Lennox15350172015-08-17 10:54:25 +100055 @abc.abstractmethod
56 def create_project(self, name, description):
57 pass
58
59 def _check_role_exists(self, role_name):
60 try:
61 roles = self._list_roles()
62 role = next(r for r in roles if r['name'] == role_name)
63 except StopIteration:
64 return None
65 return role
66
67 def create_user_role(self, role_name):
68 if not self._check_role_exists(role_name):
piyush110786afaaf262015-12-11 18:54:05 +053069 self.roles_client.create_role(name=role_name)
Jamie Lennox15350172015-08-17 10:54:25 +100070
71 def assign_user_role(self, user, project, role_name):
72 role = self._check_role_exists(role_name)
73 if not role:
74 msg = 'No "%s" role found' % role_name
75 raise lib_exc.NotFound(msg)
76 try:
ghanshyam2e6fb562016-09-06 11:14:31 +090077 self.roles_client.create_user_role_on_project(project['id'],
78 user['id'],
79 role['id'])
Jamie Lennox15350172015-08-17 10:54:25 +100080 except lib_exc.Conflict:
81 LOG.debug("Role %s already assigned on project %s for user %s" % (
82 role['id'], project['id'], user['id']))
83
84 @abc.abstractmethod
85 def get_credentials(self, user, project, password):
Andrea Frittoli (andreaf)278463c2015-10-08 15:04:09 +010086 """Produces a Credentials object from the details provided
87
88 :param user: a user dict
89 :param project: a project dict
90 :param password: the password as a string
91 :return: a Credentials object with all the available credential details
92 """
Jamie Lennox15350172015-08-17 10:54:25 +100093 pass
94
Jamie Lennox15350172015-08-17 10:54:25 +100095 def _list_roles(self):
Daniel Mellado6b16b922015-12-07 12:43:08 +000096 roles = self.roles_client.list_roles()['roles']
Jamie Lennox15350172015-08-17 10:54:25 +100097 return roles
98
99
100class V2CredsClient(CredsClient):
ghanshyam7f817db2016-08-01 18:37:13 +0900101 project_id_param = 'tenantId'
Jamie Lennox15350172015-08-17 10:54:25 +1000102
Daniel Mellado7aea5342016-02-09 09:10:12 +0000103 def __init__(self, identity_client, projects_client, users_client,
104 roles_client):
Daniel Mellado6b16b922015-12-07 12:43:08 +0000105 super(V2CredsClient, self).__init__(identity_client,
106 projects_client,
Daniel Mellado7aea5342016-02-09 09:10:12 +0000107 users_client,
108 roles_client)
Daniel Melladob04da902015-11-20 17:43:12 +0100109
Jamie Lennox15350172015-08-17 10:54:25 +1000110 def create_project(self, name, description):
Daniel Melladob04da902015-11-20 17:43:12 +0100111 tenant = self.projects_client.create_tenant(
Anusha Ramineni0cfb4612015-08-24 08:49:10 +0530112 name=name, description=description)['tenant']
Jamie Lennox15350172015-08-17 10:54:25 +1000113 return tenant
114
Yaroslav Lobankov47a93ab2016-02-07 16:32:49 -0600115 def delete_project(self, project_id):
116 self.projects_client.delete_tenant(project_id)
117
Jamie Lennox15350172015-08-17 10:54:25 +1000118 def get_credentials(self, user, project, password):
Andrea Frittoli (andreaf)278463c2015-10-08 15:04:09 +0100119 # User and project already include both ID and name here,
120 # so there's no need to use the fill_in mode
121 return auth.get_credentials(
122 auth_url=None,
123 fill_in=False,
Jamie Lennox15350172015-08-17 10:54:25 +1000124 identity_version='v2',
125 username=user['name'], user_id=user['id'],
126 tenant_name=project['name'], tenant_id=project['id'],
127 password=password)
128
Jamie Lennox15350172015-08-17 10:54:25 +1000129
130class V3CredsClient(CredsClient):
ghanshyam7f817db2016-08-01 18:37:13 +0900131 project_id_param = 'project_id'
Jamie Lennox15350172015-08-17 10:54:25 +1000132
Daniel Mellado7aea5342016-02-09 09:10:12 +0000133 def __init__(self, identity_client, projects_client, users_client,
Arx Cruz24bcb882016-02-10 15:20:16 +0100134 roles_client, domains_client, domain_name):
Daniel Mellado91a26b62016-02-11 11:13:04 +0000135 super(V3CredsClient, self).__init__(identity_client, projects_client,
Arx Cruz24bcb882016-02-10 15:20:16 +0100136 users_client, roles_client)
Daniel Mellado91a26b62016-02-11 11:13:04 +0000137 self.domains_client = domains_client
138
Jamie Lennox15350172015-08-17 10:54:25 +1000139 try:
140 # Domain names must be unique, in any case a list is returned,
141 # selecting the first (and only) element
Daniel Mellado91a26b62016-02-11 11:13:04 +0000142 self.creds_domain = self.domains_client.list_domains(
ghanshyam8af17d62016-08-01 16:19:42 +0900143 name=domain_name)['domains'][0]
Jamie Lennox15350172015-08-17 10:54:25 +1000144 except lib_exc.NotFound:
145 # TODO(andrea) we could probably create the domain on the fly
Andrea Frittoli (andreaf)278463c2015-10-08 15:04:09 +0100146 msg = "Requested domain %s could not be found" % domain_name
147 raise lib_exc.InvalidCredentials(msg)
Jamie Lennox15350172015-08-17 10:54:25 +1000148
149 def create_project(self, name, description):
Yaroslav Lobankov47a93ab2016-02-07 16:32:49 -0600150 project = self.projects_client.create_project(
Jamie Lennox15350172015-08-17 10:54:25 +1000151 name=name, description=description,
152 domain_id=self.creds_domain['id'])['project']
153 return project
154
Yaroslav Lobankov47a93ab2016-02-07 16:32:49 -0600155 def delete_project(self, project_id):
156 self.projects_client.delete_project(project_id)
157
Jamie Lennox15350172015-08-17 10:54:25 +1000158 def get_credentials(self, user, project, password):
Andrea Frittoli (andreaf)278463c2015-10-08 15:04:09 +0100159 # User, project and domain already include both ID and name here,
160 # so there's no need to use the fill_in mode.
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100161 # NOTE(andreaf) We need to set all fields in the returned credentials.
162 # Scope is then used to pick only those relevant for the type of
163 # token needed by each service client.
Andrea Frittoli (andreaf)278463c2015-10-08 15:04:09 +0100164 return auth.get_credentials(
165 auth_url=None,
166 fill_in=False,
Jamie Lennox15350172015-08-17 10:54:25 +1000167 identity_version='v3',
168 username=user['name'], user_id=user['id'],
169 project_name=project['name'], project_id=project['id'],
170 password=password,
Andrea Frittoli (andreaf)278463c2015-10-08 15:04:09 +0100171 project_domain_id=self.creds_domain['id'],
Andrea Frittoli (andreaf)bd06f982016-06-02 17:26:51 +0100172 project_domain_name=self.creds_domain['name'],
173 domain_id=self.creds_domain['id'],
174 domain_name=self.creds_domain['name'])
Jamie Lennox15350172015-08-17 10:54:25 +1000175
Andrea Frittoli (andreaf)4bee2e72015-09-22 13:06:18 +0100176 def assign_user_role_on_domain(self, user, role_name, domain=None):
177 """Assign the specified role on a domain
178
179 :param user: a user dict
180 :param role_name: name of the role to be assigned
181 :param domain: (optional) The domain to assign the role on. If not
182 specified the default domain of cred_client
183 """
184 # NOTE(andreaf) This method is very specific to the v3 case, and
185 # because of that it's not defined in the parent class.
186 if domain is None:
187 domain = self.creds_domain
188 role = self._check_role_exists(role_name)
189 if not role:
190 msg = 'No "%s" role found' % role_name
191 raise lib_exc.NotFound(msg)
192 try:
ghanshyam2e6fb562016-09-06 11:14:31 +0900193 self.roles_client.create_user_role_on_domain(
Andrea Frittoli (andreaf)4bee2e72015-09-22 13:06:18 +0100194 domain['id'], user['id'], role['id'])
195 except lib_exc.Conflict:
196 LOG.debug("Role %s already assigned on domain %s for user %s",
197 role['id'], domain['id'], user['id'])
198
Jamie Lennox15350172015-08-17 10:54:25 +1000199
Daniel Melladob04da902015-11-20 17:43:12 +0100200def get_creds_client(identity_client,
Yaroslav Lobankov47a93ab2016-02-07 16:32:49 -0600201 projects_client,
Daniel Mellado7aea5342016-02-09 09:10:12 +0000202 users_client,
Arx Cruz24bcb882016-02-10 15:20:16 +0100203 roles_client,
Daniel Mellado91a26b62016-02-11 11:13:04 +0000204 domains_client=None,
Daniel Melladob04da902015-11-20 17:43:12 +0100205 project_domain_name=None):
Jamie Lennox15350172015-08-17 10:54:25 +1000206 if isinstance(identity_client, v2_identity.IdentityClient):
Daniel Mellado7aea5342016-02-09 09:10:12 +0000207 return V2CredsClient(identity_client, projects_client, users_client,
208 roles_client)
Jamie Lennox15350172015-08-17 10:54:25 +1000209 else:
Daniel Mellado7aea5342016-02-09 09:10:12 +0000210 return V3CredsClient(identity_client, projects_client, users_client,
Arx Cruz24bcb882016-02-10 15:20:16 +0100211 roles_client, domains_client, project_domain_name)