Add policy checks to Compute.API

* Second step of blueprint interim-nova-authz-service
 * Adds policy.json to define policy
 * Add nova.policy.wrap_enforce decorator
 * wrap majority of compute api functions with wrap_enforce

Change-Id: If6702873db3249921f931a42e889ee7d0338e4b8
This commit is contained in:
Brian Waldon 2011-12-30 13:11:56 -08:00
parent e0680250c0
commit ace0252d75
8 changed files with 363 additions and 38 deletions

73
etc/nova/policy.json Normal file
View File

@ -0,0 +1,73 @@
{
"admin_or_owner": [["role:admin"], ["project_id:%(project_id)s"]],
"compute:create": [["rule:admin_or_owner"]],
"compute:create:attach_network": [["rule:admin_or_owner"]],
"compute:create:attach_volume": [["rule:admin_or_owner"]],
"compute:get": [["rule:admin_or_owner"]],
"compute:get_all" :[],
"compute:update": [["rule:admin_or_owner"]],
"compute:get_instance_metadata": [["rule:admin_or_owner"]],
"compute:update_instance_metadata": [["rule:admin_or_owner"]],
"compute:delete_instance_metadata": [["rule:admin_or_owner"]],
"compute:get_instance_faults": [["rule:admin_or_owner"]],
"compute:get_actions": [["rule:admin_or_owner"]],
"compute:get_diagnostics": [["rule:admin_or_owner"]],
"compute:get_lock": [["rule:admin_or_owner"]],
"compute:lock": [["rule:admin_or_owner"]],
"compute:unlock": [["rule:admin_or_owner"]],
"compute:get_ajax_console": [["rule:admin_or_owner"]],
"compute:get_vnc_console": [["rule:admin_or_owner"]],
"compute:get_console_output": [["rule:admin_or_owner"]],
"compute:associate_floating_ip": [["rule:admin_or_owner"]],
"compute:reset_network": [["rule:admin_or_owner"]],
"compute:inject_network_info": [["rule:admin_or_owner"]],
"compute:add_fixed_ip": [["rule:admin_or_owner"]],
"compute:remove_fixed_ip": [["rule:admin_or_owner"]],
"compute:attach_volume": [["rule:admin_or_owner"]],
"compute:detach_volume": [["rule:admin_or_owner"]],
"compute:inject_file": [["rule:admin_or_owner"]],
"compute:set_admin_password": [["rule:admin_or_owner"]],
"compute:rescue": [["rule:admin_or_owner"]],
"compute:unrescue": [["rule:admin_or_owner"]],
"compute:suspend": [["rule:admin_or_owner"]],
"compute:resume": [["rule:admin_or_owner"]],
"compute:pause": [["rule:admin_or_owner"]],
"compute:unpause": [["rule:admin_or_owner"]],
"compute:start": [["rule:admin_or_owner"]],
"compute:stop": [["rule:admin_or_owner"]],
"compute:resize": [["rule:admin_or_owner"]],
"compute:confirm_resize": [["rule:admin_or_owner"]],
"compute:revert_resize": [["rule:admin_or_owner"]],
"compute:rebuild": [["rule:admin_or_owner"]],
"compute:reboot": [["rule:admin_or_owner"]],
"compute:snapshot": [["rule:admin_or_owner"]],
"compute:backup": [["rule:admin_or_owner"]],
"compute:add_security_group": [["rule:admin_or_owner"]],
"compute:remove_security_group": [["rule:admin_or_owner"]],
"compute:delete": [["rule:admin_or_owner"]],
"compute:soft_delete": [["rule:admin_or_owner"]],
"compute:force_delete": [["rule:admin_or_owner"]],
"compute:restore": [["rule:admin_or_owner"]]
}

View File

