blob: 6cf3c4665bc700d1adf71e248471e96c92db5e26 [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
Sergey Vilgelmace8ea32018-11-19 16:25:10 -0600105class RbacUtilsMixin(object):
106 """Utility mixin responsible for switching ``os_primary`` role.
107
108 Should be used as a mixin class alongside an instance of
109 :py:class:`tempest.test.BaseTestCase` to perform Patrole class setup for a
110 base RBAC class. Child classes should not use this mixin.
111
112 Example::
113
114 class BaseRbacTest(rbac_utils.RbacUtilsMixin, base.BaseV2ComputeTest):
115
116 @classmethod
117 def setup_clients(cls):
118 super(BaseRbacTest, cls).setup_clients()
119
120 cls.hosts_client = cls.os_primary.hosts_client
121 ...
Felipe Monteiroe8d93e02017-07-19 20:52:20 +0100122
123 This class is responsible for overriding the value of the primary Tempest
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000124 credential's role (i.e. ``os_primary`` role). By doing so, it is possible
125 to seamlessly swap between admin credentials, needed for setup and clean
126 up, and primary credentials, needed to perform the API call which does
Felipe Monteiroe8d93e02017-07-19 20:52:20 +0100127 policy enforcement. The primary credentials always cycle between roles
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000128 defined by ``CONF.identity.admin_role`` and
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500129 ``CONF.patrole.rbac_test_roles``.
Felipe Monteiroe8d93e02017-07-19 20:52:20 +0100130 """
DavidPurcell029d8c32017-01-06 15:27:41 -0500131
Sergey Vilgelmace8ea32018-11-19 16:25:10 -0600132 def __init__(self, *args, **kwargs):
133 super(RbacUtilsMixin, self).__init__(*args, **kwargs)
Felipe Monteiroe8d93e02017-07-19 20:52:20 +0100134
Sergey Vilgelmace8ea32018-11-19 16:25:10 -0600135 # Shows if override_role was called.
136 self.__override_role_called = False
137 # Shows if exception raised during override_role.
138 self.__override_role_caught_exc = False
Sergey Vilgelm19e3bec2019-01-07 11:59:41 -0600139
Sergey Vilgelmace8ea32018-11-19 16:25:10 -0600140 _admin_role_id = None
141 _rbac_role_ids = None
142 _project_id = None
143 _user_id = None
144 _role_map = None
145 _role_inferences_mapping = None
146
147 admin_roles_client = None
148
149 @property
150 def rbac_utils(self):
151 return self
152
153 @classmethod
154 def setup_clients(cls):
Felipe Monteiroe8d93e02017-07-19 20:52:20 +0100155 # Intialize the admin roles_client to perform role switching.
Felipe Monteiro3e14f472017-08-17 23:02:11 +0100156 admin_mgr = clients.Manager(
157 credentials.get_configured_admin_credentials())
Felipe Monteirobf524fb2018-10-03 09:03:35 -0500158 if CONF.identity_feature_enabled.api_v3:
Felipe Monteiro3e14f472017-08-17 23:02:11 +0100159 admin_roles_client = admin_mgr.roles_v3_client
Felipe Monteiroe8d93e02017-07-19 20:52:20 +0100160 else:
Felipe Monteirobf524fb2018-10-03 09:03:35 -0500161 raise lib_exc.InvalidConfiguration(
162 "Patrole role overriding only supports v3 identity API.")
Felipe Monteiroe8d93e02017-07-19 20:52:20 +0100163
Sergey Vilgelmace8ea32018-11-19 16:25:10 -0600164 cls.admin_roles_client = admin_roles_client
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500165
Sergey Vilgelmace8ea32018-11-19 16:25:10 -0600166 cls._project_id = cls.os_primary.credentials.tenant_id
167 cls._user_id = cls.os_primary.credentials.user_id
168 cls._role_inferences_mapping = cls._prepare_role_inferences_mapping()
169
170 cls._init_roles()
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500171
172 # Change default role to admin
Sergey Vilgelmace8ea32018-11-19 16:25:10 -0600173 cls._override_role(False)
174 super(RbacUtilsMixin, cls).setup_clients()
Felipe Monteirob35de582017-05-05 00:16:53 +0100175
Sergey Vilgelmace8ea32018-11-19 16:25:10 -0600176 @classmethod
177 def _prepare_role_inferences_mapping(cls):
Sergey Vilgelm19e3bec2019-01-07 11:59:41 -0600178 """Preparing roles mapping to support role inferences
179
180 Making query to `list-all-role-inference-rules`_ keystone API
181 returns all inference rules, which makes it possible to prepare
182 roles mapping.
183
184 It walks recursively through the raw data::
185
186 {"role_inferences": [
187 {
188 "implies": [{"id": "3", "name": "reader"}],
189 "prior_role": {"id": "2", "name": "member"}
190 },
191 {
192 "implies": [{"id": "2", "name": "member"}],
193 "prior_role": {"id": "1", "name": "admin"}
194 }
195 ]
196 }
197
198 and converts it to the mapping::
199
200 {
201 "2": ["3"], # "member": ["reader"],
202 "1": ["2", "3"] # "admin": ["member", "reader"]
203 }
204
205 .. _list-all-role-inference-rules: https://developer.openstack.org/api-ref/identity/v3/#list-all-role-inference-rules
206 """ # noqa: E501
207 def process_roles(role_id, data):
208 roles = data.get(role_id, set())
209 for rid in roles.copy():
210 roles.update(process_roles(rid, data))
211
212 return roles
213
214 def convert_data(data):
215 res = {}
216 for rule in data:
217 prior_role = rule['prior_role']['id']
218 implies = {r['id'] for r in rule['implies']}
219 res[prior_role] = implies
220 return res
221
Sergey Vilgelmace8ea32018-11-19 16:25:10 -0600222 raw_data = cls.admin_roles_client.list_all_role_inference_rules()
Sergey Vilgelm19e3bec2019-01-07 11:59:41 -0600223 data = convert_data(raw_data['role_inferences'])
224 res = {}
225 for role_id in data:
226 res[role_id] = process_roles(role_id, data)
227 return res
228
229 def get_all_needed_roles(self, roles):
230 """Extending given roles with roles from mapping
231
232 Examples::
233 ["admin"] >> ["admin", "member", "reader"]
234 ["member"] >> ["member", "reader"]
235 ["reader"] >> ["reader"]
236 ["custom_role"] >> ["custom_role"]
237
238 :param roles: list of roles
239 :return: extended list of roles
240 """
241 res = set(r for r in roles)
242 for role in res.copy():
Sergey Vilgelmace8ea32018-11-19 16:25:10 -0600243 role_id = self.__class__._role_map.get(role)
244 implied_roles = self.__class__._role_inferences_mapping.get(
245 role_id, set())
246 role_names = {self.__class__._role_map[rid]
247 for rid in implied_roles}
Sergey Vilgelm19e3bec2019-01-07 11:59:41 -0600248 res.update(role_names)
249 LOG.debug('All needed roles: %s; Base roles: %s', res, roles)
250 return list(res)
DavidPurcell029d8c32017-01-06 15:27:41 -0500251
Sergey Vilgelmbab9e942018-10-11 14:04:48 -0500252 @contextlib.contextmanager
Sergey Vilgelmace8ea32018-11-19 16:25:10 -0600253 def override_role(self, test_obj=None):
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000254 """Override the role used by ``os_primary`` Tempest credentials.
255
256 Temporarily change the role used by ``os_primary`` credentials to:
Masayuki Igawa80b9aab2018-01-09 17:00:45 +0900257
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500258 * ``[patrole] rbac_test_roles`` before test execution
Masayuki Igawa80b9aab2018-01-09 17:00:45 +0900259 * ``[identity] admin_role`` after test execution
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000260
261 Automatically switches to admin role after test execution.
262
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000263 :returns: None
264
265 .. warning::
266
267 This function can alter user roles for pre-provisioned credentials.
268 Work is underway to safely clean up after this function.
269
270 Example::
271
272 @rbac_rule_validation.action(service='test',
Felipe Monteiro59f538f2018-08-22 23:34:40 -0400273 rules=['a:test:rule'])
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000274 def test_foo(self):
275 # Allocate test-level resources here.
276 with self.rbac_utils.override_role(self):
melissaml7cd21612018-05-23 21:00:50 +0800277 # The role for `os_primary` has now been overridden. Within
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000278 # this block, call the API endpoint that enforces the
279 # expected policy specified by "rule" in the decorator.
280 self.foo_service.bar_api_call()
281 # The role is switched back to admin automatically. Note that
282 # if the API call above threw an exception, any code below this
283 # point in the test is not executed.
284 """
Sergey Vilgelmace8ea32018-11-19 16:25:10 -0600285 self._set_override_role_called()
286 self._override_role(True)
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000287 try:
288 # Execute the test.
289 yield
290 finally:
Mykola Yakovliev11376ab2018-08-06 15:34:22 -0500291 # Check whether an exception was raised. If so, remember that
292 # for future validation.
293 exc = sys.exc_info()[0]
294 if exc is not None:
Sergey Vilgelmace8ea32018-11-19 16:25:10 -0600295 self._set_override_role_caught_exc()
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000296 # This code block is always executed, no matter the result of the
297 # test. Automatically switch back to the admin role for test clean
298 # up.
Sergey Vilgelmace8ea32018-11-19 16:25:10 -0600299 self._override_role(False)
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000300
Sergey Vilgelmace8ea32018-11-19 16:25:10 -0600301 @classmethod
302 def _override_role(cls, toggle_rbac_role=False):
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000303 """Private helper for overriding ``os_primary`` Tempest credentials.
304
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000305 :param toggle_rbac_role: Boolean value that controls the role that
306 overrides default role of ``os_primary`` credentials.
307 * If True: role is set to ``[patrole] rbac_test_role``
308 * If False: role is set to ``[identity] admin_role``
309 """
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000310 LOG.debug('Overriding role to: %s.', toggle_rbac_role)
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500311 roles_already_present = False
Felipe Monteiro2693bf72017-08-12 22:56:47 +0100312
DavidPurcell029d8c32017-01-06 15:27:41 -0500313 try:
Sergey Vilgelmace8ea32018-11-19 16:25:10 -0600314 target_roles = (cls._rbac_role_ids
315 if toggle_rbac_role else [cls._admin_role_id])
316 roles_already_present = cls._list_and_clear_user_roles_on_project(
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500317 target_roles)
Felipe Monteiro2693bf72017-08-12 22:56:47 +0100318
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000319 # Do not override roles if `target_role` already exists.
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500320 if not roles_already_present:
Sergey Vilgelmace8ea32018-11-19 16:25:10 -0600321 cls._create_user_role_on_project(target_roles)
DavidPurcell029d8c32017-01-06 15:27:41 -0500322 except Exception as exp:
Felipe Monteiro2693bf72017-08-12 22:56:47 +0100323 with excutils.save_and_reraise_exception():
324 LOG.exception(exp)
Felipe Monteiro34a138c2017-03-02 17:01:37 -0500325 finally:
Sergey Vilgelmace8ea32018-11-19 16:25:10 -0600326 auth_providers = cls.get_auth_providers()
Mykola Yakovliev1d829782018-08-03 14:37:37 -0500327 for provider in auth_providers:
328 provider.clear_auth()
Felipe Monteiro7be94e82017-07-26 02:17:08 +0100329 # Fernet tokens are not subsecond aware so sleep to ensure we are
330 # passing the second boundary before attempting to authenticate.
Felipe Monteiro2693bf72017-08-12 22:56:47 +0100331 # Only sleep if a token revocation occurred as a result of role
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000332 # overriding. This will optimize test runtime in the case where
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500333 # ``[identity] admin_role`` == ``[patrole] rbac_test_roles``.
334 if not roles_already_present:
Rick Bartra89f498f2017-03-20 15:54:45 -0400335 time.sleep(1)
Mykola Yakovliev1d829782018-08-03 14:37:37 -0500336
337 for provider in auth_providers:
338 provider.set_auth()
Felipe Monteiro34a138c2017-03-02 17:01:37 -0500339
Sergey Vilgelmace8ea32018-11-19 16:25:10 -0600340 @classmethod
341 def _init_roles(cls):
342 available_roles = cls.admin_roles_client.list_roles()['roles']
343 cls._role_map = {r['name']: r['id'] for r in available_roles}
344 LOG.debug('Available roles: %s', cls._role_map.keys())
Felipe Monteirofa01d5f2017-04-01 06:18:25 +0100345
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500346 rbac_role_ids = []
347 roles = CONF.patrole.rbac_test_roles
348 # TODO(vegasq) drop once CONF.patrole.rbac_test_role is removed
349 if CONF.patrole.rbac_test_role:
350 if not roles:
351 roles.append(CONF.patrole.rbac_test_role)
Felipe Monteiro2693bf72017-08-12 22:56:47 +0100352
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500353 for role_name in roles:
Sergey Vilgelmace8ea32018-11-19 16:25:10 -0600354 rbac_role_ids.append(cls._role_map.get(role_name))
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500355
Sergey Vilgelmace8ea32018-11-19 16:25:10 -0600356 admin_role_id = cls._role_map.get(CONF.identity.admin_role)
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500357
358 if not all([admin_role_id, all(rbac_role_ids)]):
Felipe Monteiro2fc29292018-06-15 18:26:27 -0400359 missing_roles = []
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500360 msg = ("Could not find `[patrole] rbac_test_roles` or "
Felipe Monteiro2fc29292018-06-15 18:26:27 -0400361 "`[identity] admin_role`, both of which are required for "
362 "RBAC testing.")
363 if not admin_role_id:
364 missing_roles.append(CONF.identity.admin_role)
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500365 if not all(rbac_role_ids):
366 missing_roles += [role_name for role_name in roles
Sergey Vilgelmace8ea32018-11-19 16:25:10 -0600367 if role_name not in cls._role_map]
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500368
Felipe Monteiro2fc29292018-06-15 18:26:27 -0400369 msg += " Following roles were not found: %s." % (
370 ", ".join(missing_roles))
Sergey Vilgelmace8ea32018-11-19 16:25:10 -0600371 msg += " Available roles: %s." % ", ".join(cls._role_map)
Felipe Monteiro2693bf72017-08-12 22:56:47 +0100372 raise rbac_exceptions.RbacResourceSetupFailed(msg)
373
Sergey Vilgelmace8ea32018-11-19 16:25:10 -0600374 cls._admin_role_id = admin_role_id
375 cls._rbac_role_ids = rbac_role_ids
Sergey Vilgelm19e3bec2019-01-07 11:59:41 -0600376 # Adding backward mapping
Sergey Vilgelmace8ea32018-11-19 16:25:10 -0600377 cls._role_map.update({v: k for k, v in cls._role_map.items()})
Felipe Monteiro2693bf72017-08-12 22:56:47 +0100378
Sergey Vilgelmace8ea32018-11-19 16:25:10 -0600379 @classmethod
380 def _create_user_role_on_project(cls, role_ids):
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500381 for role_id in role_ids:
Sergey Vilgelmace8ea32018-11-19 16:25:10 -0600382 cls.admin_roles_client.create_user_role_on_project(
383 cls._project_id, cls._user_id, role_id)
Felipe Monteirofa01d5f2017-04-01 06:18:25 +0100384
Sergey Vilgelmace8ea32018-11-19 16:25:10 -0600385 @classmethod
386 def _list_and_clear_user_roles_on_project(cls, role_ids):
387 roles = cls.admin_roles_client.list_user_roles_on_project(
388 cls._project_id, cls._user_id)['roles']
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500389 all_role_ids = [role['id'] for role in roles]
Felipe Monteiro2693bf72017-08-12 22:56:47 +0100390
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500391 # NOTE(felipemonteiro): We do not use ``role_id in all_role_ids`` here
392 # to avoid over-permission errors: if the current list of roles on the
Felipe Monteiro2693bf72017-08-12 22:56:47 +0100393 # project includes "admin" and "Member", and we are switching to the
394 # "Member" role, then we must delete the "admin" role. Thus, we only
395 # return early if the user's roles on the project are an exact match.
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500396 if set(role_ids) == set(all_role_ids):
Felipe Monteirofa01d5f2017-04-01 06:18:25 +0100397 return True
Felipe Monteirob3b7bc82017-03-03 15:58:15 -0500398
399 for role in roles:
Sergey Vilgelmace8ea32018-11-19 16:25:10 -0600400 cls.admin_roles_client.delete_role_from_user_on_project(
401 cls._project_id, cls._user_id, role['id'])
Felipe Monteirofa01d5f2017-04-01 06:18:25 +0100402
403 return False
404
Sergey Vilgelmbab9e942018-10-11 14:04:48 -0500405 @contextlib.contextmanager
Sergey Vilgelmace8ea32018-11-19 16:25:10 -0600406 def override_role_and_validate_list(self, test_obj=None,
407 admin_resources=None,
Sergey Vilgelmbab9e942018-10-11 14:04:48 -0500408 admin_resource_id=None):
409 """Call ``override_role`` and validate RBAC for a list API action.
410
411 List actions usually do soft authorization: partial or empty response
412 bodies are returned instead of exceptions. This helper validates
413 that unauthorized roles only return a subset of the available
414 resources.
415 Should only be used for validating list API actions.
416
417 :param test_obj: Instance of ``tempest.test.BaseTestCase``.
418 :param list admin_resources: The list of resources received before
419 calling the ``override_role_and_validate_list`` function.
420 :param UUID admin_resource_id: An ID of a resource created before
421 calling the ``override_role_and_validate_list`` function.
422 :return: py:class:`_ValidateListContext` object.
423
424 Example::
425
426 # the resource created by admin
427 admin_resource_id = (
428 self.ntp_client.create_dscp_marking_rule()
429 ["dscp_marking_rule"]["id'])
430 with self.rbac_utils.override_role_and_validate_list(
431 self, admin_resource_id=admin_resource_id) as ctx:
432 # the list of resources available for member role
433 ctx.resources = self.ntp_client.list_dscp_marking_rules(
434 policy_id=self.policy_id)["dscp_marking_rules"]
435 """
436 ctx = _ValidateListContext(admin_resources, admin_resource_id)
Sergey Vilgelmace8ea32018-11-19 16:25:10 -0600437 with self.override_role():
Sergey Vilgelmbab9e942018-10-11 14:04:48 -0500438 yield ctx
439 ctx._validate()
440
Felipe Monteiro07a1c172017-12-10 04:26:08 +0000441 @classmethod
Mykola Yakovliev1d829782018-08-03 14:37:37 -0500442 def get_auth_providers(cls):
443 """Returns list of auth_providers used within test.
444
445 Tests may redefine this method to include their own or third party
446 client auth_providers.
447 """
448 return [cls.os_primary.auth_provider]
449
Mykola Yakovliev11376ab2018-08-06 15:34:22 -0500450 def _set_override_role_called(self):
451 """Helper for tracking whether ``override_role`` was called."""
452 self.__override_role_called = True
453
454 def _set_override_role_caught_exc(self):
455 """Helper for tracking whether exception was thrown inside
456 ``override_role``.
457 """
458 self.__override_role_caught_exc = True
459
460 def _validate_override_role_called(self):
461 """Idempotently validate that ``override_role`` is called and reset
462 its value to False for sequential tests.
463 """
464 was_called = self.__override_role_called
465 self.__override_role_called = False
466 return was_called
467
468 def _validate_override_role_caught_exc(self):
469 """Idempotently validate that exception was caught inside
470 ``override_role``, so that, by process of elimination, it can be
471 determined whether one was thrown outside (which is invalid).
472 """
473 caught_exception = self.__override_role_caught_exc
474 self.__override_role_caught_exc = False
475 return caught_exception
476
Felipe Monteiro07a1c172017-12-10 04:26:08 +0000477
Felipe Monteiro8a043fb2017-08-06 06:29:05 +0100478def is_admin():
479 """Verifies whether the current test role equals the admin role.
480
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500481 :returns: True if ``rbac_test_roles`` contain the admin role.
Felipe Monteiro8a043fb2017-08-06 06:29:05 +0100482 """
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500483 roles = CONF.patrole.rbac_test_roles
484 # TODO(vegasq) drop once CONF.patrole.rbac_test_role is removed
485 if CONF.patrole.rbac_test_role:
486 roles.append(CONF.patrole.rbac_test_role)
487 roles = list(set(roles))
488
Felipe Monteiro2fc29292018-06-15 18:26:27 -0400489 # TODO(felipemonteiro): Make this more robust via a context is admin
490 # lookup.
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500491 return CONF.identity.admin_role in roles