Add cg policies and clean up old policy handling

This completes the "policy-in-code" work for Cinder by removing
the old policy.json handling. It also adds new in-code checks to
the legacy consistency group code for completeness.

Change-Id: I810b6cb6bba2d95cc5bb477d6e2968ac1734c96b
Depends-on: I364e401227fe43e2bacf8a799e10286ee445f835
Implements: bp policy-in-code
This commit is contained in:
Sean McGinnis 2017-12-01 15:34:32 -06:00 committed by TommyLike
parent af06ceb2c5
commit 9fe72de4b6
8 changed files with 70 additions and 90 deletions

View File

@ -25,10 +25,11 @@ from cinder.api import common
from cinder.api import extensions from cinder.api import extensions
from cinder.api.openstack import wsgi from cinder.api.openstack import wsgi
from cinder.api.views import consistencygroups as consistencygroup_views from cinder.api.views import consistencygroups as consistencygroup_views
from cinder.consistencygroup import api as consistencygroup_api
from cinder import exception from cinder import exception
from cinder import group as group_api from cinder import group as group_api
from cinder.i18n import _ from cinder.i18n import _
from cinder.policies import group_actions as gp_action_policy
from cinder.policies import groups as group_policy
from cinder.volume import group_types from cinder.volume import group_types
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -73,7 +74,7 @@ class ConsistencyGroupsController(wsgi.Controller):
try: try:
group = self._get(context, id) group = self._get(context, id)
consistencygroup_api.check_policy(context, 'delete') context.authorize(gp_action_policy.DELETE_POLICY, target_obj=group)
self.group_api.delete(context, group, force) self.group_api.delete(context, group, force)
# Not found exception will be handled at the wsgi level # Not found exception will be handled at the wsgi level
except exception.InvalidConsistencyGroup as error: except exception.InvalidConsistencyGroup as error:
@ -106,6 +107,7 @@ class ConsistencyGroupsController(wsgi.Controller):
def _get_consistencygroups(self, req, is_detail): def _get_consistencygroups(self, req, is_detail):
"""Returns a list of consistency groups through view builder.""" """Returns a list of consistency groups through view builder."""
context = req.environ['cinder.context'] context = req.environ['cinder.context']
context.authorize(group_policy.GET_ALL_POLICY)
filters = req.params.copy() filters = req.params.copy()
# make another copy of filters, since it is being modified in # make another copy of filters, since it is being modified in
@ -131,6 +133,7 @@ class ConsistencyGroupsController(wsgi.Controller):
self.assert_valid_body(body, 'consistencygroup') self.assert_valid_body(body, 'consistencygroup')
context = req.environ['cinder.context'] context = req.environ['cinder.context']
context.authorize(group_policy.CREATE_POLICY)
consistencygroup = body['consistencygroup'] consistencygroup = body['consistencygroup']
self.validate_name_and_description(consistencygroup) self.validate_name_and_description(consistencygroup)
name = consistencygroup.get('name', None) name = consistencygroup.get('name', None)
@ -153,7 +156,6 @@ class ConsistencyGroupsController(wsgi.Controller):
{'name': name}) {'name': name})
try: try:
consistencygroup_api.check_policy(context, 'create')
new_consistencygroup = self.group_api.create( new_consistencygroup = self.group_api.create(
context, name, description, group_type['id'], volume_types, context, name, description, group_type['id'], volume_types,
availability_zone=availability_zone) availability_zone=availability_zone)
@ -181,6 +183,7 @@ class ConsistencyGroupsController(wsgi.Controller):
self.assert_valid_body(body, 'consistencygroup-from-src') self.assert_valid_body(body, 'consistencygroup-from-src')
context = req.environ['cinder.context'] context = req.environ['cinder.context']
context.authorize(group_policy.CREATE_POLICY)
consistencygroup = body['consistencygroup-from-src'] consistencygroup = body['consistencygroup-from-src']
self.validate_name_and_description(consistencygroup) self.validate_name_and_description(consistencygroup)
name = consistencygroup.get('name', None) name = consistencygroup.get('name', None)
@ -213,7 +216,6 @@ class ConsistencyGroupsController(wsgi.Controller):
self._get(context, source_cgid) self._get(context, source_cgid)
if cgsnapshot_id: if cgsnapshot_id:
self._get_cgsnapshot(context, cgsnapshot_id) self._get_cgsnapshot(context, cgsnapshot_id)
consistencygroup_api.check_policy(context, 'create')
new_group = self.group_api.create_from_src( new_group = self.group_api.create_from_src(
context, name, description, cgsnapshot_id, source_cgid) context, name, description, cgsnapshot_id, source_cgid)
except exception.NotFound: except exception.NotFound:
@ -232,19 +234,18 @@ class ConsistencyGroupsController(wsgi.Controller):
"can not be all empty in the request body.") "can not be all empty in the request body.")
raise exc.HTTPBadRequest(explanation=msg) raise exc.HTTPBadRequest(explanation=msg)
def _update(self, context, id, name, description, add_volumes, def _update(self, context, group, name, description, add_volumes,
remove_volumes, remove_volumes,
allow_empty=False): allow_empty=False):
LOG.info("Updating consistency group %(id)s with name %(name)s " LOG.info("Updating consistency group %(id)s with name %(name)s "
"description: %(description)s add_volumes: " "description: %(description)s add_volumes: "
"%(add_volumes)s remove_volumes: %(remove_volumes)s.", "%(add_volumes)s remove_volumes: %(remove_volumes)s.",
{'id': id, {'id': group.id,
'name': name, 'name': name,
'description': description, 'description': description,
'add_volumes': add_volumes, 'add_volumes': add_volumes,
'remove_volumes': remove_volumes}) 'remove_volumes': remove_volumes})
group = self._get(context, id)
self.group_api.update(context, group, name, description, self.group_api.update(context, group, name, description,
add_volumes, remove_volumes) add_volumes, remove_volumes)
@ -273,6 +274,8 @@ class ConsistencyGroupsController(wsgi.Controller):
self.assert_valid_body(body, 'consistencygroup') self.assert_valid_body(body, 'consistencygroup')
context = req.environ['cinder.context'] context = req.environ['cinder.context']
group = self._get(context, id)
context.authorize(group_policy.UPDATE_POLICY, target_obj=group)
consistencygroup = body.get('consistencygroup', None) consistencygroup = body.get('consistencygroup', None)
self.validate_name_and_description(consistencygroup) self.validate_name_and_description(consistencygroup)
name = consistencygroup.get('name', None) name = consistencygroup.get('name', None)
@ -282,7 +285,7 @@ class ConsistencyGroupsController(wsgi.Controller):
self._check_update_parameters(name, description, add_volumes, self._check_update_parameters(name, description, add_volumes,
remove_volumes) remove_volumes)
self._update(context, id, name, description, add_volumes, self._update(context, group, name, description, add_volumes,
remove_volumes) remove_volumes)
return webob.Response(status_int=http_client.ACCEPTED) return webob.Response(status_int=http_client.ACCEPTED)

