From ba24a89998680725e1d277b295f0c596dac6fe2b Mon Sep 17 00:00:00 2001 From: Ghanshyam Mann Date: Sun, 11 Feb 2024 13:24:45 -0800 Subject: [PATCH] Add new default roles in vnf-package API policies This adds new defaults roles in vnf-package API policies. Backward compatibility: - Old Rules and Defaults will keep working as it is because they are added as deprecated rules and not removed. They are enabled by default. This means existing deployement will continue working in same way till deprecated rules are there and enabled by default. - Legacy/current admin stays same and no change in their access permission - Deprecation warning is added for old defaults so that operators will know that new defaults are available to opt-in. New defaults(project personas): - Add new defaults but they are disabled by defaults and operators can adopt them by enabling the oslo.policy config option. Basically add below in tacker.conf [oslo_policy] enforce_new_defaults=True - All GET (read only) APIs are default to PROJECT_READER_OR_ADMIN - Rest other APIs (write operations) are default to PROJECT_MEMBER_OR_ADMIN Adding tests also to check permissions of new defaults. Partial implement blueprint implement-project-personas Change-Id: Ic7f5a9cd5aa10d93dfa491e5e60befb1f4bf2fcd --- tacker/policies/vnf_package.py | 20 +- .../tests/unit/policies/test_vnf_package.py | 196 ++++++++++++++++-- 2 files changed, 190 insertions(+), 26 deletions(-) diff --git a/tacker/policies/vnf_package.py b/tacker/policies/vnf_package.py index 5d8f2b9b0..94a82f1cb 100644 --- a/tacker/policies/vnf_package.py +++ b/tacker/policies/vnf_package.py @@ -24,7 +24,7 @@ VNFPKGM = 'os_nfv_orchestration_api:vnf_packages:%s' rules = [ policy.DocumentedRuleDefault( name=VNFPKGM % 'create', - check_str=base.RULE_ADMIN_OR_OWNER, + check_str=base.RULE_PROJECT_MEMBER_OR_ADMIN, description="Creates a vnf package.", operations=[ { @@ -35,7 +35,7 @@ rules = [ scope_types=['project']), policy.DocumentedRuleDefault( name=VNFPKGM % 'show', - check_str=base.RULE_ADMIN_OR_OWNER, + check_str=base.RULE_PROJECT_READER_OR_ADMIN, description="Show a vnf package.", operations=[ { @@ -46,7 +46,7 @@ rules = [ scope_types=['project']), policy.DocumentedRuleDefault( name=VNFPKGM % 'index', - check_str=base.RULE_ADMIN_OR_OWNER, + check_str=base.RULE_PROJECT_READER_OR_ADMIN, description="List all vnf packages.", operations=[ { @@ -57,7 +57,7 @@ rules = [ scope_types=['project']), policy.DocumentedRuleDefault( name=VNFPKGM % 'delete', - check_str=base.RULE_ADMIN_OR_OWNER, + check_str=base.RULE_PROJECT_MEMBER_OR_ADMIN, description="Delete a vnf package.", operations=[ { @@ -68,7 +68,7 @@ rules = [ scope_types=['project']), policy.DocumentedRuleDefault( name=VNFPKGM % 'fetch_package_content', - check_str=base.RULE_ADMIN_OR_OWNER, + check_str=base.RULE_PROJECT_READER_OR_ADMIN, description="fetch the contents of an on-boarded VNF Package", operations=[ { @@ -80,7 +80,7 @@ rules = [ scope_types=['project']), policy.DocumentedRuleDefault( name=VNFPKGM % 'upload_package_content', - check_str=base.RULE_ADMIN_OR_OWNER, + check_str=base.RULE_PROJECT_MEMBER_OR_ADMIN, description="upload a vnf package content.", operations=[ { @@ -92,7 +92,7 @@ rules = [ scope_types=['project']), policy.DocumentedRuleDefault( name=VNFPKGM % 'upload_from_uri', - check_str=base.RULE_ADMIN_OR_OWNER, + check_str=base.RULE_PROJECT_MEMBER_OR_ADMIN, description="upload a vnf package content from uri.", operations=[ { @@ -104,7 +104,7 @@ rules = [ scope_types=['project']), policy.DocumentedRuleDefault( name=VNFPKGM % 'patch', - check_str=base.RULE_ADMIN_OR_OWNER, + check_str=base.RULE_PROJECT_MEMBER_OR_ADMIN, description="update information of vnf package.", operations=[ { @@ -115,7 +115,7 @@ rules = [ scope_types=['project']), policy.DocumentedRuleDefault( name=VNFPKGM % 'get_vnf_package_vnfd', - check_str=base.RULE_ADMIN_OR_OWNER, + check_str=base.RULE_PROJECT_READER_OR_ADMIN, description="reads the content of the VNFD within a VNF package.", operations=[ { @@ -126,7 +126,7 @@ rules = [ scope_types=['project']), policy.DocumentedRuleDefault( name=VNFPKGM % 'fetch_artifact', - check_str=base.RULE_ADMIN_OR_OWNER, + check_str=base.RULE_PROJECT_READER_OR_ADMIN, description="reads the content of the artifact within a VNF package.", operations=[ { diff --git a/tacker/tests/unit/policies/test_vnf_package.py b/tacker/tests/unit/policies/test_vnf_package.py index f064ceb0f..ba0f8436a 100644 --- a/tacker/tests/unit/policies/test_vnf_package.py +++ b/tacker/tests/unit/policies/test_vnf_package.py @@ -57,21 +57,39 @@ class VNFPackagePolicyTest(base_test.BasePolicyTest): ] self.project_unauthorized_contexts = [] - # Admin or any user in same project will be allowed to get, - # instantiate, terminate etc operations of VNF package of - # their project. + # Admin or any user in same project will be allowed to + # upload package content, delete, patch VNF package in their + # project. 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 ] - # User from other project will not be allowed to get or perform - # the other project's VNF package operations. + # User from other project will not be allowed to upload + # package content,delete, patch the other project's VNF package. self.project_member_unauthorized_contexts = [ self.other_project_member_context, self.other_project_reader_context ] + # Admin or any user in same project will be allowed to get, + # VNF package. + self.project_reader_authorized_contexts = ( + self.project_member_authorized_contexts) + # User from other project will not be allowed to get + # the other project's VNF package. + self.project_reader_unauthorized_contexts = ( + self.project_member_unauthorized_contexts) + + # Below user's context will be allowed to list VNF package + self.get_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 + ] + self.get_unauthorized_contexts = [] + @mock.patch.object(vnf_package, '_vnf_package_create') @mock.patch.object(vnf_package.VnfPackage, '_from_db_object') def test_create_vnf_package( @@ -95,8 +113,8 @@ class VNFPackagePolicyTest(base_test.BasePolicyTest): vnf_package_updates={'tenant_id': self.project_id}) mock_sw_image_by_id.return_value = fakes.return_software_image() rule_name = policies.VNFPKGM % 'show' - self.common_policy_check(self.project_member_authorized_contexts, - self.project_member_unauthorized_contexts, + self.common_policy_check(self.project_reader_authorized_contexts, + self.project_reader_unauthorized_contexts, rule_name, self.controller.show, req, constants.UUID) @@ -106,8 +124,8 @@ class VNFPackagePolicyTest(base_test.BasePolicyTest): self, mock_vnf_list): req = fake_request.HTTPRequest.blank('/vnf_packages/') rule_name = policies.VNFPKGM % 'index' - self.common_policy_check(self.project_authorized_contexts, - self.project_unauthorized_contexts, + self.common_policy_check(self.get_authorized_contexts, + self.get_unauthorized_contexts, rule_name, self.controller.index, req) @@ -149,8 +167,8 @@ class VNFPackagePolicyTest(base_test.BasePolicyTest): mock_get_range.return_value = "10-20, 21-30" mock_download.return_value = "Response" rule_name = policies.VNFPKGM % 'fetch_package_content' - self.common_policy_check(self.project_member_authorized_contexts, - self.project_member_unauthorized_contexts, + self.common_policy_check(self.project_reader_authorized_contexts, + self.project_reader_unauthorized_contexts, rule_name, self.controller.fetch_vnf_package_content, req, constants.UUID) @@ -274,8 +292,8 @@ class VNFPackagePolicyTest(base_test.BasePolicyTest): fake_vnfd_data = fakes.return_vnfd_data(csar_without_tosca_meta=True) mock_get_vnf_package_vnfd.return_value = fake_vnfd_data rule_name = policies.VNFPKGM % 'get_vnf_package_vnfd' - self.common_policy_check(self.project_member_authorized_contexts, - self.project_member_unauthorized_contexts, + self.common_policy_check(self.project_reader_authorized_contexts, + self.project_reader_unauthorized_contexts, rule_name, self.controller.get_vnf_package_vnfd, req, constants.UUID) @@ -302,8 +320,8 @@ class VNFPackagePolicyTest(base_test.BasePolicyTest): data = f.read() mock_download_vnf_artifact.return_value = data rule_name = policies.VNFPKGM % 'fetch_artifact' - self.common_policy_check(self.project_member_authorized_contexts, - self.project_member_unauthorized_contexts, + self.common_policy_check(self.project_reader_authorized_contexts, + self.project_reader_unauthorized_contexts, rule_name, self.controller.fetch_vnf_package_artifacts, req, constants.UUID, absolute_artifact_path) @@ -343,9 +361,155 @@ class VNFPackageScopeTypePolicyTest(VNFPackagePolicyTest): self.project_foo_context ] # With scope enabled, system scoped users will not be allowed - # to get, detele etc operations of VNF Package. + # to upload content, delete, patch VNF Package. 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] + + self.get_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 list VNF package + self.get_unauthorized_contexts = [ + self.system_admin_context, self.system_member_context, + self.system_reader_context, self.system_foo_context, + ] + + +class VNFPackageNewDefaultsPolicyTest(VNFPackagePolicyTest): + """Test VNF Package APIs policies with new defaults enabled + + This test class enable the new defaults means no legacy old rules + and check how permission level looks like. + """ + + enforce_new_defaults = True + + def setUp(self): + super(VNFPackageNewDefaultsPolicyTest, self).setUp() + + # In new defaults, admin or member roles users will be allowed + # to create VNF package in their project. + # Project reader will not be able to create VNF package. + self.project_authorized_contexts = [ + self.legacy_admin_context, self.project_admin_context, + self.project_member_context, self.other_project_member_context, + ] + # In new defaults, non admin or non member role (Project reader) + # user will not be able to create VNF package. + self.project_unauthorized_contexts = [ + self.project_reader_context, self.project_foo_context, + self.other_project_reader_context] + + # In new defaults, all admin, project members will be allowed to + # upload content, delete, patch VNF of their project. + self.project_member_authorized_contexts = [ + self.legacy_admin_context, self.project_admin_context, + self.project_member_context + ] + # In new defaults, Project reader or any other non admin|member + # role (say foo role) will not be allowed to upload content, + # delete, patch VNF package. + self.project_member_unauthorized_contexts = [ + self.project_reader_context, self.project_foo_context, + self.other_project_member_context, + self.other_project_reader_context + ] + + # In new defaults, Project reader also can get VNF package. + self.project_reader_authorized_contexts = [ + self.legacy_admin_context, self.project_admin_context, + self.project_member_context, + self.project_reader_context + ] + # In new defaults, non admin|member|reader role (say foo role) + # will not be able to get VNF package. + self.project_reader_unauthorized_contexts = [ + self.project_foo_context, + self.other_project_member_context, + self.other_project_reader_context + ] + + self.get_authorized_contexts = [ + self.legacy_admin_context, self.project_admin_context, + self.project_member_context, self.project_reader_context, + self.other_project_member_context, + self.other_project_reader_context + ] + # In new defaults, project random role like foo will not + # be allowed to list the VNF package. + self.get_unauthorized_contexts = [ + self.project_foo_context + ] + + +class VNFPackageNewDefaultsWithScopePolicyTest( + VNFPackageNewDefaultsPolicyTest): + """Test VNF Package APIs policies with new defaults rules and scope enabled + + This means scope enabled and no legacy old rules. This is the end goal + when operators will enable scope and new defaults. + """ + + def setUp(self): + super(VNFPackageNewDefaultsWithScopePolicyTest, self).setUp() + cfg.CONF.set_override('enforce_scope', True, + group='oslo_policy') + + # With scope enable and no legacy rule, only project admin/member + # will be able to create VNF Package in their project. + self.project_authorized_contexts = [ + self.legacy_admin_context, self.project_admin_context, + self.project_member_context, self.other_project_member_context + ] + # System scoped users will not be allowed. + self.project_unauthorized_contexts = [ + self.system_admin_context, self.system_member_context, + self.system_reader_context, self.system_foo_context, + self.project_reader_context, self.project_foo_context, + self.other_project_reader_context] + + self.project_member_authorized_contexts = [ + self.legacy_admin_context, self.project_admin_context, + self.project_member_context, + ] + # System scoped users will not be allowed. + self.project_member_unauthorized_contexts = [ + self.system_admin_context, self.system_member_context, + self.system_reader_context, self.system_foo_context, + self.project_reader_context, self.project_foo_context, + self.other_project_member_context, + self.other_project_reader_context] + + self.project_reader_authorized_contexts = [ + self.legacy_admin_context, self.project_admin_context, + self.project_member_context, + self.project_reader_context + ] + # System scoped users will not be allowed. + self.project_reader_unauthorized_contexts = [ + self.system_admin_context, self.system_member_context, + self.system_reader_context, self.system_foo_context, + self.project_foo_context, + self.other_project_member_context, + self.other_project_reader_context + ] + + self.get_authorized_contexts = [ + self.legacy_admin_context, self.project_admin_context, + self.project_member_context, self.project_reader_context, + self.other_project_member_context, + self.other_project_reader_context + ] + # With scope enabled, system scoped users will not be allowed + self.get_unauthorized_contexts = [ + self.project_foo_context, self.system_admin_context, + self.system_member_context, self.system_reader_context, + self.system_foo_context, + ]