Skip to content

Commit 478c0c4

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Test PROJECT_ADMIN APIs with no legacy rule case"
2 parents 4b22eea + 19cd4bf commit 478c0c4

File tree

2 files changed

+53
-21
lines changed

2 files changed

+53
-21
lines changed

nova/tests/unit/policies/base.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ def setUp(self):
130130
"role:admin and system_scope:all",
131131
"system_reader_api":
132132
"role:reader and system_scope:all",
133+
"project_admin_api":
134+
"role:admin and project_id:%(project_id)s",
133135
"project_member_api":
134136
"role:member and project_id:%(project_id)s",
135137
"project_reader_api":

nova/tests/unit/policies/test_servers.py

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,17 @@ def setUp(self):
159159
self.legacy_admin_context, self.system_admin_context,
160160
self.project_admin_context])
161161

162+
# Admin (for APIs does not pass the project id as policy target
163+
# for example, create server, list detail server) able to get
164+
# all projects servers, create server on specific host etc.
165+
# This is admin on any project because policy does not check
166+
# the project id but they will be able to create server, get
167+
# servers(unless all-tenant policy is allowed) of their own
168+
# project only.
169+
self.all_projects_admin_authorized_contexts = set([
170+
self.legacy_admin_context, self.system_admin_context,
171+
self.project_admin_context])
172+
162173
# Users able to do cross-cell migrations
163174
self.cross_cell_authorized_contexts = []
164175