View File

@ -22,6 +22,7 @@ from cinder.api.contrib import consistencygroups as cg_v2
from cinder.api import microversions as mv from cinder.api import microversions as mv
from cinder.api.openstack import wsgi from cinder.api.openstack import wsgi
from cinder.i18n import _ from cinder.i18n import _
from cinder.policies import groups as group_policy
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -72,6 +73,8 @@ class ConsistencyGroupsController(cg_v2.ConsistencyGroupsController):
self.assert_valid_body(body, 'consistencygroup') self.assert_valid_body(body, 'consistencygroup')
context = req.environ['cinder.context'] context = req.environ['cinder.context']
group = self._get(context, id)
context.authorize(group_policy.UPDATE_POLICY, target_obj=group)
consistencygroup = body.get('consistencygroup', None) consistencygroup = body.get('consistencygroup', None)
self.validate_name_and_description(consistencygroup) self.validate_name_and_description(consistencygroup)
name = consistencygroup.get('name', None) name = consistencygroup.get('name', None)
@ -83,7 +86,7 @@ class ConsistencyGroupsController(cg_v2.ConsistencyGroupsController):
description, description,
add_volumes, add_volumes,
remove_volumes) remove_volumes)
self._update(context, id, name, description, add_volumes, self._update(context, group, name, description, add_volumes,
remove_volumes, allow_empty) remove_volumes, allow_empty)
return webob.Response(status_int=http_client.ACCEPTED) return webob.Response(status_int=http_client.ACCEPTED)

