diff --git a/cinder/api/contrib/admin_actions.py b/cinder/api/contrib/admin_actions.py index aae6dae5d1e..0d9cd73740a 100644 --- a/cinder/api/contrib/admin_actions.py +++ b/cinder/api/contrib/admin_actions.py @@ -79,10 +79,10 @@ class AdminController(wsgi.Controller): return update def authorize(self, context, action_name): - # NOTE(tommylikehu): We have two different ways to authorize during + # 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 ['snapshot']: + if self.resource_name in ['backup', 'snapshot']: context.authorize( 'volume_extension:%(resource)s_admin_actions:%(action)s' % {'resource': self.resource_name, diff --git a/cinder/api/v3/backups.py b/cinder/api/v3/backups.py index 43c6c3c2c6f..f720b9d8103 100644 --- a/cinder/api/v3/backups.py +++ b/cinder/api/v3/backups.py @@ -22,9 +22,9 @@ from cinder.api.contrib import backups as backups_v2 from cinder.api import microversions as mv from cinder.api.openstack import wsgi from cinder.api.v3.views import backups as backup_views -from cinder.backup import api as backup_api from cinder import exception from cinder.i18n import _ +from cinder.policies import backups as policy LOG = logging.getLogger(__name__) @@ -81,7 +81,7 @@ class BackupsController(backups_v2.BackupsController): resp_backup = self._view_builder.detail(req, backup) if req_version.matches(mv.BACKUP_PROJECT): try: - backup_api.check_policy(context, 'backup_project_attribute') + context.authorize(policy.BACKUP_ATTRIBUTES_POLICY) self._add_backup_project_attribute(req, resp_backup['backup']) except exception.PolicyNotAuthorized: pass @@ -94,7 +94,7 @@ class BackupsController(backups_v2.BackupsController): if req_version.matches(mv.BACKUP_PROJECT): try: - backup_api.check_policy(context, 'backup_project_attribute') + context.authorize(policy.BACKUP_ATTRIBUTES_POLICY) for bak in resp_backup['backups']: self._add_backup_project_attribute(req, bak) except exception.PolicyNotAuthorized: diff --git a/cinder/backup/api.py b/cinder/backup/api.py index 0166e90a05a..748b608f67e 100644 --- a/cinder/backup/api.py +++ b/cinder/backup/api.py @@ -36,6 +36,7 @@ from cinder import exception from cinder.i18n import _ from cinder import objects from cinder.objects import fields +from cinder.policies import backups as policy import cinder.policy from cinder import quota from cinder import quota_utils @@ -56,15 +57,6 @@ QUOTAS = quota.QUOTAS IMPORT_VOLUME_ID = '00000000-0000-0000-0000-000000000000' -def check_policy(context, action): - target = { - 'project_id': context.project_id, - 'user_id': context.user_id, - } - _action = 'backup:%s' % action - cinder.policy.enforce(context, _action, target) - - class API(base.Base): """API for interacting with the volume backup manager.""" @@ -74,7 +66,7 @@ class API(base.Base): super(API, self).__init__(db) def get(self, context, backup_id): - check_policy(context, 'get') + context.authorize(policy.GET_POLICY) return objects.Backup.get_by_id(context, backup_id) def _check_support_to_force_delete(self, context, backup_host): @@ -93,7 +85,7 @@ class API(base.Base): :raises BackupDriverException: :raises ServiceNotFound: """ - check_policy(context, 'delete') + context.authorize(policy.DELETE_POLICY) if not force and backup.status not in [fields.BackupStatus.AVAILABLE, fields.BackupStatus.ERROR]: msg = _('Backup status must be available or error') @@ -118,7 +110,7 @@ class API(base.Base): def get_all(self, context, search_opts=None, marker=None, limit=None, offset=None, sort_keys=None, sort_dirs=None): - check_policy(context, 'get_all') + context.authorize(policy.GET_ALL_POLICY) search_opts = search_opts or {} @@ -204,7 +196,7 @@ class API(base.Base): container, incremental=False, availability_zone=None, force=False, snapshot_id=None, metadata=None): """Make the RPC call to create a volume backup.""" - check_policy(context, 'create') + context.authorize(policy.CREATE_POLICY) utils.check_metadata_properties(metadata) volume = self.volume_api.get(context, volume_id) snapshot = None @@ -341,7 +333,7 @@ class API(base.Base): def restore(self, context, backup_id, volume_id=None, name=None): """Make the RPC call to restore a volume backup.""" - check_policy(context, 'restore') + context.authorize(policy.RESTORE_POLICY) backup = self.get(context, backup_id) if backup['status'] != fields.BackupStatus.AVAILABLE: msg = _('Backup status must be available') @@ -444,7 +436,7 @@ class API(base.Base): :returns: contains 'backup_url' and 'backup_service' :raises InvalidBackup: """ - check_policy(context, 'backup-export') + context.authorize(policy.EXPORT_POLICY) backup = self.get(context, backup_id) if backup['status'] != fields.BackupStatus.AVAILABLE: msg = (_('Backup status must be available and not %s.') % @@ -537,7 +529,7 @@ class API(base.Base): :raises ServiceNotFound: :raises InvalidInput: """ - check_policy(context, 'backup-import') + context.authorize(policy.IMPORT_POLICY) # NOTE(ronenkat): since we don't have a backup-scheduler # we need to find a host that support the backup service @@ -563,7 +555,7 @@ class API(base.Base): return backup def update(self, context, backup_id, fields): - check_policy(context, 'update') + context.authorize(policy.UPDATE_POLICY) backup = self.get(context, backup_id) backup.update(fields) backup.save() diff --git a/cinder/policies/__init__.py b/cinder/policies/__init__.py index ee2ca80b3b5..250534cd52a 100644 --- a/cinder/policies/__init__.py +++ b/cinder/policies/__init__.py @@ -16,6 +16,8 @@ import itertools from cinder.policies import attachments +from cinder.policies import backup_actions +from cinder.policies import backups from cinder.policies import base from cinder.policies import clusters from cinder.policies import manageable_snapshots @@ -37,4 +39,6 @@ def list_rules(): snapshots.list_rules(), snapshot_actions.list_rules(), manageable_snapshots.list_rules(), + backups.list_rules(), + backup_actions.list_rules(), ) diff --git a/cinder/policies/backup_actions.py b/cinder/policies/backup_actions.py new file mode 100644 index 00000000000..ff1d66d516a --- /dev/null +++ b/cinder/policies/backup_actions.py @@ -0,0 +1,49 @@ +# 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_extension:backup_admin_actions:%s' + + +backup_actions_policies = [ + policy.DocumentedRuleDefault( + name=BASE_POLICY_NAME % 'reset_status', + check_str=base.RULE_ADMIN_API, + description="Reset status of a backup.", + operations=[ + { + 'method': 'POST', + 'path': '/backups/{backup_id}/action (os-reset_status)' + } + ]), + policy.DocumentedRuleDefault( + name=BASE_POLICY_NAME % 'force_delete', + check_str=base.RULE_ADMIN_API, + description="Force delete a backup.", + operations=[ + { + 'method': 'POST', + 'path': '/backups/{backup_id}/action (os-force_delete)' + } + ]), +] + + +def list_rules(): + return backup_actions_policies diff --git a/cinder/policies/backups.py b/cinder/policies/backups.py new file mode 100644 index 00000000000..aad59cf9f1a --- /dev/null +++ b/cinder/policies/backups.py @@ -0,0 +1,134 @@ +# 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_ALL_POLICY = 'backup:get_all' +GET_POLICY = 'backup:get' +CREATE_POLICY = 'backup:create' +UPDATE_POLICY = 'backup:update' +DELETE_POLICY = 'backup:delete' +RESTORE_POLICY = 'backup:restore' +IMPORT_POLICY = 'backup:backup-import' +EXPORT_POLICY = 'backup:export-import' +BACKUP_ATTRIBUTES_POLICY = 'backup:backup_project_attribute' + +backups_policies = [ + policy.DocumentedRuleDefault( + name=GET_ALL_POLICY, + check_str=base.RULE_ADMIN_OR_OWNER, + description="List backups.", + operations=[ + { + 'method': 'GET', + 'path': '/backups' + }, + { + 'method': 'GET', + 'path': '/backups/detail' + } + ]), + policy.DocumentedRuleDefault( + name=BACKUP_ATTRIBUTES_POLICY, + check_str=base.RULE_ADMIN_API, + description="List backups or show backup with project attributes.", + operations=[ + { + 'method': 'GET', + 'path': '/backups/{backup_id}' + }, + { + 'method': 'GET', + 'path': '/backups/detail' + } + ]), + policy.DocumentedRuleDefault( + name=CREATE_POLICY, + check_str="", + description="Create backup.", + operations=[ + { + 'method': 'POST', + 'path': '/backups' + } + ]), + policy.DocumentedRuleDefault( + name=GET_POLICY, + check_str=base.RULE_ADMIN_OR_OWNER, + description="Show backup.", + operations=[ + { + 'method': 'GET', + 'path': '/backups/{backup_id}' + } + ]), + policy.DocumentedRuleDefault( + name=UPDATE_POLICY, + check_str=base.RULE_ADMIN_OR_OWNER, + description="Update backup.", + operations=[ + { + 'method': 'PUT', + 'path': '/backups/{backup_id}' + } + ]), + policy.DocumentedRuleDefault( + name=DELETE_POLICY, + check_str=base.RULE_ADMIN_OR_OWNER, + description="Delete backup.", + operations=[ + { + 'method': 'DELETE', + 'path': '/backups/{backup_id}' + } + ]), + policy.DocumentedRuleDefault( + name=RESTORE_POLICY, + check_str=base.RULE_ADMIN_OR_OWNER, + description="Restore backup.", + operations=[ + { + 'method': 'POST', + 'path': '/backups/{backup_id}/restore' + } + ]), + policy.DocumentedRuleDefault( + name=IMPORT_POLICY, + check_str=base.RULE_ADMIN_API, + description="Import backup.", + operations=[ + { + 'method': 'POST', + 'path': '/backups/{backup_id}/import_record' + } + ]), + policy.DocumentedRuleDefault( + name=EXPORT_POLICY, + check_str=base.RULE_ADMIN_API, + description="Export backup.", + operations=[ + { + 'method': 'POST', + 'path': '/backups/{backup_id}/export_record' + } + ]), +] + + +def list_rules(): + return backups_policies diff --git a/cinder/tests/unit/policy.json b/cinder/tests/unit/policy.json index 8432eb9f5b4..a578d37bb06 100644 --- a/cinder/tests/unit/policy.json +++ b/cinder/tests/unit/policy.json @@ -44,8 +44,6 @@ "volume:thaw_host": "rule:admin_api", "volume:revert_to_snapshot": "", "volume_extension:volume_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:volume_admin_actions:force_detach": "rule:admin_api", "volume_extension:volume_admin_actions:migrate_volume": "rule:admin_api", @@ -95,16 +93,10 @@ "volume:get_transfer": "", "volume:get_all_transfers": "", - "backup:create" : "", "backup:delete": "", "backup:get": "", "backup:get_all": "", "backup:restore": "", - "backup:backup-import": "rule:admin_api", - "backup:backup-export": "rule:admin_api", - "backup:update": "rule:admin_or_owner", - "backup:backup_project_attribute": "rule:admin_api", - "consistencygroup:create" : "", "consistencygroup:delete": "", diff --git a/etc/cinder/policy.json b/etc/cinder/policy.json index dda3bd0e2b3..0b1c1c2a38f 100644 --- a/etc/cinder/policy.json +++ b/etc/cinder/policy.json @@ -46,7 +46,6 @@ "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: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:backup_admin_actions:force_delete": "rule:admin_api", @@ -79,16 +78,6 @@ "volume:freeze_host": "rule:admin_api", "volume:thaw_host": "rule:admin_api", - "backup:create" : "", - "backup:delete": "rule:admin_or_owner", - "backup:get": "rule:admin_or_owner", - "backup:get_all": "rule:admin_or_owner", - "backup:restore": "rule:admin_or_owner", - "backup:backup-import": "rule:admin_api", - "backup:backup-export": "rule:admin_api", - "backup:update": "rule:admin_or_owner", - "backup:backup_project_attribute": "rule:admin_api", - "consistencygroup:create" : "group:nobody", "consistencygroup:delete": "group:nobody", "consistencygroup:update": "group:nobody",