[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:
parent
5b5715e2ad
commit
a9e075021f
@ -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.
|
||||
|
||||
|
@ -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)
|
||||
|
||||
def _remove_worker(self, context, id):
|
||||
# Remove the cleanup worker from the DB when we change a resource
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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']
|
||||
|
||||
|
@ -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.')
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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."""
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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(),
|
||||
)
|
||||
|
@ -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)'),
|
||||
|
66
cinder/policies/manageable_volumes.py
Normal file
66
cinder/policies/manageable_volumes.py
Normal 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
|
83
cinder/policies/type_extra_specs.py
Normal file
83
cinder/policies/type_extra_specs.py
Normal 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
|
73
cinder/policies/volume_access.py
Normal file
73
cinder/policies/volume_access.py
Normal 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
|
239
cinder/policies/volume_actions.py
Normal file
239
cinder/policies/volume_actions.py
Normal 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
|
128
cinder/policies/volume_metadata.py
Normal file
128
cinder/policies/volume_metadata.py
Normal 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
|
87
cinder/policies/volume_transfer.py
Normal file
87
cinder/policies/volume_transfer.py
Normal 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
|
106
cinder/policies/volume_type.py
Normal file
106
cinder/policies/volume_type.py
Normal 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
168
cinder/policies/volumes.py
Normal 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
|
@ -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.
|
||||
|
||||
|
@ -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,
|
||||
|
@ -12,6 +12,7 @@
|
||||
# under the License.
|
||||
|
||||
import datetime
|
||||
import mock
|
||||
|
||||
from six.moves import http_client
|
||||
import webob
|
||||
@ -153,8 +154,8 @@ 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)
|
||||
|
||||
with mock.patch('cinder.context.RequestContext.authorize',
|
||||
fake_authorize):
|
||||
self.assertRaises(exception.PolicyNotAuthorized,
|
||||
self.type_access_controller.index,
|
||||
req, fake.PROJECT_ID)
|
||||
|
@ -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,8 +333,7 @@ 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',
|
||||
with mock.patch('cinder.context.RequestContext.authorize',
|
||||
side_effect=[False, True]):
|
||||
view_builder = views_types.ViewBuilder()
|
||||
now = timeutils.utcnow().isoformat()
|
||||
@ -366,8 +364,7 @@ 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',
|
||||
with mock.patch('cinder.context.RequestContext.authorize',
|
||||
side_effect=[True, False]):
|
||||
view_builder = views_types.ViewBuilder()
|
||||
now = timeutils.utcnow().isoformat()
|
||||
@ -397,8 +394,7 @@ class VolumeTypesApiTest(test.TestCase):
|
||||
)
|
||||
self.assertDictEqual(expected_volume_type, output['volume_type'])
|
||||
|
||||
with mock.patch.object(common,
|
||||
'validate_policy',
|
||||
with mock.patch('cinder.context.RequestContext.authorize',
|
||||
side_effect=[False, False]):
|
||||
view_builder = views_types.ViewBuilder()
|
||||
now = timeutils.utcnow().isoformat()
|
||||
@ -428,8 +424,7 @@ 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',
|
||||
with mock.patch('cinder.context.RequestContext.authorize',
|
||||
side_effect=[True, True]):
|
||||
view_builder = views_types.ViewBuilder()
|
||||
now = timeutils.utcnow().isoformat()
|
||||
|
@ -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'}
|
||||
|
||||
|
@ -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": "",
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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??
|
||||
|
@ -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"
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user