Merge "Move policy defaults into code"

This commit is contained in:
Zuul 2020-01-09 14:00:55 +00:00 committed by Gerrit Code Review
commit c632586ef3
14 changed files with 241 additions and 75 deletions

3
.gitignore vendored
View File

@ -22,6 +22,9 @@ tests.sqlite
glance/versioninfo
subunit.log
# generated policy file
etc/policy.yaml.sample
# Swap files range from .saa to .swp
*.s[a-w][a-p]

View File

@ -0,0 +1,3 @@
[DEFAULT]
namespace = glance
output_file = etc/policy.yaml.sample

View File

@ -1,63 +1,2 @@
{
"context_is_admin": "role:admin",
"default": "role:admin",
"add_image": "",
"delete_image": "",
"get_image": "",
"get_images": "",
"modify_image": "",
"publicize_image": "role:admin",
"communitize_image": "",
"copy_from": "",
"download_image": "",
"upload_image": "",
"delete_image_location": "",
"get_image_location": "",
"set_image_location": "",
"add_member": "",
"delete_member": "",
"get_member": "",
"get_members": "",
"modify_member": "",
"manage_image_cache": "role:admin",
"get_task": "",
"get_tasks": "",
"add_task": "",
"modify_task": "",
"tasks_api_access": "role:admin",
"deactivate": "",
"reactivate": "",
"get_metadef_namespace": "",
"get_metadef_namespaces":"",
"modify_metadef_namespace":"",
"add_metadef_namespace":"",
"get_metadef_object":"",
"get_metadef_objects":"",
"modify_metadef_object":"",
"add_metadef_object":"",
"list_metadef_resource_types":"",
"get_metadef_resource_type":"",
"add_metadef_resource_type_association":"",
"get_metadef_property":"",
"get_metadef_properties":"",
"modify_metadef_property":"",
"add_metadef_property":"",
"get_metadef_tag":"",
"get_metadef_tags":"",
"modify_metadef_tag":"",
"add_metadef_tag":"",
"add_metadef_tags":""
}

View File

@ -32,34 +32,26 @@ from oslo_policy import policy
from glance.common import exception
import glance.domain.proxy
from glance.i18n import _
from glance import policies
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
_ENFORCER = None
DEFAULT_RULES = policy.Rules.from_dict({
'context_is_admin': 'role:admin',
'default': 'role:admin',
'manage_image_cache': 'role:admin',
})
class Enforcer(policy.Enforcer):
"""Responsible for loading and enforcing rules"""
def __init__(self):
if CONF.find_file(CONF.oslo_policy.policy_file):
kwargs = dict(rules=None, use_conf=True)
else:
kwargs = dict(rules=DEFAULT_RULES, use_conf=False)
super(Enforcer, self).__init__(CONF, overwrite=False, **kwargs)
super(Enforcer, self).__init__(CONF, use_conf=True, overwrite=False)
self.register_defaults(policies.list_rules())
def add_rules(self, rules):
"""Add new rules to the Rules object"""
self.set_rules(rules, overwrite=False, use_conf=self.use_conf)
def enforce(self, context, action, target):
def enforce(self, context, action, target, registered=True):
"""Verifies that the action is valid on the target in this context.
:param context: Glance request context
@ -68,13 +60,15 @@ class Enforcer(policy.Enforcer):
:raises: `glance.common.exception.Forbidden`
:returns: A non-False value if access is allowed.
"""
if registered and action not in self.registered_rules:
raise policy.PolicyNotRegistered(action)
return super(Enforcer, self).enforce(action, target,
context.to_policy_values(),
do_raise=True,
exc=exception.Forbidden,
action=action)
def check(self, context, action, target):
def check(self, context, action, target, registered=True):
"""Verifies that the action is valid on the target in this context.
:param context: Glance request context
@ -82,6 +76,8 @@ class Enforcer(policy.Enforcer):
:param target: Dictionary representing the object of the action.
:returns: A non-False value if access is allowed.
"""
if registered and action not in self.registered_rules:
raise policy.PolicyNotRegistered(action)
return super(Enforcer, self).enforce(action,
target,
context.to_policy_values())

View File

@ -209,7 +209,7 @@ class PropertyRules(object):
def _check_policy(self, property_exp, action, context):
try:
action = ":".join([property_exp, action])
self.policy_enforcer.enforce(context, action, {})
self.policy_enforcer.enforce(context, action, {}, registered=False)
except exception.Forbidden:
return False
return True

View 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 glance.policies import base
from glance.policies import image
from glance.policies import metadef
from glance.policies import tasks
def list_rules():
return itertools.chain(
base.list_rules(),
image.list_rules(),
tasks.list_rules(),
metadef.list_rules(),
)

28
glance/policies/base.py Normal file
View File

@ -0,0 +1,28 @@
# 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
rules = [
policy.RuleDefault(name='default', check_str='',
description='Defines the default rule used for '
'policies that historically had an empty '
'policy in the supplied policy.json file.'),
policy.RuleDefault(name='context_is_admin', check_str='role:admin',
description='Defines the rule for the is_admin:True '
'check.'),
]
def list_rules():
return rules

47
glance/policies/image.py Normal file
View File

