blob: f7a9059fec6b68eeaef8298a959a6a1c215108e6 [file] [log] [blame]
Felipe Monteiroffa47e62017-07-05 03:37:55 +01001# Copyright 2017 AT&T Corporation.
2# 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
16"""Fixtures for Patrole tests."""
17from __future__ import absolute_import
18
Mykola Yakovliev11376ab2018-08-06 15:34:22 -050019from contextlib import contextmanager
Felipe Monteiroffa47e62017-07-05 03:37:55 +010020import fixtures
21import mock
Felipe Monteirob18a3f62017-09-19 04:25:51 +010022import time
Felipe Monteiroffa47e62017-07-05 03:37:55 +010023
Felipe Monteiro3e14f472017-08-17 23:02:11 +010024from tempest import clients
25from tempest.common import credentials_factory as credentials
Felipe Monteiroffa47e62017-07-05 03:37:55 +010026from tempest import config
Felipe Monteirob18a3f62017-09-19 04:25:51 +010027from tempest import test
Felipe Monteiroffa47e62017-07-05 03:37:55 +010028
29from patrole_tempest_plugin import rbac_utils
30
31
32CONF = config.CONF
33
34
35class ConfPatcher(fixtures.Fixture):
36 """Fixture to patch and restore global CONF. Adopted from Nova.
37
38 This also resets overrides for everything that is patched during
39 its teardown.
40 """
41
42 def __init__(self, **kwargs):
43 """Constructor
44
45 :params group: if specified all config options apply to that group.
46 :params **kwargs: the rest of the kwargs are processed as a
47 set of key/value pairs to be set as configuration override.
48 """
49 super(ConfPatcher, self).__init__()
50 self.group = kwargs.pop('group', None)
51 self.args = kwargs
52
53 def setUp(self):
54 super(ConfPatcher, self).setUp()
55 for k, v in self.args.items():
56 self.addCleanup(CONF.clear_override, k, self.group)
57 CONF.set_override(k, v, self.group)
58
59
60class RbacUtilsFixture(fixtures.Fixture):
Felipe Monteiro2693bf72017-08-12 22:56:47 +010061 """Fixture for `RbacUtils` class."""
Felipe Monteiroffa47e62017-07-05 03:37:55 +010062
63 USER_ID = mock.sentinel.user_id
64 PROJECT_ID = mock.sentinel.project_id
65
Felipe Monteiroffa47e62017-07-05 03:37:55 +010066 def setUp(self):
67 super(RbacUtilsFixture, self).setUp()
68
Mykola Yakovlieve0f35502018-09-26 18:26:57 -050069 self.useFixture(ConfPatcher(rbac_test_roles=['member'],
70 group='patrole'))
Felipe Monteiroffa47e62017-07-05 03:37:55 +010071 self.useFixture(ConfPatcher(
72 admin_role='admin', auth_version='v3', group='identity'))
Felipe Monteirobf524fb2018-10-03 09:03:35 -050073 self.useFixture(ConfPatcher(
74 api_v3=True, group='identity-feature-enabled'))
Felipe Monteiroffa47e62017-07-05 03:37:55 +010075
76 test_obj_kwargs = {
77 'os_primary.credentials.user_id': self.USER_ID,
78 'os_primary.credentials.tenant_id': self.PROJECT_ID,
79 'os_primary.credentials.project_id': self.PROJECT_ID,
Felipe Monteiroffa47e62017-07-05 03:37:55 +010080 }
Felipe Monteirob18a3f62017-09-19 04:25:51 +010081 self.mock_test_obj = mock.Mock(
82 __name__='patrole_unit_test', spec=test.BaseTestCase,
Mykola Yakovliev1d829782018-08-03 14:37:37 -050083 os_primary=mock.Mock(),
84 get_auth_providers=mock.Mock(return_value=[mock.Mock()]),
85 **test_obj_kwargs)
Felipe Monteiroffa47e62017-07-05 03:37:55 +010086
Felipe Monteirob18a3f62017-09-19 04:25:51 +010087 # Mock out functionality that can't be used by unit tests. Mocking out
88 # time.sleep is a test optimization.
89 self.mock_time = mock.patch.object(
90 rbac_utils, 'time', __name__='mock_time', spec=time).start()
91 mock.patch.object(credentials, 'get_configured_admin_credentials',
92 spec=object).start()
93 mock_admin_mgr = mock.patch.object(
94 clients, 'Manager', spec=clients.Manager,
95 roles_v3_client=mock.Mock(), roles_client=mock.Mock()).start()
Mykola Yakovlieve0f35502018-09-26 18:26:57 -050096 self.admin_roles_client = mock_admin_mgr.return_value.roles_v3_client
Sergey Vilgelm19e3bec2019-01-07 11:59:41 -060097 self.admin_roles_client.list_all_role_inference_rules.return_value = {
98 "role_inferences": []}
Felipe Monteiroffa47e62017-07-05 03:37:55 +010099
Felipe Monteiro2693bf72017-08-12 22:56:47 +0100100 self.set_roles(['admin', 'member'], [])
101
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000102 def override_role(self, *role_toggles):
103 """Instantiate `rbac_utils.RbacUtils` and call `override_role`.
Felipe Monteiroffa47e62017-07-05 03:37:55 +0100104
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000105 Create an instance of `rbac_utils.RbacUtils` and call `override_role`
Felipe Monteiroffa47e62017-07-05 03:37:55 +0100106 for each boolean value in `role_toggles`. The number of calls to
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000107 `override_role` is always 1 + len(`role_toggles`) because the
108 `rbac_utils.RbacUtils` constructor automatically calls `override_role`.
Felipe Monteiroffa47e62017-07-05 03:37:55 +0100109
110 :param role_toggles: the list of boolean values iterated over and
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000111 passed to `override_role`.
Felipe Monteiroffa47e62017-07-05 03:37:55 +0100112 """
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000113 _rbac_utils = rbac_utils.RbacUtils(self.mock_test_obj)
Felipe Monteiro2693bf72017-08-12 22:56:47 +0100114
Felipe Monteiroffa47e62017-07-05 03:37:55 +0100115 for role_toggle in role_toggles:
Felipe Monteiro10e82fd2017-11-21 01:47:20 +0000116 _rbac_utils._override_role(self.mock_test_obj, role_toggle)
Felipe Monteiro2693bf72017-08-12 22:56:47 +0100117 # NOTE(felipemonteiro): Simulate that a role switch has occurred
118 # by updating the user's current role to the new role. This means
119 # that all API actions involved during a role switch -- listing,
120 # deleting and adding roles -- are executed, making it easier to
121 # assert that mock calls were called as expected.
122 new_role = 'member' if role_toggle else 'admin'
123 self.set_roles(['admin', 'member'], [new_role])
Felipe Monteiroffa47e62017-07-05 03:37:55 +0100124
Mykola Yakovliev11376ab2018-08-06 15:34:22 -0500125 @contextmanager
126 def real_override_role(self, test_obj):
127 """Actual call to ``override_role``.
128
129 Useful for ensuring all the necessary mocks are performed before
130 the method in question is called.
131 """
132 _rbac_utils = rbac_utils.RbacUtils(test_obj)
133 with _rbac_utils.override_role(test_obj):
134 yield
135
Felipe Monteiro2693bf72017-08-12 22:56:47 +0100136 def set_roles(self, roles, roles_on_project=None):
137 """Set the list of available roles in the system.
138
139 :param roles: List of roles returned by ``list_roles``.
140 :param roles_on_project: List of roles returned by
141 ``list_user_roles_on_project``.
142 :returns: None.
143 """
144 if not roles_on_project:
145 roles_on_project = []
146 if not isinstance(roles, list):
147 roles = [roles]
148 if not isinstance(roles_on_project, list):
149 roles_on_project = [roles_on_project]
150
151 available_roles = {
Felipe Monteiroffa47e62017-07-05 03:37:55 +0100152 'roles': [{'name': role, 'id': '%s_id' % role} for role in roles]
153 }
Felipe Monteiro2693bf72017-08-12 22:56:47 +0100154 available_project_roles = {
155 'roles': [{'name': role, 'id': '%s_id' % role}
156 for role in roles_on_project]
157 }
158
Mykola Yakovlieve0f35502018-09-26 18:26:57 -0500159 self.admin_roles_client.list_roles.return_value = available_roles
160 self.admin_roles_client.list_user_roles_on_project.return_value = (
Felipe Monteiro2693bf72017-08-12 22:56:47 +0100161 available_project_roles)
Sergey Vilgelm19e3bec2019-01-07 11:59:41 -0600162
163 def get_all_needed_roles(self, roles):
164 self.admin_roles_client.list_all_role_inference_rules.return_value = {
165 "role_inferences": [
166 {
167 "implies": [{"id": "3", "name": "reader"}],
168 "prior_role": {"id": "2", "name": "member"}
169 },
170 {
171 "implies": [{"id": "2", "name": "member"}],
172 "prior_role": {"id": "1", "name": "admin"}
173 }
174 ]
175 }
176
177 # Call real get_all_needed_roles function
178 with mock.patch.object(rbac_utils.RbacUtils, '_override_role',
179 autospec=True):
180 obj = rbac_utils.RbacUtils(mock.Mock())
181 obj._role_map = {
182 "1": "admin", "admin": "1",
183 "2": "member", "member": "2",
184 "3": "reader", "reader": "3"
185 }
186 return obj.get_all_needed_roles(roles)