[policy in code] Add support for volume, volume type resources

This patch adds policy in code support for volume
and volume type resources.

Change-Id: I47d11a2f6423a76ca053abf075791ed70ee84b07
Partial-Implements: blueprint policy-in-code
This commit is contained in:
TommyLike 2017-10-11 09:02:13 +08:00
parent 5b5715e2ad
commit a9e075021f
38 changed files with 1117 additions and 361 deletions

View File

@ -28,7 +28,6 @@ from cinder.api import microversions as mv
from cinder.common import constants
from cinder import exception
from cinder.i18n import _
import cinder.policy
from cinder import utils
@ -85,14 +84,6 @@ def validate_key_names(key_names_list):
return True
def validate_policy(context, action):
try:
cinder.policy.enforce_action(context, action)
return True
except exception.PolicyNotAuthorized:
return False
def get_pagination_params(params, max_limit=None):
"""Return marker, limit, offset tuple from request.

View File

@ -79,18 +79,10 @@ class AdminController(wsgi.Controller):
return update
def authorize(self, context, action_name):
# TODO(tommylike): We have two different ways to authorize during
# implementing code base policies, the if/else statement can be
# removed when all resources are upgraded.
if self.resource_name in ['backup', 'snapshot']:
context.authorize(
'volume_extension:%(resource)s_admin_actions:%(action)s' %
{'resource': self.resource_name,
'action': action_name})
else:
# e.g. "snapshot_admin_actions:reset_status"
action = '%s_admin_actions:%s' % (self.resource_name, action_name)
extensions.extension_authorizer('volume', action)(context)
context.authorize(
'volume_extension:%(resource)s_admin_actions:%(action)s' %
{'resource': self.resource_name,
'action': action_name})
def _remove_worker(self, context, id):
# Remove the cleanup worker from the DB when we change a resource

View File

@ -28,6 +28,7 @@ from cinder import context as ctxt
from cinder import db
from cinder import exception
from cinder.i18n import _
from cinder.policies import type_extra_specs as policy
from cinder import rpc
from cinder import utils
from cinder.volume import volume_types
@ -46,8 +47,6 @@ extraspec_opts = [
CONF = cfg.CONF
CONF.register_opts(extraspec_opts)
authorize = extensions.extension_authorizer('volume', 'types_extra_specs')
class VolumeTypeExtraSpecsController(wsgi.Controller):
"""The volume type extra specs API controller for the OpenStack API."""
@ -66,7 +65,7 @@ class VolumeTypeExtraSpecsController(wsgi.Controller):
def index(self, req, type_id):
"""Returns the list of extra specs for a given volume type."""
context = req.environ['cinder.context']
authorize(context, action="index")
context.authorize(policy.GET_ALL_POLICY)
self._check_type(context, type_id)
return self._get_extra_specs(context, type_id)
@ -89,7 +88,7 @@ class VolumeTypeExtraSpecsController(wsgi.Controller):
def create(self, req, type_id, body=None):
context = req.environ['cinder.context']
authorize(context, action='create')
context.authorize(policy.CREATE_POLICY)
self._allow_update(context, type_id)
self.assert_valid_body(body, 'extra_specs')
@ -114,7 +113,7 @@ class VolumeTypeExtraSpecsController(wsgi.Controller):
def update(self, req, type_id, id, body=None):
context = req.environ['cinder.context']
authorize(context, action='update')
context.authorize(policy.UPDATE_POLICY)
self._allow_update(context, type_id)
if not body:
@ -147,7 +146,7 @@ class VolumeTypeExtraSpecsController(wsgi.Controller):
def show(self, req, type_id, id):
"""Return a single extra spec item."""
context = req.environ['cinder.context']
authorize(context, action='show')
context.authorize(policy.GET_POLICY)
self._check_type(context, type_id)
specs = self._get_extra_specs(context, type_id)
if id in specs['extra_specs']:
@ -160,7 +159,7 @@ class VolumeTypeExtraSpecsController(wsgi.Controller):
"""Deletes an existing extra spec."""
context = req.environ['cinder.context']
self._check_type(context, type_id)
authorize(context, action='delete')
context.authorize(policy.DELETE_POLICY)
self._allow_update(context, type_id)
# Not found exception will be handled at the wsgi level

View File

@ -24,14 +24,12 @@ from cinder.api.openstack import wsgi
from cinder.api.views import types as views_types
from cinder import exception
from cinder.i18n import _
from cinder.policies import volume_type as policy
from cinder import rpc
from cinder import utils
from cinder.volume import volume_types
authorize = extensions.extension_authorizer('volume', 'types_manage')
class VolumeTypesManageController(wsgi.Controller):
"""The volume types API controller for the OpenStack API."""
@ -53,7 +51,7 @@ class VolumeTypesManageController(wsgi.Controller):
def _create(self, req, body):
"""Creates a new volume type."""
context = req.environ['cinder.context']
authorize(context)
context.authorize(policy.MANAGE_POLICY)
self.assert_valid_body(body, 'volume_type')
@ -103,7 +101,7 @@ class VolumeTypesManageController(wsgi.Controller):
def _update(self, req, id, body):
# Update description for a given volume type.
context = req.environ['cinder.context']
authorize(context)
context.authorize(policy.MANAGE_POLICY)
self.assert_valid_body(body, 'volume_type')
@ -164,7 +162,7 @@ class VolumeTypesManageController(wsgi.Controller):
def _delete(self, req, id):
"""Deletes an existing volume type."""
context = req.environ['cinder.context']
authorize(context)
context.authorize(policy.MANAGE_POLICY)
try:
vol_type = volume_types.get_volume_type(context, id)

View File

@ -28,6 +28,7 @@ from cinder import exception
from cinder.i18n import _
from cinder.image import image_utils
from cinder import keymgr
from cinder.policies import volume_actions as policy
from cinder import utils
from cinder import volume
@ -35,11 +36,6 @@ from cinder import volume
CONF = cfg.CONF
def authorize(context, action_name):
action = 'volume_actions:%s' % action_name
extensions.extension_authorizer('volume', action)(context)
class VolumeActionsController(wsgi.Controller):
def __init__(self, *args, **kwargs):
super(VolumeActionsController, self).__init__(*args, **kwargs)
@ -239,7 +235,7 @@ class VolumeActionsController(wsgi.Controller):
# Not found exception will be handled at the wsgi level
volume = self.volume_api.get(context, id)
authorize(context, "upload_image")
context.authorize(policy.UPLOAD_IMAGE_POLICY)
# check for valid disk-format
disk_format = params.get("disk_format", "raw")
if not image_utils.validate_disk_format(disk_format):
@ -278,7 +274,7 @@ class VolumeActionsController(wsgi.Controller):
image_metadata['protected'] = params.get('protected', 'False')
if image_metadata['visibility'] == 'public':
authorize(context, 'upload_public')
context.authorize(policy.UPLOAD_PUBLIC_POLICY)
image_metadata['protected'] = (
utils.get_bool_param('protected', image_metadata))

View File

@ -18,9 +18,7 @@
from cinder.api import extensions
from cinder.api.openstack import wsgi
from cinder import db
authorize = extensions.extension_authorizer('volume',
'volume_encryption_metadata')
from cinder.policies import volumes as policy
class VolumeEncryptionMetadataController(wsgi.Controller):
@ -29,7 +27,7 @@ class VolumeEncryptionMetadataController(wsgi.Controller):
def index(self, req, volume_id):
"""Returns the encryption metadata for a given volume."""
context = req.environ['cinder.context']
authorize(context)
context.authorize(policy.ENCRYPTION_METADATA_POLICY)
return db.volume_encryption_metadata_get(context, volume_id)
def show(self, req, volume_id, id):

View File

@ -14,10 +14,7 @@
from cinder.api import extensions
from cinder.api.openstack import wsgi
authorize = extensions.soft_extension_authorizer('volume',
'volume_host_attribute')
from cinder.policies import volumes as policy
class VolumeHostAttributeController(wsgi.Controller):
@ -29,14 +26,14 @@ class VolumeHostAttributeController(wsgi.Controller):
@wsgi.extends
def show(self, req, resp_obj, id):
context = req.environ['cinder.context']
if authorize(context):
if context.authorize(policy.HOST_ATTRIBUTE_POLICY, fatal=False):
volume = resp_obj.obj['volume']
self._add_volume_host_attribute(req, volume)
@wsgi.extends
def detail(self, req, resp_obj):
context = req.environ['cinder.context']
if authorize(context):
if context.authorize(policy.HOST_ATTRIBUTE_POLICY, fatal=False):
for vol in list(resp_obj.obj['volumes']):
self._add_volume_host_attribute(req, vol)

View File

@ -23,14 +23,12 @@ from cinder.api import extensions
from cinder.api.openstack import wsgi
from cinder import exception
from cinder.i18n import _
from cinder.policies import volume_metadata as policy
from cinder import volume
LOG = logging.getLogger(__name__)
authorize = extensions.soft_extension_authorizer('volume',
'volume_image_metadata')
class VolumeImageMetadataController(wsgi.Controller):
def __init__(self, *args, **kwargs):
@ -72,13 +70,13 @@ class VolumeImageMetadataController(wsgi.Controller):
@wsgi.extends
def show(self, req, resp_obj, id):
context = req.environ['cinder.context']
if authorize(context):
if context.authorize(policy.IMAGE_METADATA_POLICY, fatal=False):
self._add_image_metadata(context, [resp_obj.obj['volume']])
@wsgi.extends
def detail(self, req, resp_obj):
context = req.environ['cinder.context']
if authorize(context):
if context.authorize(policy.IMAGE_METADATA_POLICY, fatal=False):
# Just get the image metadata of those volumes in response.
volumes = list(resp_obj.obj.get('volumes', []))
if volumes:
@ -87,7 +85,7 @@ class VolumeImageMetadataController(wsgi.Controller):
@wsgi.action("os-set_image_metadata")
def create(self, req, id, body):
context = req.environ['cinder.context']
if authorize(context):
if context.authorize(policy.IMAGE_METADATA_POLICY, fatal=False):
try:
metadata = body['os-set_image_metadata']['metadata']
except (KeyError, TypeError):
@ -130,7 +128,7 @@ class VolumeImageMetadataController(wsgi.Controller):
def delete(self, req, id, body):
"""Deletes an existing image metadata."""
context = req.environ['cinder.context']
if authorize(context):
if context.authorize(policy.IMAGE_METADATA_POLICY, fatal=False):
try:
key = body['os-unset_image_metadata']['key']
except (KeyError, TypeError):

View File

@ -24,14 +24,12 @@ from cinder.api.v2.views import volumes as volume_views
from cinder.api.views import manageable_volumes as list_manageable_view
from cinder import exception
from cinder.i18n import _
from cinder.policies import manageable_volumes as policy
from cinder import utils
from cinder import volume as cinder_volume
from cinder.volume import volume_types
LOG = logging.getLogger(__name__)
authorize_manage = extensions.extension_authorizer('volume', 'volume_manage')
authorize_list_manageable = extensions.extension_authorizer('volume',
'list_manageable')
class VolumeManageController(wsgi.Controller):
@ -99,7 +97,7 @@ class VolumeManageController(wsgi.Controller):
"""
context = req.environ['cinder.context']
authorize_manage(context)
context.authorize(policy.MANAGE_POLICY)
self.assert_valid_body(body, 'volume')
@ -155,7 +153,7 @@ class VolumeManageController(wsgi.Controller):
def index(self, req):
"""Returns a summary list of volumes available to manage."""
context = req.environ['cinder.context']
authorize_list_manageable(context)
context.authorize(policy.LIST_MANAGEABLE_POLICY)
return resource_common_manage.get_manageable_resources(
req, False, self.volume_api.get_manageable_volumes,
self._list_manageable_view)
@ -164,7 +162,7 @@ class VolumeManageController(wsgi.Controller):
def detail(self, req):
"""Returns a detailed list of volumes available to manage."""
context = req.environ['cinder.context']
authorize_list_manageable(context)
context.authorize(policy.LIST_MANAGEABLE_POLICY)
return resource_common_manage.get_manageable_resources(
req, True, self.volume_api.get_manageable_volumes,
self._list_manageable_view)

