[policy in code] Add support for backup resource
This patch adds policy in code support for backup resources and depends on the basic patch [1]. [1]: https://review.openstack.org/#/c/506976/ Change-Id: I9a79b5ececc587e80129cc980930e168e805b139 Partial-Implements: blueprint policy-in-code
This commit is contained in:
parent
274ce06f58
commit
1462d9c2e4
@ -79,10 +79,10 @@ class AdminController(wsgi.Controller):
|
|||||||
return update
|
return update
|
||||||
|
|
||||||
def authorize(self, context, action_name):
|
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
|
# implementing code base policies, the if/else statement can be
|
||||||
# removed when all resources are upgraded.
|
# removed when all resources are upgraded.
|
||||||
if self.resource_name in ['snapshot']:
|
if self.resource_name in ['backup', 'snapshot']:
|
||||||
context.authorize(
|
context.authorize(
|
||||||
'volume_extension:%(resource)s_admin_actions:%(action)s' %
|
'volume_extension:%(resource)s_admin_actions:%(action)s' %
|
||||||
{'resource': self.resource_name,
|
{'resource': self.resource_name,
|
||||||
|
@ -22,9 +22,9 @@ from cinder.api.contrib import backups as backups_v2
|
|||||||
from cinder.api import microversions as mv
|
from cinder.api import microversions as mv
|
||||||
from cinder.api.openstack import wsgi
|
from cinder.api.openstack import wsgi
|
||||||
from cinder.api.v3.views import backups as backup_views
|
from cinder.api.v3.views import backups as backup_views
|
||||||
from cinder.backup import api as backup_api
|
|
||||||
from cinder import exception
|
from cinder import exception
|
||||||
from cinder.i18n import _
|
from cinder.i18n import _
|
||||||
|
from cinder.policies import backups as policy
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -81,7 +81,7 @@ class BackupsController(backups_v2.BackupsController):
|
|||||||
resp_backup = self._view_builder.detail(req, backup)
|
resp_backup = self._view_builder.detail(req, backup)
|
||||||
if req_version.matches(mv.BACKUP_PROJECT):
|
if req_version.matches(mv.BACKUP_PROJECT):
|
||||||
try:
|
try:
|
||||||
backup_api.check_policy(context, 'backup_project_attribute')
|
context.authorize(policy.BACKUP_ATTRIBUTES_POLICY)
|
||||||
self._add_backup_project_attribute(req, resp_backup['backup'])
|
self._add_backup_project_attribute(req, resp_backup['backup'])
|
||||||
except exception.PolicyNotAuthorized:
|
except exception.PolicyNotAuthorized:
|
||||||
pass
|
pass
|
||||||
@ -94,7 +94,7 @@ class BackupsController(backups_v2.BackupsController):
|
|||||||
|
|
||||||
if req_version.matches(mv.BACKUP_PROJECT):
|
if req_version.matches(mv.BACKUP_PROJECT):
|
||||||
try:
|
try:
|
||||||
backup_api.check_policy(context, 'backup_project_attribute')
|
context.authorize(policy.BACKUP_ATTRIBUTES_POLICY)
|
||||||
for bak in resp_backup['backups']:
|
for bak in resp_backup['backups']:
|
||||||
self._add_backup_project_attribute(req, bak)
|
self._add_backup_project_attribute(req, bak)
|
||||||
except exception.PolicyNotAuthorized:
|
except exception.PolicyNotAuthorized:
|
||||||
|
@ -36,6 +36,7 @@ from cinder import exception
|
|||||||
from cinder.i18n import _
|
from cinder.i18n import _
|
||||||
from cinder import objects
|
from cinder import objects
|
||||||
from cinder.objects import fields
|
from cinder.objects import fields
|
||||||
|
from cinder.policies import backups as policy
|
||||||
import cinder.policy
|
import cinder.policy
|
||||||
from cinder import quota
|
from cinder import quota
|
||||||
from cinder import quota_utils
|
from cinder import quota_utils
|
||||||
@ -56,15 +57,6 @@ QUOTAS = quota.QUOTAS
|
|||||||
IMPORT_VOLUME_ID = '00000000-0000-0000-0000-000000000000'
|
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):
|
class API(base.Base):
|
||||||
"""API for interacting with the volume backup manager."""
|
"""API for interacting with the volume backup manager."""
|
||||||
|
|
||||||
@ -74,7 +66,7 @@ class API(base.Base):
|
|||||||
super(API, self).__init__(db)
|
super(API, self).__init__(db)
|
||||||
|
|
||||||
def get(self, context, backup_id):
|
def get(self, context, backup_id):
|
||||||
check_policy(context, 'get')
|
context.authorize(policy.GET_POLICY)
|
||||||
return objects.Backup.get_by_id(context, backup_id)
|
return objects.Backup.get_by_id(context, backup_id)
|
||||||
|
|
||||||
def _check_support_to_force_delete(self, context, backup_host):
|
def _check_support_to_force_delete(self, context, backup_host):
|
||||||
@ -93,7 +85,7 @@ class API(base.Base):
|
|||||||
:raises BackupDriverException:
|
:raises BackupDriverException:
|
||||||
:raises ServiceNotFound:
|
:raises ServiceNotFound:
|
||||||
"""
|
"""
|
||||||
check_policy(context, 'delete')
|
context.authorize(policy.DELETE_POLICY)
|
||||||
if not force and backup.status not in [fields.BackupStatus.AVAILABLE,
|
if not force and backup.status not in [fields.BackupStatus.AVAILABLE,
|
||||||
fields.BackupStatus.ERROR]:
|
fields.BackupStatus.ERROR]:
|
||||||
msg = _('Backup status must be available or 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,
|
def get_all(self, context, search_opts=None, marker=None, limit=None,
|
||||||
offset=None, sort_keys=None, sort_dirs=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 {}
|
search_opts = search_opts or {}
|
||||||
|
|
||||||
@ -204,7 +196,7 @@ class API(base.Base):
|
|||||||
container, incremental=False, availability_zone=None,
|
container, incremental=False, availability_zone=None,
|
||||||
force=False, snapshot_id=None, metadata=None):
|
force=False, snapshot_id=None, metadata=None):
|
||||||
"""Make the RPC call to create a volume backup."""
|
"""Make the RPC call to create a volume backup."""
|
||||||
check_policy(context, 'create')
|
context.authorize(policy.CREATE_POLICY)
|
||||||
utils.check_metadata_properties(metadata)
|
utils.check_metadata_properties(metadata)
|
||||||
volume = self.volume_api.get(context, volume_id)
|
volume = self.volume_api.get(context, volume_id)
|
||||||
snapshot = None
|
snapshot = None
|
||||||
@ -341,7 +333,7 @@ class API(base.Base):
|
|||||||
|
|
||||||
def restore(self, context, backup_id, volume_id=None, name=None):
|
def restore(self, context, backup_id, volume_id=None, name=None):
|
||||||
"""Make the RPC call to restore a volume backup."""
|
"""Make the RPC call to restore a volume backup."""
|
||||||
check_policy(context, 'restore')
|
context.authorize(policy.RESTORE_POLICY)
|
||||||
backup = self.get(context, backup_id)
|
backup = self.get(context, backup_id)
|
||||||
if backup['status'] != fields.BackupStatus.AVAILABLE:
|
if backup['status'] != fields.BackupStatus.AVAILABLE:
|
||||||
msg = _('Backup status must be available')
|
msg = _('Backup status must be available')
|
||||||
@ -444,7 +436,7 @@ class API(base.Base):
|
|||||||
:returns: contains 'backup_url' and 'backup_service'
|
:returns: contains 'backup_url' and 'backup_service'
|
||||||
:raises InvalidBackup:
|
:raises InvalidBackup:
|
||||||
"""
|
"""
|
||||||
check_policy(context, 'backup-export')
|
context.authorize(policy.EXPORT_POLICY)
|
||||||
backup = self.get(context, backup_id)
|
backup = self.get(context, backup_id)
|
||||||
if backup['status'] != fields.BackupStatus.AVAILABLE:
|
if backup['status'] != fields.BackupStatus.AVAILABLE:
|
||||||
msg = (_('Backup status must be available and not %s.') %
|
msg = (_('Backup status must be available and not %s.') %
|
||||||
@ -537,7 +529,7 @@ class API(base.Base):
|
|||||||
:raises ServiceNotFound:
|
:raises ServiceNotFound:
|
||||||
:raises InvalidInput:
|
:raises InvalidInput:
|
||||||
"""
|
"""
|
||||||
check_policy(context, 'backup-import')
|
context.authorize(policy.IMPORT_POLICY)
|
||||||
|
|
||||||
# NOTE(ronenkat): since we don't have a backup-scheduler
|
# NOTE(ronenkat): since we don't have a backup-scheduler
|
||||||
# we need to find a host that support the backup service
|
# we need to find a host that support the backup service
|
||||||
@ -563,7 +555,7 @@ class API(base.Base):
|
|||||||
return backup
|
return backup
|
||||||
|
|
||||||
def update(self, context, backup_id, fields):
|
def update(self, context, backup_id, fields):
|
||||||
check_policy(context, 'update')
|
context.authorize(policy.UPDATE_POLICY)
|
||||||
backup = self.get(context, backup_id)
|
backup = self.get(context, backup_id)
|
||||||
backup.update(fields)
|
backup.update(fields)
|
||||||
backup.save()
|
backup.save()
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
from cinder.policies import attachments
|
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 base
|
||||||
from cinder.policies import clusters
|
from cinder.policies import clusters
|
||||||
from cinder.policies import manageable_snapshots
|
from cinder.policies import manageable_snapshots
|
||||||
@ -37,4 +39,6 @@ def list_rules():
|
|||||||
snapshots.list_rules(),
|
snapshots.list_rules(),
|
||||||
snapshot_actions.list_rules(),
|
snapshot_actions.list_rules(),
|
||||||
manageable_snapshots.list_rules(),
|
manageable_snapshots.list_rules(),
|
||||||
|
backups.list_rules(),
|
||||||
|
backup_actions.list_rules(),
|
||||||
)
|
)
|
||||||
|
49
cinder/policies/backup_actions.py
Normal file
49
cinder/policies/backup_actions.py
Normal file
@ -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
|
134
cinder/policies/backups.py
Normal file
134
cinder/policies/backups.py
Normal file
@ -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
|
@ -44,8 +44,6 @@
|
|||||||
"volume:thaw_host": "rule:admin_api",
|
"volume:thaw_host": "rule:admin_api",
|
||||||
"volume:revert_to_snapshot": "",
|
"volume:revert_to_snapshot": "",
|
||||||
"volume_extension:volume_admin_actions:reset_status": "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:backup_admin_actions:force_delete": "rule:admin_api",
|
|
||||||
"volume_extension:volume_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:force_detach": "rule:admin_api",
|
||||||
"volume_extension:volume_admin_actions:migrate_volume": "rule:admin_api",
|
"volume_extension:volume_admin_actions:migrate_volume": "rule:admin_api",
|
||||||
@ -95,16 +93,10 @@
|
|||||||
"volume:get_transfer": "",
|
"volume:get_transfer": "",
|
||||||
"volume:get_all_transfers": "",
|
"volume:get_all_transfers": "",
|
||||||
|
|
||||||
"backup:create" : "",
|
|
||||||
"backup:delete": "",
|
"backup:delete": "",
|
||||||
"backup:get": "",
|
"backup:get": "",
|
||||||
"backup:get_all": "",
|
"backup:get_all": "",
|
||||||
"backup:restore": "",
|
"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:create" : "",
|
||||||
"consistencygroup:delete": "",
|
"consistencygroup:delete": "",
|
||||||
|
@ -46,7 +46,6 @@
|
|||||||
"volume_extension:quota_classes:validate_setup_for_nested_quota_use": "rule:admin_api",
|
"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: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_delete": "rule:admin_api",
|
||||||
"volume_extension:volume_admin_actions:force_detach": "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:backup_admin_actions:force_delete": "rule:admin_api",
|
||||||
@ -79,16 +78,6 @@
|
|||||||
"volume:freeze_host": "rule:admin_api",
|
"volume:freeze_host": "rule:admin_api",
|
||||||
"volume:thaw_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:create" : "group:nobody",
|
||||||
"consistencygroup:delete": "group:nobody",
|
"consistencygroup:delete": "group:nobody",
|
||||||
"consistencygroup:update": "group:nobody",
|
"consistencygroup:update": "group:nobody",
|
||||||
|
Loading…
Reference in New Issue
Block a user