diff --git a/heat/common/policy.py b/heat/common/policy.py index 0490461e6d..cf565e70c7 100644 --- a/heat/common/policy.py +++ b/heat/common/policy.py @@ -94,8 +94,7 @@ class ResourceEnforcer(Enforcer): super(ResourceEnforcer, self).__init__( default_rule=default_rule, **kwargs) - def enforce(self, context, res_type, scope=None, target=None): - # NOTE(pas-ha): try/except just to log the exception + def _enforce(self, context, res_type, scope=None, target=None): try: result = super(ResourceEnforcer, self).enforce( context, res_type, @@ -107,8 +106,20 @@ class ResourceEnforcer(Enforcer): if not result: if self.exc: raise self.exc(action=res_type) - else: - return result + return result + + def enforce(self, context, res_type, scope=None, target=None): + # NOTE(pas-ha): try/except just to log the exception + result = self._enforce(context, res_type, scope, target) + + if result: + # check for wildcard resource types + subparts = res_type.split("::")[:-1] + subparts.append('*') + res_type_wc = "::".join(subparts) + return self._enforce(context, res_type_wc, scope, target) + + return result def enforce_stack(self, stack, scope=None, target=None): for res in stack.resources.values(): diff --git a/heat/tests/policy/resources.json b/heat/tests/policy/resources.json index 045e60d29c..566dac3469 100644 --- a/heat/tests/policy/resources.json +++ b/heat/tests/policy/resources.json @@ -1,6 +1,7 @@ { "context_is_admin": "role:admin", - "resource_types:OS::Test::AdminOnly": "rule:context_is_admin" + "resource_types:OS::Test::AdminOnly": "rule:context_is_admin", + "resource_types:OS::Keystone::*": "rule:context_is_admin" } diff --git a/heat/tests/test_common_policy.py b/heat/tests/test_common_policy.py index 3964c094b1..672d0b9639 100644 --- a/heat/tests/test_common_policy.py +++ b/heat/tests/test_common_policy.py @@ -186,14 +186,14 @@ class TestPolicyEnforcer(common.HeatTestCase): enforcer = policy.ResourceEnforcer( policy_file=self.get_policy_file('resources.json')) res_type = "OS::Test::NotInPolicy" - self.assertIsNone(enforcer.enforce(context, res_type)) + self.assertTrue(enforcer.enforce(context, res_type)) def test_resource_enforce_success(self): context = utils.dummy_context(roles=['admin']) enforcer = policy.ResourceEnforcer( policy_file=self.get_policy_file('resources.json')) res_type = "OS::Test::AdminOnly" - self.assertIsNone(enforcer.enforce(context, res_type)) + self.assertTrue(enforcer.enforce(context, res_type)) def test_resource_enforce_fail(self): context = utils.dummy_context(roles=['non-admin']) @@ -205,6 +205,16 @@ class TestPolicyEnforcer(common.HeatTestCase): context, res_type) self.assertIn(res_type, ex.message) + def test_resource_wildcard_enforce_fail(self): + context = utils.dummy_context(roles=['non-admin']) + enforcer = policy.ResourceEnforcer( + policy_file=self.get_policy_file('resources.json')) + res_type = "OS::Keystone::User" + ex = self.assertRaises(exception.Forbidden, + enforcer.enforce, + context, res_type) + self.assertIn(res_type.split("::", 1)[0], ex.message) + def test_resource_enforce_returns_false(self): context = utils.dummy_context(roles=['non-admin']) enforcer = policy.ResourceEnforcer( @@ -212,6 +222,7 @@ class TestPolicyEnforcer(common.HeatTestCase): exc=None) res_type = "OS::Test::AdminOnly" self.assertFalse(enforcer.enforce(context, res_type)) + self.assertIsNotNone(enforcer.enforce(context, res_type)) def test_resource_enforce_exc_on_false(self): context = utils.dummy_context(roles=['non-admin'])