View File

@ -14,9 +14,7 @@
from cinder.api import extensions
from cinder.api.openstack import wsgi
authorize = extensions.soft_extension_authorizer('volume',
'volume_mig_status_attribute')
from cinder.policies import volumes as policy
class VolumeMigStatusAttributeController(wsgi.Controller):
@ -30,13 +28,13 @@ class VolumeMigStatusAttributeController(wsgi.Controller):
@wsgi.extends
def show(self, req, resp_obj, id):
context = req.environ['cinder.context']
if authorize(context):
if context.authorize(policy.MIG_ATTRIBUTE_POLICY, fatal=False):
self._add_volume_mig_status_attribute(req, resp_obj.obj['volume'])
@wsgi.extends
def detail(self, req, resp_obj):
context = req.environ['cinder.context']
if authorize(context):
if context.authorize(policy.MIG_ATTRIBUTE_POLICY, fatal=False):
for vol in list(resp_obj.obj['volumes']):
self._add_volume_mig_status_attribute(req, vol)

View File

@ -14,10 +14,7 @@
from cinder.api import extensions
from cinder.api.openstack import wsgi
authorize = extensions.soft_extension_authorizer('volume',
'volume_tenant_attribute')
from cinder.policies import volumes as policy
class VolumeTenantAttributeController(wsgi.Controller):
@ -29,14 +26,14 @@ class VolumeTenantAttributeController(wsgi.Controller):
@wsgi.extends
def show(self, req, resp_obj, id):
context = req.environ['cinder.context']
if authorize(context):
if context.authorize(policy.TENANT_ATTRIBUTE_POLICY, fatal=False):
volume = resp_obj.obj['volume']
self._add_volume_tenant_attribute(req, volume)
@wsgi.extends
def detail(self, req, resp_obj):
context = req.environ['cinder.context']
if authorize(context):
if context.authorize(policy.TENANT_ATTRIBUTE_POLICY, fatal=False):
for vol in list(resp_obj.obj['volumes']):
self._add_volume_tenant_attribute(req, vol)

View File

