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:
		
							
								
								
									
										73
									
								
								etc/nova/policy.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								etc/nova/policy.json
									
									
									
									
									
										Normal 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"]]
 | 
			
		||||
}
 | 
			
		||||
@@ -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):
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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.")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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,7 +43,7 @@ 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,
 | 
			
		||||
    utils.read_cached_file(_POLICY_PATH, _POLICY_CACHE,
 | 
			
		||||
                           reload_func=_set_brain)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,70 @@
 | 
			
		||||
{
 | 
			
		||||
    "true" : [],
 | 
			
		||||
    "compute:create_instance" :          [],
 | 
			
		||||
    "compute:attach_network" :  [],
 | 
			
		||||
    "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:list_instances":   [],
 | 
			
		||||
    "compute:get_instance":     [],
 | 
			
		||||
    "network:attach_network" :  [],
 | 
			
		||||
    "volume:create_volume":     [],
 | 
			
		||||
    "volume: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": []
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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):
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user