Browse Source

Merge "Implement system reader for implied roles"

changes/44/680844/2
Zuul 1 week ago
parent
commit
1f2d0f0ff1

+ 47
- 8
keystone/common/policies/implied_role.py View File

@@ -10,14 +10,41 @@
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
 
18
+deprecated_get_implied_role = policy.DeprecatedRule(
19
+    name=base.IDENTITY % 'get_implied_role',
20
+    check_str=base.RULE_ADMIN_REQUIRED
21
+)
22
+deprecated_list_implied_roles = policy.DeprecatedRule(
23
+    name=base.IDENTITY % 'list_implied_roles',
24
+    check_str=base.RULE_ADMIN_REQUIRED,
25
+)
26
+deprecated_list_role_inference_rules = policy.DeprecatedRule(
27
+    name=base.IDENTITY % 'list_role_inference_rules',
28
+    check_str=base.RULE_ADMIN_REQUIRED,
29
+)
30
+deprecated_check_implied_role = policy.DeprecatedRule(
31
+    name=base.IDENTITY % 'check_implied_role',
32
+    check_str=base.RULE_ADMIN_REQUIRED,
33
+)
34
+
35
+DEPRECATED_REASON = """
36
+As of the Train release, the implied role API understands how to
37
+handle system-scoped tokens in addition to project tokens, making the API
38
+more accessible to users without compromising security or manageability for
39
+administrators. The new default policies for this API account for these changes
40
+automatically.
41
+"""
42
+
43
+
17 44
 implied_role_policies = [
18 45
     policy.DocumentedRuleDefault(
19 46
         name=base.IDENTITY % 'get_implied_role',
20
-        check_str=base.RULE_ADMIN_REQUIRED,
47
+        check_str=base.SYSTEM_READER,
21 48
         # FIXME(lbragstad) The management of implied roles currently makes
22 49
         # sense as a system-only resource. Once keystone has the ability to
23 50
         # support RBAC solely over the API without having to customize policy
@@ -29,10 +56,13 @@ implied_role_policies = [
29 56
                     'the user also assumes the implied role.',
30 57
         operations=[
31 58
             {'path': '/v3/roles/{prior_role_id}/implies/{implied_role_id}',
32
-             'method': 'GET'}]),
59
+             'method': 'GET'}],
60
+        deprecated_rule=deprecated_get_implied_role,
61
+        deprecated_reason=DEPRECATED_REASON,
62
+        deprecated_since=versionutils.deprecated.TRAIN),
33 63
     policy.DocumentedRuleDefault(
34 64
         name=base.IDENTITY % 'list_implied_roles',
35
-        check_str=base.RULE_ADMIN_REQUIRED,
65
+        check_str=base.SYSTEM_READER,
36 66
         scope_types=['system'],
37 67
         description='List associations between two roles. When a relationship '
38 68
                     'exists between a prior role and an implied role and the '
@@ -42,7 +72,10 @@ implied_role_policies = [
42 72
                     'prior role.',
43 73
         operations=[
44 74
             {'path': '/v3/roles/{prior_role_id}/implies', 'method': 'GET'},
45
-            {'path': '/v3/roles/{prior_role_id}/implies', 'method': 'HEAD'}]),
75
+            {'path': '/v3/roles/{prior_role_id}/implies', 'method': 'HEAD'}],
76
+        deprecated_rule=deprecated_list_implied_roles,
77
+        deprecated_reason=DEPRECATED_REASON,
78
+        deprecated_since=versionutils.deprecated.TRAIN),
46 79
     policy.DocumentedRuleDefault(
47 80
         name=base.IDENTITY % 'create_implied_role',
48 81
         check_str=base.RULE_ADMIN_REQUIRED,
@@ -68,7 +101,7 @@ implied_role_policies = [
68 101
              'method': 'DELETE'}]),
