diff --git a/cinder/api/contrib/admin_actions.py b/cinder/api/contrib/admin_actions.py index 8056a243928..aae6dae5d1e 100644 --- a/cinder/api/contrib/admin_actions.py +++ b/cinder/api/contrib/admin_actions.py @@ -79,9 +79,18 @@ class AdminController(wsgi.Controller): return update def authorize(self, context, action_name): - # e.g. "snapshot_admin_actions:reset_status" - action = '%s_admin_actions:%s' % (self.resource_name, action_name) - extensions.extension_authorizer('volume', action)(context) + # NOTE(tommylikehu): 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 ['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 diff --git a/cinder/api/contrib/extended_snapshot_attributes.py b/cinder/api/contrib/extended_snapshot_attributes.py index f958510e995..915a0f3b249 100644 --- a/cinder/api/contrib/extended_snapshot_attributes.py +++ b/cinder/api/contrib/extended_snapshot_attributes.py @@ -16,11 +16,7 @@ from cinder.api import extensions from cinder.api.openstack import wsgi - - -authorize = extensions.soft_extension_authorizer( - 'volume', - 'extended_snapshot_attributes') +from cinder.policies import snapshots as policy class ExtendedSnapshotAttributesController(wsgi.Controller): @@ -33,7 +29,7 @@ class ExtendedSnapshotAttributesController(wsgi.Controller): @wsgi.extends def show(self, req, resp_obj, id): context = req.environ['cinder.context'] - if authorize(context): + if context.authorize(policy.EXTEND_ATTRIBUTE): # Attach our slave template to the response object snapshot = resp_obj.obj['snapshot'] self._extend_snapshot(req, snapshot) @@ -41,7 +37,7 @@ class ExtendedSnapshotAttributesController(wsgi.Controller): @wsgi.extends def detail(self, req, resp_obj): context = req.environ['cinder.context'] - if authorize(context): + if context.authorize(policy.EXTEND_ATTRIBUTE): # Attach our slave template to the response object for snapshot in list(resp_obj.obj['snapshots']): self._extend_snapshot(req, snapshot) diff --git a/cinder/api/contrib/snapshot_actions.py b/cinder/api/contrib/snapshot_actions.py index fd8cb7a0107..353692e0614 100644 --- a/cinder/api/contrib/snapshot_actions.py +++ b/cinder/api/contrib/snapshot_actions.py @@ -21,15 +21,10 @@ from cinder.api.openstack import wsgi from cinder.i18n import _ from cinder import objects from cinder.objects import fields - +from cinder.policies import snapshot_actions as policy LOG = logging.getLogger(__name__) -def authorize(context, action_name): - action = 'snapshot_actions:%s' % action_name - extensions.extension_authorizer('snapshot', action)(context) - - class SnapshotActionsController(wsgi.Controller): def __init__(self, *args, **kwargs): super(SnapshotActionsController, self).__init__(*args, **kwargs) @@ -45,7 +40,7 @@ class SnapshotActionsController(wsgi.Controller): """ context = req.environ['cinder.context'] - authorize(context, 'update_snapshot_status') + context.authorize(policy.UPDATE_STATUS_POLICY) LOG.debug("body: %s", body) try: diff --git a/cinder/api/contrib/snapshot_manage.py b/cinder/api/contrib/snapshot_manage.py index 842156996f6..072074cfcb1 100644 --- a/cinder/api/contrib/snapshot_manage.py +++ b/cinder/api/contrib/snapshot_manage.py @@ -22,13 +22,10 @@ from cinder.api.openstack import wsgi from cinder.api.views import manageable_snapshots as list_manageable_view from cinder.api.views import snapshots as snapshot_views from cinder.i18n import _ +from cinder.policies import manageable_snapshots as policy from cinder import volume as cinder_volume LOG = logging.getLogger(__name__) -authorize_manage = extensions.extension_authorizer('snapshot', - 'snapshot_manage') -authorize_list_manageable = extensions.extension_authorizer('snapshot', - 'list_manageable') class SnapshotManageController(wsgi.Controller): @@ -86,7 +83,7 @@ class SnapshotManageController(wsgi.Controller): """ context = req.environ['cinder.context'] - authorize_manage(context) + context.authorize(policy.MANAGE_POLICY) if not self.is_valid_body(body, 'snapshot'): msg = _("Missing required element snapshot in request body.") @@ -130,7 +127,7 @@ class SnapshotManageController(wsgi.Controller): def index(self, req): """Returns a summary list of snapshots 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_snapshots, self._list_manageable_view) @@ -139,7 +136,7 @@ class SnapshotManageController(wsgi.Controller): def detail(self, req): """Returns a detailed list of snapshots 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_snapshots, self._list_manageable_view) diff --git a/cinder/api/contrib/snapshot_unmanage.py b/cinder/api/contrib/snapshot_unmanage.py index a594a458cd7..25b7bebb3a5 100644 --- a/cinder/api/contrib/snapshot_unmanage.py +++ b/cinder/api/contrib/snapshot_unmanage.py @@ -20,10 +20,10 @@ from webob import exc from cinder.api import extensions from cinder.api.openstack import wsgi from cinder import exception +from cinder.policies import manageable_snapshots as policy from cinder import volume LOG = logging.getLogger(__name__) -authorize = extensions.extension_authorizer('snapshot', 'snapshot_unmanage') class SnapshotUnmanageController(wsgi.Controller): @@ -46,7 +46,7 @@ class SnapshotUnmanageController(wsgi.Controller): A Not Found error is returned if the specified snapshot does not exist. """ context = req.environ['cinder.context'] - authorize(context) + context.authorize(policy.UNMANAGE_POLICY) LOG.info("Unmanage snapshot with id: %s", id) diff --git a/cinder/policies/__init__.py b/cinder/policies/__init__.py index ce99ec7a3ba..ee2ca80b3b5 100644 --- a/cinder/policies/__init__.py +++ b/cinder/policies/__init__.py @@ -18,7 +18,11 @@ import itertools from cinder.policies import attachments from cinder.policies import base from cinder.policies import clusters +from cinder.policies import manageable_snapshots from cinder.policies import messages +from cinder.policies import snapshot_actions +from cinder.policies import snapshot_metadata +from cinder.policies import snapshots from cinder.policies import workers @@ -28,5 +32,9 @@ def list_rules(): attachments.list_rules(), messages.list_rules(), clusters.list_rules(), - workers.list_rules() + workers.list_rules(), + snapshot_metadata.list_rules(), + snapshots.list_rules(), + snapshot_actions.list_rules(), + manageable_snapshots.list_rules(), ) diff --git a/cinder/policies/manageable_snapshots.py b/cinder/policies/manageable_snapshots.py new file mode 100644 index 00000000000..485da44f72b --- /dev/null +++ b/cinder/policies/manageable_snapshots.py @@ -0,0 +1,65 @@ +# 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 = 'snapshot_extension:snapshot_manage' +UNMANAGE_POLICY = 'snapshot_extension:snapshot_unmanage' +LIST_MANAGEABLE_POLICY = 'snapshot_extension:list_manageable' + +manageable_snapshots_policies = [ + policy.DocumentedRuleDefault( + name=LIST_MANAGEABLE_POLICY, + check_str=base.RULE_ADMIN_API, + description= + "List (in detail) of snapshots which are available to manage.", + operations=[ + { + 'method': 'GET', + 'path': '/manageable_snapshots' + }, + { + 'method': 'GET', + 'path': '/manageable_snapshots/detail' + } + ]), + policy.DocumentedRuleDefault( + name=MANAGE_POLICY, + check_str=base.RULE_ADMIN_API, + description="Manage an existing snapshot.", + operations=[ + { + 'method': 'POST', + 'path': '/manageable_snapshots' + } + ]), + policy.DocumentedRuleDefault( + name=UNMANAGE_POLICY, + check_str=base.RULE_ADMIN_API, + description="Stop managing a snapshot.", + operations=[ + { + 'method': 'POST', + 'path': '/snapshots/{snapshot_id}/action (os-unmanage)' + } + ]), +] + + +def list_rules(): + return manageable_snapshots_policies diff --git a/cinder/policies/snapshot_actions.py b/cinder/policies/snapshot_actions.py new file mode 100644 index 00000000000..548f2ebfc94 --- /dev/null +++ b/cinder/policies/snapshot_actions.py @@ -0,0 +1,62 @@ +# 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 + + +RESET_STATUS_POLICY = 'volume_extension:snapshot_admin_actions:reset_status' +FORCE_DELETE_POLICY = 'volume_extension:snapshot_admin_actions:force_delete' +UPDATE_STATUS_POLICY = \ + 'snapshot_extension:snapshot_actions:update_snapshot_status' + +snapshot_actions_policies = [ + policy.DocumentedRuleDefault( + name=RESET_STATUS_POLICY, + check_str=base.RULE_ADMIN_API, + description="Reset status of a snapshot.", + operations=[ + { + 'method': 'POST', + 'path': '/snapshots/{snapshot_id}/action (os-reset_status)' + } + ]), + policy.DocumentedRuleDefault( + name=UPDATE_STATUS_POLICY, + check_str="", + description="Update database fields of snapshot.", + operations=[ + { + 'method': 'POST', + 'path': '/snapshots/{snapshot_id}/action ' + '(update_snapshot_status)' + } + ]), + policy.DocumentedRuleDefault( + name=FORCE_DELETE_POLICY, + check_str=base.RULE_ADMIN_API, + description="Force delete a snapshot.", + operations=[ + { + 'method': 'POST', + 'path': '/snapshots/{snapshot_id}/action (os-force_delete)' + } + ]) +] + + +def list_rules(): + return snapshot_actions_policies diff --git a/cinder/policies/snapshot_metadata.py b/cinder/policies/snapshot_metadata.py new file mode 100644 index 00000000000..28e270469a8 --- /dev/null +++ b/cinder/policies/snapshot_metadata.py @@ -0,0 +1,72 @@ +# 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_snapshot_metadata' +DELETE_POLICY = 'volume:delete_snapshot_metadata' +UPDATE_POLICY = 'volume:update_snapshot_metadata' + + +snapshot_metadata_policies = [ + policy.DocumentedRuleDefault( + name=GET_POLICY, + check_str=base.RULE_ADMIN_OR_OWNER, + description="Show snapshot's metadata or one specified metadata " + "with a given key.", + operations=[ + { + 'method': 'GET', + 'path': '/snapshots/{snapshot_id}/metadata' + }, + { + 'method': 'GET', + 'path': '/snapshots/{snapshot_id}/metadata/{key}' + } + ]), + policy.DocumentedRuleDefault( + name=UPDATE_POLICY, + check_str=base.RULE_ADMIN_OR_OWNER, + description="Update snapshot's metadata or one specified " + "metadata with a given key.", + operations=[ + { + 'method': 'PUT', + 'path': '/snapshots/{snapshot_id}/metadata' + }, + { + 'method': 'PUT', + 'path': '/snapshots/{snapshot_id}/metadata/{key}' + } + ]), + policy.DocumentedRuleDefault( + name=DELETE_POLICY, + check_str=base.RULE_ADMIN_OR_OWNER, + description="Delete snapshot's specified metadata " + "with a given key.", + operations=[ + { + 'method': 'DELETE', + 'path': '/snapshots/{snapshot_id}/metadata/{key}' + } + ]), +] + + +def list_rules(): + return snapshot_metadata_policies diff --git a/cinder/policies/snapshots.py b/cinder/policies/snapshots.py new file mode 100644 index 00000000000..064c2d1b58c --- /dev/null +++ b/cinder/policies/snapshots.py @@ -0,0 +1,103 @@ +# 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 + + +BASE_POLICY_NAME = 'volume:snapshots:%s' +GET_POLICY = 'volume:get_snapshot' +GET_ALL_POLICY = 'volume:get_all_snapshots' +CREATE_POLICY = 'volume:create_snapshot' +DELETE_POLICY = 'volume:delete_snapshot' +UPDATE_POLICY = 'volume:update_snapshot' +EXTEND_ATTRIBUTE = 'volume_extension:extended_snapshot_attributes' + + +snapshots_policies = [ + policy.DocumentedRuleDefault( + name=GET_ALL_POLICY, + check_str=base.RULE_ADMIN_OR_OWNER, + description="List snapshots.", + operations=[ + { + 'method': 'GET', + 'path': '/snapshots' + }, + { + 'method': 'GET', + 'path': '/snapshots/detail' + } + ]), + policy.DocumentedRuleDefault( + name=EXTEND_ATTRIBUTE, + check_str=base.RULE_ADMIN_OR_OWNER, + description="List snapshots with extended attributes.", + operations=[ + { + 'method': 'GET', + 'path': '/snapshots' + }, + { + 'method': 'GET', + 'path': '/snapshots/detail' + } + ]), + policy.DocumentedRuleDefault( + name=CREATE_POLICY, + check_str=base.RULE_ADMIN_OR_OWNER, + description="Create snapshot.", + operations=[ + { + 'method': 'POST', + 'path': '/snapshots' + } + ]), + policy.DocumentedRuleDefault( + name=GET_POLICY, + check_str=base.RULE_ADMIN_OR_OWNER, + description="Show snapshot.", + operations=[ + { + 'method': 'GET', + 'path': '/snapshots/{snapshot_id}' + } + ]), + policy.DocumentedRuleDefault( + name=UPDATE_POLICY, + check_str=base.RULE_ADMIN_OR_OWNER, + description="Update snapshot.", + operations=[ + { + 'method': 'PUT', + 'path': '/snapshots/{snapshot_id}' + } + ]), + policy.DocumentedRuleDefault( + name=DELETE_POLICY, + check_str=base.RULE_ADMIN_OR_OWNER, + description="Delete snapshot.", + operations=[ + { + 'method': 'DELETE', + 'path': '/snapshots/{snapshot_id}' + } + ]), +] + + +def list_rules(): + return snapshots_policies diff --git a/cinder/tests/unit/policy.json b/cinder/tests/unit/policy.json index 8645e21d961..8432eb9f5b4 100644 --- a/cinder/tests/unit/policy.json +++ b/cinder/tests/unit/policy.json @@ -44,11 +44,9 @@ "volume:thaw_host": "rule:admin_api", "volume:revert_to_snapshot": "", "volume_extension:volume_admin_actions:reset_status": "rule:admin_api", - "volume_extension:snapshot_admin_actions:reset_status": "rule:admin_api", "volume_extension:backup_admin_actions:reset_status": "rule:admin_api", "volume_extension:backup_admin_actions:force_delete": "rule:admin_api", "volume_extension:volume_admin_actions:force_delete": "rule:admin_api", - "volume_extension:snapshot_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", @@ -91,11 +89,6 @@ "limits_extension:used_limits": "", - "snapshot_extension:snapshot_actions:update_snapshot_status": "", - "snapshot_extension:snapshot_manage": "rule:admin_api", - "snapshot_extension:snapshot_unmanage": "rule:admin_api", - "snapshot_extension:list_manageable": "rule:admin_api", - "volume:create_transfer": "", "volume:accept_transfer": "", "volume:delete_transfer": "", diff --git a/cinder/volume/api.py b/cinder/volume/api.py index 3e0d00722eb..6e63661cd4a 100644 --- a/cinder/volume/api.py +++ b/cinder/volume/api.py @@ -44,6 +44,8 @@ from cinder import objects from cinder.objects import base as objects_base from cinder.objects import fields from cinder.policies import attachments as attachment_policy +from cinder.policies import snapshot_metadata as s_meta_policy +from cinder.policies import snapshots as snapshot_policy import cinder.policy from cinder import quota from cinder import quota_utils @@ -624,7 +626,7 @@ class API(base.Base): return volumes def get_snapshot(self, context, snapshot_id): - check_policy(context, 'get_snapshot') + context.authorize(snapshot_policy.GET_POLICY) snapshot = objects.Snapshot.get_by_id(context, snapshot_id) # FIXME(jdg): The objects don't have the db name entries @@ -643,7 +645,7 @@ class API(base.Base): def get_all_snapshots(self, context, search_opts=None, marker=None, limit=None, sort_keys=None, sort_dirs=None, offset=None): - check_policy(context, 'get_all_snapshots') + context.authorize(snapshot_policy.GET_ALL_POLICY) search_opts = search_opts or {} @@ -827,7 +829,7 @@ class API(base.Base): cgsnapshot_id, commit_quota=True, group_snapshot_id=None): - check_policy(context, 'create_snapshot', volume) + context.authorize(snapshot_policy.CREATE_POLICY) if not volume.host: msg = _("The snapshot cannot be created because volume has " @@ -1031,9 +1033,10 @@ class API(base.Base): resource=result) return result - @wrap_check_policy def delete_snapshot(self, context, snapshot, force=False, unmanage_only=False): + context.authorize(snapshot_policy.DELETE_POLICY, + target_obj=snapshot) if not unmanage_only: snapshot.assert_not_frozen() @@ -1061,8 +1064,9 @@ class API(base.Base): LOG.info("Snapshot delete request issued successfully.", resource=snapshot) - @wrap_check_policy def update_snapshot(self, context, snapshot, fields): + context.authorize(snapshot_policy.UPDATE_POLICY, + target_obj=snapshot) snapshot.update(fields) snapshot.save() @@ -1153,21 +1157,22 @@ class API(base.Base): resource=volume) return db_meta - @wrap_check_policy def get_snapshot_metadata(self, context, snapshot): """Get all metadata associated with a snapshot.""" + context.authorize(s_meta_policy.GET_POLICY, + target_obj=snapshot) LOG.info("Get snapshot metadata completed successfully.", resource=snapshot) return snapshot.metadata - @wrap_check_policy def delete_snapshot_metadata(self, context, snapshot, key): """Delete the given metadata item from a snapshot.""" + context.authorize(s_meta_policy.DELETE_POLICY, + target_obj=snapshot) snapshot.delete_metadata_key(context, key) LOG.info("Delete snapshot metadata completed successfully.", resource=snapshot) - @wrap_check_policy def update_snapshot_metadata(self, context, snapshot, metadata, delete=False): @@ -1177,6 +1182,8 @@ class API(base.Base): `metadata` argument will be deleted. """ + context.authorize(s_meta_policy.UPDATE_POLICY, + target_obj=snapshot) if delete: _metadata = metadata else: diff --git a/etc/cinder/policy.json b/etc/cinder/policy.json index a6c8b0c4357..dda3bd0e2b3 100644 --- a/etc/cinder/policy.json +++ b/etc/cinder/policy.json @@ -11,14 +11,6 @@ "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:get_snapshot": "rule:admin_or_owner", - "volume:get_all_snapshots": "rule:admin_or_owner", - "volume:create_snapshot": "rule:admin_or_owner", - "volume:delete_snapshot": "rule:admin_or_owner", - "volume:update_snapshot": "rule:admin_or_owner", - "volume:get_snapshot_metadata": "rule:admin_or_owner", - "volume:delete_snapshot_metadata": "rule:admin_or_owner", - "volume:update_snapshot_metadata": "rule:admin_or_owner", "volume:extend": "rule:admin_or_owner", "volume:extend_attached_volume": "rule:admin_or_owner", "volume:update_readonly_flag": "rule:admin_or_owner", @@ -39,7 +31,6 @@ "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": "rule:admin_or_owner", "volume_extension:volume_image_metadata": "rule:admin_or_owner", "volume_extension:qos_specs_manage:create": "rule:admin_api", @@ -55,11 +46,9 @@ "volume_extension:quota_classes:validate_setup_for_nested_quota_use": "rule:admin_api", "volume_extension:volume_admin_actions:reset_status": "rule:admin_api", - "volume_extension:snapshot_admin_actions:reset_status": "rule:admin_api", "volume_extension:backup_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:snapshot_admin_actions:force_delete": "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", @@ -100,11 +89,6 @@ "backup:update": "rule:admin_or_owner", "backup:backup_project_attribute": "rule:admin_api", - "snapshot_extension:snapshot_actions:update_snapshot_status": "", - "snapshot_extension:snapshot_manage": "rule:admin_api", - "snapshot_extension:snapshot_unmanage": "rule:admin_api", - "snapshot_extension:list_manageable": "rule:admin_api", - "consistencygroup:create" : "group:nobody", "consistencygroup:delete": "group:nobody", "consistencygroup:update": "group:nobody",