diff --git a/cinder/api/contrib/capabilities.py b/cinder/api/contrib/capabilities.py index edb5edc31c4..f40723b5c01 100644 --- a/cinder/api/contrib/capabilities.py +++ b/cinder/api/contrib/capabilities.py @@ -21,13 +21,10 @@ from cinder.api.views import capabilities as capabilities_view from cinder import exception from cinder.i18n import _ from cinder import objects +from cinder.policies import capabilities as policy from cinder.volume import rpcapi -def authorize(context, action_name): - extensions.extension_authorizer('volume', action_name)(context) - - class CapabilitiesController(wsgi.Controller): """The Capabilities controller for the OpenStack API.""" @@ -43,7 +40,7 @@ class CapabilitiesController(wsgi.Controller): def show(self, req, id): """Return capabilities list of given backend.""" context = req.environ['cinder.context'] - authorize(context, 'capabilities') + context.authorize(policy.CAPABILITIES_POLICY) filters = {'host_or_cluster': id, 'binary': 'cinder-volume'} services = objects.ServiceList.get_all(context, filters) if not services: diff --git a/cinder/api/contrib/hosts.py b/cinder/api/contrib/hosts.py index 4fc08b0752c..eca9e570994 100644 --- a/cinder/api/contrib/hosts.py +++ b/cinder/api/contrib/hosts.py @@ -27,13 +27,13 @@ from cinder import db from cinder import exception from cinder.i18n import _ from cinder import objects +from cinder.policies import hosts as policy from cinder.volume import api as volume_api CONF = cfg.CONF LOG = logging.getLogger(__name__) -authorize = extensions.extension_authorizer('volume', 'hosts') def _list_hosts(req, service=None): @@ -91,12 +91,14 @@ class HostController(wsgi.Controller): super(HostController, self).__init__() def index(self, req): - authorize(req.environ['cinder.context']) + context = req.environ['cinder.context'] + context.authorize(policy.MANAGE_POLICY) return {'hosts': _list_hosts(req)} @check_host def update(self, req, id, body): - authorize(req.environ['cinder.context']) + context = req.environ['cinder.context'] + context.authorize(policy.MANAGE_POLICY) update_values = {} for raw_key, raw_val in body.items(): key = raw_key.lower().strip() diff --git a/cinder/api/contrib/scheduler_stats.py b/cinder/api/contrib/scheduler_stats.py index 347f469eef6..abf054694cf 100644 --- a/cinder/api/contrib/scheduler_stats.py +++ b/cinder/api/contrib/scheduler_stats.py @@ -19,15 +19,11 @@ from cinder.api import extensions from cinder.api import microversions as mv from cinder.api.openstack import wsgi from cinder.api.views import scheduler_stats as scheduler_stats_view +from cinder.policies import scheduler_stats as policy from cinder.scheduler import rpcapi from cinder import utils -def authorize(context, action_name): - action = 'scheduler_stats:%s' % action_name - extensions.extension_authorizer('scheduler', action)(context) - - class SchedulerStatsController(wsgi.Controller): """The Scheduler Stats controller for the OpenStack API.""" @@ -46,7 +42,7 @@ class SchedulerStatsController(wsgi.Controller): def get_pools(self, req): """List all active pools in scheduler.""" context = req.environ['cinder.context'] - authorize(context, 'get_pools') + context.authorize(policy.GET_POOL_POLICY) detail = utils.get_bool_param('detail', req.params) diff --git a/cinder/api/contrib/services.py b/cinder/api/contrib/services.py index 676a4a8f2a4..d6737c7c3f5 100644 --- a/cinder/api/contrib/services.py +++ b/cinder/api/contrib/services.py @@ -29,6 +29,7 @@ from cinder.common import constants from cinder import exception from cinder.i18n import _ from cinder import objects +from cinder.policies import services as policy from cinder.scheduler import rpcapi as scheduler_rpcapi from cinder import utils from cinder import volume @@ -38,7 +39,6 @@ from cinder.volume import rpcapi as volume_rpcapi CONF = cfg.CONF LOG = logging.getLogger(__name__) -authorize = extensions.extension_authorizer('volume', 'services') class ServiceController(wsgi.Controller): @@ -61,7 +61,7 @@ class ServiceController(wsgi.Controller): Filter by host & service name. """ context = req.environ['cinder.context'] - authorize(context, action='index') + context.authorize(policy.GET_ALL_POLICY) detailed = self.ext_mgr.is_loaded('os-extended-services') now = timeutils.utcnow(with_timezone=True) @@ -223,7 +223,7 @@ class ServiceController(wsgi.Controller): directly in this API layer. """ context = req.environ['cinder.context'] - authorize(context, action='update') + context.authorize(policy.UPDATE_POLICY) support_dynamic_log = req.api_version_request.matches(mv.LOG_LEVEL) diff --git a/cinder/api/contrib/used_limits.py b/cinder/api/contrib/used_limits.py index 445f8082f8b..b4502d9ada7 100644 --- a/cinder/api/contrib/used_limits.py +++ b/cinder/api/contrib/used_limits.py @@ -15,19 +15,19 @@ from cinder.api import extensions from cinder.api import microversions as mv from cinder.api.openstack import wsgi +from cinder.policies import limits as policy from cinder import quota QUOTAS = quota.QUOTAS -authorize = extensions.soft_extension_authorizer('limits', 'used_limits') - class UsedLimitsController(wsgi.Controller): @wsgi.extends def index(self, req, resp_obj): context = req.environ['cinder.context'] - if authorize(context): + if context.authorize( + policy.EXTEND_LIMIT_ATTRIBUTE_POLICY, fatal=False): params = req.params.copy() req_version = req.api_version_request diff --git a/cinder/policies/__init__.py b/cinder/policies/__init__.py index d03dc2cc929..c17569779b3 100644 --- a/cinder/policies/__init__.py +++ b/cinder/policies/__init__.py @@ -19,17 +19,22 @@ 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 capabilities from cinder.policies import clusters from cinder.policies import group_actions from cinder.policies import group_snapshot_actions from cinder.policies import group_snapshots from cinder.policies import group_types 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 messages from cinder.policies import qos_specs from cinder.policies import quota_class from cinder.policies import quotas +from cinder.policies import scheduler_stats +from cinder.policies import services from cinder.policies import snapshot_actions from cinder.policies import snapshot_metadata from cinder.policies import snapshots @@ -57,4 +62,9 @@ def list_rules(): qos_specs.list_rules(), quota_class.list_rules(), quotas.list_rules(), + capabilities.list_rules(), + services.list_rules(), + scheduler_stats.list_rules(), + hosts.list_rules(), + limits.list_rules(), ) diff --git a/cinder/policies/capabilities.py b/cinder/policies/capabilities.py new file mode 100644 index 00000000000..6d15493b533 --- /dev/null +++ b/cinder/policies/capabilities.py @@ -0,0 +1,39 @@ +# 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 + + +CAPABILITIES_POLICY = "volume_extension:capabilities", + + +capabilities_policies = [ + policy.DocumentedRuleDefault( + name=CAPABILITIES_POLICY, + check_str=base.RULE_ADMIN_API, + description="Show backend capabilities.", + operations=[ + { + 'method': 'GET', + 'path': '/capabilities/{host_name}' + } + ]) +] + + +def list_rules(): + return capabilities_policies diff --git a/cinder/policies/hosts.py b/cinder/policies/hosts.py new file mode 100644 index 00000000000..bea808b53b7 --- /dev/null +++ b/cinder/policies/hosts.py @@ -0,0 +1,42 @@ +# 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:hosts" + +hosts_policies = [ + policy.DocumentedRuleDefault( + name=MANAGE_POLICY, + check_str=base.RULE_ADMIN_API, + description="List or update hosts for a project.", + operations=[ + { + 'method': 'GET', + 'path': '/os-hosts' + }, + { + 'method': 'PUT', + 'path': '/os-hosts/{host_name}' + } + ]) +] + + +def list_rules(): + return hosts_policies diff --git a/cinder/policies/limits.py b/cinder/policies/limits.py new file mode 100644 index 00000000000..0351a4c3e09 --- /dev/null +++ b/cinder/policies/limits.py @@ -0,0 +1,38 @@ +# 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_LIMIT_ATTRIBUTE_POLICY = "limits_extension:used_limits" + + +limits_policies = [ + policy.DocumentedRuleDefault( + name=EXTEND_LIMIT_ATTRIBUTE_POLICY, + check_str=base.RULE_ADMIN_OR_OWNER, + description="Show limits with used limit attributes.", + operations=[ + { + 'method': 'GET', + 'path': '/limits' + } + ]) +] + + +def list_rules(): + return limits_policies diff --git a/cinder/policies/scheduler_stats.py b/cinder/policies/scheduler_stats.py new file mode 100644 index 00000000000..e91e07ae1cc --- /dev/null +++ b/cinder/policies/scheduler_stats.py @@ -0,0 +1,38 @@ +# 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_POOL_POLICY = "scheduler_extension:scheduler_stats:get_pools" + +pools_policies = [ + policy.DocumentedRuleDefault( + name=GET_POOL_POLICY, + check_str=base.RULE_ADMIN_API, + description="List all backend pools.", + operations=[ + { + 'method': 'GET', + 'path': '/scheduler-stats/get_pools' + } + ]) +] + + +def list_rules(): + return pools_policies diff --git a/cinder/policies/services.py b/cinder/policies/services.py new file mode 100644 index 00000000000..7aee0cdc06e --- /dev/null +++ b/cinder/policies/services.py @@ -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 + + +GET_ALL_POLICY = "volume_extension:services:index" +UPDATE_POLICY = "volume_extension:services:update" +FAILOVER_POLICY = "volume:failover_host" +FREEZE_POLICY = "volume:freeze_host" +THAW_POLICY = "volume:thaw_host" + +services_policies = [ + policy.DocumentedRuleDefault( + name=GET_ALL_POLICY, + check_str=base.RULE_ADMIN_API, + description="List all services.", + operations=[ + { + 'method': 'GET', + 'path': '/os-services' + } + ]), + policy.DocumentedRuleDefault( + name=UPDATE_POLICY, + check_str=base.RULE_ADMIN_API, + description="Update service, including failover_host, thaw, freeze, " + "disable, enable, set-log and get-log actions.", + operations=[ + { + 'method': 'PUT', + 'path': '/os-services/{action}' + } + ]), + policy.DocumentedRuleDefault( + name=FREEZE_POLICY, + check_str=base.RULE_ADMIN_API, + description="Freeze a backend host.", + operations=[ + { + 'method': 'PUT', + 'path': '/os-services/freeze' + } + ]), + policy.DocumentedRuleDefault( + name=THAW_POLICY, + check_str=base.RULE_ADMIN_API, + description="Thaw a backend host.", + operations=[ + { + 'method': 'PUT', + 'path': '/os-services/thaw' + } + ]), + policy.DocumentedRuleDefault( + name=FAILOVER_POLICY, + check_str=base.RULE_ADMIN_API, + description="Failover a backend host.", + operations=[ + { + 'method': 'PUT', + 'path': '/os-services/failover_host' + } + ]), +] + + +def list_rules(): + return services_policies diff --git a/cinder/tests/unit/api/contrib/test_services.py b/cinder/tests/unit/api/contrib/test_services.py index 5bde460cd56..7a92683f7d5 100644 --- a/cinder/tests/unit/api/contrib/test_services.py +++ b/cinder/tests/unit/api/contrib/test_services.py @@ -174,7 +174,7 @@ def fake_service_update(context, service_id, values): 'disabled': values['disabled']} -def fake_policy_enforce(context, action, target): +def fake_policy_authorize(context, action, target): pass @@ -188,7 +188,7 @@ def fake_utcnow(with_timezone=False): @mock.patch('cinder.db.service_get', fake_service_get) @mock.patch('oslo_utils.timeutils.utcnow', fake_utcnow) @mock.patch('cinder.db.sqlalchemy.api.service_update', fake_service_update) -@mock.patch('cinder.policy.enforce', fake_policy_enforce) +@mock.patch('cinder.policy.authorize', fake_policy_authorize) class ServicesTest(test.TestCase): def setUp(self): diff --git a/cinder/tests/unit/api/contrib/test_used_limits.py b/cinder/tests/unit/api/contrib/test_used_limits.py index 7492e0ee5a0..48698096ebb 100644 --- a/cinder/tests/unit/api/contrib/test_used_limits.py +++ b/cinder/tests/unit/api/contrib/test_used_limits.py @@ -47,8 +47,8 @@ class UsedLimitsTestCase(test.TestCase): (mv.LIMITS_ADMIN_FILTER, True), (mv.LIMITS_ADMIN_FILTER, False)) @mock.patch('cinder.quota.QUOTAS.get_project_quotas') - @mock.patch('cinder.policy.enforce') - def test_used_limits(self, ver_project, _mock_policy_enforce, + @mock.patch('cinder.policy.authorize') + def test_used_limits(self, ver_project, _mock_policy_authorize, _mock_get_project_quotas): version, has_project = ver_project fake_req = FakeRequest(fakes.FakeRequestContext(fake.USER_ID, @@ -77,7 +77,7 @@ class UsedLimitsTestCase(test.TestCase): _mock_get_project_quotas.side_effect = get_project_quotas # allow user to access used limits - _mock_policy_enforce.return_value = None + _mock_policy_authorize.return_value = True self.controller.index(fake_req, res) abs_limits = res.obj['limits']['absolute'] @@ -111,7 +111,7 @@ class UsedLimitsTestCase(test.TestCase): res = wsgi.ResponseObject(obj) # unallow user to access used limits - _mock_policy_enforce.side_effect = exception.NotAuthorized + _mock_policy_authorize.side_effect = exception.NotAuthorized self.controller.index(fake_req, res) abs_limits = res.obj['limits']['absolute'] diff --git a/cinder/tests/unit/policy.json b/cinder/tests/unit/policy.json index 74f1bbc3b98..45f33077902 100644 --- a/cinder/tests/unit/policy.json +++ b/cinder/tests/unit/policy.json @@ -39,9 +39,6 @@ "volume:update_readonly_flag": "", "volume:retype": "", "volume:copy_volume_to_image": "", - "volume:failover_host": "rule:admin_api", - "volume:freeze_host": "rule:admin_api", - "volume:thaw_host": "rule:admin_api", "volume:revert_to_snapshot": "", "volume_extension:volume_admin_actions:reset_status": "rule:admin_api", "volume_extension:volume_admin_actions:force_delete": "rule:admin_api", @@ -68,13 +65,11 @@ "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:hosts": "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", - "volume_extension:capabilities": "rule:admin_api", "limits_extension:used_limits": "", @@ -115,8 +110,6 @@ "group:enable_replication": "", "group:disable_replication": "", "group:failover_replication": "", - "group:list_replication_targets": "", - - "scheduler_extension:scheduler_stats:get_pools" : "rule:admin_api", + "group:list_replication_targets": "" } diff --git a/cinder/volume/api.py b/cinder/volume/api.py index 6e63661cd4a..4e97b786c4c 100644 --- a/cinder/volume/api.py +++ b/cinder/volume/api.py @@ -44,6 +44,7 @@ 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 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 @@ -1843,7 +1844,7 @@ class API(base.Base): return cluster, services def failover(self, ctxt, host, cluster_name, secondary_id=None): - check_policy(ctxt, 'failover_host') + ctxt.authorize(svr_policy.FAILOVER_POLICY) ctxt = ctxt if ctxt.is_admin else ctxt.elevated() # TODO(geguileo): In P - Remove this version check @@ -1864,7 +1865,7 @@ class API(base.Base): self.volume_rpcapi.failover(ctxt, services[0], secondary_id) def freeze_host(self, ctxt, host, cluster_name): - check_policy(ctxt, 'freeze_host') + ctxt.authorize(svr_policy.FREEZE_POLICY) ctxt = ctxt if ctxt.is_admin else ctxt.elevated() expected = False @@ -1879,7 +1880,7 @@ class API(base.Base): self.volume_rpcapi.freeze_host(ctxt, services[0]) def thaw_host(self, ctxt, host, cluster_name): - check_policy(ctxt, 'thaw_host') + ctxt.authorize(svr_policy.THAW_POLICY) ctxt = ctxt if ctxt.is_admin else ctxt.elevated() expected = True diff --git a/etc/cinder/policy.json b/etc/cinder/policy.json index 0ce079db2fd..c7ede8d822e 100644 --- a/etc/cinder/policy.json +++ b/etc/cinder/policy.json @@ -46,26 +46,17 @@ "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:hosts": "rule:admin_api", - "volume_extension:services:index": "rule:admin_api", - "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", - "volume_extension:capabilities": "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", - "volume:failover_host": "rule:admin_api", - "volume:freeze_host": "rule:admin_api", - "volume:thaw_host": "rule:admin_api", - "consistencygroup:create" : "group:nobody", "consistencygroup:delete": "group:nobody", "consistencygroup:update": "group:nobody", @@ -77,6 +68,4 @@ "consistencygroup:get_cgsnapshot": "group:nobody", "consistencygroup:get_all_cgsnapshots": "group:nobody", - "scheduler_extension:scheduler_stats:get_pools" : "rule:admin_api", - }