69 102
     policy.DocumentedRuleDefault(
70 103
         name=base.IDENTITY % 'list_role_inference_rules',
71
-        check_str=base.RULE_ADMIN_REQUIRED,
104
+        check_str=base.SYSTEM_READER,
72 105
         scope_types=['system'],
73 106
         description='List all associations between two roles in the system. '
74 107
                     'When a relationship exists between a prior role and an '
@@ -76,10 +109,13 @@ implied_role_policies = [
76 109
                     'the user also assumes the implied role.',
77 110
         operations=[
78 111
             {'path': '/v3/role_inferences', 'method': 'GET'},
79
-            {'path': '/v3/role_inferences', 'method': 'HEAD'}]),
112
+            {'path': '/v3/role_inferences', 'method': 'HEAD'}],
113
+        deprecated_rule=deprecated_list_role_inference_rules,
114
+        deprecated_reason=DEPRECATED_REASON,
115
+        deprecated_since=versionutils.deprecated.TRAIN),
80 116
     policy.DocumentedRuleDefault(
81 117
         name=base.IDENTITY % 'check_implied_role',
82
-        check_str=base.RULE_ADMIN_REQUIRED,
118
+        check_str=base.SYSTEM_READER,
83 119
         scope_types=['system'],
84 120
         description='Check an association between two roles. When a '
85 121
                     'relationship exists between a prior role and an implied '
@@ -87,7 +123,10 @@ implied_role_policies = [
87 123
                     'also assumes the implied role.',
88 124
         operations=[
89 125
             {'path': '/v3/roles/{prior_role_id}/implies/{implied_role_id}',
90
-             'method': 'HEAD'}])
126
+             'method': 'HEAD'}],
127
+        deprecated_rule=deprecated_check_implied_role,
128
+        deprecated_reason=DEPRECATED_REASON,
129
+        deprecated_since=versionutils.deprecated.TRAIN),
91 130
 ]
92 131
 
93 132
 

+ 171
- 0
keystone/tests/unit/protection/v3/test_implied_roles.py View File

