blob: 69ba0454c309699379519cea1cb008e178d1d745 [file] [log] [blame]
Felipe Monteiroc8ec1f62017-11-15 08:32:56 +00001.. _rbac-utils:
2
Felipe Monteiro144ec1e2017-12-26 17:38:11 +00003RBAC Utils Module
4=================
Felipe Monteiroc8ec1f62017-11-15 08:32:56 +00005
6Overview
7--------
8
9Patrole manipulates the ``os_primary`` `Tempest credentials`_, which are the
10primary set of Tempest credentials. It is necessary to use the same credentials
11across the entire test setup/test execution/test teardown workflow
12because otherwise 400-level errors will be thrown by OpenStack services.
13
14This is because many services check the request context's project scope -- and
15in very rare cases, user scope. However, each set of Tempest credentials (via
16`dynamic credentials`_) is allocated its own distinct project. For example, the
17``os_admin`` and ``os_primary`` credentials each have a distinct project,
18meaning that it is not always possible for the ``os_primary`` credentials to
19access resources created by the ``os_admin`` credentials.
20
21The only foolproof solution is to manipulate the role for the same set of
22credentials, rather than using distinct credentials for setup/teardown
23and test execution, respectively. This is especially true when considering
24custom policy rule definitions, which can be arbitrarily complex.
25
26Patrole, therefore, implicitly splits up each test into 3 stages: set up,
27test execution, and teardown.
28
29The role workflow is as follows:
30
31#. Setup: Admin role is used automatically. The primary credentials are
32 overridden with the admin role.
Felipe Monteiro144ec1e2017-12-26 17:38:11 +000033#. Test execution: ``[patrole] rbac_test_role`` is used manually via the
34 call to ``with rbac_utils.override_role(self)``. Everything that
35 is executed within this contextmanager uses the primary
36 credentials overridden with the ``[patrole] rbac_test_role``.
Felipe Monteiroc8ec1f62017-11-15 08:32:56 +000037#. Teardown: Admin role is used automatically. The primary credentials have
38 been overridden with the admin role.
39
40.. _Tempest credentials: https://docs.openstack.org/tempest/latest/library/credential_providers.html
41.. _dynamic credentials: https://docs.openstack.org/tempest/latest/configuration.html#dynamic-credentials
42
43Test Setup
44----------
45
46Automatic role switch in background.
47
48Resources can be set up inside the ``resource_setup`` class method that Tempest
49provides. These resources are typically reserved for "expensive" resources
50in terms of memory or storage requirements, like volumes and VMs. These
51resources are **always** created via the admin role; Patrole automatically
52handles this.
53
54Like Tempest, however, Patrole must also create resources inside tests
55themselves. At the beginning of each test, the primary credentials have already
56been overridden with the admin role. One can create whatever test-level
57resources one needs, without having to worry about permissions.
58
59Test Execution
60--------------
61
62Manual role switch required.
63
64"Test execution" here means calling the API endpoint that enforces the policy
65action expected by the ``rbac_rule_validation`` decorator. Test execution
66should be performed *only after* calling
Felipe Monteiro144ec1e2017-12-26 17:38:11 +000067``with rbac_utils.override_role(self)``.
Felipe Monteiroc8ec1f62017-11-15 08:32:56 +000068
69Immediately after that call, the API endpoint that enforces the policy should
70be called.
71
Felipe Monteiro144ec1e2017-12-26 17:38:11 +000072Examples
73^^^^^^^^
74
75Always use the contextmanager before calling the API that enforces the
76expected policy action.
77
Felipe Monteiroc8ec1f62017-11-15 08:32:56 +000078Example::
79
Felipe Monteiroc8ec1f62017-11-15 08:32:56 +000080 @rbac_rule_validation.action(
81 service="nova",
82 rule="os_compute_api:os-aggregates:show")
83 def test_show_aggregate_rbac(self):
Felipe Monteiro144ec1e2017-12-26 17:38:11 +000084 # Do test setup before the ``override_role`` call.
Felipe Monteiroc8ec1f62017-11-15 08:32:56 +000085 aggregate_id = self._create_aggregate()
Felipe Monteiro144ec1e2017-12-26 17:38:11 +000086 # Call the ``override_role`` method so that the primary credentials
87 # have the test role needed for test execution.
88 with self.rbac_utils.override_role(self):
89 self.aggregates_client.show_aggregate(aggregate_id)
90
91When using a waiter, do the wait outside the contextmanager. "Waiting" always
92entails executing a ``GET`` request to the server, until the state of the
93returned resource matches a desired state. These ``GET`` requests enforce
94a different policy than the one expected. This is undesirable because
95Patrole should only test policies in isolation from one another.
96
97Otherwise, the test result will be tainted, because instead of only the
98expected policy getting enforced with the ``os_primary`` role, at least
99two policies get enforced.
100
101Example using waiter::
102
103 @rbac_rule_validation.action(
104 service="nova",
105 rule="os_compute_api:os-admin-password")
106 def test_change_server_password(self):
107 original_password = self.servers_client.show_password(
108 self.server['id'])
109 self.addCleanup(self.servers_client.change_password, self.server['id'],
110 adminPass=original_password)
111
112 with self.rbac_utils.override_role(self):
113 self.servers_client.change_password(
114 self.server['id'], adminPass=data_utils.rand_password())
115 # Call the waiter outside the ``override_role`` contextmanager, so that
116 # it is executed with admin role.
117 waiters.wait_for_server_status(
118 self.servers_client, self.server['id'], 'ACTIVE')
119
120Below is an example of a method that enforces multiple policies getting
121called inside the contextmanager. The ``_complex_setup_method`` below
122performs the correct API that enforces the expected policy -- in this
123case ``self.resources_client.create_resource`` -- but then proceeds to
124use a waiter.
125
126Incorrect::
127
128 def _complex_setup_method(self):
129 resource = self.resources_client.create_resource(
130 **kwargs)['resource']
131 self.addCleanup(test_utils.call_and_ignore_notfound_exc,
132 self._delete_resource, resource)
133 waiters.wait_for_resource_status(
134 self.resources_client, resource['id'], 'available')
135 return resource
136
137 @rbac_rule_validation.action(
138 service="example-service",
139 rule="example-rule")
140 def test_change_server_password(self):
141 # Never call a helper function inside the contextmanager that calls a
142 # bunch of APIs. Only call the API that enforces the policy action
143 # contained in the decorator above.
144 with self.rbac_utils.override_role(self):
145 self._complex_setup_method()
146
147To fix this test, see the "Example using waiter" section above. It is
148recommended to re-implement the logic in a helper method inside a test such
149that only the relevant API is called inside the contextmanager, with
150everything extraneous outside.
Felipe Monteiroc8ec1f62017-11-15 08:32:56 +0000151
152Test Cleanup
153------------
154
155Automatic role switch in background.
156
157After the test -- no matter whether it ended successfully or in failure --
158the credentials are overridden with the admin role by the Patrole framework,
159*before* ``tearDown`` or ``tearDownClass`` are called. This means that
160resources are always cleaned up using the admin role.
161
162Implementation
163--------------
164
165.. automodule:: patrole_tempest_plugin.rbac_utils
166 :members:
167 :private-members: