@@ -159,6 +159,17 @@ def setUp(self):
159
159
self .legacy_admin_context , self .system_admin_context ,
160
160
self .project_admin_context ])
161
161
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
+
162
173
# Users able to do cross-cell migrations
163
174
self .cross_cell_authorized_contexts = []
164
175
@@ -207,7 +218,7 @@ def fake_get_all(context, search_opts=None,
207
218
else :
208
219
check_rule = functools .partial (rule_if_system , rule , rule_name )
209
220
210
- self .common_policy_auth (self .project_admin_authorized_contexts ,
221
+ self .common_policy_auth (self .all_projects_admin_authorized_contexts ,
211
222
check_rule ,
212
223
self .controller .index ,
213
224
req )
@@ -258,7 +269,7 @@ def fake_get_all(context, search_opts=None,
258
269
else :
259
270
check_rule = functools .partial (rule_if_system , rule , rule_name )
260
271
261
- self .common_policy_auth (self .project_admin_authorized_contexts ,
272
+ self .common_policy_auth (self .all_projects_admin_authorized_contexts ,
262
273
check_rule ,
263
274
self .controller .detail ,
264
275
req )
@@ -275,9 +286,9 @@ def fake_get_all(context, search_opts=None,
275
286
expected_attrs = None , sort_keys = None , sort_dirs = None ,
276
287
cell_down_support = False , all_tenants = False ):
277
288
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 :
279
290
self .assertNotIn ('host' , search_opts )
280
- if context in self .project_admin_authorized_contexts :
291
+ if context in self .all_projects_admin_authorized_contexts :
281
292
self .assertIn ('host' , search_opts )
282
293
return objects .InstanceList (objects = self .servers )
283
294
@@ -286,7 +297,7 @@ def fake_get_all(context, search_opts=None,
286
297
req = fakes .HTTPRequest .blank ('/servers?host=1' )
287
298
rule_name = policies .SERVERS % 'allow_all_filters'
288
299
self .common_policy_auth (
289
- self .project_admin_authorized_contexts ,
300
+ self .all_projects_admin_authorized_contexts ,
290
301
rule_name ,
291
302
self .controller .index ,
292
303
req , fatal = False )
@@ -303,17 +314,17 @@ def fake_get_all(context, search_opts=None,
303
314
expected_attrs = None , sort_keys = None , sort_dirs = None ,
304
315
cell_down_support = False , all_tenants = False ):
305
316
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 :
307
318
self .assertNotIn ('host' , search_opts )
308
- if context in self .project_admin_authorized_contexts :
319
+ if context in self .all_projects_admin_authorized_contexts :
309
320
self .assertIn ('host' , search_opts )
310
321
return objects .InstanceList (objects = self .servers )
311
322
self .mock_get_all .side_effect = fake_get_all
312
323
313
324
req = fakes .HTTPRequest .blank ('/servers?host=1' )
314
325
rule_name = policies .SERVERS % 'allow_all_filters'
315
326
self .common_policy_auth (
316
- self .project_admin_authorized_contexts ,
327
+ self .all_projects_admin_authorized_contexts ,
317
328
rule_name ,
318
329
self .controller .detail ,
319
330
req , fatal = False )
@@ -350,7 +361,7 @@ def test_create_forced_host_server_policy(self, mock_az, mock_create):
350
361
self .policy .set_rules ({rule : "@" }, overwrite = False )
351
362
mock_create .return_value = ([self .instance ], '' )
352
363
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 ,
354
365
self .rule_forced_host ,
355
366
self .controller .create ,
356
367
self .req , body = self .body )
@@ -789,7 +800,7 @@ def fake_get_all(context, search_opts=None,
789
800
req = fakes .HTTPRequest .blank ('' , version = '2.3' )
790
801
rule_name = ea_policies .BASE_POLICY_NAME
791
802
authorize_res , unauthorize_res = self .common_policy_auth (
792
- self .project_admin_authorized_contexts ,
803
+ self .all_projects_admin_authorized_contexts ,
793
804
rule_name , self .controller .detail , req ,
794
805
fatal = False )
795
806
for attr in self .extended_attr :
@@ -849,8 +860,11 @@ def test_server_rebuild_with_extended_attr_policy(self, mock_rebuild,
849
860
@mock .patch ('nova.objects.BlockDeviceMappingList.bdms_by_instance_uuid' )
850
861
@mock .patch .object (InstanceGroup , 'get_by_instance_uuid' )
851
862
@mock .patch ('nova.compute.api.API.update_instance' )
863
+ @mock .patch ('nova.compute.api.API.get_instance_host_status' )
852
864
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
854
868
rule = policies .SERVERS % 'update'
855
869
# server 'update' policy is checked before extended attributes
856
870
# 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,
886
900
req = fakes .HTTPRequest .blank ('' , version = '2.16' )
887
901
rule_name = policies .SERVERS % 'show:host_status'
888
902
authorize_res , unauthorize_res = self .common_policy_auth (
889
- self .project_admin_authorized_contexts ,
903
+ self .all_projects_admin_authorized_contexts ,
890
904
rule_name , self .controller .detail , req ,
891
905
fatal = False )
892
906
for resp in authorize_res :
@@ -940,8 +954,11 @@ def test_server_rebuild_with_host_status_policy(self, mock_rebuild,
940
954
@mock .patch ('nova.objects.BlockDeviceMappingList.bdms_by_instance_uuid' )
941
955
@mock .patch .object (InstanceGroup , 'get_by_instance_uuid' )
942
956
@mock .patch ('nova.compute.api.API.update_instance' )
957
+ @mock .patch ('nova.compute.api.API.get_instance_host_status' )
943
958
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
945
962
rule = policies .SERVERS % 'update'
946
963
# server 'update' policy is checked before host_status
947
964
# 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,
984
1001
req = fakes .HTTPRequest .blank ('' , version = '2.16' )
985
1002
rule_name = policies .SERVERS % 'show:host_status:unknown-only'
986
1003
authorize_res , unauthorize_res = self .common_policy_auth (
987
- self .project_admin_authorized_contexts ,
1004
+ self .all_projects_admin_authorized_contexts ,
988
1005
rule_name , self .controller .detail , req ,
989
1006
fatal = False )
990
1007
for resp in authorize_res :
@@ -1057,6 +1074,7 @@ def test_server_rebuild_with_unknown_host_status_policy(self, mock_rebuild,
1057
1074
@mock .patch ('nova.compute.api.API.update_instance' )
1058
1075
def test_server_update_with_unknown_host_status_policy (self ,
1059
1076
mock_update , mock_group , mock_status , mock_bdm ):
1077
+ mock_update .return_value = self .instance
1060
1078
mock_status .return_value = fields .HostStatus .UNKNOWN
1061
1079
rule = policies .SERVERS % 'update'
1062
1080
# server 'update' policy is checked before unknown host_status
@@ -1094,9 +1112,9 @@ def test_create_requested_destination_server_policy(self,
1094
1112
1095
1113
def fake_create (context , * args , ** kwargs ):
1096
1114
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 :
1098
1116
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 :
1100
1118
self .assertNotIn (attr , kwargs )
1101
1119
return ([self .instance ], '' )
1102
1120
mock_create .side_effect = fake_create
@@ -1114,7 +1132,7 @@ def fake_create(context, *args, **kwargs):
1114
1132
},
1115
1133
}
1116
1134
1117
- self .common_policy_auth (self .project_admin_authorized_contexts ,
1135
+ self .common_policy_auth (self .all_projects_admin_authorized_contexts ,
1118
1136
self .rule_requested_destination ,
1119
1137
self .controller .create ,
1120
1138
req , body = body )
@@ -1188,11 +1206,11 @@ def test_network_attach_external_network_policy(self):
1188
1206
# which raise different error then PolicyNotAuthorized
1189
1207
# if not allowed.
1190
1208
neutron_api = neutron .API ()
1191
- for context in self .project_admin_authorized_contexts :
1209
+ for context in self .all_projects_admin_authorized_contexts :
1192
1210
neutron_api ._check_external_network_attach (context ,
1193
1211
[{'id' : 1 , 'router:external' : 'ext' }])
1194
1212
unauth = (set (self .all_contexts ) -
1195
- set (self .project_admin_authorized_contexts ))
1213
+ set (self .all_projects_admin_authorized_contexts ))
1196
1214
for context in unauth :
1197
1215
self .assertRaises (exception .ExternalNetworkAttachForbidden ,
1198
1216
neutron_api ._check_external_network_attach ,
@@ -1206,11 +1224,11 @@ def test_zero_disk_flavor_policy(self):
1206
1224
flavor = objects .Flavor (
1207
1225
vcpus = 1 , memory_mb = 512 , root_gb = 0 , extra_specs = {'hw:pmu' : "true" })
1208
1226
compute_api = compute .API ()
1209
- for context in self .project_admin_authorized_contexts :
1227
+ for context in self .all_projects_admin_authorized_contexts :
1210
1228
compute_api ._validate_flavor_image_nostatus (context ,
1211
1229
image , flavor , None )
1212
1230
unauth = (set (self .all_contexts ) -
1213
- set (self .project_admin_authorized_contexts ))
1231
+ set (self .all_projects_admin_authorized_contexts ))
1214
1232
for context in unauth :
1215
1233
self .assertRaises (
1216
1234
exception .BootFromVolumeRequiredForZeroDiskFlavor ,
@@ -1236,6 +1254,10 @@ def setUp(self):
1236
1254
self .project_admin_context , self .project_member_context ,
1237
1255
]))
1238
1256
1257
+ self .reduce_set ('project_admin_authorized' , set ([
1258
+ self .project_admin_context
1259
+ ]))
1260
+
1239
1261
# The only additional role that can read our resources is our
1240
1262
# own project_reader.
1241
1263
self .project_reader_authorized_contexts = (
@@ -1314,6 +1336,9 @@ def setUp(self):
1314
1336
self .reduce_set ('project_admin_authorized' ,
1315
1337
set ([self .legacy_admin_context ,
1316
1338
self .project_admin_context ]))
1339
+ self .reduce_set ('all_projects_admin_authorized' ,
1340
+ set ([self .legacy_admin_context ,
1341
+ self .project_admin_context ]))
1317
1342
1318
1343
# With scope checking enabled, system users also lose access to read
1319
1344
# project resources.
@@ -1340,6 +1365,11 @@ def setUp(self):
1340
1365
self .project_member_context ,
1341
1366
]))
1342
1367
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
+
1343
1373
# Only project_reader has additional read access to our
1344
1374
# project resources.
1345
1375
self .project_reader_authorized_contexts = (
0 commit comments