@ -22,7 +22,7 @@ import urllib
import urllib2
class NotAllowed(Exception):
class NotAuthorized(Exception):
pass
@ -91,14 +91,14 @@ def enforce(match_list, target_dict, credentials_dict):
Credentials dicts contain as much information as we can about the user
performing the action.
:raises NotAllowed if the check fails
:raises NotAuthorized if the check fails
"""
global _BRAIN
if not _BRAIN:
_BRAIN = Brain()
if not _BRAIN.check(match_list, target_dict, credentials_dict):
raise NotAllowed()
raise NotAuthorized()
class Brain(object):

View File

@ -37,6 +37,7 @@ from nova import flags
import nova.image
from nova import log as logging
from nova import network
from nova import policy
from nova import quota
from nova import rpc
from nova.scheduler import api as scheduler_api
@ -88,6 +89,20 @@ def check_instance_state(vm_state=None, task_state=None):
return outer
def wrap_check_policy(func):
"""Check corresponding policy prior of wrapped method to execution"""
@functools.wraps(func)
def wrapped(self, context, target, *args, **kwargs):
check_policy(context, func.__name__, target)
return func(self, context, target, *args, **kwargs)
return wrapped
def check_policy(context, action, target):
_action = 'compute:%s' % action
nova.policy.enforce(context, _action, target)
class API(base.Base):
"""API for interacting with the compute manager."""
@ -426,6 +441,8 @@ class API(base.Base):
self.db.block_device_mapping_update_or_create(elevated_context,
values)
#NOTE(bcwaldon): No policy check since this is only used by scheduler and
# the compute api. That should probably be cleaned up, though.
def create_db_entry_for_new_instance(self, context, instance_type, image,
base_options, security_group, block_device_mapping, num=1):
"""Create an entry in the DB for this new instance,
@ -552,6 +569,16 @@ class API(base.Base):
could be 'None' or a list of instance dicts depending on if
we waited for information from the scheduler or not.
"""
target = {'project_id': context.project_id,
'user_id': context.user_id,
'availability_zone': availability_zone}
check_policy(context, 'create', target)
if requested_networks:
check_policy(context, 'create:attach_network', target)
if block_device_mapping:
check_policy(context, 'create:attach_volume', target)
# We can create the DB entry for the instance here if we're
# only going to create 1 instance and we're in a single
@ -588,16 +615,6 @@ class API(base.Base):
return (inst_ret_list, reservation_id)
def has_finished_migration(self, context, instance_uuid):
"""Returns true if an instance has a finished migration."""
try:
self.db.migration_get_by_instance_and_status(context,
instance_uuid,
'finished')
return True
except exception.NotFound:
return False
def ensure_default_security_group(self, context):
"""Ensure that a context has a security group.
@ -705,6 +722,7 @@ class API(base.Base):
return False
@wrap_check_policy
def add_security_group(self, context, instance, security_group_name):
"""Add security group to the instance"""
security_group = self.db.security_group_get_by_name(context,
@ -733,6 +751,7 @@ class API(base.Base):
{"method": "refresh_security_group_rules",
"args": {"security_group_id": security_group['id']}})
@wrap_check_policy
def remove_security_group(self, context, instance, security_group_name):
"""Remove the security group associated with the instance"""
security_group = self.db.security_group_get_by_name(context,
@ -761,6 +780,7 @@ class API(base.Base):
{"method": "refresh_security_group_rules",
"args": {"security_group_id": security_group['id']}})
@wrap_check_policy
@scheduler_api.reroute_compute("update")
def update(self, context, instance, **kwargs):
"""Updates the instance in the datastore.
@ -776,6 +796,7 @@ class API(base.Base):
rv = self.db.instance_update(context, instance["id"], kwargs)
return dict(rv.iteritems())
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
vm_states.ERROR])
@scheduler_api.reroute_compute("soft_delete")
@ -821,6 +842,7 @@ class API(base.Base):
# NOTE(jerdfelt): The API implies that only ACTIVE and ERROR are
# allowed but the EC2 API appears to allow from RESCUED and STOPPED
# too
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
vm_states.ERROR, vm_states.RESCUED,
vm_states.STOPPED])
@ -834,6 +856,7 @@ class API(base.Base):
self._delete(context, instance)
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.SOFT_DELETE])
@scheduler_api.reroute_compute("restore")
def restore(self, context, instance):
@ -852,12 +875,14 @@ class API(base.Base):
self._cast_compute_message('power_on_instance', context,
instance['uuid'], host)
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.SOFT_DELETE])
@scheduler_api.reroute_compute("force_delete")
def force_delete(self, context, instance):
"""Force delete a previously deleted (but not reclaimed) instance."""
self._delete(context, instance)
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
vm_states.RESCUED],
task_state=[None, task_states.RESIZE_VERIFY])
@ -884,6 +909,7 @@ class API(base.Base):
else:
self._call_compute_message('stop_instance', context, instance)
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.STOPPED, vm_states.SHUTOFF])
def start(self, context, instance):
"""Start an instance."""
@ -915,11 +941,14 @@ class API(base.Base):
"args": {"topic": FLAGS.compute_topic,
"instance_uuid": instance_uuid}})
#NOTE(bcwaldon): no policy check here since it should be rolled in to
# search_opts in get_all
def get_active_by_window(self, context, begin, end=None, project_id=None):
"""Get instances that were continuously active over a window."""
return self.db.instance_get_active_by_window(context, begin, end,
project_id)
#NOTE(bcwaldon): this doesn't really belong in this class
def get_instance_type(self, context, instance_type_id):
"""Get an instance type by instance type id."""
return instance_types.get_instance_type(instance_type_id)
@ -932,6 +961,8 @@ class API(base.Base):
else:
instance = self.db.instance_get(context, instance_id)
check_policy(context, 'get', instance)
inst = dict(instance.iteritems())
# NOTE(comstud): Doesn't get returned with iteritems
inst['name'] = instance['name']
@ -957,6 +988,14 @@ class API(base.Base):
search option that says otherwise.
"""
#TODO(bcwaldon): determine the best argument for target here
target = {
'project_id': context.project_id,
'user_id': context.user_id,
}
check_policy(context, "get_all", target)
if search_opts is None:
search_opts = {}
@ -1101,6 +1140,7 @@ class API(base.Base):
raise exception.Error(_("Unable to find host for Instance %s")
% instance_uuid)
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF],
task_state=[None, task_states.RESIZE_VERIFY])
@scheduler_api.reroute_compute("backup")
@ -1120,6 +1160,7 @@ class API(base.Base):
extra_properties=extra_properties)
return recv_meta
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF],
task_state=[None, task_states.RESIZE_VERIFY])
@scheduler_api.reroute_compute("snapshot")
@ -1199,6 +1240,7 @@ class API(base.Base):
return min_ram, min_disk
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
vm_states.RESCUED],
task_state=[None, task_states.RESIZE_VERIFY])
@ -1216,6 +1258,7 @@ class API(base.Base):
instance['uuid'],
params={'reboot_type': reboot_type})
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF],
task_state=[None, task_states.RESIZE_VERIFY])
@scheduler_api.reroute_compute("rebuild")
@ -1246,6 +1289,7 @@ class API(base.Base):
instance["uuid"],
params=rebuild_params)
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF],
task_state=[task_states.RESIZE_VERIFY])
@scheduler_api.reroute_compute("revert_resize")
@ -1272,6 +1316,7 @@ class API(base.Base):
self.db.migration_update(context, migration_ref['id'],
{'status': 'reverted'})
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF],
task_state=[task_states.RESIZE_VERIFY])
@scheduler_api.reroute_compute("confirm_resize")
@ -1300,6 +1345,7 @@ class API(base.Base):
self.db.instance_update(context, instance['uuid'],
{'host': migration_ref['dest_compute'], })
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF],
task_state=[None])
@scheduler_api.reroute_compute("resize")
@ -1355,6 +1401,7 @@ class API(base.Base):
"instance_type_id": new_instance_type['id'],
"request_spec": request_spec}})
@wrap_check_policy
@scheduler_api.reroute_compute("add_fixed_ip")
def add_fixed_ip(self, context, instance, network_id):
"""Add fixed_ip from specified network to given instance."""
@ -1364,6 +1411,7 @@ class API(base.Base):
instance_uuid,
params=dict(network_id=network_id))
@wrap_check_policy
@scheduler_api.reroute_compute("remove_fixed_ip")
def remove_fixed_ip(self, context, instance, address):
"""Remove fixed_ip from specified network to given instance."""
@ -1383,6 +1431,7 @@ class API(base.Base):
# didn't raise so this is the correct zone
self.network_api.add_network_to_project(context, project_id)
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
vm_states.RESCUED],
task_state=[None, task_states.RESIZE_VERIFY])
@ -1396,6 +1445,7 @@ class API(base.Base):
task_state=task_states.PAUSING)
self._cast_compute_message('pause_instance', context, instance_uuid)
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.PAUSED])
@scheduler_api.reroute_compute("unpause")
def unpause(self, context, instance):
@ -1423,6 +1473,7 @@ class API(base.Base):
return self._call_compute_message_for_host("host_power_action",
context, host=host, params={"action": action})
@wrap_check_policy
@scheduler_api.reroute_compute("diagnostics")
def get_diagnostics(self, context, instance):
"""Retrieve diagnostics for the given instance."""
@ -1430,10 +1481,12 @@ class API(base.Base):
context,
instance)
@wrap_check_policy
def get_actions(self, context, instance):
"""Retrieve actions for the given instance."""
return self.db.instance_get_actions(context, instance['uuid'])
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
vm_states.RESCUED],
task_state=[None, task_states.RESIZE_VERIFY])
@ -1447,6 +1500,7 @@ class API(base.Base):
task_state=task_states.SUSPENDING)
self._cast_compute_message('suspend_instance', context, instance_uuid)
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.SUSPENDED])
@scheduler_api.reroute_compute("resume")
def resume(self, context, instance):
@ -1458,6 +1512,7 @@ class API(base.Base):
task_state=task_states.RESUMING)
self._cast_compute_message('resume_instance', context, instance_uuid)
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
vm_states.STOPPED],
task_state=[None, task_states.RESIZE_VERIFY])
@ -1476,6 +1531,7 @@ class API(base.Base):
instance['uuid'],
params=rescue_params)
@wrap_check_policy
@check_instance_state(vm_state=[vm_states.RESCUED])
@scheduler_api.reroute_compute("unrescue")
def unrescue(self, context, instance):
@ -1487,6 +1543,7 @@ class API(base.Base):
self._cast_compute_message('unrescue_instance', context,
instance['uuid'])
@wrap_check_policy
@scheduler_api.reroute_compute("set_admin_password")
def set_admin_password(self, context, instance, password=None):
"""Set the root/admin password for the given instance."""
@ -1504,6 +1561,7 @@ class API(base.Base):
"args": {
"instance_uuid": instance_uuid, "new_pass": password}})
@wrap_check_policy
@scheduler_api.reroute_compute("inject_file")
def inject_file(self, context, instance, path, file_contents):
"""Write a file to the given instance."""
@ -1511,6 +1569,7 @@ class API(base.Base):
self._cast_compute_message('inject_file', context,
instance['uuid'], params=params)
@wrap_check_policy
def get_ajax_console(self, context, instance):
"""Get a url to an AJAX Console."""
output = self._call_compute_message('get_ajax_console',
@ -1523,6 +1582,7 @@ class API(base.Base):
return {'url': '%s/?token=%s' % (FLAGS.ajax_console_proxy_url,
output['token'])}
@wrap_check_policy
def get_vnc_console(self, context, instance):
"""Get a url to a VNC Console."""
output = self._call_compute_message('get_vnc_console',
@ -1541,6 +1601,7 @@ class API(base.Base):
'hostignore',
'portignore')}
@wrap_check_policy
def get_console_output(self, context, instance, tail_length=None):
"""Get console output for an an instance."""
return self._call_compute_message('get_console_output',
@ -1548,29 +1609,35 @@ class API(base.Base):
instance,
{'tail_length': tail_length})
@wrap_check_policy
def lock(self, context, instance):
"""Lock the given instance."""
self._cast_compute_message('lock_instance', context, instance['uuid'])
@wrap_check_policy
def unlock(self, context, instance):
"""Unlock the given instance."""
self._cast_compute_message('unlock_instance',
context,
instance['uuid'])
@wrap_check_policy
def get_lock(self, context, instance):
"""Return the boolean state of given instance's lock."""
return self.get(context, instance['uuid'])['locked']
@wrap_check_policy
def reset_network(self, context, instance):
"""Reset networking on the instance."""
self._cast_compute_message('reset_network', context, instance['uuid'])
@wrap_check_policy
def inject_network_info(self, context, instance):
"""Inject network info for the instance."""
self._cast_compute_message('inject_network_info', context,
instance['uuid'])
@wrap_check_policy
def attach_volume(self, context, instance, volume_id, device):
"""Attach an existing volume to an existing instance."""
if not re.match("^/dev/x{0,1}[a-z]d[a-z]+$", device):
@ -1590,6 +1657,9 @@ class API(base.Base):
instance = self.db.volume_get_instance(context.elevated(), volume_id)
if not instance:
raise exception.ApiError(_("Volume isn't attached to anything!"))
check_policy(context, 'detach_volume', instance)
self.volume_api.check_detach(context, volume_id=volume_id)
host = instance['host']
rpc.cast(context,
@ -1599,6 +1669,7 @@ class API(base.Base):
"volume_id": volume_id}})
return instance
@wrap_check_policy
def associate_floating_ip(self, context, instance, address):
"""Makes calls to network_api to associate_floating_ip.
@ -1630,15 +1701,18 @@ class API(base.Base):
floating_address=address,
fixed_address=fixed_ip_addrs[0])
@wrap_check_policy
def get_instance_metadata(self, context, instance):
"""Get all metadata associated with an instance."""
rv = self.db.instance_metadata_get(context, instance['id'])
return dict(rv.iteritems())
@wrap_check_policy
def delete_instance_metadata(self, context, instance, key):
"""Delete the given metadata item from an instance."""
self.db.instance_metadata_delete(context, instance['id'], key)
@wrap_check_policy
def update_instance_metadata(self, context, instance,
metadata, delete=False):
"""Updates or creates instance metadata.
@ -1660,5 +1734,9 @@ class API(base.Base):
def get_instance_faults(self, context, instances):
"""Get all faults for a list of instance uuids."""
for instance in instances:
check_policy(context, 'get_instance_faults', instance)
uuids = [instance['uuid'] for instance in instances]
return self.db.instance_fault_get_by_instance_uuids(context, uuids)