@@ -207,7 +218,7 @@ def fake_get_all(context, search_opts=None,
207218
else:
208219
check_rule = functools.partial(rule_if_system, rule, rule_name)
209220

210-
self.common_policy_auth(self.project_admin_authorized_contexts,
221+
self.common_policy_auth(self.all_projects_admin_authorized_contexts,
211222
check_rule,
212223
self.controller.index,
213224
req)
@@ -258,7 +269,7 @@ def fake_get_all(context, search_opts=None,
258269
else:
259270
check_rule = functools.partial(rule_if_system, rule, rule_name)
260271

261-
self.common_policy_auth(self.project_admin_authorized_contexts,
272+
self.common_policy_auth(self.all_projects_admin_authorized_contexts,
262273
check_rule,
263274
self.controller.detail,
264275
req)
@@ -275,9 +286,9 @@ def fake_get_all(context, search_opts=None,
275286
expected_attrs=None, sort_keys=None, sort_dirs=None,
276287
cell_down_support=False, all_tenants=False):
277288
self.assertIsNotNone(search_opts)
278-
if context not in self.project_admin_authorized_contexts:
289+
if context not in self.all_projects_admin_authorized_contexts:
279290
self.assertNotIn('host', search_opts)
280-
if context in self.project_admin_authorized_contexts:
291+
if context in self.all_projects_admin_authorized_contexts:
281292
self.assertIn('host', search_opts)
282293
return objects.InstanceList(objects=self.servers)
283294

@@ -286,7 +297,7 @@ def fake_get_all(context, search_opts=None,
286297
req = fakes.HTTPRequest.blank('/servers?host=1')
287298
rule_name = policies.SERVERS % 'allow_all_filters'
288299
self.common_policy_auth(
289-
self.project_admin_authorized_contexts,
300+
self.all_projects_admin_authorized_contexts,
290301
rule_name,
291302
self.controller.index,
292303
req, fatal=False)
@@ -303,17 +314,17 @@ def fake_get_all(context, search_opts=None,
303314
expected_attrs=None, sort_keys=None, sort_dirs=None,
304315
cell_down_support=False, all_tenants=False):
305316
self.assertIsNotNone(search_opts)
306-
if context not in self.project_admin_authorized_contexts:
317+
if context not in self.all_projects_admin_authorized_contexts:
307318
self.assertNotIn('host', search_opts)
308-
if context in self.project_admin_authorized_contexts:
319+
if context in self.all_projects_admin_authorized_contexts:
309320
self.assertIn('host', search_opts)
310321
return objects.InstanceList(objects=self.servers)
311322
self.mock_get_all.side_effect = fake_get_all
312323

313324
req = fakes.HTTPRequest.blank('/servers?host=1')
314325
rule_name = policies.SERVERS % 'allow_all_filters'
315326
self.common_policy_auth(
316-
self.project_admin_authorized_contexts,
327+
self.all_projects_admin_authorized_contexts,
317328
rule_name,
318329
self.controller.detail,
319330
req, fatal=False)
@@ -350,7 +361,7 @@ def test_create_forced_host_server_policy(self, mock_az, mock_create):
350361
self.policy.set_rules({rule: "@"}, overwrite=False)
351362
mock_create.return_value = ([self.instance], '')
352363
mock_az.return_value = ('test', 'host', None)
353-
self.common_policy_auth(self.project_admin_authorized_contexts,
364+
self.common_policy_auth(self.all_projects_admin_authorized_contexts,
354365
self.rule_forced_host,
355366
self.controller.create,
356367
self.req, body=self.body)
@@ -789,7 +800,7 @@ def fake_get_all(context, search_opts=None,
789800
req = fakes.HTTPRequest.blank('', version='2.3')
790801
rule_name = ea_policies.BASE_POLICY_NAME
791802
authorize_res, unauthorize_res = self.common_policy_auth(
792-
self.project_admin_authorized_contexts,
803+
self.all_projects_admin_authorized_contexts,
793804
rule_name, self.controller.detail, req,
794805
fatal=False)
795806
for attr in self.extended_attr:
@@ -849,8 +860,11 @@ def test_server_rebuild_with_extended_attr_policy(self, mock_rebuild,
849860
@mock.patch('nova.objects.BlockDeviceMappingList.bdms_by_instance_uuid')
850861
@mock.patch.object(InstanceGroup, 'get_by_instance_uuid')
851862
@mock.patch('nova.compute.api.API.update_instance')
863+
@mock.patch('nova.compute.api.API.get_instance_host_status')
852864
def test_server_update_with_extended_attr_policy(self,
853-
mock_update, mock_group, mock_bdm):
865+
mock_status, mock_update, mock_group, mock_bdm):
866+
mock_update.return_value = self.instance
867+
mock_status.return_value = fields.HostStatus.UP
854868
rule = policies.SERVERS % 'update'
855869
# server 'update' policy is checked before extended attributes
856870
# policy so we have to allow it for everyone otherwise it will fail
@@ -886,7 +900,7 @@ def fake_get_all(context, search_opts=None,
886900
req = fakes.HTTPRequest.blank('', version='2.16')
887901
rule_name = policies.SERVERS % 'show:host_status'
888902
authorize_res, unauthorize_res = self.common_policy_auth(
889-
self.project_admin_authorized_contexts,
903+
self.all_projects_admin_authorized_contexts,
890904
rule_name, self.controller.detail, req,
891905
fatal=False)
892906
for resp in authorize_res:
@@ -940,8 +954,11 @@ def test_server_rebuild_with_host_status_policy(self, mock_rebuild,
940954
@mock.patch('nova.objects.BlockDeviceMappingList.bdms_by_instance_uuid')
941955
@mock.patch.object(InstanceGroup, 'get_by_instance_uuid')
942956
@mock.patch('nova.compute.api.API.update_instance')
957+
@mock.patch('nova.compute.api.API.get_instance_host_status')
943958
def test_server_update_with_host_status_policy(self,
944-
mock_update, mock_group, mock_bdm):
959+
mock_status, mock_update, mock_group, mock_bdm):
960+
mock_update.return_value = self.instance
961+
mock_status.return_value = fields.HostStatus.UP
945962
rule = policies.SERVERS % 'update'
946963
# server 'update' policy is checked before host_status
947964
# policy so we have to allow it for everyone otherwise it will fail
@@ -984,7 +1001,7 @@ def fake_get_all(context, search_opts=None,
9841001
req = fakes.HTTPRequest.blank('', version='2.16')
9851002
rule_name = policies.SERVERS % 'show:host_status:unknown-only'
9861003
authorize_res, unauthorize_res = self.common_policy_auth(
987-
self.project_admin_authorized_contexts,
1004+
self.all_projects_admin_authorized_contexts,
9881005
rule_name, self.controller.detail, req,
9891006
fatal=False)
9901007
for resp in authorize_res:
@@ -1057,6 +1074,7 @@ def test_server_rebuild_with_unknown_host_status_policy(self, mock_rebuild,
10571074
@mock.patch('nova.compute.api.API.update_instance')
10581075
def test_server_update_with_unknown_host_status_policy(self,
10591076
mock_update, mock_group, mock_status, mock_bdm):
1077+
mock_update.return_value = self.instance
10601078
mock_status.return_value = fields.HostStatus.UNKNOWN
10611079
rule = policies.SERVERS % 'update'
10621080
# server 'update' policy is checked before unknown host_status
@@ -1094,9 +1112,9 @@ def test_create_requested_destination_server_policy(self,
10941112

10951113
def fake_create(context, *args, **kwargs):
10961114
for attr in ['requested_host', 'requested_hypervisor_hostname']:
1097-
if context in self.project_admin_authorized_contexts:
1115+
if context in self.all_projects_admin_authorized_contexts:
10981116
self.assertIn(attr, kwargs)
1099-
if context not in self.project_admin_authorized_contexts:
1117+
if context not in self.all_projects_admin_authorized_contexts:
11001118
self.assertNotIn(attr, kwargs)
11011119
return ([self.instance], '')
11021120
mock_create.side_effect = fake_create
@@ -1114,7 +1132,7 @@ def fake_create(context, *args, **kwargs):
11141132
},
11151133
}
11161134

1117-
self.common_policy_auth(self.project_admin_authorized_contexts,
1135+
self.common_policy_auth(self.all_projects_admin_authorized_contexts,
11181136
self.rule_requested_destination,
11191137
self.controller.create,
11201138
req, body=body)
@@ -1188,11 +1206,11 @@ def test_network_attach_external_network_policy(self):
11881206
# which raise different error then PolicyNotAuthorized
11891207
# if not allowed.
11901208
neutron_api = neutron.API()
1191-
for context in self.project_admin_authorized_contexts:
1209+
for context in self.all_projects_admin_authorized_contexts:
11921210
neutron_api._check_external_network_attach(context,
11931211
[{'id': 1, 'router:external': 'ext'}])
11941212
unauth = (set(self.all_contexts) -
1195-
set(self.project_admin_authorized_contexts))
1213+
set(self.all_projects_admin_authorized_contexts))
11961214
for context in unauth:
11971215
self.assertRaises(exception.ExternalNetworkAttachForbidden,
11981216
neutron_api._check_external_network_attach,
@@ -1206,11 +1224,11 @@ def test_zero_disk_flavor_policy(self):
12061224
flavor = objects.Flavor(
12071225
vcpus=1, memory_mb=512, root_gb=0, extra_specs={'hw:pmu': "true"})
12081226
compute_api = compute.API()
1209-
for context in self.project_admin_authorized_contexts:
1227+
for context in self.all_projects_admin_authorized_contexts:
12101228
compute_api._validate_flavor_image_nostatus(context,
12111229
image, flavor, None)
12121230
unauth = (set(self.all_contexts) -
1213-
set(self.project_admin_authorized_contexts))
1231+
set(self.all_projects_admin_authorized_contexts))
12141232
for context in unauth:
12151233
self.assertRaises(
12161234
exception.BootFromVolumeRequiredForZeroDiskFlavor,
@@ -1236,6 +1254,10 @@ def setUp(self):
12361254
self.project_admin_context, self.project_member_context,
12371255
]))
12381256

1257+
self.reduce_set('project_admin_authorized', set([
1258+
self.project_admin_context
1259+
]))
1260+
12391261
# The only additional role that can read our resources is our
12401262
# own project_reader.
12411263
self.project_reader_authorized_contexts = (
@@ -1314,6 +1336,9 @@ def setUp(self):
13141336
self.reduce_set('project_admin_authorized',
13151337
set([self.legacy_admin_context,
13161338
self.project_admin_context]))
1339+
self.reduce_set('all_projects_admin_authorized',
1340+
set([self.legacy_admin_context,
1341+
self.project_admin_context]))
13171342

13181343
# With scope checking enabled, system users also lose access to read
13191344
# project resources.
@@ -1340,6 +1365,11 @@ def setUp(self):
13401365
self.project_member_context,
13411366
]))
13421367

1368+
# With no legacy rule and scope checks enable, only project
1369+
# admin can do admin things on project resource.
1370+
self.reduce_set('project_admin_authorized',
1371+
set([self.project_admin_context]))
1372+
13431373
# Only project_reader has additional read access to our
13441374
# project resources.
13451375
self.project_reader_authorized_contexts = (

0 commit comments

Comments
 (0)