@ -0,0 +1,47 @@
# 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
image_policies = [
policy.RuleDefault(name="add_image", check_str="rule:default"),
policy.RuleDefault(name="delete_image", check_str="rule:default"),
policy.RuleDefault(name="get_image", check_str="rule:default"),
policy.RuleDefault(name="get_images", check_str="rule:default"),
policy.RuleDefault(name="modify_image", check_str="rule:default"),
policy.RuleDefault(name="publicize_image", check_str="role:admin"),
policy.RuleDefault(name="communitize_image", check_str="rule:default"),
policy.RuleDefault(name="copy_from", check_str="rule:default"),
policy.RuleDefault(name="download_image", check_str="rule:default"),
policy.RuleDefault(name="upload_image", check_str="rule:default"),
policy.RuleDefault(name="delete_image_location", check_str="rule:default"),
policy.RuleDefault(name="get_image_location", check_str="rule:default"),
policy.RuleDefault(name="set_image_location", check_str="rule:default"),
policy.RuleDefault(name="add_member", check_str="rule:default"),
policy.RuleDefault(name="delete_member", check_str="rule:default"),
policy.RuleDefault(name="get_member", check_str="rule:default"),
policy.RuleDefault(name="get_members", check_str="rule:default"),
policy.RuleDefault(name="modify_member", check_str="rule:default"),
policy.RuleDefault(name="manage_image_cache", check_str="role:admin"),
policy.RuleDefault(name="deactivate", check_str="rule:default"),
policy.RuleDefault(name="reactivate", check_str="rule:default"),
]
def list_rules():
return image_policies

View 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
metadef_policies = [
policy.RuleDefault(name="get_metadef_namespace", check_str="rule:default"),
policy.RuleDefault(name="get_metadef_namespaces",
check_str="rule:default"),
policy.RuleDefault(name="modify_metadef_namespace",
check_str="rule:default"),
policy.RuleDefault(name="add_metadef_namespace", check_str="rule:default"),
policy.RuleDefault(name="get_metadef_object", check_str="rule:default"),
policy.RuleDefault(name="get_metadef_objects", check_str="rule:default"),
policy.RuleDefault(name="modify_metadef_object", check_str="rule:default"),
policy.RuleDefault(name="add_metadef_object", check_str="rule:default"),
policy.RuleDefault(name="list_metadef_resource_types",
check_str="rule:default"),
policy.RuleDefault(name="get_metadef_resource_type",
check_str="rule:default"),
policy.RuleDefault(name="add_metadef_resource_type_association",
check_str="rule:default"),
policy.RuleDefault(name="get_metadef_property", check_str="rule:default"),
policy.RuleDefault(name="get_metadef_properties",
check_str="rule:default"),
policy.RuleDefault(name="modify_metadef_property",
check_str="rule:default"),
policy.RuleDefault(name="add_metadef_property", check_str="rule:default"),
policy.RuleDefault(name="get_metadef_tag", check_str="rule:default"),
policy.RuleDefault(name="get_metadef_tags", check_str="rule:default"),
policy.RuleDefault(name="modify_metadef_tag", check_str="rule:default"),
policy.RuleDefault(name="add_metadef_tag", check_str="rule:default"),
policy.RuleDefault(name="add_metadef_tags", check_str="rule:default"),
]
def list_rules():
return metadef_policies

26
glance/policies/tasks.py Normal file
View File

@ -0,0 +1,26 @@
# 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
task_policies = [
policy.RuleDefault(name="get_task", check_str="rule:default"),
policy.RuleDefault(name="get_tasks", check_str="rule:default"),
policy.RuleDefault(name="add_task", check_str="rule:default"),
policy.RuleDefault(name="modify_task", check_str="rule:default"),
policy.RuleDefault(name="tasks_api_access", check_str="role:admin"),
]
def list_rules():
return task_policies

View File

@ -162,6 +162,22 @@ class TaskFactoryStub(object):
class TestPolicyEnforcer(base.IsolatedUnitTest):
def test_policy_enforce_unregistered(self):
enforcer = glance.api.policy.Enforcer()
context = glance.context.RequestContext(roles=[])
self.assertRaises(glance.api.policy.policy.PolicyNotRegistered,
enforcer.enforce,
context, 'wibble', {})
def test_policy_check_unregistered(self):
enforcer = glance.api.policy.Enforcer()
context = glance.context.RequestContext(roles=[])
self.assertRaises(glance.api.policy.policy.PolicyNotRegistered,
enforcer.check,
context, 'wibble', {})
def test_policy_file_default_rules_default_location(self):
enforcer = glance.api.policy.Enforcer()

View File

@ -0,0 +1,22 @@
---
upgrade:
- |
Policy defaults are now defined in code, as they already were in other
OpenStack services. After upgrading there is no need to provide a
``policy.json`` file (and you should not do so) unless you want to override
the default policies, and only policies you want to override need be
mentioned in the file. You should no longer rely on the ``default`` rule,
and especially not the default value of the rule (which has been relaxed),
to assign a non-default policy to rules not explicitly specified in the
policy file.
security:
- |
If the existing ``policy.json`` file relies on the ``default`` rule for
some policies (i.e. not all policies are explicitly specified in the file)
then the ``default`` rule must be explicitly set (e.g. to
``"role:admin"``) in the file. The new default value for the ``default``
rule is ``""``, whereas since the Queens release it has been
``"role:admin"`` (prior to Queens it was ``"@"``, which allows everything).
After upgrading to this release, the policy file should be replaced by one
that overrides only policies that need to be different from the defaults,
without relying on the ``default`` rule.

View File

@ -66,6 +66,8 @@ glance.database.metadata_backend =
oslo.policy.enforcer =
glance = glance.api.policy:get_enforcer
oslo.policy.policies =
glance = glance.policies:list_rules
glance.flows =
api_image_import = glance.async_.flows.api_image_import:get_flow

View File

@ -62,6 +62,11 @@ commands = {[testenv:functional]commands}
setenv = {[testenv:functional]setenv}
commands = {[testenv:functional]commands}
[testenv:genpolicy]
basepython = python3
commands =
oslopolicy-sample-generator --config-file=etc/glance-policy-generator.conf
[testenv:gateonly]
# NOTE(rosmaita): these tests catch configuration problems for some code
# constants that must be maintained manually; we have them separated out