@@ -0,0 +1,171 @@
1
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
2
+#    not use this file except in compliance with the License. You may obtain
3
+#    a copy of the License at
4
+#
5
+#         http://www.apache.org/licenses/LICENSE-2.0
6
+#
7
+#    Unless required by applicable law or agreed to in writing, software
8
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10
+#    License for the specific language governing permissions and limitations
11
+#    under the License.
12
+
13
+from six.moves import http_client
14
+
15
+from keystone.common import provider_api
16
+import keystone.conf
17
+from keystone.tests.common import auth as common_auth
18
+from keystone.tests import unit
19
+from keystone.tests.unit import base_classes
20
+from keystone.tests.unit import ksfixtures
21
+
22
+CONF = keystone.conf.CONF
23
+PROVIDERS = provider_api.ProviderAPIs
24
+
25
+
26
+class _ImpliedRolesSetupMixin(object):
27
+    def _create_test_roles(self):
28
+        ref = unit.new_role_ref()
29
+        role = PROVIDERS.role_api.create_role(ref['id'], ref)
30
+        self.prior_role_id = role['id']
31
+        ref = unit.new_role_ref()
32
+        role = PROVIDERS.role_api.create_role(ref['id'], ref)
33
+        self.implied_role_id = role['id']
34
+
35
+
36
+class _SystemUserImpliedRoleTests(object):
37
+    """Common default functionality for all system users."""
38
+
39
+    def test_user_can_list_implied_roles(self):
40
+        PROVIDERS.role_api.create_implied_role(self.prior_role_id,
41
+                                               self.implied_role_id)
42
+
43
+        with self.test_client() as c:
44
+            r = c.get('/v3/roles/%s/implies' % self.prior_role_id,
45
+                      headers=self.headers)
46
+            self.assertEqual(1, len(r.json['role_inference']['implies']))
47
+
48
+    def test_user_can_get_an_implied_role(self):
49
+        PROVIDERS.role_api.create_implied_role(self.prior_role_id,
50
+                                               self.implied_role_id)
51
+
52
+        with self.test_client() as c:
53
+            c.get(
54
+                '/v3/roles/%s/implies/%s' % (
55
+                    self.prior_role_id, self.implied_role_id),
56
+                headers=self.headers)
57
+            c.head(
58
+                '/v3/roles/%s/implies/%s' % (
59
+                    self.prior_role_id, self.implied_role_id),
60
+                headers=self.headers,
61
+                expected_status_code=http_client.NO_CONTENT)
62
+
63
+    def test_user_can_list_role_inference_rules(self):
64
+        PROVIDERS.role_api.create_implied_role(self.prior_role_id,
65
+                                               self.implied_role_id)
66
+
67
+        with self.test_client() as c:
68
+            r = c.get('/v3/role_inferences',
69
+                      headers=self.headers)
70
+            # There should be three role inferences: two from the defaults and
71
+            # one from the test setup
72
+            self.assertEqual(3, len(r.json['role_inferences']))
73
+
74
+
75
+class _SystemReaderAndMemberImpliedRoleTests(object):
76
+    """Common default functionality for system readers and system members."""
77
+
78
+    def test_user_cannot_create_implied_roles(self):
79
+        with self.test_client() as c:
80
+            c.put(
81
+                '/v3/roles/%s/implies/%s' % (
82
+                    self.prior_role_id, self.implied_role_id),
83
+                headers=self.headers,
84
+                expected_status_code=http_client.FORBIDDEN
85
+            )
86
+
87
+    def test_user_cannot_delete_implied_roles(self):
88
+        PROVIDERS.role_api.create_implied_role(self.prior_role_id,
89
+                                               self.implied_role_id)
90
+
91
+        with self.test_client() as c:
92
+            c.delete(
93
+                '/v3/roles/%s/implies/%s' % (
94
+                    self.prior_role_id, self.implied_role_id),
95
+                headers=self.headers,
96
+                expected_status_code=http_client.FORBIDDEN
97
+            )
98
+
99
+
100
+class SystemReaderTests(base_classes.TestCaseWithBootstrap,
101
+                        common_auth.AuthTestMixin,
102
+                        _ImpliedRolesSetupMixin,
103
+                        _SystemUserImpliedRoleTests,
104
+                        _SystemReaderAndMemberImpliedRoleTests):
105
+
106
+    def setUp(self):
107
+        super(SystemReaderTests, self).setUp()
108
+        self.loadapp()
109
+        self.useFixture(ksfixtures.Policy(self.config_fixture))
110
+        self.config_fixture.config(group='oslo_policy', enforce_scope=True)
111
+
112
+        self._create_test_roles()
113
+
114
+        system_reader = unit.new_user_ref(
115
+            domain_id=CONF.identity.default_domain_id
116
+        )
117
+        self.user_id = PROVIDERS.identity_api.create_user(
118
+            system_reader
119
+        )['id']
120
+        PROVIDERS.assignment_api.create_system_grant_for_user(
121
+            self.user_id, self.bootstrapper.reader_role_id
122
+        )
123
+
124
+        auth = self.build_authentication_request(
125
+            user_id=self.user_id, password=system_reader['password'],
126
+            system=True
127
+        )
128
+
129
+        # Grab a token using the persona we're testing and prepare headers
130
+        # for requests we'll be making in the tests.
131
+        with self.test_client() as c:
132
+            r = c.post('/v3/auth/tokens', json=auth)
133
+            self.token_id = r.headers['X-Subject-Token']
134
+            self.headers = {'X-Auth-Token': self.token_id}
135
+
136
+
137
+class SystemMemberTests(base_classes.TestCaseWithBootstrap,
138
+                        common_auth.AuthTestMixin,
139
+                        _ImpliedRolesSetupMixin,
140
+                        _SystemUserImpliedRoleTests,
141
+                        _SystemReaderAndMemberImpliedRoleTests):
142
+
143
+    def setUp(self):
144
+        super(SystemMemberTests, self).setUp()
145
+        self.loadapp()
146
+        self.useFixture(ksfixtures.Policy(self.config_fixture))
147
+        self.config_fixture.config(group='oslo_policy', enforce_scope=True)
148
+
149
+        self._create_test_roles()
150
+
151
+        system_member = unit.new_user_ref(
152
+            domain_id=CONF.identity.default_domain_id
153
+        )
154
+        self.user_id = PROVIDERS.identity_api.create_user(
155
+            system_member
156
+        )['id']
157
+        PROVIDERS.assignment_api.create_system_grant_for_user(
158
+            self.user_id, self.bootstrapper.member_role_id
159
+        )
160
+
161
+        auth = self.build_authentication_request(
162
+            user_id=self.user_id, password=system_member['password'],
163
+            system=True
164
+        )
165
+
166
+        # Grab a token using the persona we're testing and prepare headers
167
+        # for requests we'll be making in the tests.
168
+        with self.test_client() as c:
169
+            r = c.post('/v3/auth/tokens', json=auth)
170
+            self.token_id = r.headers['X-Subject-Token']
171
+            self.headers = {'X-Auth-Token': self.token_id}

Loading…
Cancel
Save