From f421f25d65d89a29becc570b597361310eb08205 Mon Sep 17 00:00:00 2001 From: Ghanshyam Mann Date: Sat, 10 Feb 2024 20:40:11 -0800 Subject: [PATCH] Introduce project scope_types in VNF LCM policy oslo.policy introduced the scope_type feature which can control the access level at system-level and project-level. - https://docs.openstack.org/oslo.policy/latest/user/usage.html#setting-scope As per the SRBAC design, OpenStack does not support system scope so we need to make scope type of each policy rule to project. - https://governance.openstack.org/tc/goals/selected/consistent-and-secure-rbac.html#phase-1 The policy with 'project' scope means user with 'project-scoped' token have permission to access which is nothing but the current case so no change in permission level. By adding the scope_type to project explicitly gives benefit of better error message. For example, if any user with system scope token try to access tacker APIs then oslo policy will fail early (instead of failing in lower layer at DB or VIM level) and give clear error message of invalid scope. This commit adds project scope in VNF LCM policies and its tests also. Partial implement blueprint implement-project-personas Change-Id: Iead7f82b8c22c0c67981f5a7ae3a86016ee64734 --- tacker/policies/vnf_lcm.py | 51 ++++++++++++++-------- tacker/tests/unit/policies/base.py | 18 ++++++++ tacker/tests/unit/policies/test_vnf_lcm.py | 42 ++++++++++++++++++ 3 files changed, 94 insertions(+), 17 deletions(-) diff --git a/tacker/policies/vnf_lcm.py b/tacker/policies/vnf_lcm.py index a55032b34..3e97c2d18 100644 --- a/tacker/policies/vnf_lcm.py +++ b/tacker/policies/vnf_lcm.py @@ -31,7 +31,8 @@ rules = [ 'method': 'GET', 'path': '/vnflcm/v1/api_versions' } - ] + ], + scope_types=['project'], ), policy.DocumentedRuleDefault( name=VNFLCM % 'create', @@ -42,7 +43,8 @@ rules = [ 'method': 'POST', 'path': '/vnflcm/v1/vnf_instances' } - ] + ], + scope_types=['project'], ), policy.DocumentedRuleDefault( name=VNFLCM % 'instantiate', @@ -53,7 +55,8 @@ rules = [ 'method': 'POST', 'path': '/vnflcm/v1/vnf_instances/{vnfInstanceId}/instantiate' } - ] + ], + scope_types=['project'], ), policy.DocumentedRuleDefault( name=VNFLCM % 'show', @@ -64,7 +67,8 @@ rules = [ 'method': 'GET', 'path': '/vnflcm/v1/vnf_instances/{vnfInstanceId}' } - ] + ], + scope_types=['project'], ), policy.DocumentedRuleDefault( name=VNFLCM % 'terminate', @@ -75,7 +79,8 @@ rules = [ 'method': 'POST', 'path': '/vnflcm/v1/vnf_instances/{vnfInstanceId}/terminate' } - ] + ], + scope_types=['project'], ), policy.DocumentedRuleDefault( name=VNFLCM % 'heal', @@ -86,7 +91,8 @@ rules = [ 'method': 'POST', 'path': '/vnflcm/v1/vnf_instances/{vnfInstanceId}/heal' } - ] + ], + scope_types=['project'], ), policy.DocumentedRuleDefault( name=VNFLCM % 'scale', @@ -97,7 +103,8 @@ rules = [ 'method': 'POST', 'path': '/vnflcm/v1/vnf_instances/{vnfInstanceId}/scale' } - ] + ], + scope_types=['project'], ), policy.DocumentedRuleDefault( name=VNFLCM % 'show_lcm_op_occs', @@ -108,7 +115,8 @@ rules = [ 'method': 'GET', 'path': '/vnflcm/v1/vnf_lcm_op_occs/{vnfLcmOpOccId}' } - ] + ], + scope_types=['project'], ), policy.DocumentedRuleDefault( name=VNFLCM % 'list_lcm_op_occs', @@ -119,7 +127,8 @@ rules = [ 'method': 'GET', 'path': '/vnflcm/v1/vnf_lcm_op_occs' } - ] + ], + scope_types=['project'], ), policy.DocumentedRuleDefault( name=VNFLCM % 'index', @@ -130,7 +139,8 @@ rules = [ 'method': 'GET', 'path': '/vnflcm/v1/vnf_instances' } - ] + ], + scope_types=['project'], ), policy.DocumentedRuleDefault( name=VNFLCM % 'delete', @@ -141,7 +151,8 @@ rules = [ 'method': 'DELETE', 'path': '/vnflcm/v1/vnf_instances/{vnfInstanceId}' } - ] + ], + scope_types=['project'], ), policy.DocumentedRuleDefault( name=VNFLCM % 'update_vnf', @@ -152,7 +163,8 @@ rules = [ 'method': 'PATCH', 'path': '/vnflcm/v1/vnf_instances/{vnfInstanceId}' } - ] + ], + scope_types=['project'], ), policy.DocumentedRuleDefault( name=VNFLCM % 'rollback', @@ -163,7 +175,8 @@ rules = [ 'method': 'POST', 'path': '/vnflcm/v1/vnf_lcm_op_occs/{vnfLcmOpOccId}/rollback' } - ] + ], + scope_types=['project'], ), policy.DocumentedRuleDefault( name=VNFLCM % 'cancel', @@ -174,7 +187,8 @@ rules = [ 'method': 'POST', 'path': '/vnflcm/v1/vnf_lcm_op_occs/{vnfLcmOpOccId}/cancel' } - ] + ], + scope_types=['project'], ), policy.DocumentedRuleDefault( name=VNFLCM % 'fail', @@ -185,7 +199,8 @@ rules = [ 'method': 'POST', 'path': '/vnflcm/v1/vnf_lcm_op_occs/{vnfLcmOpOccId}/fail' } - ] + ], + scope_types=['project'], ), policy.DocumentedRuleDefault( name=VNFLCM % 'retry', @@ -196,7 +211,8 @@ rules = [ 'method': 'POST', 'path': '/vnflcm/v1/vnf_lcm_op_occs/{vnfLcmOpOccId}/retry' } - ] + ], + scope_types=['project'], ), policy.DocumentedRuleDefault( name=VNFLCM % 'change_ext_conn', @@ -208,7 +224,8 @@ rules = [ 'path': '/vnflcm/v1/vnf_instances/{vnfInstanceId}/change_ext_conn' } - ] + ], + scope_types=['project'], ), ] diff --git a/tacker/tests/unit/policies/base.py b/tacker/tests/unit/policies/base.py index 8a694ff1a..21ba24ff9 100644 --- a/tacker/tests/unit/policies/base.py +++ b/tacker/tests/unit/policies/base.py @@ -69,6 +69,24 @@ class BasePolicyTest(base.TestCase): project_id=self.other_project_id, roles=['reader']) + # system scoped users to check if system scope tokens are not + # allowed in new RBAC. + self.system_admin_context = context.Context( + user_id="admin", roles=['admin', 'member', 'reader'], + system_scope='all') + + self.system_member_context = context.Context( + user_id="member", roles=['member', 'reader'], + system_scope='all') + + self.system_reader_context = context.Context( + user_id="reader", roles=['reader'], + system_scope='all') + + self.system_foo_context = context.Context( + user_id="foo", roles=['foo'], + system_scope='all') + self.all_contexts = [ self.legacy_admin_context, self.project_admin_context, self.project_member_context, self.project_reader_context, diff --git a/tacker/tests/unit/policies/test_vnf_lcm.py b/tacker/tests/unit/policies/test_vnf_lcm.py index 2fd2a5f7a..a2617e987 100644 --- a/tacker/tests/unit/policies/test_vnf_lcm.py +++ b/tacker/tests/unit/policies/test_vnf_lcm.py @@ -15,6 +15,8 @@ from unittest import mock +from oslo_config import cfg + from tacker.api.vnflcm.v1 import controller import tacker.conductor.conductorrpc.vnf_lcm_rpc as vnf_lcm_rpc from tacker import objects @@ -428,3 +430,43 @@ class VNFLCMPolicyTest(base_test.BasePolicyTest): self.controller.change_ext_conn, req, uuidsentinel.instance_id, body=body) + + +class VNFLCMScopeTypePolicyTest(VNFLCMPolicyTest): + """Test VNF LCM APIs policies with scope enabled. + + This class set the tacker.conf [oslo_policy] enforce_scope to True + so that we can switch on the scope checking on oslo policy side. + This check that system scope users are not allowed to access the + Tacker VNF LCM APIs. + """ + + def setUp(self): + super(VNFLCMScopeTypePolicyTest, self).setUp() + cfg.CONF.set_override('enforce_scope', True, + group='oslo_policy') + self.project_authorized_contexts = [ + self.legacy_admin_context, self.project_admin_context, + self.project_member_context, self.project_reader_context, + self.project_foo_context, self.other_project_member_context, + self.other_project_reader_context + ] + # With scope enabled, system scoped users will not be + # allowed to create VNF or a few of the VNF operations + # in their project. + self.project_unauthorized_contexts = [ + self.system_admin_context, self.system_member_context, + self.system_reader_context, self.system_foo_context + ] + self.project_member_authorized_contexts = [ + self.legacy_admin_context, self.project_admin_context, + self.project_member_context, self.project_reader_context, + self.project_foo_context + ] + # With scope enabled, system scoped users will not be allowed + # to get, instantiate, terminate etc operations of VNF + self.project_member_unauthorized_contexts = [ + self.system_admin_context, self.system_member_context, + self.system_reader_context, self.system_foo_context, + self.other_project_member_context, + self.other_project_reader_context]