Browse Source

Merge "Implement system reader role for trusts API"

changes/22/678322/10
Zuul 1 week ago
parent
commit
43f9bf7432
2 changed files with 210 additions and 44 deletions
  1. 59
    16
      keystone/common/policies/trust.py
  2. 151
    28
      keystone/tests/unit/protection/v3/test_trusts.py

+ 59
- 16
keystone/common/policies/trust.py View File

@@ -10,12 +10,43 @@
10 10
 # License for the specific language governing permissions and limitations
11 11
 # under the License.
12 12
 
13
+from oslo_log import versionutils
13 14
 from oslo_policy import policy
14 15
 
15 16
 from keystone.common.policies import base
16 17
 
17 18
 RULE_TRUSTOR = 'user_id:%(target.trust.trustor_user_id)s'
18 19
 RULE_TRUSTEE = 'user_id:%(target.trust.trustee_user_id)s'
20
+SYSTEM_READER_OR_TRUSTOR_OR_TRUSTEE = (
21
+    base.SYSTEM_READER + ' or ' + RULE_TRUSTOR + ' or ' + RULE_TRUSTEE
22
+)
23
+SYSTEM_READER_OR_TRUSTOR = base.SYSTEM_READER + ' or ' + RULE_TRUSTOR
24
+SYSTEM_READER_OR_TRUSTEE = base.SYSTEM_READER + ' or ' + RULE_TRUSTEE
25
+
26
+deprecated_list_trusts = policy.DeprecatedRule(
27
+    name=base.IDENTITY % 'list_trusts',
28
+    check_str=base.RULE_ADMIN_REQUIRED
29
+)
30
+deprecated_list_roles_for_trust = policy.DeprecatedRule(
31
+    name=base.IDENTITY % 'list_roles_for_trust',
32
+    check_str=RULE_TRUSTOR + ' or ' + RULE_TRUSTEE
33
+)
34
+deprecated_get_role_for_trust = policy.DeprecatedRule(
35
+    name=base.IDENTITY % 'get_role_for_trust',
36
+    check_str=RULE_TRUSTOR + ' or ' + RULE_TRUSTEE
37
+)
38
+deprecated_get_trust = policy.DeprecatedRule(
39
+    name=base.IDENTITY % 'get_trust',
40
+    check_str=RULE_TRUSTOR + ' or ' + RULE_TRUSTEE
41
+)
42
+
43
+DEPRECATED_REASON = """
44
+As of the Train release, the trust API now understands default roles and
45
+system-scoped tokens, making the API more granular by default without
46
+compromising security. The new policy defaults account for these changes
47
+automatically. Be sure to take these new defaults into consideration if you are
48
+relying on overrides in your deployment for the service API.
49
+"""
19 50
 
20 51
 trust_policies = [
21 52
     policy.DocumentedRuleDefault(
@@ -30,17 +61,20 @@ trust_policies = [
30 61
                      'method': 'POST'}]),
31 62
     policy.DocumentedRuleDefault(
32 63
         name=base.IDENTITY % 'list_trusts',
33
-        check_str=base.RULE_ADMIN_REQUIRED,
34
-        scope_types=['project'],
64
+        check_str=base.SYSTEM_READER,
65
+        scope_types=['system'],
35 66
         description='List trusts.',
36 67
         operations=[{'path': '/v3/OS-TRUST/trusts',
37 68
                      'method': 'GET'},
38 69
                     {'path': '/v3/OS-TRUST/trusts',
39
-                     'method': 'HEAD'}]),
70
+                     'method': 'HEAD'}],
71
+        deprecated_rule=deprecated_list_trusts,
72
+        deprecated_reason=DEPRECATED_REASON,
73
+        deprecated_since=versionutils.deprecated.TRAIN),
40 74
     policy.DocumentedRuleDefault(
41 75
         name=base.IDENTITY % 'list_trusts_for_trustor',
42
-        check_str=RULE_TRUSTOR,
43
-        scope_types=['project'],
76
+        check_str=SYSTEM_READER_OR_TRUSTOR,
77
+        scope_types=['system', 'project'],
44 78
         description='List trusts for trustor.',
45 79
         operations=[{'path': '/v3/OS-TRUST/trusts?trustor_user_id={trustor_user_id}',
46 80
                      'method': 'GET'},
@@ -48,8 +82,8 @@ trust_policies = [
48 82
                      'method': 'HEAD'}]),
