From 236483285d1beb3c7b17beab8db1443047f27ebe Mon Sep 17 00:00:00 2001 From: Ghanshyam Mann Date: Wed, 1 Apr 2020 01:37:01 -0500 Subject: [PATCH] Add new default roles in rescue server policies This adds new defaults roles in rescue server API policies to system admin or project member. Also add tests to simulates the future where we drop the deprecation fall back in the policy by overriding the rules with a version where there are no deprecated rule options. Operators can do the same by adding overrides in their policy files that match the default but stop the rule deprecation fallback from happening. Partial implement blueprint policy-defaults-refresh Change-Id: I5816abd33002b2036068cc686c3d0d44d66ee976 --- nova/api/openstack/compute/rescue.py | 2 +- nova/policies/rescue.py | 25 +++++++++++++++++-- nova/tests/unit/fake_policy.py | 1 + nova/tests/unit/policies/test_rescue.py | 32 ++++++++++++++++++++++++- nova/tests/unit/test_policy.py | 1 + 5 files changed, 57 insertions(+), 4 deletions(-) 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 f9bc531c5803..69ad7719cfec 100644 --- a/nova/tests/unit/fake_policy.py +++ b/nova/tests/unit/fake_policy.py @@ -73,6 +73,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 83409c3c3515..2bad3c9658e0 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",