Add default policy rule
If a specific rule is not found, we will check the rule defined in FLAGS.policy_default_action. Change-Id: Ib1b1aa4bbeec74bdb1562d0fc649d33838076f01
This commit is contained in:
@@ -1,95 +1,17 @@
|
|||||||
{
|
{
|
||||||
"admin_or_owner": [["role:admin"], ["project_id:%(project_id)s"]],
|
"admin_or_owner": [["role:admin"], ["project_id:%(project_id)s"]],
|
||||||
|
"default": [["rule:admin_or_owner"]],
|
||||||
|
|
||||||
|
|
||||||
"compute:create": [["rule:admin_or_owner"]],
|
"compute:create": [],
|
||||||
"compute:create:attach_network": [["rule:admin_or_owner"]],
|
"compute:create:attach_network": [],
|
||||||
"compute:create:attach_volume": [["rule:admin_or_owner"]],
|
"compute:create:attach_volume": [],
|
||||||
|
|
||||||
"compute:get": [["rule:admin_or_owner"]],
|
|
||||||
"compute:get_all" :[],
|
"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"]],
|
|
||||||
|
|
||||||
|
|
||||||
"volume:create": [],
|
"volume:create": [],
|
||||||
"volume:get": [],
|
|
||||||
"volume:get_all": [],
|
"volume:get_all": [],
|
||||||
"volume:get_volume_metadata": [],
|
"volume:get_volume_metadata": [],
|
||||||
"volume:delete": [],
|
|
||||||
"volume:update": [],
|
|
||||||
"volume:delete_volume_metadata": [],
|
|
||||||
"volume:update_volume_metadata": [],
|
|
||||||
|
|
||||||
"volume:attach": [],
|
|
||||||
"volume:detach": [],
|
|
||||||
"volume:check_attach": [],
|
|
||||||
"volume:check_detach": [],
|
|
||||||
"volume:initialize_connection": [],
|
|
||||||
"volume:terminate_connection": [],
|
|
||||||
|
|
||||||
"volume:create_snapshot": [],
|
|
||||||
"volume:delete_snapshot": [],
|
|
||||||
"volume:get_snapshot": [],
|
"volume:get_snapshot": [],
|
||||||
"volume:get_all_snapshots": []
|
"volume:get_all_snapshots": []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,13 +104,14 @@ def enforce(match_list, target_dict, credentials_dict):
|
|||||||
class Brain(object):
|
class Brain(object):
|
||||||
"""Implements policy checking."""
|
"""Implements policy checking."""
|
||||||
@classmethod
|
@classmethod
|
||||||
def load_json(cls, data):
|
def load_json(cls, data, default_rule=None):
|
||||||
"""Init a brain using json instead of a rules dictionary."""
|
"""Init a brain using json instead of a rules dictionary."""
|
||||||
rules_dict = json.loads(data)
|
rules_dict = json.loads(data)
|
||||||
return cls(rules=rules_dict)
|
return cls(rules=rules_dict, default_rule=default_rule)
|
||||||
|
|
||||||
def __init__(self, rules=None):
|
def __init__(self, rules=None, default_rule=None):
|
||||||
self.rules = rules or {}
|
self.rules = rules or {}
|
||||||
|
self.default_rule = default_rule
|
||||||
|
|
||||||
def add_rule(self, key, match):
|
def add_rule(self, key, match):
|
||||||
self.rules[key] = match
|
self.rules[key] = match
|
||||||
@@ -154,7 +155,11 @@ class Brain(object):
|
|||||||
try:
|
try:
|
||||||
new_match_list = self.rules[match]
|
new_match_list = self.rules[match]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return False
|
if self.default_rule and match != self.default_rule:
|
||||||
|
new_match_list = ('rule:%s' % self.default_rule,)
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
return self.check(new_match_list, target_dict, cred_dict)
|
return self.check(new_match_list, target_dict, cred_dict)
|
||||||
|
|
||||||
def _check_role(self, match, target_dict, cred_dict):
|
def _check_role(self, match, target_dict, cred_dict):
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ from nova import utils
|
|||||||
FLAGS = flags.FLAGS
|
FLAGS = flags.FLAGS
|
||||||
flags.DEFINE_string('policy_file', 'policy.json',
|
flags.DEFINE_string('policy_file', 'policy.json',
|
||||||
_('JSON file representing policy'))
|
_('JSON file representing policy'))
|
||||||
|
flags.DEFINE_string('policy_default_rule', 'default',
|
||||||
|
_('Rule checked when requested rule is not found'))
|
||||||
|
|
||||||
_POLICY_PATH = None
|
_POLICY_PATH = None
|
||||||
_POLICY_CACHE = {}
|
_POLICY_CACHE = {}
|
||||||
@@ -48,7 +50,8 @@ def init():
|
|||||||
|
|
||||||
|
|
||||||
def _set_brain(data):
|
def _set_brain(data):
|
||||||
policy.set_brain(policy.HttpBrain.load_json(data))
|
default_rule = FLAGS.policy_default_rule
|
||||||
|
policy.set_brain(policy.HttpBrain.load_json(data, default_rule))
|
||||||
|
|
||||||
|
|
||||||
def enforce(context, action, target):
|
def enforce(context, action, target):
|
||||||
@@ -69,10 +72,11 @@ def enforce(context, action, target):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
init()
|
init()
|
||||||
|
|
||||||
match_list = ('rule:%s' % action,)
|
match_list = ('rule:%s' % action,)
|
||||||
target_dict = target
|
credentials = context.to_dict()
|
||||||
credentials_dict = context.to_dict()
|
|
||||||
try:
|
try:
|
||||||
policy.enforce(match_list, target_dict, credentials_dict)
|
policy.enforce(match_list, target, credentials)
|
||||||
except policy.NotAuthorized:
|
except policy.NotAuthorized:
|
||||||
raise exception.PolicyNotAuthorized(action=action)
|
raise exception.PolicyNotAuthorized(action=action)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ from nova.common import policy as common_policy
|
|||||||
from nova import context
|
from nova import context
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova import flags
|
from nova import flags
|
||||||
|
import nova.common.policy
|
||||||
from nova import policy
|
from nova import policy
|
||||||
from nova import test
|
from nova import test
|
||||||
|
|
||||||
@@ -137,3 +138,40 @@ class PolicyTestCase(test.TestCase):
|
|||||||
def test_early_OR_enforcement(self):
|
def test_early_OR_enforcement(self):
|
||||||
action = "example:early_or_success"
|
action = "example:early_or_success"
|
||||||
policy.enforce(self.context, action, self.target)
|
policy.enforce(self.context, action, self.target)
|
||||||
|
|
||||||
|
|
||||||
|
class DefaultPolicyTestCase(test.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(DefaultPolicyTestCase, self).setUp()
|
||||||
|
policy.reset()
|
||||||
|
policy.init()
|
||||||
|
|
||||||
|
self.rules = {
|
||||||
|
"default": [],
|
||||||
|
"example:exist": [["false:false"]]
|
||||||
|
}
|
||||||
|
|
||||||
|
self._set_brain('default')
|
||||||
|
|
||||||
|
self.context = context.RequestContext('fake', 'fake')
|
||||||
|
|
||||||
|
def _set_brain(self, default_rule):
|
||||||
|
brain = nova.common.policy.HttpBrain(self.rules, default_rule)
|
||||||
|
nova.common.policy.set_brain(brain)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super(DefaultPolicyTestCase, self).setUp()
|
||||||
|
policy.reset()
|
||||||
|
|
||||||
|
def test_policy_called(self):
|
||||||
|
self.assertRaises(exception.PolicyNotAuthorized, policy.enforce,
|
||||||
|
self.context, "example:exist", {})
|
||||||
|
|
||||||
|
def test_not_found_policy_calls_default(self):
|
||||||
|
policy.enforce(self.context, "example:noexist", {})
|
||||||
|
|
||||||
|
def test_default_not_found(self):
|
||||||
|
self._set_brain("default_noexist")
|
||||||
|
self.assertRaises(exception.PolicyNotAuthorized, policy.enforce,
|
||||||
|
self.context, "example:noexist", {})
|
||||||
|
|||||||
@@ -146,9 +146,10 @@ class API(base.Base):
|
|||||||
self.db.volume_update(context, volume['id'], fields)
|
self.db.volume_update(context, volume['id'], fields)
|
||||||
|
|
||||||
def get(self, context, volume_id):
|
def get(self, context, volume_id):
|
||||||
check_policy(context, 'get', {'id': volume_id})
|
|
||||||
rv = self.db.volume_get(context, volume_id)
|
rv = self.db.volume_get(context, volume_id)
|
||||||
return dict(rv.iteritems())
|
volume = dict(rv.iteritems())
|
||||||
|
check_policy(context, 'get', volume)
|
||||||
|
return volume
|
||||||
|
|
||||||
def get_all(self, context, search_opts={}):
|
def get_all(self, context, search_opts={}):
|
||||||
check_policy(context, 'get_all')
|
check_policy(context, 'get_all')
|
||||||
@@ -262,7 +263,7 @@ class API(base.Base):
|
|||||||
|
|
||||||
def _create_snapshot(self, context, volume, name, description,
|
def _create_snapshot(self, context, volume, name, description,
|
||||||
force=False):
|
force=False):
|
||||||
check_policy(context, 'create_snapshot')
|
check_policy(context, 'create_snapshot', volume)
|
||||||
|
|
||||||
if ((not force) and (volume['status'] != "available")):
|
if ((not force) and (volume['status'] != "available")):
|
||||||
raise exception.ApiError(_("Volume status must be available"))
|
raise exception.ApiError(_("Volume status must be available"))
|
||||||
|
|||||||
Reference in New Issue
Block a user