49 83
     policy.DocumentedRuleDefault(
50 84
         name=base.IDENTITY % 'list_trusts_for_trustee',
51
-        check_str=RULE_TRUSTEE,
52
-        scope_types=['project'],
85
+        check_str=SYSTEM_READER_OR_TRUSTEE,
86
+        scope_types=['system', 'project'],
53 87
         description='List trusts for trustee.',
54 88
         operations=[{'path': '/v3/OS-TRUST/trusts?trustee_user_id={trustee_user_id}',
55 89
                      'method': 'GET'},
@@ -57,22 +91,28 @@ trust_policies = [
57 91
                      'method': 'HEAD'}]),
58 92
     policy.DocumentedRuleDefault(
59 93
         name=base.IDENTITY % 'list_roles_for_trust',
60
-        check_str=RULE_TRUSTOR + ' or ' + RULE_TRUSTEE,
61
-        scope_types=['project'],
94
+        check_str=SYSTEM_READER_OR_TRUSTOR_OR_TRUSTEE,
95
+        scope_types=['system', 'project'],
62 96
         description='List roles delegated by a trust.',
63 97
         operations=[{'path': '/v3/OS-TRUST/trusts/{trust_id}/roles',
64 98
                      'method': 'GET'},
65 99
                     {'path': '/v3/OS-TRUST/trusts/{trust_id}/roles',
66
-                     'method': 'HEAD'}]),
100
+                     'method': 'HEAD'}],
101
+        deprecated_rule=deprecated_list_roles_for_trust,
102
+        deprecated_reason=DEPRECATED_REASON,
103
+        deprecated_since=versionutils.deprecated.TRAIN),
67 104
     policy.DocumentedRuleDefault(
68 105
         name=base.IDENTITY % 'get_role_for_trust',
69
-        check_str=RULE_TRUSTOR + ' or ' + RULE_TRUSTEE,
70
-        scope_types=['project'],
106
+        check_str=SYSTEM_READER_OR_TRUSTOR_OR_TRUSTEE,
107
+        scope_types=['system', 'project'],
71 108
         description='Check if trust delegates a particular role.',
72 109
         operations=[{'path': '/v3/OS-TRUST/trusts/{trust_id}/roles/{role_id}',
73 110
                      'method': 'GET'},
74 111
                     {'path': '/v3/OS-TRUST/trusts/{trust_id}/roles/{role_id}',
75
-                     'method': 'HEAD'}]),
112
+                     'method': 'HEAD'}],
113
+        deprecated_rule=deprecated_get_role_for_trust,
114
+        deprecated_reason=DEPRECATED_REASON,
115
+        deprecated_since=versionutils.deprecated.TRAIN),
76 116
     policy.DocumentedRuleDefault(
77 117
         name=base.IDENTITY % 'delete_trust',
78 118
         check_str=RULE_TRUSTOR,
@@ -82,13 +122,16 @@ trust_policies = [
82 122
                      'method': 'DELETE'}]),
83 123
     policy.DocumentedRuleDefault(
84 124
         name=base.IDENTITY % 'get_trust',
85
-        check_str=RULE_TRUSTOR + ' or ' + RULE_TRUSTEE,
86
-        scope_types=['project'],
125
+        check_str=SYSTEM_READER_OR_TRUSTOR_OR_TRUSTEE,
126
+        scope_types=['system', 'project'],
87 127
         description='Get trust.',
88 128
         operations=[{'path': '/v3/OS-TRUST/trusts/{trust_id}',
89 129
                      'method': 'GET'},
90 130
                     {'path': '/v3/OS-TRUST/trusts/{trust_id}',
91
-                     'method': 'HEAD'}])
131
+                     'method': 'HEAD'}],
132
+        deprecated_rule=deprecated_get_trust,
133
+        deprecated_reason=DEPRECATED_REASON,
134
+        deprecated_since=versionutils.deprecated.TRAIN)
92 135
 ]
93 136
 
94 137
 

+ 151
- 28
keystone/tests/unit/protection/v3/test_trusts.py View File

@@ -147,15 +147,28 @@ class _AdminTestsMixin(object):
147 147
             )
148 148
         self.assertEqual(1, len(r.json['trusts']))
149 149
 