View File

@ -40,25 +40,17 @@ def reset():
_ENFORCER = None _ENFORCER = None
def init(policy_file=None, rules=None, default_rule=None, use_conf=True): def init(use_conf=True):
"""Init an Enforcer class. """Init an Enforcer class.
:param policy_file: Custom policy file to use, if none is specified, :param use_conf: Whether to load rules from config file.
`CONF.policy_file` will be used.
:param rules: Default dictionary / Rules to use. It will be
considered just in the first instantiation.
:param default_rule: Default rule to use, CONF.default_rule will
be used if none is specified.
:param use_conf: Whether to load rules from config file.
""" """
global _ENFORCER global _ENFORCER
if not _ENFORCER: if not _ENFORCER:
_ENFORCER = policy.Enforcer(CONF, _ENFORCER = policy.Enforcer(
policy_file=policy_file, CONF,
rules=rules, use_conf=use_conf)
default_rule=default_rule,
use_conf=use_conf)
register_rules(_ENFORCER) register_rules(_ENFORCER)
_ENFORCER.load_rules() _ENFORCER.load_rules()
@ -66,19 +58,19 @@ def init(policy_file=None, rules=None, default_rule=None, use_conf=True):
def enforce(context, action, target): def enforce(context, action, target):
"""Verifies that the action is valid on the target in this context. """Verifies that the action is valid on the target in this context.
:param context: cinder context :param context: cinder context
:param action: string representing the action to be checked :param action: string representing the action to be checked
this should be colon separated for clarity. this should be colon separated for clarity.
i.e. ``compute:create_instance``, i.e. ``compute:create_instance``,
``compute:attach_volume``, ``compute:attach_volume``,
``volume:attach_volume`` ``volume:attach_volume``
:param object: dictionary representing the object of the action :param target: dictionary representing the object of the action for object
for object creation this should be a dictionary representing the creation this should be a dictionary representing the
location of the object e.g. ``{'project_id': context.project_id}`` location of the object e.g.
``{'project_id': context.project_id}``
:raises PolicyNotAuthorized: if verification fails.
:raises PolicyNotAuthorized: if verification fails.
""" """
init() init()
@ -93,10 +85,10 @@ def enforce(context, action, target):
def set_rules(rules, overwrite=True, use_conf=False): def set_rules(rules, overwrite=True, use_conf=False):
"""Set rules based on the provided dict of rules. """Set rules based on the provided dict of rules.
:param rules: New rules to use. It should be an instance of dict. :param rules: New rules to use. It should be an instance of dict.
:param overwrite: Whether to overwrite current rules or update them :param overwrite: Whether to overwrite current rules or update them
with the new rules. with the new rules.
:param use_conf: Whether to reload rules from config file. :param use_conf: Whether to reload rules from config file.
""" """
init(use_conf=False) init(use_conf=False)
@ -135,30 +127,31 @@ def get_enforcer():
def authorize(context, action, target, do_raise=True, exc=None): def authorize(context, action, target, do_raise=True, exc=None):
"""Verifies that the action is valid on the target in this context. """Verifies that the action is valid on the target in this context.
:param context: cinder context :param context: cinder context
:param action: string representing the action to be checked :param action: string representing the action to be checked
this should be colon separated for clarity. this should be colon separated for clarity.
i.e. ``compute:create_instance``, i.e. ``compute:create_instance``,
``compute:attach_volume``, ``compute:attach_volume``,
``volume:attach_volume`` ``volume:attach_volume``
:param target: dictionary representing the object of the action :param target: dictionary representing the object of the action for object
for object creation this should be a dictionary representing the creation this should be a dictionary representing the
location of the object e.g. ``{'project_id': context.project_id}`` location of the object e.g.
:param do_raise: if True (the default), raises PolicyNotAuthorized; ``{'project_id': context.project_id}``
if False, returns False :param do_raise: if True (the default), raises PolicyNotAuthorized;
:param exc: Class of the exception to raise if the check fails. if False, returns False
Any remaining arguments passed to :meth:`authorize` (both :param exc: Class of the exception to raise if the check fails.
positional and keyword arguments) will be passed to Any remaining arguments passed to :meth:`authorize` (both
the exception class. If not specified, positional and keyword arguments) will be passed to
:class:`PolicyNotAuthorized` will be used. the exception class. If not specified,
:class:`PolicyNotAuthorized` will be used.
:raises cinder.exception.PolicyNotAuthorized: if verification fails :raises cinder.exception.PolicyNotAuthorized: if verification fails
and do_raise is True. Or if 'exc' is specified it will raise an and do_raise is True. Or if 'exc' is specified it will raise an
exception of that type. exception of that type.
:return: returns a non-False value (not necessarily "True") if :return: returns a non-False value (not necessarily "True") if
authorized, and the exact value False if not authorized and authorized, and the exact value False if not authorized and
do_raise is False. do_raise is False.
""" """
init() init()
credentials = context.to_policy_values() credentials = context.to_policy_values()
@ -179,9 +172,7 @@ def authorize(context, action, target, do_raise=True, exc=None):
def check_is_admin(context): def check_is_admin(context):
"""Whether or not user is admin according to policy setting. """Whether or not user is admin according to policy setting."""
"""
init() init()
# the target is user-self # the target is user-self
credentials = context.to_policy_values() credentials = context.to_policy_values()

