blob: a587b7263d78748bc30dee36b5d29a49b5486d81 [file] [log] [blame]
DavidPurcellb25f93d2017-01-27 12:46:27 -05001# Copyright 2017 AT&T Corporation.
DavidPurcell029d8c32017-01-06 15:27:41 -05002# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
Sergey Vilgelmbab9e942018-10-11 14:04:48 -050016import contextlib
Mykola Yakovliev11376ab2018-08-06 15:34:22 -050017import sys
DavidPurcell029d8c32017-01-06 15:27:41 -050018import time
Felipe Monteiro34a138c2017-03-02 17:01:37 -050019
Rajiv Kumar645dfc92017-01-19 13:48:27 +053020from oslo_log import log as logging
Felipe Monteiro2693bf72017-08-12 22:56:47 +010021from oslo_utils import excutils
Felipe Monteirofa01d5f2017-04-01 06:18:25 +010022
Felipe Monteiro3e14f472017-08-17 23:02:11 +010023from tempest import clients
Felipe Monteiroe7e552e2017-05-02 17:04:12 +010024from tempest.common import credentials_factory as credentials
Felipe Monteirofa01d5f2017-04-01 06:18:25 +010025from tempest import config
Felipe Monteirobf524fb2018-10-03 09:03:35 -050026from tempest.lib import exceptions as lib_exc
DavidPurcell029d8c32017-01-06 15:27:41 -050027
Felipe Monteiro34a138c2017-03-02 17:01:37 -050028from patrole_tempest_plugin import rbac_exceptions
DavidPurcell029d8c32017-01-06 15:27:41 -050029
DavidPurcell029d8c32017-01-06 15:27:41 -050030CONF = config.CONF
Felipe Monteiro34a138c2017-03-02 17:01:37 -050031LOG = logging.getLogger(__name__)
DavidPurcell029d8c32017-01-06 15:27:41 -050032
33
Sergey Vilgelmbab9e942018-10-11 14:04:48 -050034class _ValidateListContext(object):
35 """Context class responsible for validation of the list functions.
36
37 This class is used in ``override_role_and_validate_list`` function and
38 the result of a list function must be assigned to the ``ctx.resources``
39 variable.
40
41 Example::
42
43 with self.rbac_utils.override_role_and_validate_list(...) as ctx:
44 ctx.resources = list_function()
45
46 """
47 def __init__(self, admin_resources=None, admin_resource_id=None):
48 """Constructor for ``ValidateListContext``.
49
50 Either ``admin_resources`` or ``admin_resource_id`` should be used,
51 not both.
52
53 :param list admin_resources: The list of resources received before
54 calling the ``override_role_and_validate_list`` function. To
55 validate will be used the ``_validate_len`` function.
56 :param UUID admin_resource_id: An ID of a resource created before
57 calling the ``override_role_and_validate_list`` function. To
58 validate will be used the ``_validate_resource`` function.
59 :raises RbacValidateListException: if both ``admin_resources`` and
60 ``admin_resource_id`` are set or unset.
61 """
62 self.resources = None
63 if admin_resources is not None and not admin_resource_id:
64 self._admin_len = len(admin_resources)
65 if not self._admin_len:
66 raise rbac_exceptions.RbacValidateListException(
67 reason="the list of admin resources cannot be empty")
68 self._validate_func = self._validate_len
69 elif admin_resource_id and admin_resources is None:
70 self._admin_resource_id = admin_resource_id
71 self._validate_func = self._validate_resource
72 else:
73 raise rbac_exceptions.RbacValidateListException(
74 reason="admin_resources and admin_resource_id are mutually "
75 "exclusive")
76
77 def _validate_len(self):
78 """Validates that the number of resources is less than admin resources.
79 """
80 if not len(self.resources):
81 raise rbac_exceptions.RbacEmptyResponseBody()
82 elif self._admin_len > len(self.resources):
83 raise rbac_exceptions.RbacPartialResponseBody(body=self.resources)
84
85 def _validate_resource(self):
86 """Validates that the admin resource is present in the resources.
87 """
88 for resource in self.resources:
89 if resource['id'] == self._admin_resource_id:
90 return
91 raise rbac_exceptions.RbacPartialResponseBody(body=self.resources)
92
93 def _validate(self):
94 """Calls the proper validation function.
95
96 :raises RbacValidateListException: if the ``ctx.resources`` variable is
97 not assigned.
98 """
99 if self.resources is None:
100 raise rbac_exceptions.RbacValidateListException(
101 reason="ctx.resources is not assigned")
102 self._validate_func()
103
104
DavidPurcell029d8c32017-01-06 15:27:41 -0500105class RbacUtils(object):
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000106 """Utility class responsible for switching ``os_primary`` role.
Felipe Monteiroe8d93e02017-07-19 20:52:20 +0100107
108 This class is responsible for overriding the value of the primary Tempest
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000109 credential's role (i.e. ``os_primary`` role). By doing so, it is possible
110 to seamlessly swap between admin credentials, needed for setup and clean
111 up, and primary credentials, needed to perform the API call which does
Felipe Monteiroe8d93e02017-07-19 20:52:20 +0100112 policy enforcement. The primary credentials always cycle between roles
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000113 defined by ``CONF.identity.admin_role`` and
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500114 ``CONF.patrole.rbac_test_roles``.
Felipe Monteiroe8d93e02017-07-19 20:52:20 +0100115 """
DavidPurcell029d8c32017-01-06 15:27:41 -0500116
Felipe Monteirob35de582017-05-05 00:16:53 +0100117 def __init__(self, test_obj):
Felipe Monteiroe8d93e02017-07-19 20:52:20 +0100118 """Constructor for ``RbacUtils``.
119
Felipe Monteiro2693bf72017-08-12 22:56:47 +0100120 :param test_obj: An instance of `tempest.test.BaseTestCase`.
Felipe Monteiroe8d93e02017-07-19 20:52:20 +0100121 """
Sergey Vilgelm19e3bec2019-01-07 11:59:41 -0600122 self.admin_role_id = None
123 self.rbac_role_ids = None
124 self._role_map = None
125
Felipe Monteiroe8d93e02017-07-19 20:52:20 +0100126 # Intialize the admin roles_client to perform role switching.
Felipe Monteiro3e14f472017-08-17 23:02:11 +0100127 admin_mgr = clients.Manager(
128 credentials.get_configured_admin_credentials())
Felipe Monteirobf524fb2018-10-03 09:03:35 -0500129 if CONF.identity_feature_enabled.api_v3:
Felipe Monteiro3e14f472017-08-17 23:02:11 +0100130 admin_roles_client = admin_mgr.roles_v3_client
Felipe Monteiroe8d93e02017-07-19 20:52:20 +0100131 else:
Felipe Monteirobf524fb2018-10-03 09:03:35 -0500132 raise lib_exc.InvalidConfiguration(
133 "Patrole role overriding only supports v3 identity API.")
Felipe Monteiroe8d93e02017-07-19 20:52:20 +0100134
135 self.admin_roles_client = admin_roles_client
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500136
137 self.user_id = test_obj.os_primary.credentials.user_id
138 self.project_id = test_obj.os_primary.credentials.tenant_id
Sergey Vilgelm19e3bec2019-01-07 11:59:41 -0600139 self._role_inferences_mapping = self._prepare_role_inferences_mapping()
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500140
141 # Change default role to admin
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000142 self._override_role(test_obj, False)
Felipe Monteirob35de582017-05-05 00:16:53 +0100143
Sergey Vilgelm19e3bec2019-01-07 11:59:41 -0600144 def _prepare_role_inferences_mapping(self):
145 """Preparing roles mapping to support role inferences
146
147 Making query to `list-all-role-inference-rules`_ keystone API
148 returns all inference rules, which makes it possible to prepare
149 roles mapping.
150
151 It walks recursively through the raw data::
152
153 {"role_inferences": [
154 {
155 "implies": [{"id": "3", "name": "reader"}],
156 "prior_role": {"id": "2", "name": "member"}
157 },
158 {
159 "implies": [{"id": "2", "name": "member"}],
160 "prior_role": {"id": "1", "name": "admin"}
161 }
162 ]
163 }
164
165 and converts it to the mapping::
166
167 {
168 "2": ["3"], # "member": ["reader"],
169 "1": ["2", "3"] # "admin": ["member", "reader"]
170 }
171
172 .. _list-all-role-inference-rules: https://developer.openstack.org/api-ref/identity/v3/#list-all-role-inference-rules
173 """ # noqa: E501
174 def process_roles(role_id, data):
175 roles = data.get(role_id, set())
176 for rid in roles.copy():
177 roles.update(process_roles(rid, data))
178
179 return roles
180
181 def convert_data(data):
182 res = {}
183 for rule in data:
184 prior_role = rule['prior_role']['id']
185 implies = {r['id'] for r in rule['implies']}
186 res[prior_role] = implies
187 return res
188
189 raw_data = self.admin_roles_client.list_all_role_inference_rules()
190 data = convert_data(raw_data['role_inferences'])
191 res = {}
192 for role_id in data:
193 res[role_id] = process_roles(role_id, data)
194 return res
195
196 def get_all_needed_roles(self, roles):
197 """Extending given roles with roles from mapping
198
199 Examples::
200 ["admin"] >> ["admin", "member", "reader"]
201 ["member"] >> ["member", "reader"]
202 ["reader"] >> ["reader"]
203 ["custom_role"] >> ["custom_role"]
204
205 :param roles: list of roles
206 :return: extended list of roles
207 """
208 res = set(r for r in roles)
209 for role in res.copy():
210 role_id = self._role_map.get(role)
211 implied_roles = self._role_inferences_mapping.get(role_id, set())
212 role_names = {self._role_map[rid] for rid in implied_roles}
213 res.update(role_names)
214 LOG.debug('All needed roles: %s; Base roles: %s', res, roles)
215 return list(res)
DavidPurcell029d8c32017-01-06 15:27:41 -0500216
Sergey Vilgelmbab9e942018-10-11 14:04:48 -0500217 @contextlib.contextmanager
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000218 def override_role(self, test_obj):
219 """Override the role used by ``os_primary`` Tempest credentials.
220
221 Temporarily change the role used by ``os_primary`` credentials to:
Masayuki Igawa80b9aab2018-01-09 17:00:45 +0900222
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500223 * ``[patrole] rbac_test_roles`` before test execution
Masayuki Igawa80b9aab2018-01-09 17:00:45 +0900224 * ``[identity] admin_role`` after test execution
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000225
226 Automatically switches to admin role after test execution.
227
228 :param test_obj: Instance of ``tempest.test.BaseTestCase``.
229 :returns: None
230
231 .. warning::
232
233 This function can alter user roles for pre-provisioned credentials.
234 Work is underway to safely clean up after this function.
235
236 Example::
237
238 @rbac_rule_validation.action(service='test',
Felipe Monteiro59f538f2018-08-22 23:34:40 -0400239 rules=['a:test:rule'])
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000240 def test_foo(self):
241 # Allocate test-level resources here.
242 with self.rbac_utils.override_role(self):
melissaml7cd21612018-05-23 21:00:50 +0800243 # The role for `os_primary` has now been overridden. Within
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000244 # this block, call the API endpoint that enforces the
245 # expected policy specified by "rule" in the decorator.
246 self.foo_service.bar_api_call()
247 # The role is switched back to admin automatically. Note that
248 # if the API call above threw an exception, any code below this
249 # point in the test is not executed.
250 """
Mykola Yakovliev11376ab2018-08-06 15:34:22 -0500251 test_obj._set_override_role_called()
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000252 self._override_role(test_obj, True)
253 try:
254 # Execute the test.
255 yield
256 finally:
Mykola Yakovliev11376ab2018-08-06 15:34:22 -0500257 # Check whether an exception was raised. If so, remember that
258 # for future validation.
259 exc = sys.exc_info()[0]
260 if exc is not None:
261 test_obj._set_override_role_caught_exc()
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000262 # This code block is always executed, no matter the result of the
263 # test. Automatically switch back to the admin role for test clean
264 # up.
265 self._override_role(test_obj, False)
266
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000267 def _override_role(self, test_obj, toggle_rbac_role=False):
268 """Private helper for overriding ``os_primary`` Tempest credentials.
269
Felipe Monteiro07a1c172017-12-10 04:26:08 +0000270 :param test_obj: instance of :py:class:`tempest.test.BaseTestCase`
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000271 :param toggle_rbac_role: Boolean value that controls the role that
272 overrides default role of ``os_primary`` credentials.
273 * If True: role is set to ``[patrole] rbac_test_role``
274 * If False: role is set to ``[identity] admin_role``
275 """
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000276 LOG.debug('Overriding role to: %s.', toggle_rbac_role)
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500277 roles_already_present = False
Felipe Monteiro2693bf72017-08-12 22:56:47 +0100278
DavidPurcell029d8c32017-01-06 15:27:41 -0500279 try:
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500280 if not all([self.admin_role_id, self.rbac_role_ids]):
Felipe Monteiro2693bf72017-08-12 22:56:47 +0100281 self._get_roles_by_name()
DavidPurcell029d8c32017-01-06 15:27:41 -0500282
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500283 target_roles = (self.rbac_role_ids
284 if toggle_rbac_role else [self.admin_role_id])
285 roles_already_present = self._list_and_clear_user_roles_on_project(
286 target_roles)
Felipe Monteiro2693bf72017-08-12 22:56:47 +0100287
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000288 # Do not override roles if `target_role` already exists.
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500289 if not roles_already_present:
290 self._create_user_role_on_project(target_roles)
DavidPurcell029d8c32017-01-06 15:27:41 -0500291 except Exception as exp:
Felipe Monteiro2693bf72017-08-12 22:56:47 +0100292 with excutils.save_and_reraise_exception():
293 LOG.exception(exp)
Felipe Monteiro34a138c2017-03-02 17:01:37 -0500294 finally:
Mykola Yakovliev1d829782018-08-03 14:37:37 -0500295 auth_providers = test_obj.get_auth_providers()
296 for provider in auth_providers:
297 provider.clear_auth()
Felipe Monteiro7be94e82017-07-26 02:17:08 +0100298 # Fernet tokens are not subsecond aware so sleep to ensure we are
299 # passing the second boundary before attempting to authenticate.
Felipe Monteiro2693bf72017-08-12 22:56:47 +0100300 # Only sleep if a token revocation occurred as a result of role
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000301 # overriding. This will optimize test runtime in the case where
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500302 # ``[identity] admin_role`` == ``[patrole] rbac_test_roles``.
303 if not roles_already_present:
Rick Bartra89f498f2017-03-20 15:54:45 -0400304 time.sleep(1)
Mykola Yakovliev1d829782018-08-03 14:37:37 -0500305
306 for provider in auth_providers:
307 provider.set_auth()
Felipe Monteiro34a138c2017-03-02 17:01:37 -0500308
Felipe Monteiro2693bf72017-08-12 22:56:47 +0100309 def _get_roles_by_name(self):
Felipe Monteiro2fc29292018-06-15 18:26:27 -0400310 available_roles = self.admin_roles_client.list_roles()['roles']
Sergey Vilgelm19e3bec2019-01-07 11:59:41 -0600311 self._role_map = {r['name']: r['id'] for r in available_roles}
312 LOG.debug('Available roles: %s', list(self._role_map.keys()))
Felipe Monteirofa01d5f2017-04-01 06:18:25 +0100313
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500314 rbac_role_ids = []
315 roles = CONF.patrole.rbac_test_roles
316 # TODO(vegasq) drop once CONF.patrole.rbac_test_role is removed
317 if CONF.patrole.rbac_test_role:
318 if not roles:
319 roles.append(CONF.patrole.rbac_test_role)
Felipe Monteiro2693bf72017-08-12 22:56:47 +0100320
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500321 for role_name in roles:
Sergey Vilgelm19e3bec2019-01-07 11:59:41 -0600322 rbac_role_ids.append(self._role_map.get(role_name))
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500323
Sergey Vilgelm19e3bec2019-01-07 11:59:41 -0600324 admin_role_id = self._role_map.get(CONF.identity.admin_role)
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500325
326 if not all([admin_role_id, all(rbac_role_ids)]):
Felipe Monteiro2fc29292018-06-15 18:26:27 -0400327 missing_roles = []
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500328 msg = ("Could not find `[patrole] rbac_test_roles` or "
Felipe Monteiro2fc29292018-06-15 18:26:27 -0400329 "`[identity] admin_role`, both of which are required for "
330 "RBAC testing.")
331 if not admin_role_id:
332 missing_roles.append(CONF.identity.admin_role)
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500333 if not all(rbac_role_ids):
334 missing_roles += [role_name for role_name in roles
Sergey Vilgelm19e3bec2019-01-07 11:59:41 -0600335 if not self._role_map.get(role_name)]
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500336
Felipe Monteiro2fc29292018-06-15 18:26:27 -0400337 msg += " Following roles were not found: %s." % (
338 ", ".join(missing_roles))
Sergey Vilgelm19e3bec2019-01-07 11:59:41 -0600339 msg += " Available roles: %s." % ", ".join(list(
340 self._role_map.keys()))
Felipe Monteiro2693bf72017-08-12 22:56:47 +0100341 raise rbac_exceptions.RbacResourceSetupFailed(msg)
342
343 self.admin_role_id = admin_role_id
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500344 self.rbac_role_ids = rbac_role_ids
Sergey Vilgelm19e3bec2019-01-07 11:59:41 -0600345 # Adding backward mapping
346 self._role_map.update({v: k for k, v in self._role_map.items()})
Felipe Monteiro2693bf72017-08-12 22:56:47 +0100347
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500348 def _create_user_role_on_project(self, role_ids):
349 for role_id in role_ids:
350 self.admin_roles_client.create_user_role_on_project(
351 self.project_id, self.user_id, role_id)
Felipe Monteirofa01d5f2017-04-01 06:18:25 +0100352
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500353 def _list_and_clear_user_roles_on_project(self, role_ids):
Felipe Monteiroe8d93e02017-07-19 20:52:20 +0100354 roles = self.admin_roles_client.list_user_roles_on_project(
Felipe Monteirofa01d5f2017-04-01 06:18:25 +0100355 self.project_id, self.user_id)['roles']
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500356 all_role_ids = [role['id'] for role in roles]
Felipe Monteiro2693bf72017-08-12 22:56:47 +0100357
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500358 # NOTE(felipemonteiro): We do not use ``role_id in all_role_ids`` here
359 # to avoid over-permission errors: if the current list of roles on the
Felipe Monteiro2693bf72017-08-12 22:56:47 +0100360 # project includes "admin" and "Member", and we are switching to the
361 # "Member" role, then we must delete the "admin" role. Thus, we only
362 # return early if the user's roles on the project are an exact match.
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500363 if set(role_ids) == set(all_role_ids):
Felipe Monteirofa01d5f2017-04-01 06:18:25 +0100364 return True
Felipe Monteirob3b7bc82017-03-03 15:58:15 -0500365
366 for role in roles:
Felipe Monteiroe8d93e02017-07-19 20:52:20 +0100367 self.admin_roles_client.delete_role_from_user_on_project(
Felipe Monteirofa01d5f2017-04-01 06:18:25 +0100368 self.project_id, self.user_id, role['id'])
369
370 return False
371
Sergey Vilgelmbab9e942018-10-11 14:04:48 -0500372 @contextlib.contextmanager
373 def override_role_and_validate_list(self, test_obj, admin_resources=None,
374 admin_resource_id=None):
375 """Call ``override_role`` and validate RBAC for a list API action.
376
377 List actions usually do soft authorization: partial or empty response
378 bodies are returned instead of exceptions. This helper validates
379 that unauthorized roles only return a subset of the available
380 resources.
381 Should only be used for validating list API actions.
382
383 :param test_obj: Instance of ``tempest.test.BaseTestCase``.
384 :param list admin_resources: The list of resources received before
385 calling the ``override_role_and_validate_list`` function.
386 :param UUID admin_resource_id: An ID of a resource created before
387 calling the ``override_role_and_validate_list`` function.
388 :return: py:class:`_ValidateListContext` object.
389
390 Example::
391
392 # the resource created by admin
393 admin_resource_id = (
394 self.ntp_client.create_dscp_marking_rule()
395 ["dscp_marking_rule"]["id'])
396 with self.rbac_utils.override_role_and_validate_list(
397 self, admin_resource_id=admin_resource_id) as ctx:
398 # the list of resources available for member role
399 ctx.resources = self.ntp_client.list_dscp_marking_rules(
400 policy_id=self.policy_id)["dscp_marking_rules"]
401 """
402 ctx = _ValidateListContext(admin_resources, admin_resource_id)
403 with self.override_role(test_obj):
404 yield ctx
405 ctx._validate()
406
Felipe Monteiro17e9b492017-05-27 05:45:20 +0100407
Felipe Monteiro07a1c172017-12-10 04:26:08 +0000408class RbacUtilsMixin(object):
409 """Mixin class to be used alongside an instance of
410 :py:class:`tempest.test.BaseTestCase`.
411
412 Should be used to perform Patrole class setup for a base RBAC class. Child
413 classes should not use this mixin.
414
415 Example::
416
417 class BaseRbacTest(rbac_utils.RbacUtilsMixin, base.BaseV2ComputeTest):
418
419 @classmethod
420 def skip_checks(cls):
421 super(BaseRbacTest, cls).skip_checks()
422 cls.skip_rbac_checks()
423
424 @classmethod
425 def setup_clients(cls):
426 super(BaseRbacTest, cls).setup_clients()
427 cls.setup_rbac_utils()
428 """
429
Mykola Yakovliev11376ab2018-08-06 15:34:22 -0500430 # Shows if override_role was called.
431 __override_role_called = False
432 # Shows if exception raised during override_role.
433 __override_role_caught_exc = False
434
Felipe Monteiro07a1c172017-12-10 04:26:08 +0000435 @classmethod
Mykola Yakovliev1d829782018-08-03 14:37:37 -0500436 def get_auth_providers(cls):
437 """Returns list of auth_providers used within test.
438
439 Tests may redefine this method to include their own or third party
440 client auth_providers.
441 """
442 return [cls.os_primary.auth_provider]
443
444 @classmethod
Felipe Monteiro07a1c172017-12-10 04:26:08 +0000445 def setup_rbac_utils(cls):
446 cls.rbac_utils = RbacUtils(cls)
447
Mykola Yakovliev11376ab2018-08-06 15:34:22 -0500448 def _set_override_role_called(self):
449 """Helper for tracking whether ``override_role`` was called."""
450 self.__override_role_called = True
451
452 def _set_override_role_caught_exc(self):
453 """Helper for tracking whether exception was thrown inside
454 ``override_role``.
455 """
456 self.__override_role_caught_exc = True
457
458 def _validate_override_role_called(self):
459 """Idempotently validate that ``override_role`` is called and reset
460 its value to False for sequential tests.
461 """
462 was_called = self.__override_role_called
463 self.__override_role_called = False
464 return was_called
465
466 def _validate_override_role_caught_exc(self):
467 """Idempotently validate that exception was caught inside
468 ``override_role``, so that, by process of elimination, it can be
469 determined whether one was thrown outside (which is invalid).
470 """
471 caught_exception = self.__override_role_caught_exc
472 self.__override_role_caught_exc = False
473 return caught_exception
474
Felipe Monteiro07a1c172017-12-10 04:26:08 +0000475
Felipe Monteiro8a043fb2017-08-06 06:29:05 +0100476def is_admin():
477 """Verifies whether the current test role equals the admin role.
478
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500479 :returns: True if ``rbac_test_roles`` contain the admin role.
Felipe Monteiro8a043fb2017-08-06 06:29:05 +0100480 """
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500481 roles = CONF.patrole.rbac_test_roles
482 # TODO(vegasq) drop once CONF.patrole.rbac_test_role is removed
483 if CONF.patrole.rbac_test_role:
484 roles.append(CONF.patrole.rbac_test_role)
485 roles = list(set(roles))
486
Felipe Monteiro2fc29292018-06-15 18:26:27 -0400487 # TODO(felipemonteiro): Make this more robust via a context is admin
488 # lookup.
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500489 return CONF.identity.admin_role in roles