150
-    def test_admin_cannot_get_trust_for_other_user(self):
151
-        PROVIDERS.trust_api.create_trust(
150
+
151
+class AdminTokenTests(TrustTests, _AdminTestsMixin):
152
+    """Tests for the is_admin user.
153
+
154
+    The Trusts API has hardcoded is_admin checks that we need to ensure are
155
+    preserved through the system-scope transition.
156
+    """
157
+
158
+    def setUp(self):
159
+        super(AdminTokenTests, self).setUp()
160
+        self.config_fixture.config(admin_token='ADMIN')
161
+        self.headers = {'X-Auth-Token': 'ADMIN'}
162
+
163
+    def test_admin_can_delete_trust_for_other_user(self):
164
+        ref = PROVIDERS.trust_api.create_trust(
152 165
             self.trust_id, **self.trust_data)
153 166
 
154 167
         with self.test_client() as c:
155
-            c.get(
156
-                '/v3/OS-TRUST/trusts/%s' % self.trust_id,
168
+            c.delete(
169
+                '/v3/OS-TRUST/trusts/%s' % ref['id'],
157 170
                 headers=self.headers,
158
-                expected_status_code=http_client.FORBIDDEN
171
+                expected_status_code=http_client.NO_CONTENT
159 172
             )
160 173
 
161 174
     def test_admin_can_get_non_existent_trust_not_found(self):
@@ -167,6 +180,17 @@ class _AdminTestsMixin(object):
167 180
                 expected_status_code=http_client.NOT_FOUND
168 181
             )
169 182
 
183
+    def test_admin_cannot_get_trust_for_other_user(self):
184
+        PROVIDERS.trust_api.create_trust(
185
+            self.trust_id, **self.trust_data)
186
+
187
+        with self.test_client() as c:
188
+            c.get(
189
+                '/v3/OS-TRUST/trusts/%s' % self.trust_id,
190
+                headers=self.headers,
191
+                expected_status_code=http_client.FORBIDDEN
192
+            )
193
+
170 194
     def test_admin_cannot_list_trust_roles_for_other_user(self):
171 195
         PROVIDERS.trust_api.create_trust(
172 196
             self.trust_id, **self.trust_data)
@@ -191,19 +215,118 @@ class _AdminTestsMixin(object):
191 215
             )
192 216
 
193 217
 
194
-class AdminTokenTests(TrustTests, _AdminTestsMixin):
195
-    """Tests for the is_admin user.
218
+class _SystemUserTests(object):
219
+    """Tests for system admin, member, and reader."""
196 220
 
197
-    The Trusts API has hardcoded is_admin checks that we need to ensure are
198
-    preserved through the system-scope transition.
199
-    """
221
+    def test_user_can_get_non_existent_trust(self):
222
+        trust_id = uuid.uuid4().hex
223
+        with self.test_client() as c:
224
+            c.get(
225
+                '/v3/OS-TRUST/trusts/%s' % trust_id,
226
+                headers=self.headers,
227
+                expected_status_code=http_client.NOT_FOUND
228
+            )
229
+
230
+    def test_user_can_get_trust_for_other_user(self):
231
+        PROVIDERS.trust_api.create_trust(
232
+            self.trust_id, **self.trust_data)
233
+
234
+        with self.test_client() as c:
235
+            r = c.get(
236
+                '/v3/OS-TRUST/trusts/%s' % self.trust_id,
237
+                headers=self.headers
238
+            )
239
+        self.assertEqual(r.json['trust']['id'], self.trust_id)
240
+
241
+    def test_user_can_list_trusts_for_trustee(self):
242
+        PROVIDERS.trust_api.create_trust(
243
+            self.trust_id, **self.trust_data)
244
+
245
+        with self.test_client() as c:
246
+            c.get(
247
+                ('/v3/OS-TRUST/trusts?trustee_user_id=%s' %
248
+                 self.trustee_user_id),
249
+                headers=self.headers
250
+            )
251
+
252
+    def test_user_can_list_trusts_for_trustor(self):
253
+        PROVIDERS.trust_api.create_trust(
254
+            self.trust_id, **self.trust_data)
255
+
256
+        with self.test_client() as c:
257
+            c.get(
258
+                ('/v3/OS-TRUST/trusts?trustor_user_id=%s' %
259
+                 self.trustor_user_id),
260
+                headers=self.headers
261
+            )
262
+
263
+    def test_user_can_list_trust_roles_for_other_user(self):
264
+        PROVIDERS.trust_api.create_trust(
265
+            self.trust_id, **self.trust_data)
266
+
267
+        with self.test_client() as c:
268
+            r = c.get(
269
+                '/v3/OS-TRUST/trusts/%s/roles' % self.trust_id,
270
+                headers=self.headers
271
+            )
272
+        self.assertEqual(r.json['roles'][0]['id'],
273
+                         self.bootstrapper.member_role_id)
274
+
275
+    def test_user_can_get_trust_role_for_other_user(self):
276
+        PROVIDERS.trust_api.create_trust(
277
+            self.trust_id, **self.trust_data)
278
+
279
+        with self.test_client() as c:
280
+            c.get(
281
+                ('/v3/OS-TRUST/trusts/%s/roles/%s' %
282
+                 (self.trust_id, self.bootstrapper.member_role_id)),
283
+                headers=self.headers
284
+            )
285
+
286
+
287
+class SystemReaderTests(TrustTests, _SystemUserTests):
288
+    """Tests for system reader users."""
200 289
 
