Policy in code
Adding the beginning implementation for registering and using default policy rules in code. Rules are defined in the new policies module and added to the return list __init__.py. Default policies can now be maintained in code and registered via listing mechanisms in the policies module. As we go, we can remove the duplicated default policies from our policy.json file. This commit specifically: - Creates a new module called `policies` to hold our in code defaults. - Ensure we pass our in code policy list to our policy ENFORCER. - Add base policy module for common policy rules. - Add service default policy module for policy rules. - Add endpoint default policy module for policy rules. - Add regions default policy module for policy rules. partially-implements blueprint policy-in-code Co-Authored-By: Richard Avelar csravelar@gmail.com Change-Id: Ic47b1e8b0d479032d8a7b9891ed9800be7036d94
This commit is contained in:
parent
c16f68e3a3
commit
c734b58581
@ -1,33 +1,4 @@
|
||||
{
|
||||
"admin_required": "role:admin or is_admin:1",
|
||||
"service_role": "role:service",
|
||||
"service_or_admin": "rule:admin_required or rule:service_role",
|
||||
"owner" : "user_id:%(user_id)s",
|
||||
"admin_or_owner": "rule:admin_required or rule:owner",
|
||||
"token_subject": "user_id:%(target.token.user_id)s",
|
||||
"admin_or_token_subject": "rule:admin_required or rule:token_subject",
|
||||
"service_admin_or_token_subject": "rule:service_or_admin or rule:token_subject",
|
||||
|
||||
"default": "rule:admin_required",
|
||||
|
||||
"identity:get_region": "",
|
||||
"identity:list_regions": "",
|
||||
"identity:create_region": "rule:admin_required",
|
||||
"identity:update_region": "rule:admin_required",
|
||||
"identity:delete_region": "rule:admin_required",
|
||||
|
||||
"identity:get_service": "rule:admin_required",
|
||||
"identity:list_services": "rule:admin_required",
|
||||
"identity:create_service": "rule:admin_required",
|
||||
"identity:update_service": "rule:admin_required",
|
||||
"identity:delete_service": "rule:admin_required",
|
||||
|
||||
"identity:get_endpoint": "rule:admin_required",
|
||||
"identity:list_endpoints": "rule:admin_required",
|
||||
"identity:create_endpoint": "rule:admin_required",
|
||||
"identity:update_endpoint": "rule:admin_required",
|
||||
"identity:delete_endpoint": "rule:admin_required",
|
||||
|
||||
"identity:get_domain": "rule:admin_required or token.project.domain.id:%(target.domain.id)s",
|
||||
"identity:list_domains": "rule:admin_required",
|
||||
"identity:create_domain": "rule:admin_required",
|
||||
|
@ -23,6 +23,7 @@ import six
|
||||
from keystone.common import authorization
|
||||
from keystone.common import dependency
|
||||
from keystone.common import driver_hints
|
||||
from keystone.common import policy
|
||||
from keystone.common import utils
|
||||
from keystone.common import wsgi
|
||||
import keystone.conf
|
||||
@ -157,9 +158,7 @@ def protected(callback=None):
|
||||
# Add in the kwargs, which means that any entity provided as a
|
||||
# parameter for calls like create and update will be included.
|
||||
policy_dict.update(kwargs)
|
||||
self.policy_api.enforce(creds,
|
||||
action,
|
||||
utils.flatten_dict(policy_dict))
|
||||
policy.enforce(creds, action, utils.flatten_dict(policy_dict))
|
||||
LOG.debug('RBAC: Authorization granted')
|
||||
return f(self, request, *args, **kwargs)
|
||||
return inner
|
||||
@ -225,9 +224,7 @@ def filterprotected(*filters, **callback):
|
||||
for key in kwargs:
|
||||
target[key] = kwargs[key]
|
||||
|
||||
self.policy_api.enforce(creds,
|
||||
action,
|
||||
utils.flatten_dict(target))
|
||||
policy.enforce(creds, action, utils.flatten_dict(target))
|
||||
|
||||
LOG.debug('RBAC: Authorization granted')
|
||||
else:
|
||||
@ -772,9 +769,7 @@ class V3Controller(wsgi.Application):
|
||||
policy_dict.update(prep_info['input_attr'])
|
||||
if 'filter_attr' in prep_info:
|
||||
policy_dict.update(prep_info['filter_attr'])
|
||||
self.policy_api.enforce(creds,
|
||||
action,
|
||||
utils.flatten_dict(policy_dict))
|
||||
policy.enforce(creds, action, utils.flatten_dict(policy_dict))
|
||||
LOG.debug('RBAC: Authorization granted')
|
||||
|
||||
@classmethod
|
||||
|
27
keystone/common/policies/__init__.py
Normal file
27
keystone/common/policies/__init__.py
Normal file
@ -0,0 +1,27 @@
|
||||
# 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.
|
||||
|
||||
import itertools
|
||||
|
||||
from keystone.common.policies import base
|
||||
from keystone.common.policies import endpoint
|
||||
from keystone.common.policies import region
|
||||
from keystone.common.policies import service
|
||||
|
||||
|
||||
def list_rules():
|
||||
return itertools.chain(
|
||||
base.list_rules(),
|
||||
endpoint.list_rules(),
|
||||
region.list_rules(),
|
||||
service.list_rules()
|
||||
)
|
52
keystone/common/policies/base.py
Normal file
52
keystone/common/policies/base.py
Normal file
@ -0,0 +1,52 @@
|
||||
# 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
|
||||
|
||||
IDENTITY = 'identity:%s'
|
||||
RULE_ADMIN_REQUIRED = 'rule:admin_required'
|
||||
RULE_ADMIN_OR_OWNER = 'rule:admin_or_owner'
|
||||
|
||||
|
||||
rules = [
|
||||
policy.RuleDefault(
|
||||
name='admin_required',
|
||||
check_str='role:admin or is_admin:1'),
|
||||
policy.RuleDefault(
|
||||
name='service_role',
|
||||
check_str='role:service'),
|
||||
policy.RuleDefault(
|
||||
name='service_or_admin',
|
||||
check_str='rule:admin_required or rule:service_role'),
|
||||
policy.RuleDefault(
|
||||
name='owner',
|
||||
check_str='user_id:%(user_id)s'),
|
||||
policy.RuleDefault(
|
||||
name='admin_or_owner',
|
||||
check_str='rule:admin_required or rule:owner'),
|
||||
policy.RuleDefault(
|
||||
name='token_subject',
|
||||
check_str='user_id:%(target.token.user_id)s'),
|
||||
policy.RuleDefault(
|
||||
name='admin_or_token_subject',
|
||||
check_str='rule:admin_required or rule:token_subject'),
|
||||
policy.RuleDefault(
|
||||
name='service_admin_or_token_subject',
|
||||
check_str='rule:service_or_admin or rule:token_subject'),
|
||||
policy.RuleDefault(
|
||||
name='default',
|
||||
check_str='rule:admin_required')
|
||||
]
|
||||
|
||||
|
||||
def list_rules():
|
||||
return rules
|
37
keystone/common/policies/endpoint.py
Normal file
37
keystone/common/policies/endpoint.py
Normal file
@ -0,0 +1,37 @@
|
||||
# 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 keystone.common.policies import base
|
||||
|
||||
endpoint_policies = [
|
||||
policy.RuleDefault(
|
||||
name=base.IDENTITY % 'get_endpoint',
|
||||
check_str=base.RULE_ADMIN_REQUIRED),
|
||||
policy.RuleDefault(
|
||||
name=base.IDENTITY % 'list_endpoints',
|
||||
check_str=base.RULE_ADMIN_REQUIRED),
|
||||
policy.RuleDefault(
|
||||
name=base.IDENTITY % 'create_endpoint',
|
||||
check_str=base.RULE_ADMIN_REQUIRED),
|
||||
policy.RuleDefault(
|
||||
name=base.IDENTITY % 'update_endpoint',
|
||||
check_str=base.RULE_ADMIN_REQUIRED),
|
||||
policy.RuleDefault(
|
||||
name=base.IDENTITY % 'delete_endpoint',
|
||||
check_str=base.RULE_ADMIN_REQUIRED)
|
||||
]
|
||||
|
||||
|
||||
def list_rules():
|
||||
return endpoint_policies
|
37
keystone/common/policies/region.py
Normal file
37
keystone/common/policies/region.py
Normal file
@ -0,0 +1,37 @@
|
||||
# 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 keystone.common.policies import base
|
||||
|
||||
region_policies = [
|
||||
policy.RuleDefault(
|
||||
name=base.IDENTITY % 'get_region',
|
||||
check_str=''),
|
||||
policy.RuleDefault(
|
||||
name=base.IDENTITY % 'list_regions',
|
||||
check_str=''),
|
||||
policy.RuleDefault(
|
||||
name=base.IDENTITY % 'create_region',
|
||||
check_str=base.RULE_ADMIN_REQUIRED),
|
||||
policy.RuleDefault(
|
||||
name=base.IDENTITY % 'update_region',
|
||||
check_str=base.RULE_ADMIN_REQUIRED),
|
||||
policy.RuleDefault(
|
||||
name=base.IDENTITY % 'delete_region',
|
||||
check_str=base.RULE_ADMIN_REQUIRED),
|
||||
]
|
||||
|
||||
|
||||
def list_rules():
|
||||
return region_policies
|
37
keystone/common/policies/service.py
Normal file
37
keystone/common/policies/service.py
Normal file
@ -0,0 +1,37 @@
|
||||
# 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 keystone.common.policies import base
|
||||
|
||||
service_policies = [
|
||||
policy.RuleDefault(
|
||||
name=base.IDENTITY % 'get_service',
|
||||
check_str=base.RULE_ADMIN_REQUIRED),
|
||||
policy.RuleDefault(
|
||||
name=base.IDENTITY % 'list_services',
|
||||
check_str=base.RULE_ADMIN_REQUIRED),
|
||||
policy.RuleDefault(
|
||||
name=base.IDENTITY % 'create_service',
|
||||
check_str=base.RULE_ADMIN_REQUIRED),
|
||||
policy.RuleDefault(
|
||||
name=base.IDENTITY % 'update_service',
|
||||
check_str=base.RULE_ADMIN_REQUIRED),
|
||||
policy.RuleDefault(
|
||||
name=base.IDENTITY % 'delete_service',
|
||||
check_str=base.RULE_ADMIN_REQUIRED)
|
||||
]
|
||||
|
||||
|
||||
def list_rules():
|
||||
return service_policies
|
67
keystone/common/policy.py
Normal file
67
keystone/common/policy.py
Normal file
@ -0,0 +1,67 @@
|
||||
# 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 as common_policy
|
||||
|
||||
from keystone.common import policies
|
||||
import keystone.conf
|
||||
from keystone import exception
|
||||
|
||||
|
||||
CONF = keystone.conf.CONF
|
||||
|
||||
|
||||
_ENFORCER = None
|
||||
|
||||
|
||||
def reset():
|
||||
global _ENFORCER
|
||||
_ENFORCER = None
|
||||
|
||||
|
||||
def init():
|
||||
global _ENFORCER
|
||||
if not _ENFORCER:
|
||||
_ENFORCER = common_policy.Enforcer(CONF)
|
||||
register_rules(_ENFORCER)
|
||||
|
||||
|
||||
def enforce(credentials, action, target, do_raise=True):
|
||||
"""Verify that the action is valid on the target in this context.
|
||||
|
||||
:param credentials: user credentials
|
||||
:param action: string representing the action to be checked, which should
|
||||
be colon separated for clarity.
|
||||
:param target: dictionary representing the object of the action for object
|
||||
creation this should be a dictionary representing the
|
||||
location of the object e.g. {'project_id':
|
||||
object.project_id}
|
||||
:raises keystone.exception.Forbidden: If verification fails.
|
||||
|
||||
Actions should be colon separated for clarity. For example:
|
||||
|
||||
* identity:list_users
|
||||
|
||||
"""
|
||||
init()
|
||||
|
||||
# Add the exception arguments if asked to do a raise
|
||||
extra = {}
|
||||
if do_raise:
|
||||
extra.update(exc=exception.ForbiddenAction, action=action,
|
||||
do_raise=do_raise)
|
||||
|
||||
return _ENFORCER.enforce(action, target, credentials, **extra)
|
||||
|
||||
|
||||
def register_rules(enforcer):
|
||||
enforcer.register_defaults(policies.list_rules())
|
@ -37,6 +37,7 @@ import webob.exc
|
||||
|
||||
from keystone.common import dependency
|
||||
from keystone.common import json_home
|
||||
from keystone.common import policy
|
||||
from keystone.common import request as request_mod
|
||||
from keystone.common import utils
|
||||
import keystone.conf
|
||||
@ -312,7 +313,7 @@ class Application(BaseApplication):
|
||||
|
||||
creds['roles'] = user_token_ref.role_names
|
||||
# Accept either is_admin or the admin role
|
||||
self.policy_api.enforce(creds, 'admin_required', {})
|
||||
policy.enforce(creds, 'admin_required', {})
|
||||
|
||||
def _attribute_is_empty(self, ref, attribute):
|
||||
"""Determine if the attribute in ref is empty or None."""
|
||||
|
@ -16,8 +16,8 @@
|
||||
"""Policy engine for keystone."""
|
||||
|
||||
from oslo_log import log
|
||||
from oslo_policy import policy as common_policy
|
||||
|
||||
from keystone.common import policy
|
||||
import keystone.conf
|
||||
from keystone import exception
|
||||
from keystone.policy.backends import base
|
||||
@ -27,55 +27,13 @@ CONF = keystone.conf.CONF
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
_ENFORCER = None
|
||||
|
||||
|
||||
def reset():
|
||||
global _ENFORCER
|
||||
_ENFORCER = None
|
||||
|
||||
|
||||
def init():
|
||||
global _ENFORCER
|
||||
if not _ENFORCER:
|
||||
_ENFORCER = common_policy.Enforcer(CONF)
|
||||
|
||||
|
||||
def enforce(credentials, action, target, do_raise=True):
|
||||
"""Verify that the action is valid on the target in this context.
|
||||
|
||||
:param credentials: user credentials
|
||||
:param action: string representing the action to be checked, which should
|
||||
be colon separated for clarity.
|
||||
:param target: dictionary representing the object of the action for object
|
||||
creation this should be a dictionary representing the
|
||||
location of the object e.g. {'project_id':
|
||||
object.project_id}
|
||||
:raises keystone.exception.Forbidden: If verification fails.
|
||||
|
||||
Actions should be colon separated for clarity. For example:
|
||||
|
||||
* identity:list_users
|
||||
|
||||
"""
|
||||
init()
|
||||
|
||||
# Add the exception arguments if asked to do a raise
|
||||
extra = {}
|
||||
if do_raise:
|
||||
extra.update(exc=exception.ForbiddenAction, action=action,
|
||||
do_raise=do_raise)
|
||||
|
||||
return _ENFORCER.enforce(action, target, credentials, **extra)
|
||||
|
||||
|
||||
class Policy(base.PolicyDriverBase):
|
||||
def enforce(self, credentials, action, target):
|
||||
msg = 'enforce %(action)s: %(credentials)s'
|
||||
LOG.debug(msg, {
|
||||
'action': action,
|
||||
'credentials': credentials})
|
||||
enforce(credentials, action, target)
|
||||
policy.enforce(credentials, action, target)
|
||||
|
||||
def create_policy(self, policy_id, policy):
|
||||
raise exception.NotImplemented()
|
||||
|
@ -14,7 +14,7 @@
|
||||
import fixtures
|
||||
from oslo_policy import opts
|
||||
|
||||
from keystone.policy.backends import rules
|
||||
from keystone.common import policy
|
||||
|
||||
|
||||
class Policy(fixtures.Fixture):
|
||||
@ -29,5 +29,5 @@ class Policy(fixtures.Fixture):
|
||||
opts.set_defaults(self._config_fixture.conf)
|
||||
self._config_fixture.config(group='oslo_policy',
|
||||
policy_file=self._policy_file)
|
||||
rules.init()
|
||||
self.addCleanup(rules.reset)
|
||||
policy.init()
|
||||
self.addCleanup(policy.reset)
|
||||
|
@ -21,9 +21,10 @@ from oslo_policy import policy as common_policy
|
||||
import six
|
||||
from testtools import matchers
|
||||
|
||||
from keystone.common import policies
|
||||
from keystone.common import policy
|
||||
import keystone.conf
|
||||
from keystone import exception
|
||||
from keystone.policy.backends import rules
|
||||
from keystone.tests import unit
|
||||
from keystone.tests.unit import ksfixtures
|
||||
from keystone.tests.unit.ksfixtures import temporaryfile
|
||||
@ -50,11 +51,11 @@ class PolicyFileTestCase(unit.TestCase):
|
||||
empty_credentials = {}
|
||||
with open(self.tmpfilename, "w") as policyfile:
|
||||
policyfile.write("""{"example:test": []}""")
|
||||
rules.enforce(empty_credentials, action, self.target)
|
||||
policy.enforce(empty_credentials, action, self.target)
|
||||
with open(self.tmpfilename, "w") as policyfile:
|
||||
policyfile.write("""{"example:test": ["false:false"]}""")
|
||||
rules._ENFORCER.clear()
|
||||
self.assertRaises(exception.ForbiddenAction, rules.enforce,
|
||||
policy._ENFORCER.clear()
|
||||
self.assertRaises(exception.ForbiddenAction, policy.enforce,
|
||||
empty_credentials, action, self.target)
|
||||
|
||||
|
||||
@ -81,39 +82,39 @@ class PolicyTestCase(unit.TestCase):
|
||||
|
||||
def _set_rules(self):
|
||||
these_rules = common_policy.Rules.from_dict(self.rules)
|
||||
rules._ENFORCER.set_rules(these_rules)
|
||||
policy._ENFORCER.set_rules(these_rules)
|
||||
|
||||
def test_enforce_nonexistent_action_throws(self):
|
||||
action = "example:noexist"
|
||||
self.assertRaises(exception.ForbiddenAction, rules.enforce,
|
||||
self.assertRaises(exception.ForbiddenAction, policy.enforce,
|
||||
self.credentials, action, self.target)
|
||||
|
||||
def test_enforce_bad_action_throws(self):
|
||||
action = "example:denied"
|
||||
self.assertRaises(exception.ForbiddenAction, rules.enforce,
|
||||
self.assertRaises(exception.ForbiddenAction, policy.enforce,
|
||||
self.credentials, action, self.target)
|
||||
|
||||
def test_enforce_good_action(self):
|
||||
action = "example:allowed"
|
||||
rules.enforce(self.credentials, action, self.target)
|
||||
policy.enforce(self.credentials, action, self.target)
|
||||
|
||||
def test_templatized_enforcement(self):
|
||||
target_mine = {'project_id': 'fake'}
|
||||
target_not_mine = {'project_id': 'another'}
|
||||
credentials = {'project_id': 'fake', 'roles': []}
|
||||
action = "example:my_file"
|
||||
rules.enforce(credentials, action, target_mine)
|
||||
self.assertRaises(exception.ForbiddenAction, rules.enforce,
|
||||
policy.enforce(credentials, action, target_mine)
|
||||
self.assertRaises(exception.ForbiddenAction, policy.enforce,
|
||||
credentials, action, target_not_mine)
|
||||
|
||||
def test_early_AND_enforcement(self):
|
||||
action = "example:early_and_fail"
|
||||
self.assertRaises(exception.ForbiddenAction, rules.enforce,
|
||||
self.assertRaises(exception.ForbiddenAction, policy.enforce,
|
||||
self.credentials, action, self.target)
|
||||
|
||||
def test_early_OR_enforcement(self):
|
||||
action = "example:early_or_success"
|
||||
rules.enforce(self.credentials, action, self.target)
|
||||
policy.enforce(self.credentials, action, self.target)
|
||||
|
||||
def test_ignore_case_role_check(self):
|
||||
lowercase_action = "example:lowercase_admin"
|
||||
@ -121,8 +122,8 @@ class PolicyTestCase(unit.TestCase):
|
||||
# NOTE(dprince): We mix case in the Admin role here to ensure
|
||||
# case is ignored
|
||||
admin_credentials = {'roles': ['AdMiN']}
|
||||
rules.enforce(admin_credentials, lowercase_action, self.target)
|
||||
rules.enforce(admin_credentials, uppercase_action, self.target)
|
||||
policy.enforce(admin_credentials, lowercase_action, self.target)
|
||||
policy.enforce(admin_credentials, uppercase_action, self.target)
|
||||
|
||||
|
||||
class DefaultPolicyTestCase(unit.TestCase):
|
||||
@ -142,21 +143,21 @@ class DefaultPolicyTestCase(unit.TestCase):
|
||||
# monkeypatch load_roles() so it does nothing. This seem like a bug in
|
||||
# Oslo policy as we shouldn't have to reload the rules if they have
|
||||
# already been set using set_rules().
|
||||
self._old_load_rules = rules._ENFORCER.load_rules
|
||||
self.addCleanup(setattr, rules._ENFORCER, 'load_rules',
|
||||
self._old_load_rules = policy._ENFORCER.load_rules
|
||||
self.addCleanup(setattr, policy._ENFORCER, 'load_rules',
|
||||
self._old_load_rules)
|
||||
rules._ENFORCER.load_rules = lambda *args, **kwargs: None
|
||||
policy._ENFORCER.load_rules = lambda *args, **kwargs: None
|
||||
|
||||
def _set_rules(self, default_rule):
|
||||
these_rules = common_policy.Rules.from_dict(self.rules, default_rule)
|
||||
rules._ENFORCER.set_rules(these_rules)
|
||||
policy._ENFORCER.set_rules(these_rules)
|
||||
|
||||
def test_policy_called(self):
|
||||
self.assertRaises(exception.ForbiddenAction, rules.enforce,
|
||||
self.assertRaises(exception.ForbiddenAction, policy.enforce,
|
||||
self.credentials, "example:exist", {})
|
||||
|
||||
def test_not_found_policy_calls_default(self):
|
||||
rules.enforce(self.credentials, "example:noexist", {})
|
||||
policy.enforce(self.credentials, "example:noexist", {})
|
||||
|
||||
def test_default_not_found(self):
|
||||
new_default_rule = "default_noexist"
|
||||
@ -164,9 +165,9 @@ class DefaultPolicyTestCase(unit.TestCase):
|
||||
# as it is recreating the rules with its own default_rule instead
|
||||
# of the default_rule passed in from set_rules(). I think this is a
|
||||
# bug in Oslo policy.
|
||||
rules._ENFORCER.default_rule = new_default_rule
|
||||
policy._ENFORCER.default_rule = new_default_rule
|
||||
self._set_rules(new_default_rule)
|
||||
self.assertRaises(exception.ForbiddenAction, rules.enforce,
|
||||
self.assertRaises(exception.ForbiddenAction, policy.enforce,
|
||||
self.credentials, "example:noexist", {})
|
||||
|
||||
|
||||
@ -175,6 +176,18 @@ class PolicyJsonTestCase(unit.TestCase):
|
||||
def _load_entries(self, filename):
|
||||
return set(json.load(open(filename)))
|
||||
|
||||
def _add_missing_default_rules(self, rules):
|
||||
"""Add default rules and their values to the given rules dict.
|
||||
|
||||
The given rules dict may have an incomplete set of policy rules.
|
||||
This method will add the default policy rules and their values to the
|
||||
dict. It will not override the existing rules. This method is temporary
|
||||
and is only needed until we move all policy.json rules into code.
|
||||
"""
|
||||
for rule in policies.list_rules():
|
||||
if rule.name not in rules:
|
||||
rules[rule.name] = rule.check_str
|
||||
|
||||
def test_json_examples_have_matching_entries(self):
|
||||
policy_keys = self._load_entries(unit.dirs.etc('policy.json'))
|
||||
cloud_policy_keys = self._load_entries(
|
||||
@ -202,9 +215,11 @@ class PolicyJsonTestCase(unit.TestCase):
|
||||
'is_admin_project': True, 'project_id': None,
|
||||
'domain_id': uuid.uuid4().hex}
|
||||
|
||||
standard_policy = unit.dirs.etc('policy.json')
|
||||
enforcer = common_policy.Enforcer(CONF, policy_file=standard_policy)
|
||||
result = enforcer.enforce(action, target, credentials)
|
||||
# Since we are moving policy.json defaults to code, we instead call
|
||||
# `policy.init()` which does the enforce setup for us with the added
|
||||
# bonus of registering the in code default policies.
|
||||
policy.init()
|
||||
result = policy._ENFORCER.enforce(action, target, credentials)
|
||||
self.assertTrue(result)
|
||||
|
||||
domain_policy = unit.dirs.etc('policy.v3cloudsample.json')
|
||||
@ -215,8 +230,8 @@ class PolicyJsonTestCase(unit.TestCase):
|
||||
def test_all_targets_documented(self):
|
||||
# All the targets in the sample policy file must be documented in
|
||||
# doc/source/policy_mapping.rst.
|
||||
|
||||
policy_keys = self._load_entries(unit.dirs.etc('policy.json'))
|
||||
policy_keys = json.load(open((unit.dirs.etc('policy.json'))))
|
||||
self._add_missing_default_rules(policy_keys)
|
||||
|
||||
# These keys are in the policy.json but aren't targets.
|
||||
policy_rule_keys = [
|
||||
|
@ -33,12 +33,12 @@ from testtools import testcase
|
||||
|
||||
from keystone import auth
|
||||
from keystone.auth.plugins import totp
|
||||
from keystone.common import policy
|
||||
from keystone.common import utils
|
||||
import keystone.conf
|
||||
from keystone.credential.providers import fernet as credential_fernet
|
||||
from keystone import exception
|
||||
from keystone.identity.backends import resource_options as ro
|
||||
from keystone.policy.backends import rules
|
||||
from keystone.tests.common import auth as common_auth
|
||||
from keystone.tests import unit
|
||||
from keystone.tests.unit import ksfixtures
|
||||
@ -4276,7 +4276,7 @@ class TrustAPIBehavior(test_v3.RestfulTestCase):
|
||||
self.chained_trust_ref['roles'] = [{'id': role['id']}]
|
||||
|
||||
# Bypass policy enforcement
|
||||
with mock.patch.object(rules, 'enforce', return_value=True):
|
||||
with mock.patch.object(policy, 'enforce', return_value=True):
|
||||
self.post('/OS-TRUST/trusts',
|
||||
body={'trust': self.chained_trust_ref},
|
||||
token=trust_token,
|
||||
@ -4969,7 +4969,7 @@ class TestTrustChain(test_v3.RestfulTestCase):
|
||||
self.identity_api.update_user(disabled['id'], disabled)
|
||||
|
||||
# Bypass policy enforcement
|
||||
with mock.patch.object(rules, 'enforce', return_value=True):
|
||||
with mock.patch.object(policy, 'enforce', return_value=True):
|
||||
headers = {'X-Subject-Token': self.last_token}
|
||||
self.head('/auth/tokens', headers=headers,
|
||||
expected_status=http_client.FORBIDDEN)
|
||||
@ -4981,7 +4981,7 @@ class TestTrustChain(test_v3.RestfulTestCase):
|
||||
|
||||
# Bypass policy enforcement
|
||||
# Delete trustee will invalidate the trust.
|
||||
with mock.patch.object(rules, 'enforce', return_value=True):
|
||||
with mock.patch.object(policy, 'enforce', return_value=True):
|
||||
headers = {'X-Subject-Token': self.last_token}
|
||||
self.head('/auth/tokens', headers=headers,
|
||||
expected_status=http_client.NOT_FOUND)
|
||||
|
Loading…
Reference in New Issue
Block a user