@ -22,14 +22,10 @@ from cinder.api import extensions
from cinder.api.openstack import wsgi
from cinder import exception
from cinder.i18n import _
from cinder.policies import volume_access as policy
from cinder.volume import volume_types
soft_authorize = extensions.soft_extension_authorizer('volume',
'volume_type_access')
authorize = extensions.extension_authorizer('volume', 'volume_type_access')
def _marshall_volume_type_access(vol_type):
rval = []
for project_id in vol_type['projects']:
@ -44,7 +40,7 @@ class VolumeTypeAccessController(object):
def index(self, req, type_id):
context = req.environ['cinder.context']
authorize(context)
context.authorize(policy.TYPE_ACCESS_POLICY)
# Not found exception will be handled at the wsgi level
vol_type = volume_types.get_volume_type(
@ -77,14 +73,14 @@ class VolumeTypeActionController(wsgi.Controller):
@wsgi.extends
def show(self, req, resp_obj, id):
context = req.environ['cinder.context']
if soft_authorize(context):
if context.authorize(policy.TYPE_ACCESS_POLICY, fatal=False):
vol_type = req.cached_resource_by_id(id, name='types')
self._extend_vol_type(resp_obj.obj['volume_type'], vol_type)
@wsgi.extends
def index(self, req, resp_obj):
context = req.environ['cinder.context']
if soft_authorize(context):
if context.authorize(policy.TYPE_ACCESS_POLICY, fatal=False):
for vol_type_rval in list(resp_obj.obj['volume_types']):
type_id = vol_type_rval['id']
vol_type = req.cached_resource_by_id(type_id, name='types')
@ -93,7 +89,7 @@ class VolumeTypeActionController(wsgi.Controller):
@wsgi.extends
def detail(self, req, resp_obj):
context = req.environ['cinder.context']
if soft_authorize(context):
if context.authorize(policy.TYPE_ACCESS_POLICY, fatal=False):
for vol_type_rval in list(resp_obj.obj['volume_types']):
type_id = vol_type_rval['id']
vol_type = req.cached_resource_by_id(type_id, name='types')
@ -102,7 +98,7 @@ class VolumeTypeActionController(wsgi.Controller):
@wsgi.extends(action='create')
def create(self, req, body, resp_obj):
context = req.environ['cinder.context']
if soft_authorize(context):
if context.authorize(policy.TYPE_ACCESS_POLICY, fatal=False):
type_id = resp_obj.obj['volume_type']['id']
vol_type = req.cached_resource_by_id(type_id, name='types')
self._extend_vol_type(resp_obj.obj['volume_type'], vol_type)
@ -110,7 +106,7 @@ class VolumeTypeActionController(wsgi.Controller):
@wsgi.action('addProjectAccess')
def _addProjectAccess(self, req, id, body):
context = req.environ['cinder.context']
authorize(context, action="addProjectAccess")
context.authorize(policy.ADD_PROJECT_POLICY)
self._check_body(body, 'addProjectAccess')
project = body['addProjectAccess']['project']
@ -124,7 +120,7 @@ class VolumeTypeActionController(wsgi.Controller):
@wsgi.action('removeProjectAccess')
def _removeProjectAccess(self, req, id, body):
context = req.environ['cinder.context']
authorize(context, action="removeProjectAccess")
context.authorize(policy.REMOVE_PROJECT_POLICY)
self._check_body(body, 'removeProjectAccess')
project = body['removeProjectAccess']['project']

View File

@ -23,12 +23,11 @@ from cinder.api.openstack import wsgi
from cinder import db
from cinder import exception
from cinder.i18n import _
from cinder.policies import volume_type as policy
from cinder import rpc
from cinder import utils
from cinder.volume import volume_types
authorize = extensions.extension_authorizer('volume',
'volume_type_encryption')
CONTROL_LOCATION = ['front-end', 'back-end']
@ -84,14 +83,14 @@ class VolumeTypeEncryptionController(wsgi.Controller):
def index(self, req, type_id):
"""Returns the encryption specs for a given volume type."""
context = req.environ['cinder.context']
authorize(context)
context.authorize(policy.ENCRYPTION_POLICY)
self._check_type(context, type_id)
return self._get_volume_type_encryption(context, type_id)
def create(self, req, type_id, body=None):
"""Create encryption specs for an existing volume type."""
context = req.environ['cinder.context']
authorize(context)
context.authorize(policy.ENCRYPTION_POLICY)
if self._encrypted_type_in_use(context, type_id):
expl = _('Cannot create encryption specs. Volume type in use.')
@ -118,7 +117,7 @@ class VolumeTypeEncryptionController(wsgi.Controller):
def update(self, req, type_id, id, body=None):
"""Update encryption specs for a given volume type."""
context = req.environ['cinder.context']
authorize(context)
context.authorize(policy.ENCRYPTION_POLICY)
self.assert_valid_body(body, 'encryption')
@ -145,7 +144,7 @@ class VolumeTypeEncryptionController(wsgi.Controller):
def show(self, req, type_id, id):
"""Return a single encryption item."""
context = req.environ['cinder.context']
authorize(context)
context.authorize(policy.ENCRYPTION_POLICY)
self._check_type(context, type_id)
@ -159,7 +158,7 @@ class VolumeTypeEncryptionController(wsgi.Controller):
def delete(self, req, type_id, id):
"""Delete encryption specs for a given volume type."""
context = req.environ['cinder.context']
authorize(context)
context.authorize(policy.ENCRYPTION_POLICY)
if self._encrypted_type_in_use(context, type_id):
expl = _('Cannot delete encryption specs. Volume type in use.')

View File

@ -18,10 +18,10 @@ import webob
from cinder.api import extensions
from cinder.api.openstack import wsgi
from cinder.policies import manageable_volumes as policy
from cinder import volume
LOG = logging.getLogger(__name__)
authorize = extensions.extension_authorizer('volume', 'volume_unmanage')
class VolumeUnmanageController(wsgi.Controller):
@ -47,7 +47,7 @@ class VolumeUnmanageController(wsgi.Controller):
attached to an instance.
"""
context = req.environ['cinder.context']
authorize(context)
context.authorize(policy.UNMANAGE_POLICY)
LOG.info("Unmanage volume with id: %s", id)

View File

@ -329,17 +329,5 @@ def extension_authorizer(api_name, extension_name):
act = '%s_extension:%s' % (api_name, extension_name)
else:
act = '%s_extension:%s:%s' % (api_name, extension_name, action)
cinder.policy.enforce(context, act, target)
return authorize
def soft_extension_authorizer(api_name, extension_name):
hard_authorize = extension_authorizer(api_name, extension_name)
def authorize(context):
try:
hard_authorize(context)
return True
except exception.NotAuthorized:
return False
cinder.policy.authorize(context, act, target)
return authorize

View File

@ -37,7 +37,6 @@ from cinder import i18n
i18n.enable_lazy()
from cinder.i18n import _
from cinder import policy
from cinder import utils
from cinder.wsgi import common as wsgi
@ -1285,23 +1284,6 @@ class Controller(object):
except exception.InvalidInput as error:
raise webob.exc.HTTPBadRequest(explanation=error.msg)
@staticmethod
def get_policy_checker(prefix):
@staticmethod
def policy_checker(req, action, resource=None):
ctxt = req.environ['cinder.context']
target = {
'project_id': ctxt.project_id,
'user_id': ctxt.user_id,
}
if resource:
target.update(resource)
_action = '%s:%s' % (prefix, action)
policy.enforce(ctxt, _action, target)
return ctxt
return policy_checker
class Fault(webob.exc.HTTPException):
"""Wrap webob.exc.HTTPException to provide API friendly response."""

View File

@ -15,6 +15,7 @@
# under the License.
from cinder.api import common
from cinder.policies import volume_type as policy
class ViewBuilder(common.ViewBuilder):
@ -26,13 +27,9 @@ class ViewBuilder(common.ViewBuilder):
name=volume_type.get('name'),
is_public=volume_type.get('is_public'),
description=volume_type.get('description'))
if common.validate_policy(
context,
'volume_extension:access_types_extra_specs'):
if context.authorize(policy.EXTRA_SPEC_POLICY, fatal=False):
trimmed['extra_specs'] = volume_type.get('extra_specs')
if common.validate_policy(
context,
'volume_extension:access_types_qos_specs_id'):
if context.authorize(policy.QOS_POLICY, fatal=False):
trimmed['qos_specs_id'] = volume_type.get('qos_specs_id')
return trimmed if brief else dict(volume_type=trimmed)

View File

@ -29,23 +29,12 @@ from cinder import exception
from cinder import group as group_api
from cinder.i18n import _
from cinder import objects
import cinder.policy
from cinder.policies import volumes as policy
from cinder import utils
LOG = logging.getLogger(__name__)
def check_policy(context, action, target_obj=None):
target = {
'project_id': context.project_id,
'user_id': context.user_id
}
target.update(target_obj or {})
_action = 'volume:%s' % action
cinder.policy.enforce(context, _action, target)
class VolumeController(volumes_v2.VolumeController):
"""The Volumes API controller for the OpenStack API V3."""
@ -74,7 +63,7 @@ class VolumeController(volumes_v2.VolumeController):
{'id': id, 'params': params}, context=context)
if force:
check_policy(context, 'force_delete')
context.authorize(policy.FORCE_DELETE_POLICY)
volume = self.volume_api.get(context, id)

View File

@ -29,6 +29,7 @@ from cinder.policies import groups
from cinder.policies import hosts
from cinder.policies import limits
from cinder.policies import manageable_snapshots
from cinder.policies import manageable_volumes
from cinder.policies import messages
from cinder.policies import qos_specs
from cinder.policies import quota_class
@ -38,6 +39,13 @@ from cinder.policies import services
from cinder.policies import snapshot_actions
from cinder.policies import snapshot_metadata
from cinder.policies import snapshots
from cinder.policies import type_extra_specs
from cinder.policies import volume_access
from cinder.policies import volume_actions
from cinder.policies import volume_metadata
from cinder.policies import volume_transfer
from cinder.policies import volume_type
from cinder.policies import volumes
from cinder.policies import workers
@ -67,4 +75,12 @@ def list_rules():
scheduler_stats.list_rules(),
hosts.list_rules(),
limits.list_rules(),
manageable_volumes.list_rules(),
volume_type.list_rules(),
volume_access.list_rules(),
volume_actions.list_rules(),
volume_transfer.list_rules(),
volume_metadata.list_rules(),
type_extra_specs.list_rules(),
volumes.list_rules(),
)

View File

@ -23,8 +23,6 @@ rules = [
policy.RuleDefault('admin_or_owner',
'is_admin:True or (role:admin and '
'is_admin_project:True) or project_id:%(project_id)s'),
policy.RuleDefault('default',
'rule:admin_or_owner'),
policy.RuleDefault('admin_api',
'is_admin:True or (role:admin and '
'is_admin_project:True)'),

View File

@ -0,0 +1,66 @@
# Copyright (c) 2017 Huawei Technologies Co., Ltd.
# All Rights Reserved.
#
# 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 cinder.policies import base
MANAGE_POLICY = "volume_extension:volume_manage"
UNMANAGE_POLICY = "volume_extension:volume_unmanage"
LIST_MANAGEABLE_POLICY = "volume_extension:list_manageable"
manageable_volumes_policies = [
policy.DocumentedRuleDefault(
name=LIST_MANAGEABLE_POLICY,
check_str=base.RULE_ADMIN_API,
description=
"""List (in detail) of volumes which are available to manage.""",
operations=[
{
'method': 'GET',
'path': '/manageable_volumes'
},
{
'method': 'GET',
'path': '/manageable_volumes/detail'
}
]),
policy.DocumentedRuleDefault(
name=MANAGE_POLICY,
check_str=base.RULE_ADMIN_API,
description="""Manage existing volumes.""",
operations=[
{
'method': 'POST',
'path': '/manageable_volumes'
}
]),
policy.DocumentedRuleDefault(
name=UNMANAGE_POLICY,
check_str=base.RULE_ADMIN_API,
description="""Stop managing a volume.""",
operations=[
{
'method': 'POST',
'path': '/volumes/{volume_id}/action (os-unmanage)'
}
]),
]
def list_rules():
return manageable_volumes_policies

View File

@ -0,0 +1,83 @@
# Copyright (c) 2017 Huawei Technologies Co., Ltd.
# All Rights Reserved.
#
# 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 cinder.policies import base
CREATE_POLICY = "volume_extension:types_extra_specs:create"
DELETE_POLICY = "volume_extension:types_extra_specs:delete"
GET_ALL_POLICY = "volume_extension:types_extra_specs:index"
GET_POLICY = "volume_extension:types_extra_specs:show"
UPDATE_POLICY = "volume_extension:types_extra_specs:update"
type_extra_specs_policies = [
policy.DocumentedRuleDefault(
name=GET_ALL_POLICY,
check_str=base.RULE_ADMIN_API,
description="List type extra specs.",
operations=[
{
'method': 'GET',
'path': '/types/{type_id}/extra_specs'
}
]),
policy.DocumentedRuleDefault(
name=CREATE_POLICY,
check_str=base.RULE_ADMIN_API,
description="Create type extra specs.",
operations=[
{
'method': 'POST',
'path': '/types/{type_id}/extra_specs'
}
]),
policy.DocumentedRuleDefault(
name=GET_POLICY,
check_str=base.RULE_ADMIN_API,
description="Show one specified type extra specs.",
operations=[
{
'method': 'GET',
'path': '/types/{type_id}/extra_specs/{extra_spec_key}'
}
]),
policy.DocumentedRuleDefault(
name=UPDATE_POLICY,
check_str=base.RULE_ADMIN_API,
description="Update type extra specs.",
operations=[
{
'method': 'PUT',
'path': '/types/{type_id}/extra_specs/{extra_spec_key}'
}
]),
policy.DocumentedRuleDefault(
name=DELETE_POLICY,
check_str=base.RULE_ADMIN_API,
description="Delete type extra specs.",
operations=[
{
'method': 'DELETE',
'path': '/types/{type_id}/extra_specs/{extra_spec_key}'
}
]),
]
def list_rules():
return type_extra_specs_policies

View File

@ -0,0 +1,73 @@
# Copyright (c) 2017 Huawei Technologies Co., Ltd.
# All Rights Reserved.
#
# 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 cinder.policies import base
ADD_PROJECT_POLICY = "volume_extension:volume_type_access:addProjectAccess"
REMOVE_PROJECT_POLICY = \
"volume_extension:volume_type_access:removeProjectAccess"
TYPE_ACCESS_POLICY = "volume_extension:volume_type_access"
volume_access_policies = [
policy.DocumentedRuleDefault(
name=TYPE_ACCESS_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="Volume type access related APIs.",
operations=[
{
'method': 'GET',
'path': '/types'
},
{
'method': 'GET',
'path': '/types/detail'
},
{
'method': 'GET',
'path': '/types/{type_id}'
},
{
'method': 'POST',
'path': '/types'
}
]),
policy.DocumentedRuleDefault(
name=ADD_PROJECT_POLICY,
check_str=base.RULE_ADMIN_API,
description="Add volume type access for project.",
operations=[
{
'method': 'POST',
'path': '/types/{type_id}/action (addProjectAccess)'
}
]),
policy.DocumentedRuleDefault(
name=REMOVE_PROJECT_POLICY,
check_str=base.RULE_ADMIN_API,
description="Remove volume type access for project.",
operations=[
{
'method': 'POST',
'path': '/types/{type_id}/action (removeProjectAccess)'
}
]),
]
def list_rules():
return volume_access_policies

View File

@ -0,0 +1,239 @@
# Copyright (c) 2017 Huawei Technologies Co., Ltd.
# All Rights Reserved.
#
# 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 cinder.policies import base
EXTEND_POLICY = "volume:extend"
EXTEND_ATTACHED_POLICY = "volume:extend_attached_volume"
REVERT_POLICY = "volume:revert_to_snapshot"
RESET_STATUS = "volume_extension:volume_admin_actions:reset_status"
RETYPE_POLICY = "volume:retype"
UPDATE_READONLY_POLICY = "volume:update_readonly_flag"
FORCE_DELETE_POLICY = "volume_extension:volume_admin_actions:force_delete"
FORCE_DETACH_POLICY = "volume_extension:volume_admin_actions:force_detach"
UPLOAD_PUBLIC_POLICY = "volume_extension:volume_actions:upload_public"
UPLOAD_IMAGE_POLICY = "volume_extension:volume_actions:upload_image"
MIGRATE_POLICY = "volume_extension:volume_admin_actions:migrate_volume"
MIGRATE_COMPLETE_POLICY = \
"volume_extension:volume_admin_actions:migrate_volume_completion"
DETACH_POLICY = "volume_extension:volume_actions:detach"
ATTACH_POLICY = "volume_extension:volume_actions:attach"
BEGIN_DETACHING_POLICY = "volume_extension:volume_actions:begin_detaching"
UNRESERVE_POLICY = "volume_extension:volume_actions:unreserve"
RESERVE_POLICY = "volume_extension:volume_actions:reserve"
ROLL_DETACHING_POLICY = "volume_extension:volume_actions:roll_detaching"
TERMINATE_POLICY = "volume_extension:volume_actions:terminate_connection"
INITIALIZE_POLICY = "volume_extension:volume_actions:initialize_connection"
volume_action_policies = [
policy.DocumentedRuleDefault(
name=EXTEND_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="Extend a volume.",
operations=[
{
'method': 'POST',
'path': '/volumes/{volume_id}/action (os-extend)'
}
]),
policy.DocumentedRuleDefault(
name=EXTEND_ATTACHED_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="Extend a attached volume.",
operations=[
{
'method': 'POST',
'path': '/volumes/{volume_id}/action (os-extend)'
}
]),
policy.DocumentedRuleDefault(
name=REVERT_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="Revert a volume to a snapshot.",
operations=[
{
'method': 'POST',
'path': '/volumes/{volume_id}/action (revert)'
}
]),
policy.DocumentedRuleDefault(
name=RESET_STATUS,
check_str=base.RULE_ADMIN_API,
description="Reset status of a volume.",
operations=[
{
'method': 'POST',
'path': '/volumes/{volume_id}/action (os-reset_status)'
}
]),
policy.DocumentedRuleDefault(
name=RETYPE_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="Retype a volume.",
operations=[
{
'method': 'POST',
'path': '/volumes/{volume_id}/action (os-retype)'
}
]),
policy.DocumentedRuleDefault(
name=UPDATE_READONLY_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="Update a volume's readonly flag.",
operations=[
{
'method': 'POST',
'path': '/volumes/{volume_id}/action (os-update_readonly_flag)'
}
]),
policy.DocumentedRuleDefault(
name=FORCE_DELETE_POLICY,
check_str=base.RULE_ADMIN_API,
description="Force delete a volume.",
operations=[
{
'method': 'POST',
'path': '/volumes/{volume_id}/action (os-force_delete)'
}
]),
policy.DocumentedRuleDefault(
name=UPLOAD_PUBLIC_POLICY,
check_str=base.RULE_ADMIN_API,
description="Upload a volume to image with public visibility.",
operations=[
{
'method': 'POST',
'path': '/volumes/{volume_id}/action (os-volume_upload_image)'
}
]),
policy.DocumentedRuleDefault(
name=UPLOAD_IMAGE_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="Upload a volume to image.",
operations=[
{
'method': 'POST',
'path': '/volumes/{volume_id}/action (os-volume_upload_image)'
}
]),
policy.DocumentedRuleDefault(
name=FORCE_DETACH_POLICY,
check_str=base.RULE_ADMIN_API,
description="Force detach a volume.",
operations=[
{
'method': 'POST',
'path': '/volumes/{volume_id}/action (os-force_detach)'
}
]),
policy.DocumentedRuleDefault(
name=MIGRATE_POLICY,
check_str=base.RULE_ADMIN_API,
description="migrate a volume to a specified host.",
operations=[
{
'method': 'POST',
'path': '/volumes/{volume_id}/action (os-migrate_volume)'
}
]),
policy.DocumentedRuleDefault(
name=MIGRATE_COMPLETE_POLICY,
check_str=base.RULE_ADMIN_API,
description="Complete a volume migration.",
operations=[{
'method': 'POST',
'path':
'/volumes/{volume_id}/action (os-migrate_volume_completion)'}
]),
policy.DocumentedRuleDefault(
name=INITIALIZE_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="Initialize volume attachment.",
operations=[{
'method': 'POST',
'path':
'/volumes/{volume_id}/action (os-initialize_connection)'}
]),
policy.DocumentedRuleDefault(
name=TERMINATE_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="Terminate volume attachment.",
operations=[{
'method': 'POST',
'path':
'/volumes/{volume_id}/action (os-terminate_connection)'}
]),
policy.DocumentedRuleDefault(
name=ROLL_DETACHING_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="Roll back volume status to 'in-use'.",
operations=[{
'method': 'POST',
'path':
'/volumes/{volume_id}/action (os-roll_detaching)'}
]),
policy.DocumentedRuleDefault(
name=RESERVE_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="Mark volume as reserved.",
operations=[{
'method': 'POST',
'path':
'/volumes/{volume_id}/action (os-reserve)'}
]),
policy.DocumentedRuleDefault(
name=UNRESERVE_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="Unmark volume as reserved.",
operations=[{
'method': 'POST',
'path':
'/volumes/{volume_id}/action (os-unreserve)'}
]),
policy.DocumentedRuleDefault(
name=BEGIN_DETACHING_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="Begin detach volumes.",
operations=[{
'method': 'POST',
'path':
'/volumes/{volume_id}/action (os-begin_detaching)'}
]),
policy.DocumentedRuleDefault(
name=ATTACH_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="Add attachment metadata.",
operations=[{
'method': 'POST',
'path':
'/volumes/{volume_id}/action (os-attach)'}
]),
policy.DocumentedRuleDefault(
name=DETACH_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="Clear attachment metadata.",
operations=[{
'method': 'POST',
'path':
'/volumes/{volume_id}/action (os-detach)'}
]),
]
def list_rules():
return volume_action_policies

View File

@ -0,0 +1,128 @@
# Copyright (c) 2017 Huawei Technologies Co., Ltd.
# All Rights Reserved.
#
# 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 cinder.policies import base
GET_POLICY = "volume:get_volume_metadata"
CREATE_POLICY = "volume:create_volume_metadata"
DELETE_POLICY = "volume:delete_volume_metadata"
UPDATE_POLICY = "volume:update_volume_metadata"
IMAGE_METADATA_POLICY = "volume_extension:volume_image_metadata"
UPDATE_ADMIN_METADATA_POLICY = "volume:update_volume_admin_metadata"
BASE_POLICY_NAME = 'volume:volume_metadata:%s'
volume_metadata_policies = [
policy.DocumentedRuleDefault(
name=GET_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="Show volume's metadata or one specified metadata "
"with a given key.",
operations=[
{
'method': 'GET',
'path': '/volumes/{volume_id}/metadata'
},
{
'method': 'GET',
'path': '/volumes/{volume_id}/metadata/{key}'
}
]),
policy.DocumentedRuleDefault(
name=CREATE_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="Create volume metadata.",
operations=[
{
'method': 'POST',
'path': '/volumes/{volume_id}/metadata'
}
]),
policy.DocumentedRuleDefault(
name=UPDATE_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="Update volume's metadata or one specified "
"metadata with a given key.",
operations=[
{
'method': 'PUT',
'path': '/volumes/{volume_id}/metadata'
},
{
'method': 'PUT',
'path': '/volumes/{volume_id}/metadata/{key}'
}
]),
policy.DocumentedRuleDefault(
name=DELETE_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="Delete volume's specified metadata with a given key.",
operations=[
{
'method': 'DELETE',
'path': '/volumes/{volume_id}/metadata/{key}'
}
]),
policy.DocumentedRuleDefault(
name=IMAGE_METADATA_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="Volume's image metadata related operation, create, "
"delete, show and list.",
operations=[
{
'method': 'GET',
'path': '/volumes/detail'
},
{
'method': 'GET',
'path': '/volumes/{volume_id}'
},
{
'method': 'POST',
'path': '/volumes/{volume_id}/action (os-set_image_metadata)'
},
{
'method': 'POST',
'path': '/volumes/{volume_id}/action (os-show_image_metadata)'
},
{
'method': 'POST',
'path': '/volumes/{volume_id}/action (os-unset_image_metadata)'
}
]),
policy.DocumentedRuleDefault(
name=UPDATE_ADMIN_METADATA_POLICY,
check_str=base.RULE_ADMIN_API,
description="Update volume admin metadata. It's used in `attach` "
"and `os-update_readonly_flag` APIs",
operations=[
{
'method': 'POST',
'path': '/volumes/{volume_id}/action (os-update_readonly_flag)'
},
{
'method': 'POST',
'path': '/volumes/{volume_id}/action (os-attach)'
}
]),
]
def list_rules():
return volume_metadata_policies

View File

@ -0,0 +1,87 @@
# Copyright (c) 2017 Huawei Technologies Co., Ltd.
# All Rights Reserved.
#
# 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 cinder.policies import base
CREATE_POLICY = "volume:create_transfer"
ACCEPT_POLICY = "volume:accept_transfer"
DELETE_POLICY = "volume:delete_transfer"
GET_POLICY = "volume:get_transfer"
GET_ALL_POLICY = "volume:get_all_transfers"
volume_transfer_policies = [
policy.DocumentedRuleDefault(
name=GET_ALL_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="List volume transfer.",
operations=[
{
'method': 'GET',
'path': '/os-volume-transfer'
},
{
'method': 'GET',
'path': '/os-volume-transfer/detail'
}
]),
policy.DocumentedRuleDefault(
name=CREATE_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="Create a volume transfer.",
operations=[
{
'method': 'POST',
'path': '/os-volume-transfer'
}
]),
policy.DocumentedRuleDefault(
name=GET_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="Show one specified volume transfer.",
operations=[
{
'method': 'GET',
'path': '/os-volume-transfer/{transfer_id}'
}
]),
policy.DocumentedRuleDefault(
name=ACCEPT_POLICY,
check_str="",
description="Accept a volume transfer.",
operations=[
{
'method': 'POST',
'path': '/os-volume-transfer/{transfer_id}/accept'
}
]),
policy.DocumentedRuleDefault(
name=DELETE_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="Delete volume transfer.",
operations=[
{
'method': 'DELETE',
'path': '/os-volume-transfer/{transfer_id}'
}
]),
]
def list_rules():
return volume_transfer_policies

View File

@ -0,0 +1,106 @@
# Copyright (c) 2017 Huawei Technologies Co., Ltd.
# All Rights Reserved.
#
# 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 cinder.policies import base
MANAGE_POLICY = "volume_extension:types_manage"
ENCRYPTION_POLICY = "volume_extension:volume_type_encryption"
QOS_POLICY = "volume_extension:access_types_qos_specs_id"
EXTRA_SPEC_POLICY = "volume_extension:access_types_extra_specs"
volume_type_policies = [
policy.DocumentedRuleDefault(
name=MANAGE_POLICY,
check_str=base.RULE_ADMIN_API,
description="""Create, update and delete volume type.""",
operations=[
{
'method': 'POST',
'path': '/types'
},
{
'method': 'PUT',
'path': '/types'
},
{
'method': 'DELETE',
'path': '/types'
}
]),
policy.DocumentedRuleDefault(
name=ENCRYPTION_POLICY,
check_str=base.RULE_ADMIN_API,
description="""List, show, create, update and delete volume
type encryption.""",
operations=[
{
'method': 'POST',
'path': '/types/{type_id}/encryption'
},
{
'method': 'PUT',
'path': '/types/{type_id}/encryption/{encryption_id}'
},
{
'method': 'GET',
'path': '/types/{type_id}/encryption'
},
{
'method': 'GET',
'path': '/types/{type_id}/encryption/{encryption_id}'
},
{
'method': 'DELETE',
'path': '/types/{type_id}/encryption/{encryption_id}'
}
]),
policy.DocumentedRuleDefault(
name=EXTRA_SPEC_POLICY,
check_str=base.RULE_ADMIN_API,
description="""List or show volume type with access type extra
specs attribute.""",
operations=[
{
'method': 'GET',
'path': '/types/{type_id}'
},
{
'method': 'GET',
'path': '/types'
}
]),
policy.DocumentedRuleDefault(
name=QOS_POLICY,
check_str=base.RULE_ADMIN_API,
description="""List or show volume type with access type qos specs
id attribute.""",
operations=[
{
'method': 'GET',
'path': '/types/{type_id}'
},
{
'method': 'GET',
'path': '/types'
}
]),
]
def list_rules():
return volume_type_policies

168
cinder/policies/volumes.py Normal file
View File

@ -0,0 +1,168 @@
# Copyright (c) 2017 Huawei Technologies Co., Ltd.
# All Rights Reserved.
#
# 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 cinder.policies import base
CREATE_POLICY = "volume:create"
CREATE_FROM_IMAGE_POLICY = "volume:create_from_image"
GET_POLICY = "volume:get"
GET_ALL_POLICY = "volume:get_all"
UPDATE_POLICY = "volume:update"
DELETE_POLICY = "volume:delete"
FORCE_DELETE_POLICY = "volume:force_delete"
HOST_ATTRIBUTE_POLICY = "volume_extension:volume_host_attribute"
TENANT_ATTRIBUTE_POLICY = "volume_extension:volume_tenant_attribute"
MIG_ATTRIBUTE_POLICY = "volume_extension:volume_mig_status_attribute"
ENCRYPTION_METADATA_POLICY = "volume_extension:volume_encryption_metadata"
volumes_policies = [
policy.DocumentedRuleDefault(
name=CREATE_POLICY,
check_str="",
description="Create volume.",
operations=[
{
'method': 'POST',
'path': '/volumes'
}
]),
policy.DocumentedRuleDefault(
name=CREATE_FROM_IMAGE_POLICY,
check_str="",
description="Create volume from image.",
operations=[
{
'method': 'POST',
'path': '/volumes'
}
]),
policy.DocumentedRuleDefault(
name=GET_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="Show volume.",
operations=[
{
'method': 'GET',
'path': '/volumes/{volume_id}'
}
]),
policy.DocumentedRuleDefault(
name=GET_ALL_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="List volumes.",
operations=[
{
'method': 'GET',
'path': '/volumes'
},
{
'method': 'GET',
'path': '/volumes/detail'
}
]),
policy.DocumentedRuleDefault(
name=UPDATE_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="Update volume.",
operations=[
{
'method': 'PUT',
'path': '/volumes'
}
]),
policy.DocumentedRuleDefault(
name=DELETE_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="Delete volume.",
operations=[
{
'method': 'DELETE',
'path': '/volumes/{volume_id}'
}
]),
policy.DocumentedRuleDefault(
name=FORCE_DELETE_POLICY,
check_str=base.RULE_ADMIN_API,
description="Force Delete a volume.",
operations=[
{
'method': 'DELETE',
'path': '/volumes/{volume_id}'
}
]),
policy.DocumentedRuleDefault(
name=HOST_ATTRIBUTE_POLICY,
check_str=base.RULE_ADMIN_API,
description="List or show volume with host attribute.",
operations=[
{
'method': 'GET',
'path': '/volumes/{volume_id}'
},
{
'method': 'GET',
'path': '/volumes/detail'
}
]),
policy.DocumentedRuleDefault(
name=TENANT_ATTRIBUTE_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="List or show volume with tenant attribute.",
operations=[
{
'method': 'GET',
'path': '/volumes/{volume_id}'
},
{
'method': 'GET',
'path': '/volumes/detail'
}
]),
policy.DocumentedRuleDefault(
name=MIG_ATTRIBUTE_POLICY,
check_str=base.RULE_ADMIN_API,
description="List or show volume with migration status attribute.",
operations=[
{
'method': 'GET',
'path': '/volumes/{volume_id}'
},
{
'method': 'GET',
'path': '/volumes/detail'
}
]),
policy.DocumentedRuleDefault(
name=ENCRYPTION_METADATA_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="Show volume's encryption metadata.",
operations=[
{
'method': 'GET',
'path': '/volumes/{volume_id}/encryption'
},
{
'method': 'GET',
'path': '/volumes/{volume_id}/encryption/{encryption_key}'
}
]),
]
def list_rules():
return volumes_policies

View File

@ -63,17 +63,6 @@ def init(policy_file=None, rules=None, default_rule=None, use_conf=True):
_ENFORCER.load_rules()
def enforce_action(context, action):
"""Checks that the action can be done by the given context.
Applies a check to ensure the context's project_id and user_id can be
applied to the given action using the policy enforcement api.
"""
return enforce(context, action, {'project_id': context.project_id,
'user_id': context.user_id})
def enforce(context, action, target):
"""Verifies that the action is valid on the target in this context.

View File

@ -183,12 +183,12 @@ class VolumeTypesManageApiTest(test.TestCase):
@mock.patch('cinder.volume.volume_types.destroy')
@mock.patch('cinder.volume.volume_types.get_volume_type')
@mock.patch('cinder.policy.enforce')
def test_volume_types_delete_with_non_admin(self, mock_policy_enforce,
@mock.patch('cinder.policy.authorize')
def test_volume_types_delete_with_non_admin(self, mock_policy_authorize,
mock_get, mock_destroy):
# allow policy authorized user to delete type
mock_policy_enforce.return_value = None
mock_policy_authorize.return_value = None
mock_get.return_value = \
{'extra_specs': {"key1": "value1"},
'id': DEFAULT_VOLUME_TYPE,
@ -203,7 +203,7 @@ class VolumeTypesManageApiTest(test.TestCase):
self.controller._delete(req, DEFAULT_VOLUME_TYPE)
self.assertEqual(1, len(self.notifier.notifications))
# non policy authorized user fails to delete type
mock_policy_enforce.side_effect = (
mock_policy_authorize.side_effect = (
exception.PolicyNotAuthorized(action='type_delete'))
self.assertRaises(exception.PolicyNotAuthorized,
self.controller._delete,
@ -329,13 +329,13 @@ class VolumeTypesManageApiTest(test.TestCase):
@mock.patch('cinder.volume.volume_types.create')
@mock.patch('cinder.volume.volume_types.get_volume_type_by_name')
@mock.patch('cinder.policy.enforce')
def test_create_with_none_admin(self, mock_policy_enforce,
@mock.patch('cinder.policy.authorize')
def test_create_with_none_admin(self, mock_policy_authorize,
mock_get_volume_type_by_name,
mock_create_type):
# allow policy authorized user to create type
mock_policy_enforce.return_value = None
mock_policy_authorize.return_value = None
mock_get_volume_type_by_name.return_value = \
{'extra_specs': {"key1": "value1"},
'id': DEFAULT_VOLUME_TYPE,
@ -356,7 +356,7 @@ class VolumeTypesManageApiTest(test.TestCase):
'expected_name': 'vol_type_1', 'expected_desc': 'vol_type_desc_1'})
# non policy authorized user fails to create type
mock_policy_enforce.side_effect = (
mock_policy_authorize.side_effect = (
exception.PolicyNotAuthorized(action='type_create'))
self.assertRaises(exception.PolicyNotAuthorized,
self.controller._create,
@ -651,12 +651,12 @@ class VolumeTypesManageApiTest(test.TestCase):
@mock.patch('cinder.volume.volume_types.update')
@mock.patch('cinder.volume.volume_types.get_volume_type')
@mock.patch('cinder.policy.enforce')
def test_update_with_non_admin(self, mock_policy_enforce, mock_get,
@mock.patch('cinder.policy.authorize')
def test_update_with_non_admin(self, mock_policy_authorize, mock_get,
mock_update):
# allow policy authorized user to update type
mock_policy_enforce.return_value = None
mock_policy_authorize.return_value = None
mock_get.return_value = return_volume_types_get_volume_type_updated(
DEFAULT_VOLUME_TYPE, is_public=False)
name = "vol_type_%s" % DEFAULT_VOLUME_TYPE
@ -681,7 +681,7 @@ class VolumeTypesManageApiTest(test.TestCase):
'is_public': False})
# non policy authorized user fails to update type
mock_policy_enforce.side_effect = (
mock_policy_authorize.side_effect = (
exception.PolicyNotAuthorized(action='type_update'))
self.assertRaises(exception.PolicyNotAuthorized,
self.controller._update,

View File

@ -12,6 +12,7 @@
# under the License.
import datetime
import mock
from six.moves import http_client
import webob
@ -153,11 +154,11 @@ class VolumeTypeAccessTest(test.TestCase):
def fake_authorize(context, target=None, action=None):
raise exception.PolicyNotAuthorized(action='index')
self.mock_object(type_access, 'authorize', fake_authorize)
self.assertRaises(exception.PolicyNotAuthorized,
self.type_access_controller.index,
req, fake.PROJECT_ID)
with mock.patch('cinder.context.RequestContext.authorize',
fake_authorize):
self.assertRaises(exception.PolicyNotAuthorized,
self.type_access_controller.index,
req, fake.PROJECT_ID)
def test_list_type_with_admin_default_proj1(self):
expected = {'volume_types': [{'id': fake.VOLUME_TYPE_ID},

View File

@ -20,7 +20,6 @@ from oslo_utils import timeutils
import six
import webob
import cinder.api.common as common
from cinder.api.v2 import types
from cinder.api.v2.views import types as views_types
from cinder import context
@ -334,9 +333,8 @@ class VolumeTypesApiTest(test.TestCase):
self.assertDictEqual(expected_volume_type, output['volume_type'])
def test_view_builder_show_qos_specs_id_policy(self):
with mock.patch.object(common,
'validate_policy',
side_effect=[False, True]):
with mock.patch('cinder.context.RequestContext.authorize',
side_effect=[False, True]):
view_builder = views_types.ViewBuilder()
now = timeutils.utcnow().isoformat()
raw_volume_type = dict(
@ -366,9 +364,8 @@ class VolumeTypesApiTest(test.TestCase):
self.assertDictEqual(expected_volume_type, output['volume_type'])
def test_view_builder_show_extra_specs_policy(self):
with mock.patch.object(common,
'validate_policy',
side_effect=[True, False]):
with mock.patch('cinder.context.RequestContext.authorize',
side_effect=[True, False]):
view_builder = views_types.ViewBuilder()
now = timeutils.utcnow().isoformat()
raw_volume_type = dict(
@ -397,9 +394,8 @@ class VolumeTypesApiTest(test.TestCase):
)
self.assertDictEqual(expected_volume_type, output['volume_type'])
with mock.patch.object(common,
'validate_policy',
side_effect=[False, False]):
with mock.patch('cinder.context.RequestContext.authorize',
side_effect=[False, False]):
view_builder = views_types.ViewBuilder()
now = timeutils.utcnow().isoformat()
raw_volume_type = dict(
@ -428,9 +424,8 @@ class VolumeTypesApiTest(test.TestCase):
self.assertDictEqual(expected_volume_type, output['volume_type'])
def test_view_builder_show_pass_all_policy(self):
with mock.patch.object(common,
'validate_policy',
side_effect=[True, True]):
with mock.patch('cinder.context.RequestContext.authorize',
side_effect=[True, True]):
view_builder = views_types.ViewBuilder()
now = timeutils.utcnow().isoformat()
raw_volume_type = dict(

View File

@ -39,8 +39,7 @@ class AttachmentManagerTestCase(test.TestCase):
self.context.project_id = self.project_id
self.volume_api = volume_api.API()
@mock.patch('cinder.volume.api.check_policy')
def test_attachment_create_no_connector(self, mock_policy):
def test_attachment_create_no_connector(self):
"""Test attachment_create no connector."""
volume_params = {'status': 'available'}
@ -55,11 +54,9 @@ class AttachmentManagerTestCase(test.TestCase):
self.assertEqual(vref.id, aref.volume_id)
self.assertEqual({}, aref.connection_info)
@mock.patch('cinder.volume.api.check_policy')
@mock.patch('cinder.volume.rpcapi.VolumeAPI.attachment_update')
def test_attachment_create_with_connector(self,
mock_rpc_attachment_update,
mock_policy):
mock_rpc_attachment_update):
"""Test attachment_create with connector."""
volume_params = {'status': 'available'}
connection_info = {'fake_key': 'fake_value',
@ -80,11 +77,9 @@ class AttachmentManagerTestCase(test.TestCase):
attachment.id)
self.assertEqual(connection_info, new_attachment.connection_info)
@mock.patch('cinder.volume.api.check_policy')
@mock.patch('cinder.volume.rpcapi.VolumeAPI.attachment_delete')
def test_attachment_delete_reserved(self,
mock_rpc_attachment_delete,
mock_policy):
mock_rpc_attachment_delete):
"""Test attachment_delete with reserved."""
volume_params = {'status': 'available'}
@ -103,14 +98,12 @@ class AttachmentManagerTestCase(test.TestCase):
# rpc call
mock_rpc_attachment_delete.assert_not_called()
@mock.patch('cinder.volume.api.check_policy')
@mock.patch('cinder.volume.rpcapi.VolumeAPI.attachment_delete')
@mock.patch('cinder.volume.rpcapi.VolumeAPI.attachment_update')
def test_attachment_create_update_and_delete(
self,
mock_rpc_attachment_update,
mock_rpc_attachment_delete,
mock_policy):
mock_rpc_attachment_delete):
"""Test attachment_delete."""
volume_params = {'status': 'available'}
connection_info = {'fake_key': 'fake_value',
@ -151,8 +144,7 @@ class AttachmentManagerTestCase(test.TestCase):
aref.id,
mock.ANY)
@mock.patch('cinder.volume.api.check_policy')
def test_additional_attachment_create_no_connector(self, mock_policy):
def test_additional_attachment_create_no_connector(self):
"""Test attachment_create no connector."""
volume_params = {'status': 'available'}
@ -179,12 +171,10 @@ class AttachmentManagerTestCase(test.TestCase):
vref.id)
self.assertEqual(2, len(vref.volume_attachment))
@mock.patch('cinder.volume.api.check_policy')
@mock.patch('cinder.volume.rpcapi.VolumeAPI.attachment_update')
def test_attachment_create_reserve_delete(
self,
mock_rpc_attachment_update,
mock_policy):
mock_rpc_attachment_update):
volume_params = {'status': 'available'}
connector = {
"initiator": "iqn.1993-08.org.debian:01:cad181614cec",
@ -223,8 +213,7 @@ class AttachmentManagerTestCase(test.TestCase):
vref.id)
self.assertEqual('reserved', vref.status)
@mock.patch('cinder.volume.api.check_policy')
def test_reserve_reserve_delete(self, mock_policy):
def test_reserve_reserve_delete(self):
"""Test that we keep reserved status across multiple reserves."""
volume_params = {'status': 'available'}

View File

@ -11,8 +11,6 @@
"volume:create_volume_metadata": "",
"volume:delete_volume_metadata": "",
"volume:update_volume_metadata": "",
"volume:get_volume_admin_metadata": "rule:admin_api",
"volume:update_volume_admin_metadata": "rule:admin_api",
"volume:delete": "",
"volume:force_delete": "rule:admin_api",
"volume:update": "",
@ -34,47 +32,26 @@
"volume:update_snapshot_metadata": "",
"volume:extend": "",
"volume:extend_attached_volume": "",
"volume:migrate_volume": "rule:admin_api",
"volume:migrate_volume_completion": "rule:admin_api",
"volume:update_readonly_flag": "",
"volume:retype": "",
"volume:copy_volume_to_image": "",
"volume:revert_to_snapshot": "",
"volume_extension:volume_admin_actions:reset_status": "rule:admin_api",
"volume_extension:volume_admin_actions:force_delete": "rule:admin_api",
"volume_extension:volume_admin_actions:force_detach": "rule:admin_api",
"volume_extension:volume_admin_actions:migrate_volume": "rule:admin_api",
"volume_extension:volume_admin_actions:migrate_volume_completion": "rule:admin_api",
"volume_extension:volume_actions:upload_image": "",
"volume_extension:volume_actions:upload_public": "rule:admin_api",
"volume_extension:types_manage": "",
"volume_extension:types_extra_specs:create": "",
"volume_extension:types_extra_specs:delete": "",
"volume_extension:types_extra_specs:index": "",
"volume_extension:types_extra_specs:show": "",
"volume_extension:types_extra_specs:update": "",
"volume_extension:access_types_qos_specs_id": "rule:admin_api",
"volume_extension:access_types_extra_specs": "rule:admin_api",
"volume_extension:volume_type_access": "",
"volume_extension:volume_type_access:addProjectAccess": "rule:admin_api",
"volume_extension:volume_type_access:removeProjectAccess": "rule:admin_api",
"volume_extension:volume_type_encryption": "rule:admin_api",
"volume_extension:volume_encryption_metadata": "rule:admin_or_owner",
"volume_extension:extended_snapshot_attributes": "",
"volume_extension:volume_image_metadata": "",
"volume_extension:volume_host_attribute": "rule:admin_api",
"volume_extension:volume_tenant_attribute": "rule:admin_api",
"volume_extension:volume_mig_status_attribute": "rule:admin_api",
"volume_extension:services:index": "",
"volume_extension:services:update" : "rule:admin_api",
"volume_extension:volume_manage": "rule:admin_api",
"volume_extension:volume_unmanage": "rule:admin_api",
"volume_extension:list_manageable": "rule:admin_api",
"limits_extension:used_limits": "",
"volume:create_transfer": "",
"volume:accept_transfer": "",
"volume:delete_transfer": "",
"volume:get_transfer": "",
"volume:get_all_transfers": "",

View File

@ -31,6 +31,7 @@ from cinder.db import base
from cinder import exception
from cinder.i18n import _
from cinder import objects
from cinder.policies import volume_transfer as policy
from cinder import quota
from cinder import quota_utils
from cinder.volume import api as volume_api
@ -59,13 +60,13 @@ class API(base.Base):
super(API, self).__init__(db_driver)
def get(self, context, transfer_id):
volume_api.check_policy(context, 'get_transfer')
context.authorize(policy.GET_POLICY)
rv = self.db.transfer_get(context, transfer_id)
return dict(rv)
def delete(self, context, transfer_id):
"""Make the RPC call to delete a volume transfer."""
volume_api.check_policy(context, 'delete_transfer')
context.authorize(policy.DELETE_POLICY)
transfer = self.db.transfer_get(context, transfer_id)
volume_ref = self.db.volume_get(context, transfer.volume_id)
@ -79,7 +80,7 @@ class API(base.Base):
def get_all(self, context, filters=None):
filters = filters or {}
volume_api.check_policy(context, 'get_all_transfers')
context.authorize(policy.GET_ALL_POLICY)
if context.is_admin and 'all_tenants' in filters:
transfers = self.db.transfer_get_all(context)
else:
@ -114,7 +115,7 @@ class API(base.Base):
def create(self, context, volume_id, display_name):
"""Creates an entry in the transfers table."""
volume_api.check_policy(context, 'create_transfer')
context.authorize(policy.CREATE_POLICY)
LOG.info("Generating transfer record for volume %s", volume_id)
volume_ref = self.db.volume_get(context, volume_id)
if volume_ref['status'] != "available":
@ -151,7 +152,7 @@ class API(base.Base):
"""Accept a volume that has been offered for transfer."""
# We must use an elevated context to see the volume that is still
# owned by the donor.
volume_api.check_policy(context, 'accept_transfer')
context.authorize(policy.ACCEPT_POLICY)
transfer = self.db.transfer_get(context.elevated(), transfer_id)
crypt_hash = self._get_crypt_hash(transfer['salt'], auth_key)

View File

@ -19,7 +19,6 @@
import ast
import collections
import datetime
import functools
from oslo_config import cfg
from oslo_log import log as logging
@ -47,7 +46,10 @@ from cinder.policies import attachments as attachment_policy
from cinder.policies import services as svr_policy
from cinder.policies import snapshot_metadata as s_meta_policy
from cinder.policies import snapshots as snapshot_policy
import cinder.policy
from cinder.policies import volume_actions as vol_action_policy
from cinder.policies import volume_metadata as vol_meta_policy
from cinder.policies import volume_transfer as vol_transfer_policy
from cinder.policies import volumes as vol_policy
from cinder import quota
from cinder import quota_utils
from cinder.scheduler import rpcapi as scheduler_rpcapi
@ -93,36 +95,6 @@ QUOTAS = quota.QUOTAS
AO_LIST = objects.VolumeAttachmentList
def wrap_check_policy(func):
"""Check policy corresponding to the wrapped methods prior to execution
This decorator requires the first 3 args of the wrapped function
to be (self, context, volume)
"""
@functools.wraps(func)
def wrapped(self, context, target_obj, *args, **kwargs):
check_policy(context, func.__name__, target_obj)
return func(self, context, target_obj, *args, **kwargs)
return wrapped
def check_policy(context, action, target_obj=None):
target = {
'project_id': context.project_id,
'user_id': context.user_id,
}
if isinstance(target_obj, objects_base.CinderObject):
# Turn object into dict so target.update can work
target.update(
target_obj.obj_to_primitive()['versioned_object.data'] or {})
else:
target.update(target_obj or {})
_action = 'volume:%s' % action
cinder.policy.enforce(context, _action, target)
class API(base.Base):
"""API for interacting with the volume manager."""
@ -227,7 +199,7 @@ class API(base.Base):
cgsnapshot=None, multiattach=False, source_cg=None,
group=None, group_snapshot=None, source_group=None):
check_policy(context, 'create_from_image' if image_id else 'create')
context.authorize(vol_policy.CREATE_FROM_IMAGE_POLICY)
# Check up front for legacy replication parameters to quick fail
if source_replica:
@ -368,10 +340,10 @@ class API(base.Base):
self.list_availability_zones(enable_cache=True,
refresh_cache=True)
@wrap_check_policy
def revert_to_snapshot(self, context, volume, snapshot):
"""revert a volume to a snapshot"""
context.authorize(vol_action_policy.REVERT_POLICY,
target_obj=volume)
v_res = volume.update_single_status_where(
'reverting', 'available')
if not v_res:
@ -388,11 +360,11 @@ class API(base.Base):
self.volume_rpcapi.revert_to_snapshot(context, volume, snapshot)
@wrap_check_policy
def delete(self, context, volume,
force=False,
unmanage_only=False,
cascade=False):
context.authorize(vol_policy.DELETE_POLICY, target_obj=volume)
if context.is_admin and context.project_id != volume.project_id:
project_id = volume.project_id
else:
@ -522,8 +494,8 @@ class API(base.Base):
LOG.info("Delete volume request issued successfully.",
resource=volume)
@wrap_check_policy
def update(self, context, volume, fields):
context.authorize(vol_policy.UPDATE_POLICY, target_obj=volume)
# TODO(karthikp): Making sure volume is always oslo-versioned
# If not we convert it at the start of update method. This check
# needs to be removed once we have moved to ovo.
@ -547,7 +519,7 @@ class API(base.Base):
volume = objects.Volume.get_by_id(context, volume_id)
try:
check_policy(context, 'get', volume)
context.authorize(vol_policy.GET_POLICY, target_obj=volume)
except exception.PolicyNotAuthorized:
# raise VolumeNotFound to avoid providing info about
# the existence of an unauthorized volume id
@ -566,7 +538,7 @@ class API(base.Base):
def get_all(self, context, marker=None, limit=None, sort_keys=None,
sort_dirs=None, filters=None, viewable_admin_meta=False,
offset=None):
check_policy(context, 'get_all')
context.authorize(vol_policy.GET_ALL_POLICY)
if filters is None:
filters = {}
@ -613,7 +585,7 @@ class API(base.Base):
return volumes
def get_volume_summary(self, context, filters=None):
check_policy(context, 'get_all')
context.authorize(vol_policy.GET_ALL_POLICY)
if filters is None:
filters = {}
@ -638,7 +610,7 @@ class API(base.Base):
return snapshot
def get_volume(self, context, volume_id):
check_policy(context, 'get_volume')
context.authorize(vol_policy.GET_POLICY)
volume = objects.Volume.get_by_id(context, volume_id)
LOG.info("Volume retrieved successfully.", resource=volume)
return volume
@ -664,8 +636,8 @@ class API(base.Base):
LOG.info("Get all snapshots completed successfully.")
return snapshots
@wrap_check_policy
def reserve_volume(self, context, volume):
context.authorize(vol_action_policy.RETYPE_POLICY, target_obj=volume)
expected = {'multiattach': volume.multiattach,
'status': (('available', 'in-use') if volume.multiattach
else 'available')}
@ -683,8 +655,9 @@ class API(base.Base):
LOG.info("Reserve volume completed successfully.",
resource=volume)
@wrap_check_policy
def unreserve_volume(self, context, volume):
context.authorize(vol_action_policy.UNRESERVE_POLICY,
target_obj=volume)
expected = {'status': 'attaching'}
# Status change depends on whether it has attachments (in-use) or not
# (available)
@ -701,8 +674,9 @@ class API(base.Base):
LOG.info("Unreserve volume completed successfully.",
resource=volume)
@wrap_check_policy
def begin_detaching(self, context, volume):
context.authorize(vol_action_policy.BEGIN_DETACHING_POLICY,
target_obj=volume)
# If we are in the middle of a volume migration, we don't want the
# user to see that the volume is 'detaching'. Having
# 'migration_status' set will have the same effect internally.
@ -721,16 +695,18 @@ class API(base.Base):
LOG.info("Begin detaching volume completed successfully.",
resource=volume)
@wrap_check_policy
def roll_detaching(self, context, volume):
context.authorize(vol_action_policy.ROLL_DETACHING_POLICY,
target_obj=volume)
volume.conditional_update({'status': 'in-use'},
{'status': 'detaching'})
LOG.info("Roll detaching of volume completed successfully.",
resource=volume)
@wrap_check_policy
def attach(self, context, volume, instance_uuid, host_name,
mountpoint, mode):
context.authorize(vol_action_policy.ATTACH_POLICY,
target_obj=volume)
if volume.status == 'maintenance':
LOG.info('Unable to attach volume, '
'because it is in maintenance.', resource=volume)
@ -756,8 +732,9 @@ class API(base.Base):
resource=volume)
return attach_results
@wrap_check_policy
def detach(self, context, volume, attachment_id):
context.authorize(vol_action_policy.DETACH_POLICY,
target_obj=volume)
if volume['status'] == 'maintenance':
LOG.info('Unable to detach volume, '
'because it is in maintenance.', resource=volume)
@ -769,8 +746,9 @@ class API(base.Base):
resource=volume)
return detach_results
@wrap_check_policy
def initialize_connection(self, context, volume, connector):
context.authorize(vol_action_policy.INITIALIZE_POLICY,
target_obj=volume)
if volume.status == 'maintenance':
LOG.info('Unable to initialize the connection for '
'volume, because it is in '
@ -785,8 +763,9 @@ class API(base.Base):
resource=volume)
return init_results
@wrap_check_policy
def terminate_connection(self, context, volume, connector, force=False):
context.authorize(vol_action_policy.TERMINATE_POLICY,
target_obj=volume)
self.volume_rpcapi.terminate_connection(context,
volume,
connector,
@ -795,8 +774,9 @@ class API(base.Base):
resource=volume)
self.unreserve_volume(context, volume)
@wrap_check_policy
def accept_transfer(self, context, volume, new_user, new_project):
context.authorize(vol_transfer_policy.ACCEPT_POLICY,
target_obj=volume)
if volume['status'] == 'maintenance':
LOG.info('Unable to accept transfer for volume, '
'because it is in maintenance.', resource=volume)
@ -948,7 +928,7 @@ class API(base.Base):
return snapshot_list
def _create_snapshot_in_db_validate(self, context, volume):
check_policy(context, 'create_snapshot', volume)
context.authorize(snapshot_policy.CREATE_POLICY, target_obj=volume)
if volume['status'] == 'maintenance':
LOG.info('Unable to create the snapshot for volume, '
@ -1071,27 +1051,27 @@ class API(base.Base):
snapshot.update(fields)
snapshot.save()
@wrap_check_policy
def get_volume_metadata(self, context, volume):
"""Get all metadata associated with a volume."""
context.authorize(vol_meta_policy.GET_POLICY, target_obj=volume)
rv = self.db.volume_metadata_get(context, volume['id'])
LOG.info("Get volume metadata completed successfully.",
resource=volume)
return dict(rv)
@wrap_check_policy
def create_volume_metadata(self, context, volume, metadata):
"""Creates volume metadata."""
context.authorize(vol_meta_policy.CREATE_POLICY, target_obj=volume)
db_meta = self._update_volume_metadata(context, volume, metadata)
LOG.info("Create volume metadata completed successfully.",
resource=volume)
return db_meta
@wrap_check_policy
def delete_volume_metadata(self, context, volume,
key, meta_type=common.METADATA_TYPES.user):
"""Delete the given metadata item from a volume."""
context.authorize(vol_meta_policy.DELETE_POLICY, target_obj=volume)
if volume.status in ('maintenance', 'uploading'):
msg = _('Deleting volume metadata is not allowed for volumes in '
'%s status.') % volume.status
@ -1112,7 +1092,6 @@ class API(base.Base):
return self.db.volume_metadata_update(context, volume['id'],
metadata, delete, meta_type)
@wrap_check_policy
def update_volume_metadata(self, context, volume, metadata, delete=False,
meta_type=common.METADATA_TYPES.user):
"""Updates volume metadata.
@ -1121,6 +1100,7 @@ class API(base.Base):
`metadata` argument will be deleted.
"""
context.authorize(vol_meta_policy.UPDATE_POLICY, target_obj=volume)
db_meta = self._update_volume_metadata(context, volume, metadata,
delete, meta_type)
@ -1130,7 +1110,6 @@ class API(base.Base):
resource=volume)
return db_meta
@wrap_check_policy
def get_volume_admin_metadata(self, context, volume):
"""Get all administration metadata associated with a volume."""
rv = self.db.volume_admin_metadata_get(context, volume['id'])
@ -1138,7 +1117,6 @@ class API(base.Base):
resource=volume)
return dict(rv)
@wrap_check_policy
def update_volume_admin_metadata(self, context, volume, metadata,
delete=False, add=True, update=True):
"""Updates or creates volume administration metadata.
@ -1147,6 +1125,8 @@ class API(base.Base):
`metadata` argument will be deleted.
"""
context.authorize(vol_meta_policy.UPDATE_ADMIN_METADATA_POLICY,
target_obj=volume)
utils.check_metadata_properties(metadata)
db_meta = self.db.volume_admin_metadata_update(context, volume.id,
metadata, delete, add,
@ -1210,7 +1190,7 @@ class API(base.Base):
pass
def get_volumes_image_metadata(self, context):
check_policy(context, 'get_volumes_image_metadata')
context.authorize(vol_meta_policy.GET_POLICY)
db_data = self.db.volume_glance_metadata_get_all(context)
results = collections.defaultdict(dict)
for meta_entry in db_data:
@ -1218,8 +1198,8 @@ class API(base.Base):
meta_entry['value']})
return results
@wrap_check_policy
def get_volume_image_metadata(self, context, volume):
context.authorize(vol_meta_policy.GET_POLICY, target_obj=volume)
db_data = self.db.volume_glance_metadata_get(context, volume['id'])
LOG.info("Get volume image-metadata completed successfully.",
resource=volume)
@ -1234,7 +1214,6 @@ class API(base.Base):
meta_entry['value']})
return results
@wrap_check_policy
def copy_volume_to_image(self, context, volume, metadata, force):
"""Create a new image from the specified volume."""
if not CONF.enable_force_upload and force:
@ -1407,22 +1386,25 @@ class API(base.Base):
LOG.info("Extend volume request issued successfully.",
resource=volume)
@wrap_check_policy
def extend(self, context, volume, new_size):
context.authorize(vol_action_policy.EXTEND_POLICY,
target_obj=volume)
self._extend(context, volume, new_size, attached=False)
# NOTE(tommylikehu): New method is added here so that administrator
# can enable/disable this ability by editing the policy file if the
# cloud environment doesn't allow this operation.
@wrap_check_policy
def extend_attached_volume(self, context, volume, new_size):
context.authorize(vol_action_policy.EXTEND_ATTACHED_POLICY,
target_obj=volume)
self._extend(context, volume, new_size, attached=True)
@wrap_check_policy
def migrate_volume(self, context, volume, host, cluster_name, force_copy,
lock_volume):
"""Migrate the volume to the specified host or cluster."""
elevated = context.elevated()
context.authorize(vol_action_policy.MIGRATE_POLICY,
target_obj=volume)
# If we received a request to migrate to a host
# Look for the service - must be up and enabled
@ -1504,8 +1486,9 @@ class API(base.Base):
LOG.info("Migrate volume request issued successfully.",
resource=volume)
@wrap_check_policy
def migrate_volume_completion(self, context, volume, new_volume, error):
context.authorize(vol_action_policy.MIGRATE_COMPLETE_POLICY,
target_obj=volume)
if not (volume.migration_status or new_volume.migration_status):
# When we're not migrating and haven't hit any errors, we issue
# volume attach and detach requests so the volumes don't end in
@ -1543,8 +1526,9 @@ class API(base.Base):
return self.volume_rpcapi.migrate_volume_completion(context, volume,
new_volume, error)
@wrap_check_policy
def update_readonly_flag(self, context, volume, flag):
context.authorize(vol_action_policy.UPDATE_READONLY_POLICY,
target_obj=volume)
if volume['status'] != 'available':
msg = _('Volume %(vol_id)s status must be available '
'to update readonly flag, but current status is: '
@ -1557,9 +1541,9 @@ class API(base.Base):
"completed successfully.",
resource=volume)
@wrap_check_policy
def retype(self, context, volume, new_type, migration_policy=None):
"""Attempt to modify the type associated with an existing volume."""
context.authorize(vol_action_policy.RETYPE_POLICY, target_obj=volume)
if migration_policy and migration_policy not in ('on-demand', 'never'):
msg = _('migration_policy must be \'on-demand\' or \'never\', '
'passed: %s') % new_type

View File

@ -26,7 +26,7 @@ from cinder import flow_utils
from cinder.i18n import _
from cinder import objects
from cinder.objects import fields
from cinder import policy
from cinder.policies import volumes as policy
from cinder import quota
from cinder import quota_utils
from cinder import utils
@ -420,7 +420,7 @@ class ExtractVolumeRequestTask(flow_utils.CinderTask):
utils.check_exclusive_options(snapshot=snapshot,
imageRef=image_id,
source_volume=source_volume)
policy.enforce_action(context, ACTION)
context.authorize(policy.CREATE_POLICY)
# TODO(harlowja): what guarantee is there that the snapshot or source
# volume will remain available after we do this initial verification??

View File

@ -1,61 +1,4 @@
{
"volume:create": "",
"volume:create_from_image": "",
"volume:delete": "rule:admin_or_owner",
"volume:force_delete": "rule:admin_api",
"volume:get": "rule:admin_or_owner",
"volume:get_all": "rule:admin_or_owner",
"volume:get_volume_metadata": "rule:admin_or_owner",
"volume:create_volume_metadata": "rule:admin_or_owner",
"volume:delete_volume_metadata": "rule:admin_or_owner",
"volume:update_volume_metadata": "rule:admin_or_owner",
"volume:get_volume_admin_metadata": "rule:admin_api",
"volume:update_volume_admin_metadata": "rule:admin_api",
"volume:extend": "rule:admin_or_owner",
"volume:extend_attached_volume": "rule:admin_or_owner",
"volume:update_readonly_flag": "rule:admin_or_owner",
"volume:retype": "rule:admin_or_owner",
"volume:update": "rule:admin_or_owner",
"volume:revert_to_snapshot": "rule:admin_or_owner",
"volume_extension:types_manage": "rule:admin_api",
"volume_extension:types_extra_specs:create": "rule:admin_api",
"volume_extension:types_extra_specs:delete": "rule:admin_api",
"volume_extension:types_extra_specs:index": "rule:admin_api",
"volume_extension:types_extra_specs:show": "rule:admin_api",
"volume_extension:types_extra_specs:update": "rule:admin_api",
"volume_extension:access_types_qos_specs_id": "rule:admin_api",
"volume_extension:access_types_extra_specs": "rule:admin_api",
"volume_extension:volume_type_access": "rule:admin_or_owner",
"volume_extension:volume_type_access:addProjectAccess": "rule:admin_api",
"volume_extension:volume_type_access:removeProjectAccess": "rule:admin_api",
"volume_extension:volume_type_encryption": "rule:admin_api",
"volume_extension:volume_encryption_metadata": "rule:admin_or_owner",
"volume_extension:volume_image_metadata": "rule:admin_or_owner",
"volume_extension:volume_admin_actions:reset_status": "rule:admin_api",
"volume_extension:volume_admin_actions:force_delete": "rule:admin_api",
"volume_extension:volume_admin_actions:force_detach": "rule:admin_api",
"volume_extension:backup_admin_actions:force_delete": "rule:admin_api",
"volume_extension:volume_admin_actions:migrate_volume": "rule:admin_api",
"volume_extension:volume_admin_actions:migrate_volume_completion": "rule:admin_api",
"volume_extension:volume_actions:upload_public": "rule:admin_api",
"volume_extension:volume_actions:upload_image": "rule:admin_or_owner",
"volume_extension:volume_host_attribute": "rule:admin_api",
"volume_extension:volume_tenant_attribute": "rule:admin_or_owner",
"volume_extension:volume_mig_status_attribute": "rule:admin_api",
"volume_extension:volume_manage": "rule:admin_api",
"volume_extension:volume_unmanage": "rule:admin_api",
"volume_extension:list_manageable": "rule:admin_api",
"volume:create_transfer": "rule:admin_or_owner",
"volume:accept_transfer": "",
"volume:delete_transfer": "rule:admin_or_owner",
"volume:get_transfer": "rule:admin_or_owner",
"volume:get_all_transfers": "rule:admin_or_owner",
"consistencygroup:create" : "group:nobody",
"consistencygroup:delete": "group:nobody",
@ -66,6 +9,6 @@
"consistencygroup:create_cgsnapshot" : "group:nobody",
"consistencygroup:delete_cgsnapshot": "group:nobody",
"consistencygroup:get_cgsnapshot": "group:nobody",
"consistencygroup:get_all_cgsnapshots": "group:nobody",
"consistencygroup:get_all_cgsnapshots": "group:nobody"
}