201 290
     def setUp(self):
202
-        super(AdminTokenTests, self).setUp()
203
-        self.config_fixture.config(admin_token='ADMIN')
204
-        self.headers = {'X-Auth-Token': 'ADMIN'}
291
+        super(SystemReaderTests, self).setUp()
292
+        self.config_fixture.config(group='oslo_policy', enforce_scope=True)
205 293
 
206
-    def test_admin_can_delete_trust_for_other_user(self):
294
+        system_reader = unit.new_user_ref(
295
+            domain_id=CONF.identity.default_domain_id
296
+        )
297
+        self.user_id = PROVIDERS.identity_api.create_user(
298
+            system_reader
299
+        )['id']
300
+        PROVIDERS.assignment_api.create_system_grant_for_user(
301
+            self.user_id, self.bootstrapper.reader_role_id
302
+        )
303
+
304
+        auth = self.build_authentication_request(
305
+            user_id=self.user_id,
306
+            password=system_reader['password'],
307
+            system=True
308
+        )
309
+
310
+        # Grab a token using the persona we're testing and prepare headers
311
+        # for requests we'll be making in the tests.
312
+        with self.test_client() as c:
313
+            r = c.post('/v3/auth/tokens', json=auth)
314
+            self.token_id = r.headers['X-Subject-Token']
315
+            self.headers = {'X-Auth-Token': self.token_id}
316
+
317
+    def test_user_cannot_create_trust(self):
318
+        json = {'trust': self.trust_data['trust']}
319
+        json['trust']['roles'] = self.trust_data['roles']
320
+
321
+        with self.test_client() as c:
322
+            c.post(
323
+                '/v3/OS-TRUST/trusts',
324
+                json=json,
325
+                headers=self.headers,
326
+                expected_status_code=http_client.FORBIDDEN
327
+            )
328
+
329
+    def test_user_cannot_delete_trust(self):
207 330
         ref = PROVIDERS.trust_api.create_trust(
208 331
             self.trust_id, **self.trust_data)
209 332
 
@@ -211,11 +334,11 @@ class AdminTokenTests(TrustTests, _AdminTestsMixin):
211 334
             c.delete(
212 335
                 '/v3/OS-TRUST/trusts/%s' % ref['id'],
213 336
                 headers=self.headers,
214
-                expected_status_code=http_client.NO_CONTENT
337
+                expected_status_code=http_client.FORBIDDEN
215 338
             )
216 339
 
217 340
 
218
-class SystemAdminTests(TrustTests, _AdminTestsMixin):
341
+class SystemAdminTests(TrustTests, _AdminTestsMixin, _SystemUserTests):
219 342
     """Tests for system admin users."""
220 343
 
221 344
     def setUp(self):
@@ -250,18 +373,6 @@ class SystemAdminTests(TrustTests, _AdminTestsMixin):
250 373
                 expected_status_code=http_client.FORBIDDEN
251 374
             )
252 375
 
253
-    def test_admin_list_all_trusts_overridden_defaults(self):
254
-        self._override_policy_old_defaults()
255
-        PROVIDERS.trust_api.create_trust(
256
-            self.trust_id, **self.trust_data)
257
-
258
-        with self.test_client() as c:
259
-            r = c.get(
260
-                '/v3/OS-TRUST/trusts',
261
-                headers=self.headers
262
-            )
263
-        self.assertEqual(1, len(r.json['trusts']))
264
-
265 376
     def test_admin_cannot_delete_trust_for_user_overridden_defaults(self):
266 377
         # only the is_admin admin can do this
267 378
         self._override_policy_old_defaults()
@@ -312,6 +423,18 @@ class SystemAdminTests(TrustTests, _AdminTestsMixin):
312 423
                 expected_status_code=http_client.FORBIDDEN
313 424
             )
314 425
 
426
+    def test_user_list_all_trusts_overridden_defaults(self):
427
+        self._override_policy_old_defaults()
428
+        PROVIDERS.trust_api.create_trust(
429
+            self.trust_id, **self.trust_data)
430
+
431
+        with self.test_client() as c:
432
+            r = c.get(
433
+                '/v3/OS-TRUST/trusts',
434
+                headers=self.headers
435
+            )
436
+        self.assertEqual(1, len(r.json['trusts']))
437
+
315 438
 
316 439
 class ProjectUserTests(TrustTests):
317 440
     """Tests for all project users."""

Loading…
Cancel
Save