diff --git a/nova/api/openstack/compute/rescue.py b/nova/api/openstack/compute/rescue.py index 74fb3a3b5a7f..6afb62c008c2 100644 --- a/nova/api/openstack/compute/rescue.py +++ b/nova/api/openstack/compute/rescue.py @@ -84,7 +84,7 @@ class RescueController(wsgi.Controller): """Unrescue an instance.""" context = req.environ["nova.context"] instance = common.get_instance(self.compute_api, context, id) - context.can(rescue_policies.BASE_POLICY_NAME, + context.can(rescue_policies.UNRESCUE_POLICY_NAME, target={'project_id': instance.project_id}) try: self.compute_api.unrescue(context, instance) diff --git a/nova/policies/rescue.py b/nova/policies/rescue.py index ef2068451b86..6e8c83807e38 100644 --- a/nova/policies/rescue.py +++ b/nova/policies/rescue.py @@ -19,24 +19,45 @@ from nova.policies import base BASE_POLICY_NAME = 'os_compute_api:os-rescue' +UNRESCUE_POLICY_NAME = 'os_compute_api:os-unrescue' + +DEPRECATED_POLICY = policy.DeprecatedRule( + 'os_compute_api:os-rescue', + base.RULE_ADMIN_OR_OWNER, +) + +DEPRECATED_REASON = """ +Rescue/Unrescue API policies are made granular with new policy +for unrescue and keeping old policy for rescue. +""" rescue_policies = [ policy.DocumentedRuleDefault( name=BASE_POLICY_NAME, check_str=base.RULE_ADMIN_OR_OWNER, - description="Rescue/unrescue a server", + description="Rescue a server", operations=[ { 'path': '/servers/{server_id}/action (rescue)', 'method': 'POST' }, + ], + scope_types=['system', 'project']), + policy.DocumentedRuleDefault( + name=UNRESCUE_POLICY_NAME, + check_str=base.RULE_ADMIN_OR_OWNER, + description="Unrescue a server", + operations=[ { 'path': '/servers/{server_id}/action (unrescue)', 'method': 'POST' } ], - scope_types=['system', 'project'] + scope_types=['system', 'project'], + deprecated_rule=DEPRECATED_POLICY, + deprecated_reason=DEPRECATED_REASON, + deprecated_since='21.0.0' ), ] diff --git a/nova/tests/unit/fake_policy.py b/nova/tests/unit/fake_policy.py index 1d24f542317d..6e8f93615407 100644 --- a/nova/tests/unit/fake_policy.py +++ b/nova/tests/unit/fake_policy.py @@ -84,6 +84,7 @@ policy_data = """ "os_compute_api:os-quota-class-sets:update": "", "os_compute_api:os-quota-class-sets:show": "", "os_compute_api:os-rescue": "", + "os_compute_api:os-unrescue": "", "os_compute_api:os-server-diagnostics": "", "os_compute_api:os-server-password": "", "os_compute_api:os-server-tags:index": "", diff --git a/nova/tests/unit/policies/test_rescue.py b/nova/tests/unit/policies/test_rescue.py index 41dc97720c93..44f7a4f8093c 100644 --- a/nova/tests/unit/policies/test_rescue.py +++ b/nova/tests/unit/policies/test_rescue.py @@ -12,6 +12,7 @@ import fixtures import mock +from nova.policies import base as base_policy from nova.policies import rescue as rs_policies from oslo_utils.fixture import uuidsentinel as uuids from oslo_utils import timeutils @@ -73,7 +74,7 @@ class RescueServerPolicyTest(base.BasePolicyTest): @mock.patch('nova.compute.api.API.unrescue') def test_unrescue_server_policy(self, mock_unrescue): - rule_name = rs_policies.BASE_POLICY_NAME + rule_name = rs_policies.UNRESCUE_POLICY_NAME self.common_policy_check(self.admin_or_owner_authorized_contexts, self.admin_or_owner_unauthorized_contexts, rule_name, @@ -117,3 +118,32 @@ class RescueServerScopeTypePolicyTest(RescueServerPolicyTest): def setUp(self): super(RescueServerScopeTypePolicyTest, self).setUp() self.flags(enforce_scope=True, group="oslo_policy") + + +class RescueServerNoLegacyPolicyTest(RescueServerScopeTypePolicyTest): + """Test Rescue Server APIs policies with system scope enabled, + and no more deprecated rules that allow the legacy admin API to + access system APIs. + """ + without_deprecated_rules = True + rules_without_deprecation = { + rs_policies.UNRESCUE_POLICY_NAME: + base_policy.PROJECT_MEMBER_OR_SYSTEM_ADMIN, + rs_policies.BASE_POLICY_NAME: + base_policy.PROJECT_MEMBER_OR_SYSTEM_ADMIN} + + def setUp(self): + super(RescueServerNoLegacyPolicyTest, self).setUp() + # Check that system admin or and server owner is able to + # rescue/unrescue the sevrer + self.admin_or_owner_authorized_contexts = [ + self.system_admin_context, + self.project_admin_context, self.project_member_context] + # Check that non-system/admin/owner is not able to rescue/unrescue + # the server + self.admin_or_owner_unauthorized_contexts = [ + self.legacy_admin_context, self.system_member_context, + self.system_reader_context, self.system_foo_context, + self.other_project_member_context, self.project_reader_context, + self.project_foo_context + ] diff --git a/nova/tests/unit/test_policy.py b/nova/tests/unit/test_policy.py index a31f4872eae3..a4f65e82430a 100644 --- a/nova/tests/unit/test_policy.py +++ b/nova/tests/unit/test_policy.py @@ -429,6 +429,7 @@ class RealRolePolicyTestCase(test.NoDBTestCase): "os_compute_api:os-multinic", "os_compute_api:os-networks:view", "os_compute_api:os-rescue", +"os_compute_api:os-unrescue", "os_compute_api:os-security-groups", "os_compute_api:os-server-password", "os_compute_api:os-server-tags:delete",