View File

@ -203,7 +203,7 @@ class AdminRequired(NotAuthorized):
message = _("User does not have admin privileges")
class PolicyNotAllowed(NotAuthorized):
class PolicyNotAuthorized(NotAuthorized):
message = _("Policy Doesn't allow %(action)s to be performed.")

View File

@ -17,10 +17,10 @@
"""Policy Engine For Nova"""
from nova.common import policy
from nova import exception
from nova import flags
from nova import utils
from nova.common import policy
FLAGS = flags.FLAGS
flags.DEFINE_string('policy_file', 'policy.json',
@ -43,8 +43,8 @@ def init():
global _POLICY_CACHE
if not _POLICY_PATH:
_POLICY_PATH = utils.find_config(FLAGS.policy_file)
data = utils.read_cached_file(_POLICY_PATH, _POLICY_CACHE,
reload_func=_set_brain)
utils.read_cached_file(_POLICY_PATH, _POLICY_CACHE,
reload_func=_set_brain)
def _set_brain(data):
@ -74,5 +74,5 @@ def enforce(context, action, target):
credentials_dict = context.to_dict()
try:
policy.enforce(match_list, target_dict, credentials_dict)
except policy.NotAllowed:
raise exception.PolicyNotAllowed(action=action)
except policy.NotAuthorized:
raise exception.PolicyNotAuthorized(action=action)

View File

@ -1,11 +1,70 @@
{
"true" : [],
"compute:create_instance" : [],
"compute:attach_network" : [],
"compute:attach_volume" : [],
"compute:list_instances": [],
"compute:get_instance": [],
"network:attach_network" : [],
"volume:create_volume": [],
"volume:attach_volume": []
"compute:create": [],
"compute:create:attach_network": [],
"compute:create:attach_volume": [],
"compute:get": [],
"compute:get_all" :[],
"compute:update": [],
"compute:get_instance_metadata": [],
"compute:update_instance_metadata": [],
"compute:delete_instance_metadata": [],
"compute:get_instance_faults": [],
"compute:get_actions": [],
"compute:get_diagnostics": [],
"compute:get_lock": [],
"compute:lock": [],
"compute:unlock": [],
"compute:get_ajax_console": [],
"compute:get_vnc_console": [],
"compute:get_console_output": [],
"compute:associate_floating_ip": [],
"compute:reset_network": [],
"compute:inject_network_info": [],
"compute:add_fixed_ip": [],
"compute:remove_fixed_ip": [],
"compute:attach_volume": [],
"compute:detach_volume": [],
"compute:inject_file": [],
"compute:set_admin_password": [],
"compute:rescue": [],
"compute:unrescue": [],
"compute:suspend": [],
"compute:resume": [],
"compute:pause": [],
"compute:unpause": [],
"compute:start": [],
"compute:stop": [],
"compute:resize": [],
"compute:confirm_resize": [],
"compute:revert_resize": [],
"compute:rebuild": [],
"compute:reboot": [],
"compute:snapshot": [],
"compute:backup": [],
"compute:add_security_group": [],
"compute:remove_security_group": [],
"compute:delete": [],
"compute:soft_delete": [],
"compute:force_delete": [],
"compute:restore": []
}

View File

@ -28,7 +28,9 @@ import mox
import webob.exc
import nova
import nova.common.policy
from nova import compute
import nova.compute.api
from nova.compute import instance_types
from nova.compute import manager as compute_manager
from nova.compute import power_state
@ -42,8 +44,9 @@ from nova.image import fake as fake_image
from nova import log as logging
from nova.network.quantum import client as quantum_client
from nova.notifier import test_notifier
from nova.scheduler import driver as scheduler_driver
import nova.policy
from nova import rpc
from nova.scheduler import driver as scheduler_driver
from nova import test
from nova.tests import fake_network
from nova import utils
@ -111,7 +114,8 @@ class BaseTestCase(test.TestCase):
self.compute = utils.import_object(FLAGS.compute_manager)
self.user_id = 'fake'
self.project_id = 'fake'
self.context = context.RequestContext(self.user_id, self.project_id)
self.context = context.RequestContext(self.user_id,
self.project_id)
test_notifier.NOTIFICATIONS = []
self.mox = mox.Mox()
self.total_waits = 0
@ -878,7 +882,9 @@ class ComputeTestCase(BaseTestCase):
instance_uuid = instance['uuid']
self.compute.run_instance(self.context, instance_uuid)
non_admin_context = context.RequestContext(None, None, is_admin=False)
non_admin_context = context.RequestContext(None,
None,
is_admin=False)
# decorator should return False (fail) with locked nonadmin context
self.compute.lock_instance(self.context, instance_uuid)
@ -2815,7 +2821,7 @@ class ComputeAPITestCase(BaseTestCase):
def test_attach_volume_invalid(self):
self.assertRaises(exception.ApiError,
self.compute_api.attach_volume,
None,
self.context,
None,
None,
'/dev/invalid')
@ -2966,3 +2972,112 @@ class ComputeAPITestCase(BaseTestCase):
self.compute_api.inject_file(self.context, instance,
"/tmp/test", "File Contents")
db.instance_destroy(self.context, instance['id'])
class ComputePolicyTestCase(BaseTestCase):
def setUp(self):
super(ComputePolicyTestCase, self).setUp()
nova.policy.reset()
nova.policy.init()
self.compute_api = compute.API()
def tearDown(self):
super(ComputePolicyTestCase, self).tearDown()
nova.policy.reset()
def _set_rules(self, rules):
nova.common.policy.set_brain(nova.common.policy.HttpBrain(rules))
def test_actions_are_prefixed(self):
self.mox.StubOutWithMock(nova.policy, 'enforce')
nova.policy.enforce(self.context, 'compute:reboot', {})
self.mox.ReplayAll()
nova.compute.api.check_policy(self.context, 'reboot', {})
self.mox.UnsetStubs()
self.mox.VerifyAll()
def test_wrapped_method(self):
instance = self._create_fake_instance()
self.compute.run_instance(self.context, instance['uuid'])
# force delete to fail
rules = {"compute:delete": [["false:false"]]}
self._set_rules(rules)
self.assertRaises(exception.PolicyNotAuthorized,
self.compute_api.delete, self.context, instance)
# reset rules to allow deletion
rules = {"compute:delete": []}
self._set_rules(rules)
self.compute_api.delete(self.context, instance)
def test_create_fail(self):
rules = {"compute:create": [["false:false"]]}
self._set_rules(rules)
self.assertRaises(exception.PolicyNotAuthorized,
self.compute_api.create, self.context, '1', '1')
def test_create_attach_volume_fail(self):
rules = {
"compute:create": [],
"compute:create:attach_network": [["false:false"]],
"compute:create:attach_volume": [],
}
self._set_rules(rules)
self.assertRaises(exception.PolicyNotAuthorized,
self.compute_api.create, self.context, '1', '1',
requested_networks='blah',
block_device_mapping='blah')
def test_create_attach_network_fail(self):
rules = {
"compute:create": [],
"compute:create:attach_network": [],
"compute:create:attach_volume": [["false:false"]],
}
self._set_rules(rules)
self.assertRaises(exception.PolicyNotAuthorized,
self.compute_api.create, self.context, '1', '1',
requested_networks='blah',
block_device_mapping='blah')
def test_get_fail(self):
instance = self._create_fake_instance()
rules = {
"compute:get": [["false:false"]],
}
self._set_rules(rules)
self.assertRaises(exception.PolicyNotAuthorized,
self.compute_api.get, self.context, instance['uuid'])
def test_get_all_fail(self):
rules = {
"compute:get_all": [["false:false"]],
}
self._set_rules(rules)
self.assertRaises(exception.PolicyNotAuthorized,
self.compute_api.get_all, self.context)
def test_get_instance_faults(self):
instance1 = self._create_fake_instance()
instance2 = self._create_fake_instance()
instances = [instance1, instance2]
rules = {
"compute:get_instance_faults": [["false:false"]],
}
self._set_rules(rules)
self.assertRaises(exception.PolicyNotAuthorized,
self.compute_api.get_instance_faults,
self.context, instances)

View File

@ -53,7 +53,7 @@ class PolicyFileTestCase(test.TestCase):
policyfile.write("""{"example:test": ["false:false"]}""")
# NOTE(vish): reset stored policy cache so we don't have to sleep(1)
policy._POLICY_CACHE = {}
self.assertRaises(exception.PolicyNotAllowed, policy.enforce,
self.assertRaises(exception.PolicyNotAuthorized, policy.enforce,
self.context, action, self.target)
@ -89,12 +89,12 @@ class PolicyTestCase(test.TestCase):
def test_enforce_nonexistent_action_throws(self):
action = "example:noexist"
self.assertRaises(exception.PolicyNotAllowed, policy.enforce,
self.assertRaises(exception.PolicyNotAuthorized, policy.enforce,
self.context, action, self.target)
def test_enforce_bad_action_throws(self):
action = "example:denied"
self.assertRaises(exception.PolicyNotAllowed, policy.enforce,
self.assertRaises(exception.PolicyNotAuthorized, policy.enforce,
self.context, action, self.target)
def test_enforce_good_action(self):
@ -118,7 +118,7 @@ class PolicyTestCase(test.TestCase):
self.stubs.Set(urllib2, 'urlopen', fakeurlopen)
action = "example:get_http"
target = {}
self.assertRaises(exception.PolicyNotAllowed, policy.enforce,
self.assertRaises(exception.PolicyNotAuthorized, policy.enforce,
self.context, action, target)
def test_templatized_enforcement(self):
@ -126,12 +126,12 @@ class PolicyTestCase(test.TestCase):
target_not_mine = {'project_id': 'another'}
action = "example:my_file"
policy.enforce(self.context, action, target_mine)
self.assertRaises(exception.PolicyNotAllowed, policy.enforce,
self.assertRaises(exception.PolicyNotAuthorized, policy.enforce,
self.context, action, target_not_mine)
def test_early_AND_enforcement(self):
action = "example:early_and_fail"
self.assertRaises(exception.PolicyNotAllowed, policy.enforce,
self.assertRaises(exception.PolicyNotAuthorized, policy.enforce,
self.context, action, self.target)
def test_early_OR_enforcement(self):