Merge "[policy in code] part3 (resource types)"
This commit is contained in:
commit
3fb7b4eb2e
|
@ -47,21 +47,5 @@
|
||||||
"software_deployments:delete": "rule:deny_stack_user",
|
"software_deployments:delete": "rule:deny_stack_user",
|
||||||
"software_deployments:metadata": "",
|
"software_deployments:metadata": "",
|
||||||
|
|
||||||
"service:index": "rule:context_is_admin",
|
"service:index": "rule:context_is_admin"
|
||||||
|
|
||||||
"resource_types:OS::Nova::Flavor": "rule:project_admin",
|
|
||||||
"resource_types:OS::Cinder::EncryptedVolumeType": "rule:project_admin",
|
|
||||||
"resource_types:OS::Cinder::VolumeType": "rule:project_admin",
|
|
||||||
"resource_types:OS::Cinder::Quota": "rule:project_admin",
|
|
||||||
"resource_types:OS::Neutron::Quota": "rule:project_admin",
|
|
||||||
"resource_types:OS::Nova::Quota": "rule:project_admin",
|
|
||||||
"resource_types:OS::Manila::ShareType": "rule:project_admin",
|
|
||||||
"resource_types:OS::Neutron::ProviderNet": "rule:project_admin",
|
|
||||||
"resource_types:OS::Neutron::QoSPolicy": "rule:project_admin",
|
|
||||||
"resource_types:OS::Neutron::QoSBandwidthLimitRule": "rule:project_admin",
|
|
||||||
"resource_types:OS::Neutron::Segment": "rule:project_admin",
|
|
||||||
"resource_types:OS::Nova::HostAggregate": "rule:project_admin",
|
|
||||||
"resource_types:OS::Cinder::QoSSpecs": "rule:project_admin",
|
|
||||||
"resource_types:OS::Cinder::QoSAssociation": "rule:project_admin",
|
|
||||||
"resource_types:OS::Keystone::*": "rule:project_admin"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,12 +125,15 @@ class ResourceEnforcer(Enforcer):
|
||||||
super(ResourceEnforcer, self).__init__(
|
super(ResourceEnforcer, self).__init__(
|
||||||
default_rule=default_rule, **kwargs)
|
default_rule=default_rule, **kwargs)
|
||||||
|
|
||||||
def _enforce(self, context, res_type, scope=None, target=None):
|
def _enforce(self, context, res_type, scope=None, target=None,
|
||||||
|
is_registered_policy=False):
|
||||||
try:
|
try:
|
||||||
result = super(ResourceEnforcer, self).enforce(
|
result = super(ResourceEnforcer, self).enforce(
|
||||||
context, res_type,
|
context, res_type,
|
||||||
scope=scope or 'resource_types',
|
scope=scope or 'resource_types',
|
||||||
target=target)
|
target=target, is_registered_policy=is_registered_policy)
|
||||||
|
except policy.PolicyNotRegistered:
|
||||||
|
result = True
|
||||||
except self.exc as ex:
|
except self.exc as ex:
|
||||||
LOG.info(six.text_type(ex))
|
LOG.info(six.text_type(ex))
|
||||||
raise
|
raise
|
||||||
|
@ -139,19 +142,27 @@ class ResourceEnforcer(Enforcer):
|
||||||
raise self.exc(action=res_type)
|
raise self.exc(action=res_type)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def enforce(self, context, res_type, scope=None, target=None):
|
def enforce(self, context, res_type, scope=None, target=None,
|
||||||
|
is_registered_policy=False):
|
||||||
# NOTE(pas-ha): try/except just to log the exception
|
# NOTE(pas-ha): try/except just to log the exception
|
||||||
result = self._enforce(context, res_type, scope, target)
|
result = self._enforce(context, res_type, scope, target,
|
||||||
|
is_registered_policy=is_registered_policy)
|
||||||
|
|
||||||
if result:
|
if result:
|
||||||
# check for wildcard resource types
|
# check for wildcard resource types
|
||||||
subparts = res_type.split("::")[:-1]
|
subparts = res_type.split("::")[:-1]
|
||||||
subparts.append('*')
|
subparts.append('*')
|
||||||
res_type_wc = "::".join(subparts)
|
res_type_wc = "::".join(subparts)
|
||||||
return self._enforce(context, res_type_wc, scope, target)
|
try:
|
||||||
|
return self._enforce(context, res_type_wc, scope, target,
|
||||||
|
is_registered_policy=is_registered_policy)
|
||||||
|
except self.exc:
|
||||||
|
raise self.exc(action=res_type)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def enforce_stack(self, stack, scope=None, target=None):
|
def enforce_stack(self, stack, scope=None, target=None,
|
||||||
|
is_registered_policy=False):
|
||||||
for res in stack.resources.values():
|
for res in stack.resources.values():
|
||||||
self.enforce(stack.context, res.type(), scope=scope, target=target)
|
self.enforce(stack.context, res.type(), scope=scope, target=target,
|
||||||
|
is_registered_policy=is_registered_policy)
|
||||||
|
|
|
@ -618,7 +618,7 @@ class ResourceRegistry(object):
|
||||||
if cnxt is None:
|
if cnxt is None:
|
||||||
return True
|
return True
|
||||||
try:
|
try:
|
||||||
enforcer.enforce(cnxt, name)
|
enforcer.enforce(cnxt, name, is_registered_policy=True)
|
||||||
except enforcer.exc:
|
except enforcer.exc:
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -730,7 +730,7 @@ class EngineService(service.ServiceBase):
|
||||||
parent_resource=parent_resource_name,
|
parent_resource=parent_resource_name,
|
||||||
**common_params)
|
**common_params)
|
||||||
|
|
||||||
self.resource_enforcer.enforce_stack(stack)
|
self.resource_enforcer.enforce_stack(stack, is_registered_policy=True)
|
||||||
self._validate_deferred_auth_context(cnxt, stack)
|
self._validate_deferred_auth_context(cnxt, stack)
|
||||||
is_root = stack.nested_depth == 0
|
is_root = stack.nested_depth == 0
|
||||||
stack.validate()
|
stack.validate()
|
||||||
|
@ -964,7 +964,8 @@ class EngineService(service.ServiceBase):
|
||||||
if invalid_params:
|
if invalid_params:
|
||||||
raise exception.ImmutableParameterModified(*invalid_params)
|
raise exception.ImmutableParameterModified(*invalid_params)
|
||||||
|
|
||||||
self.resource_enforcer.enforce_stack(updated_stack)
|
self.resource_enforcer.enforce_stack(updated_stack,
|
||||||
|
is_registered_policy=True)
|
||||||
updated_stack.parameters.set_stack_id(current_stack.identifier())
|
updated_stack.parameters.set_stack_id(current_stack.identifier())
|
||||||
|
|
||||||
self._validate_deferred_auth_context(cnxt, updated_stack)
|
self._validate_deferred_auth_context(cnxt, updated_stack)
|
||||||
|
@ -999,7 +1000,8 @@ class EngineService(service.ServiceBase):
|
||||||
cnxt, stack=db_stack, use_stored_context=True)
|
cnxt, stack=db_stack, use_stored_context=True)
|
||||||
else:
|
else:
|
||||||
current_stack = parser.Stack.load(cnxt, stack=db_stack)
|
current_stack = parser.Stack.load(cnxt, stack=db_stack)
|
||||||
self.resource_enforcer.enforce_stack(current_stack)
|
self.resource_enforcer.enforce_stack(current_stack,
|
||||||
|
is_registered_policy=True)
|
||||||
|
|
||||||
if current_stack.action == current_stack.SUSPEND:
|
if current_stack.action == current_stack.SUSPEND:
|
||||||
msg = _('Updating a stack when it is suspended')
|
msg = _('Updating a stack when it is suspended')
|
||||||
|
@ -1417,7 +1419,7 @@ class EngineService(service.ServiceBase):
|
||||||
|
|
||||||
LOG.info('Deleting stack %s', st.name)
|
LOG.info('Deleting stack %s', st.name)
|
||||||
stack = parser.Stack.load(cnxt, stack=st)
|
stack = parser.Stack.load(cnxt, stack=st)
|
||||||
self.resource_enforcer.enforce_stack(stack)
|
self.resource_enforcer.enforce_stack(stack, is_registered_policy=True)
|
||||||
|
|
||||||
if stack.convergence and cfg.CONF.convergence_engine:
|
if stack.convergence and cfg.CONF.convergence_engine:
|
||||||
def convergence_delete():
|
def convergence_delete():
|
||||||
|
@ -1465,7 +1467,8 @@ class EngineService(service.ServiceBase):
|
||||||
def reload():
|
def reload():
|
||||||
st = self._get_stack(cnxt, stack_identity)
|
st = self._get_stack(cnxt, stack_identity)
|
||||||
stack = parser.Stack.load(cnxt, stack=st)
|
stack = parser.Stack.load(cnxt, stack=st)
|
||||||
self.resource_enforcer.enforce_stack(stack)
|
self.resource_enforcer.enforce_stack(stack,
|
||||||
|
is_registered_policy=True)
|
||||||
return stack
|
return stack
|
||||||
|
|
||||||
def wait_then_delete(stack):
|
def wait_then_delete(stack):
|
||||||
|
@ -1642,7 +1645,8 @@ class EngineService(service.ServiceBase):
|
||||||
:param type_name: Name of the resource type to obtain the schema of.
|
:param type_name: Name of the resource type to obtain the schema of.
|
||||||
:param with_description: Return result with description or not.
|
:param with_description: Return result with description or not.
|
||||||
"""
|
"""
|
||||||
self.resource_enforcer.enforce(cnxt, type_name)
|
self.resource_enforcer.enforce(cnxt, type_name,
|
||||||
|
is_registered_policy=True)
|
||||||
try:
|
try:
|
||||||
resource_class = resources.global_env().get_class(type_name)
|
resource_class = resources.global_env().get_class(type_name)
|
||||||
except exception.NotFound:
|
except exception.NotFound:
|
||||||
|
@ -1703,7 +1707,8 @@ class EngineService(service.ServiceBase):
|
||||||
:param type_name: Name of the resource type to generate a template for.
|
:param type_name: Name of the resource type to generate a template for.
|
||||||
:param template_type: the template type to generate, cfn or hot.
|
:param template_type: the template type to generate, cfn or hot.
|
||||||
"""
|
"""
|
||||||
self.resource_enforcer.enforce(cnxt, type_name)
|
self.resource_enforcer.enforce(cnxt, type_name,
|
||||||
|
is_registered_policy=True)
|
||||||
try:
|
try:
|
||||||
resource_class = resources.global_env().get_class(type_name)
|
resource_class = resources.global_env().get_class(type_name)
|
||||||
except exception.NotFound:
|
except exception.NotFound:
|
||||||
|
@ -2047,7 +2052,7 @@ class EngineService(service.ServiceBase):
|
||||||
s = self._get_stack(cnxt, stack_identity)
|
s = self._get_stack(cnxt, stack_identity)
|
||||||
|
|
||||||
stack = parser.Stack.load(cnxt, stack=s)
|
stack = parser.Stack.load(cnxt, stack=s)
|
||||||
self.resource_enforcer.enforce_stack(stack)
|
self.resource_enforcer.enforce_stack(stack, is_registered_policy=True)
|
||||||
self.thread_group_mgr.start_with_lock(cnxt, stack, self.engine_id,
|
self.thread_group_mgr.start_with_lock(cnxt, stack, self.engine_id,
|
||||||
_stack_suspend, stack)
|
_stack_suspend, stack)
|
||||||
|
|
||||||
|
@ -2061,7 +2066,7 @@ class EngineService(service.ServiceBase):
|
||||||
s = self._get_stack(cnxt, stack_identity)
|
s = self._get_stack(cnxt, stack_identity)
|
||||||
|
|
||||||
stack = parser.Stack.load(cnxt, stack=s)
|
stack = parser.Stack.load(cnxt, stack=s)
|
||||||
self.resource_enforcer.enforce_stack(stack)
|
self.resource_enforcer.enforce_stack(stack, is_registered_policy=True)
|
||||||
self.thread_group_mgr.start_with_lock(cnxt, stack, self.engine_id,
|
self.thread_group_mgr.start_with_lock(cnxt, stack, self.engine_id,
|
||||||
_stack_resume, stack)
|
_stack_resume, stack)
|
||||||
|
|
||||||
|
@ -2146,7 +2151,7 @@ class EngineService(service.ServiceBase):
|
||||||
|
|
||||||
s = self._get_stack(cnxt, stack_identity)
|
s = self._get_stack(cnxt, stack_identity)
|
||||||
stack = parser.Stack.load(cnxt, stack=s)
|
stack = parser.Stack.load(cnxt, stack=s)
|
||||||
self.resource_enforcer.enforce_stack(stack)
|
self.resource_enforcer.enforce_stack(stack, is_registered_policy=True)
|
||||||
snapshot = snapshot_object.Snapshot.get_snapshot_by_stack(
|
snapshot = snapshot_object.Snapshot.get_snapshot_by_stack(
|
||||||
cnxt, snapshot_id, s)
|
cnxt, snapshot_id, s)
|
||||||
# FIXME(pas-ha) has to be amended to deny restoring stacks
|
# FIXME(pas-ha) has to be amended to deny restoring stacks
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
from heat.policies import base
|
from heat.policies import base
|
||||||
|
from heat.policies import resource_types
|
||||||
from heat.policies import stacks
|
from heat.policies import stacks
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,4 +22,5 @@ def list_rules():
|
||||||
return itertools.chain(
|
return itertools.chain(
|
||||||
base.list_rules(),
|
base.list_rules(),
|
||||||
stacks.list_rules(),
|
stacks.list_rules(),
|
||||||
|
resource_types.list_rules(),
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from oslo_policy import policy
|
||||||
|
|
||||||
|
from heat.policies import base
|
||||||
|
|
||||||
|
POLICY_ROOT = 'resource_types:%s'
|
||||||
|
|
||||||
|
resource_types_policies = [
|
||||||
|
policy.RuleDefault(
|
||||||
|
name=POLICY_ROOT % 'OS::Nova::Flavor',
|
||||||
|
check_str=base.RULE_PROJECT_ADMIN),
|
||||||
|
policy.RuleDefault(
|
||||||
|
name=POLICY_ROOT % 'OS::Cinder::EncryptedVolumeType',
|
||||||
|
check_str=base.RULE_PROJECT_ADMIN),
|
||||||
|
policy.RuleDefault(
|
||||||
|
name=POLICY_ROOT % 'OS::Cinder::VolumeType',
|
||||||
|
check_str=base.RULE_PROJECT_ADMIN),
|
||||||
|
policy.RuleDefault(
|
||||||
|
name=POLICY_ROOT % 'OS::Cinder::Quota',
|
||||||
|
check_str=base.RULE_PROJECT_ADMIN),
|
||||||
|
policy.RuleDefault(
|
||||||
|
name=POLICY_ROOT % 'OS::Neutron::Quota',
|
||||||
|
check_str=base.RULE_PROJECT_ADMIN),
|
||||||
|
policy.RuleDefault(
|
||||||
|
name=POLICY_ROOT % 'OS::Nova::Quota',
|
||||||
|
check_str=base.RULE_PROJECT_ADMIN),
|
||||||
|
policy.RuleDefault(
|
||||||
|
name=POLICY_ROOT % 'OS::Manila::ShareType',
|
||||||
|
check_str=base.RULE_PROJECT_ADMIN),
|
||||||
|
policy.RuleDefault(
|
||||||
|
name=POLICY_ROOT % 'OS::Neutron::ProviderNet',
|
||||||
|
check_str=base.RULE_PROJECT_ADMIN),
|
||||||
|
policy.RuleDefault(
|
||||||
|
name=POLICY_ROOT % 'OS::Neutron::QoSPolicy',
|
||||||
|
check_str=base.RULE_PROJECT_ADMIN),
|
||||||
|
policy.RuleDefault(
|
||||||
|
name=POLICY_ROOT % 'OS::Neutron::QoSBandwidthLimitRule',
|
||||||
|
check_str=base.RULE_PROJECT_ADMIN),
|
||||||
|
policy.RuleDefault(
|
||||||
|
name=POLICY_ROOT % 'OS::Neutron::Segment',
|
||||||
|
check_str=base.RULE_PROJECT_ADMIN),
|
||||||
|
policy.RuleDefault(
|
||||||
|
name=POLICY_ROOT % 'OS::Nova::HostAggregate',
|
||||||
|
check_str=base.RULE_PROJECT_ADMIN),
|
||||||
|
policy.RuleDefault(
|
||||||
|
name=POLICY_ROOT % 'OS::Cinder::QoSSpecs',
|
||||||
|
check_str=base.RULE_PROJECT_ADMIN),
|
||||||
|
policy.RuleDefault(
|
||||||
|
name=POLICY_ROOT % 'OS::Cinder::QoSAssociation',
|
||||||
|
check_str=base.RULE_PROJECT_ADMIN),
|
||||||
|
policy.RuleDefault(
|
||||||
|
name=POLICY_ROOT % 'OS::Keystone::*',
|
||||||
|
check_str=base.RULE_PROJECT_ADMIN)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def list_rules():
|
||||||
|
return resource_types_policies
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"context_is_admin": "role:admin",
|
"context_is_admin": "role:admin",
|
||||||
|
|
||||||
"resource_types:OS::Test::AdminOnly": "rule:context_is_admin",
|
"resource_types:OS::Cinder::Quota": "!",
|
||||||
"resource_types:OS::Keystone::*": "rule:context_is_admin"
|
"resource_types:OS::Keystone::*": "rule:context_is_admin"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
from oslo_config import fixture as config_fixture
|
from oslo_config import fixture as config_fixture
|
||||||
from oslo_policy import policy as base_policy
|
|
||||||
|
|
||||||
from heat.common import exception
|
from heat.common import exception
|
||||||
from heat.common import policy
|
from heat.common import policy
|
||||||
|
@ -177,55 +176,70 @@ class TestPolicyEnforcer(common.HeatTestCase):
|
||||||
|
|
||||||
def test_resource_default_rule(self):
|
def test_resource_default_rule(self):
|
||||||
context = utils.dummy_context(roles=['non-admin'])
|
context = utils.dummy_context(roles=['non-admin'])
|
||||||
enforcer = policy.ResourceEnforcer(
|
enforcer = policy.ResourceEnforcer()
|
||||||
policy_file=self.get_policy_file('resources.json'))
|
|
||||||
res_type = "OS::Test::NotInPolicy"
|
res_type = "OS::Test::NotInPolicy"
|
||||||
self.assertTrue(enforcer.enforce(context, res_type))
|
self.assertTrue(enforcer.enforce(context, res_type,
|
||||||
|
is_registered_policy=True))
|
||||||
|
|
||||||
def test_resource_enforce_success(self):
|
def test_resource_enforce_success(self):
|
||||||
context = utils.dummy_context(roles=['admin'])
|
context = utils.dummy_context(roles=['admin'])
|
||||||
enforcer = policy.ResourceEnforcer(
|
enforcer = policy.ResourceEnforcer()
|
||||||
policy_file=self.get_policy_file('resources.json'))
|
res_type = "OS::Keystone::User"
|
||||||
res_type = "OS::Test::AdminOnly"
|
self.assertTrue(enforcer.enforce(context, res_type,
|
||||||
self.assertTrue(enforcer.enforce(context, res_type))
|
is_registered_policy=True))
|
||||||
|
|
||||||
def test_resource_enforce_fail(self):
|
def test_resource_enforce_fail(self):
|
||||||
context = utils.dummy_context(roles=['non-admin'])
|
context = utils.dummy_context(roles=['non-admin'])
|
||||||
enforcer = policy.ResourceEnforcer(
|
enforcer = policy.ResourceEnforcer()
|
||||||
policy_file=self.get_policy_file('resources.json'))
|
res_type = "OS::Nova::Quota"
|
||||||
res_type = "OS::Test::AdminOnly"
|
|
||||||
ex = self.assertRaises(exception.Forbidden,
|
ex = self.assertRaises(exception.Forbidden,
|
||||||
enforcer.enforce,
|
enforcer.enforce,
|
||||||
context, res_type)
|
context, res_type,
|
||||||
|
None, None,
|
||||||
|
True)
|
||||||
self.assertIn(res_type, ex.message)
|
self.assertIn(res_type, ex.message)
|
||||||
|
|
||||||
def test_resource_wildcard_enforce_fail(self):
|
def test_resource_wildcard_enforce_fail(self):
|
||||||
context = utils.dummy_context(roles=['non-admin'])
|
context = utils.dummy_context(roles=['non-admin'])
|
||||||
enforcer = policy.ResourceEnforcer(
|
enforcer = policy.ResourceEnforcer()
|
||||||
policy_file=self.get_policy_file('resources.json'))
|
|
||||||
res_type = "OS::Keystone::User"
|
res_type = "OS::Keystone::User"
|
||||||
ex = self.assertRaises(exception.Forbidden,
|
ex = self.assertRaises(exception.Forbidden,
|
||||||
enforcer.enforce,
|
enforcer.enforce,
|
||||||
context, res_type)
|
context, res_type,
|
||||||
|
None, None,
|
||||||
|
True)
|
||||||
|
|
||||||
self.assertIn(res_type.split("::", 1)[0], ex.message)
|
self.assertIn(res_type.split("::", 1)[0], ex.message)
|
||||||
|
|
||||||
def test_resource_enforce_returns_false(self):
|
def test_resource_enforce_returns_false(self):
|
||||||
context = utils.dummy_context(roles=['non-admin'])
|
context = utils.dummy_context(roles=['non-admin'])
|
||||||
enforcer = policy.ResourceEnforcer(
|
enforcer = policy.ResourceEnforcer(exc=None)
|
||||||
policy_file=self.get_policy_file('resources.json'),
|
res_type = "OS::Keystone::User"
|
||||||
exc=None)
|
self.assertFalse(enforcer.enforce(context, res_type,
|
||||||
res_type = "OS::Test::AdminOnly"
|
is_registered_policy=True))
|
||||||
self.assertFalse(enforcer.enforce(context, res_type))
|
self.assertIsNotNone(enforcer.enforce(context, res_type,
|
||||||
self.assertIsNotNone(enforcer.enforce(context, res_type))
|
is_registered_policy=True))
|
||||||
|
|
||||||
def test_resource_enforce_exc_on_false(self):
|
def test_resource_enforce_exc_on_false(self):
|
||||||
context = utils.dummy_context(roles=['non-admin'])
|
context = utils.dummy_context(roles=['non-admin'])
|
||||||
enforcer = policy.ResourceEnforcer(
|
enforcer = policy.ResourceEnforcer()
|
||||||
policy_file=self.get_policy_file('resources.json'))
|
res_type = "OS::Keystone::User"
|
||||||
res_type = "OS::Test::AdminOnly"
|
|
||||||
self.patchobject(base_policy.Enforcer, 'enforce',
|
|
||||||
return_value=False)
|
|
||||||
ex = self.assertRaises(exception.Forbidden,
|
ex = self.assertRaises(exception.Forbidden,
|
||||||
enforcer.enforce,
|
enforcer.enforce,
|
||||||
context, res_type)
|
context, res_type,
|
||||||
|
None, None,
|
||||||
|
True)
|
||||||
|
|
||||||
|
self.assertIn(res_type, ex.message)
|
||||||
|
|
||||||
|
def test_resource_enforce_override_deny_admin(self):
|
||||||
|
context = utils.dummy_context(roles=['admin'])
|
||||||
|
enforcer = policy.ResourceEnforcer(
|
||||||
|
policy_file=self.get_policy_file('resources.json'))
|
||||||
|
res_type = "OS::Cinder::Quota"
|
||||||
|
ex = self.assertRaises(exception.Forbidden,
|
||||||
|
enforcer.enforce,
|
||||||
|
context, res_type,
|
||||||
|
None, None,
|
||||||
|
True)
|
||||||
self.assertIn(res_type, ex.message)
|
self.assertIn(res_type, ex.message)
|
||||||
|
|
Loading…
Reference in New Issue