View File

@ -28,7 +28,6 @@ import uuid
import fixtures import fixtures
import mock import mock
from oslo_concurrency import lockutils from oslo_concurrency import lockutils
from oslo_config import cfg
from oslo_config import fixture as config_fixture from oslo_config import fixture as config_fixture
from oslo_log.fixture import logging_error as log_fixture from oslo_log.fixture import logging_error as log_fixture
import oslo_messaging import oslo_messaging
@ -39,7 +38,7 @@ from oslo_utils import timeutils
import six import six
import testtools import testtools
from cinder.common import config # noqa Need to register global_opts from cinder.common import config
from cinder import context from cinder import context
from cinder import coordination from cinder import coordination
from cinder.db import migration from cinder.db import migration
@ -54,7 +53,7 @@ from cinder.tests.unit import fake_notifier
from cinder.volume import utils from cinder.volume import utils
CONF = cfg.CONF CONF = config.CONF
_DB_CACHE = None _DB_CACHE = None
SESSION_CONFIGURED = False SESSION_CONFIGURED = False

View File

@ -61,18 +61,6 @@
"backup:get_all": "", "backup:get_all": "",
"backup:restore": "", "backup:restore": "",
"consistencygroup:create" : "",
"consistencygroup:delete": "",
"consistencygroup:update": "",
"consistencygroup:get": "",
"consistencygroup:get_all": "",
"consistencygroup:create_cgsnapshot" : "",
"consistencygroup:delete_cgsnapshot": "",
"consistencygroup:get_cgsnapshot": "",
"consistencygroup:get_all_cgsnapshots": "",
"group:delete": "", "group:delete": "",
"group:update": "", "group:update": "",
"group:get": "", "group:get": "",

View File

@ -1,14 +0,0 @@
{
"consistencygroup:create" : "group:nobody",
"consistencygroup:delete": "group:nobody",
"consistencygroup:update": "group:nobody",
"consistencygroup:get": "group:nobody",
"consistencygroup:get_all": "group:nobody",
"consistencygroup:create_cgsnapshot" : "group:nobody",
"consistencygroup:delete_cgsnapshot": "group:nobody",
"consistencygroup:get_cgsnapshot": "group:nobody",
"consistencygroup:get_all_cgsnapshots": "group:nobody"
}

View File

@ -0,0 +1,11 @@
---
features:
- |
Cinder now support policy in code, which means if users don't need to
modify any of the default policy rules, they do not need a policy file.
Users can modify/generate a `policy.yaml` file which will override specific
policy rules from their defaults.
other:
- |
Default `policy.json` file is now removed as Cinder now uses default
policies. A policy file is only needed if overriding one of the defaults.

View File

@ -24,7 +24,6 @@ setup-hooks =
data_files = data_files =
etc/cinder = etc/cinder =
etc/cinder/api-paste.ini etc/cinder/api-paste.ini
etc/cinder/policy.json
etc/cinder/rootwrap.conf etc/cinder/rootwrap.conf
etc/cinder/rootwrap.d = etc/cinder/rootwrap.d/* etc/cinder/rootwrap.d = etc/cinder/rootwrap.d/*
packages = packages =