From 84becedfd79f603377d24a4e6aaeb4a2fc6869c1 Mon Sep 17 00:00:00 2001 From: Aleksandr Alferov Date: Fri, 14 Apr 2023 07:59:52 +0000 Subject: [PATCH 1/9] ADCM-3705 Rework tests --- python/adcm/tests/base.py | 19 ++--- python/api/tests/files/bundle_cluster.tar | Bin 0 -> 10240 bytes python/api/tests/test_api.py | 2 - python/api/tests/test_bundle.py | 6 ++ python/api/tests/test_job.py | 2 - python/audit/tests/test_action.py | 3 - python/audit/tests/test_bundle.py | 7 ++ python/audit/tests/test_cluster.py | 2 - python/audit/tests/test_provider.py | 2 - python/audit/tests/test_service.py | 3 - python/audit/tests/test_views.py | 2 - python/cm/tests/test_job.py | 4 +- python/rbac/tests/test_api.py | 15 +--- python/rbac/tests/test_base.py | 1 + python/rbac/tests/test_role.py | 82 ++++++++++++++-------- 15 files changed, 76 insertions(+), 74 deletions(-) create mode 100644 python/api/tests/files/bundle_cluster.tar diff --git a/python/adcm/tests/base.py b/python/adcm/tests/base.py index 016190b3f4..13ba3fbfab 100644 --- a/python/adcm/tests/base.py +++ b/python/adcm/tests/base.py @@ -18,7 +18,8 @@ from django.conf import settings from django.test import Client, TestCase from django.urls import reverse -from rbac.models import Role, User +from rbac.models import User +from rbac.upgrade.role import init_roles from rest_framework.response import Response from rest_framework.status import HTTP_200_OK, HTTP_201_CREATED @@ -29,6 +30,8 @@ class BaseTestCase(TestCase): # pylint: disable=too-many-instance-attributes def setUp(self) -> None: + init_roles() + self.test_user_username = "test_user" self.test_user_password = "test_user_password" @@ -48,20 +51,6 @@ def setUp(self) -> None: self.client = Client(HTTP_USER_AGENT="Mozilla/5.0") self.login() - self.cluster_admin_role = Role.objects.create( - name="Cluster Administrator", - display_name="Cluster Administrator", - ) - Role.objects.create(name="Provider Administrator", display_name="Provider Administrator") - Role.objects.create(name="Service Administrator", display_name="Service Administrator") - - self.test_bundle_filename = "test_bundle.tar" - self.test_bundle_path = Path( - settings.BASE_DIR, - "python/audit/tests/files", - self.test_bundle_filename, - ) - def tearDown(self) -> None: dirs_to_clear = ( *Path(settings.BUNDLE_DIR).iterdir(), diff --git a/python/api/tests/files/bundle_cluster.tar b/python/api/tests/files/bundle_cluster.tar new file mode 100644 index 0000000000000000000000000000000000000000..c10911620500c9a18f8fc5974ae5dc6e23826273 GIT binary patch literal 10240 zcmeIuy$XXc5Ww-wKE-WLksM7d_%=ao5&S4>3%-5R&En9>!ScW5k~?x-e)Txq+Iz9a zzB5OKOHr2Iq@>s2w)xXa&0y4f4Z(-fsd1_oRU_`Gg|>5_^AaZ!6XU!s@6Nyay9ehe zSZgyY%lc?4sk?;HOqs|K`>*?}ndWvJD#^8Qd3KJku_XcsAb None: super().setUp() init() - init_roles() self.files_dir = settings.BASE_DIR / "python" / "cm" / "tests" / "files" self.bundle_adh_name = "adh.1.5.tar" diff --git a/python/api/tests/test_bundle.py b/python/api/tests/test_bundle.py index 83f0a9de73..8adc67d246 100644 --- a/python/api/tests/test_bundle.py +++ b/python/api/tests/test_bundle.py @@ -41,6 +41,12 @@ def setUp(self) -> None: license="unaccepted", ) Prototype.objects.create(bundle=self.bundle_2, name=self.bundle_2.name) + self.test_bundle_filename = "bundle_cluster.tar" + self.test_bundle_path = Path( + settings.BASE_DIR, + "python/api/tests/files", + self.test_bundle_filename, + ) def test_upload_bundle(self) -> None: self.upload_bundle(path=self.test_bundle_path) diff --git a/python/api/tests/test_job.py b/python/api/tests/test_job.py index 8fde27362f..a9a2dc1a4a 100644 --- a/python/api/tests/test_job.py +++ b/python/api/tests/test_job.py @@ -29,7 +29,6 @@ from django.contrib.contenttypes.models import ContentType from django.urls import reverse from rbac.models import Policy, Role -from rbac.upgrade.role import init_roles from rest_framework.response import Response from rest_framework.status import HTTP_201_CREATED @@ -208,7 +207,6 @@ def test_task_permissions(self): cluster_prototype = Prototype.objects.get(bundle=bundle, type="cluster") cluster = Cluster.objects.create(name="test_cluster", prototype=cluster_prototype) - init_roles() role = Role.objects.get(name="Cluster Administrator") policy = Policy.objects.create(name="test_policy", role=role) policy.user.add(self.no_rights_user) diff --git a/python/audit/tests/test_action.py b/python/audit/tests/test_action.py index 3e8869bda4..cad190f2b2 100644 --- a/python/audit/tests/test_action.py +++ b/python/audit/tests/test_action.py @@ -38,7 +38,6 @@ from django.contrib.contenttypes.models import ContentType from django.urls import reverse from rbac.models import Policy, Role, User -from rbac.upgrade.role import init_roles from rest_framework.response import Response from rest_framework.status import HTTP_201_CREATED, HTTP_404_NOT_FOUND @@ -235,8 +234,6 @@ def test_component_launch_denied(self): ) def test_host_denied(self): - init_roles() - adcm_role = Role.objects.get(name="View ADCM settings") adcm_policy = Policy.objects.create(name="test_adcm_policy", role=adcm_role) adcm_policy.user.add(self.no_rights_user) diff --git a/python/audit/tests/test_bundle.py b/python/audit/tests/test_bundle.py index 7812a355eb..9661cbdcd6 100644 --- a/python/audit/tests/test_bundle.py +++ b/python/audit/tests/test_bundle.py @@ -50,6 +50,13 @@ def setUp(self) -> None: license="unaccepted", ) + self.test_bundle_filename = "test_bundle.tar" + self.test_bundle_path = Path( + settings.BASE_DIR, + "python/audit/tests/files", + self.test_bundle_filename, + ) + def check_log_upload(self, log: AuditLog, operation_result: AuditLogOperationResult, user: User) -> None: self.assertFalse(log.audit_object) self.assertEqual(log.operation_name, "Bundle uploaded") diff --git a/python/audit/tests/test_cluster.py b/python/audit/tests/test_cluster.py index 9e44c7bb8a..c6aafaa0c3 100644 --- a/python/audit/tests/test_cluster.py +++ b/python/audit/tests/test_cluster.py @@ -42,7 +42,6 @@ from django.conf import settings from django.urls import reverse from rbac.models import Policy, Role, User -from rbac.upgrade.role import init_roles from rest_framework.response import Response from rest_framework.status import ( HTTP_201_CREATED, @@ -246,7 +245,6 @@ def get_component(self) -> tuple[ServiceComponent, ConfigLog]: ) def add_no_rights_user_cluster_view_rights(self) -> None: - init_roles() role = Role.objects.get(name="View cluster configurations") policy = Policy.objects.create(name="test_policy", role=role) policy.user.add(self.no_rights_user) diff --git a/python/audit/tests/test_provider.py b/python/audit/tests/test_provider.py index 3bf2b89443..72cbcb4235 100644 --- a/python/audit/tests/test_provider.py +++ b/python/audit/tests/test_provider.py @@ -31,7 +31,6 @@ ) from django.urls import reverse from rbac.models import Policy, Role, User -from rbac.upgrade.role import init_roles from rest_framework.response import Response from rest_framework.status import ( HTTP_200_OK, @@ -202,7 +201,6 @@ def test_delete_denied_view_permission(self): prototype=self.prototype, ) - init_roles() role = Role.objects.get(name="View provider configurations") policy = Policy.objects.create(name="test_policy", role=role) policy.user.add(self.no_rights_user) diff --git a/python/audit/tests/test_service.py b/python/audit/tests/test_service.py index 44b70de3a4..8195b10718 100644 --- a/python/audit/tests/test_service.py +++ b/python/audit/tests/test_service.py @@ -36,7 +36,6 @@ from django.conf import settings from django.urls import reverse from rbac.models import Policy, Role, User -from rbac.upgrade.role import init_roles from rest_framework.response import Response from rest_framework.status import ( HTTP_201_CREATED, @@ -311,7 +310,6 @@ def test_delete(self): self.assertFalse(log.audit_object) def test_delete_denied(self): - init_roles() role = Role.objects.get(name="View service config") policy = Policy.objects.create(name="test_policy", role=role) policy.user.add(self.no_rights_user) @@ -339,7 +337,6 @@ def test_delete_denied(self): ) def test_delete_new(self): - init_roles() role = Role.objects.get(name="View service configurations") bundle_filename = "import.tar" with open( diff --git a/python/audit/tests/test_views.py b/python/audit/tests/test_views.py index b7ea9f27b7..a05e0ade59 100644 --- a/python/audit/tests/test_views.py +++ b/python/audit/tests/test_views.py @@ -24,7 +24,6 @@ ) from django.urls import reverse from init_db import init as init_adcm -from rbac.upgrade.role import init_roles from rest_framework.response import Response from rest_framework.status import HTTP_200_OK, HTTP_403_FORBIDDEN @@ -38,7 +37,6 @@ def setUp(self) -> None: super().setUp() init_adcm() - init_roles() self.object_name_first = "object_name_first" self.object_name_second = "object_name_second" diff --git a/python/cm/tests/test_job.py b/python/cm/tests/test_job.py index fbef5a0fcc..2a284a68ae 100644 --- a/python/cm/tests/test_job.py +++ b/python/cm/tests/test_job.py @@ -60,7 +60,6 @@ from django.urls import reverse from django.utils import timezone from init_db import init -from rbac.upgrade.role import init_roles from rest_framework.response import Response from rest_framework.status import HTTP_200_OK, HTTP_201_CREATED, HTTP_409_CONFLICT @@ -71,6 +70,8 @@ class TestJob(BaseTestCase): # pylint: disable=too-many-public-methods def setUp(self): + super().setUp() + self.files_dir = settings.BASE_DIR / "python" / "cm" / "tests" / "files" self.multijob_bundle = "multijob_cluster.tar" self.multijob_cluster_name = "multijob_cluster" @@ -82,7 +83,6 @@ def setUp(self): @staticmethod def init_adcm(): init() - init_roles() def create_multijob_cluster(self) -> Response: bundle_id = self.upload_and_load_bundle(path=Path(self.files_dir, self.multijob_bundle)).pk diff --git a/python/rbac/tests/test_api.py b/python/rbac/tests/test_api.py index d2c8870a88..4715e4e268 100644 --- a/python/rbac/tests/test_api.py +++ b/python/rbac/tests/test_api.py @@ -12,10 +12,8 @@ import json -from django.test import Client from init_db import init -from rbac.models import Policy, Role, User -from rbac.upgrade.role import init_roles +from rbac.models import Policy, Role from rest_framework import status from rest_framework.response import Response from rest_framework.reverse import reverse @@ -26,18 +24,9 @@ class ApiTests(BaseTestCase): def setUp(self) -> None: - self.test_user_username = "test_user" - self.test_user_password = "test_user_password" - self.test_user = User.objects.create_user( - username=self.test_user_username, - password=self.test_user_password, - is_superuser=True, - ) - self.client = Client(HTTP_USER_AGENT="Mozilla/5.0") - self.login() + super().setUp() init() - init_roles() invalid_data_dict_exp_int_got_str = "Invalid data. Expected a dictionary, but got int." invalid_data_dict_exp_str_got_str = "Invalid data. Expected a dictionary, but got str." diff --git a/python/rbac/tests/test_base.py b/python/rbac/tests/test_base.py index cd74bf48bd..1bbc5b8ee7 100644 --- a/python/rbac/tests/test_base.py +++ b/python/rbac/tests/test_base.py @@ -40,6 +40,7 @@ def cook_role(name, class_name, obj_type=None): class RBACBaseTestCase(BaseTestCase): # pylint: disable=too-many-instance-attributes def setUp(self) -> None: + super().setUp() self.create_bundles_and_prototypes() self.create_permissions() diff --git a/python/rbac/tests/test_role.py b/python/rbac/tests/test_role.py index 1ca2368948..c131a5f0a8 100644 --- a/python/rbac/tests/test_role.py +++ b/python/rbac/tests/test_role.py @@ -32,15 +32,15 @@ from django.conf import settings from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType -from django.test import Client from django.urls import reverse from init_db import init as init_adcm -from rbac.models import Role, RoleTypes, User +from rbac.models import Role, RoleTypes from rbac.roles import ModelRole from rbac.services.policy import policy_create from rbac.services.role import role_create from rbac.tests.test_base import RBACBaseTestCase -from rbac.upgrade.role import init_roles, prepare_action_roles +from rbac.upgrade.role import prepare_action_roles +from rest_framework.status import HTTP_404_NOT_FOUND from adcm.tests.base import APPLICATION_JSON, BaseTestCase @@ -168,7 +168,6 @@ def setUp(self): super().setUp() init_adcm() - init_roles() category = ProductCategory.objects.create( value="Sample Cluster", @@ -578,10 +577,10 @@ def check_roles(self): # pylint: disable=too-many-instance-attributes, protected-access class TestMMRoles(RBACBaseTestCase): def setUp(self) -> None: + super().setUp() + init_adcm() - init_roles() - self.create_bundles_and_prototypes() self.cluster = Cluster.objects.create(name="testcluster", prototype=self.clp) self.provider = HostProvider.objects.create( name="test_provider", @@ -596,16 +595,6 @@ def setUp(self) -> None: prototype=self.cop_11, ) - self.test_user_username = "test_user" - self.test_user_password = "test_user_password" - self.test_user = User.objects.create_user( - username=self.test_user_username, - password=self.test_user_password, - ) - - self.client = Client(HTTP_USER_AGENT="Mozilla/5.0") - self.login() - self.mm_role_host = role_create( name="mm role host", display_name="mm role host", @@ -617,18 +606,55 @@ def setUp(self) -> None: child=[Role.objects.get(name="Manage cluster Maintenance mode")], ) - def test_no_roles(self): - for view_name, url_kwarg_name, obj in ( - ("host-details", "host_id", self.host), - ("component-details", "component_id", self.component), - ("service-details", "service_id", self.service), - ): - url = reverse(view_name, kwargs={url_kwarg_name: obj.pk}) - response = self.client.get(path=url, content_type=APPLICATION_JSON) - self.assertEqual(response.status_code, 404) - - with self.assertRaises(PermissionDenied): - check_custom_perm(self.test_user, "change_maintenance_mode", obj._meta.model_name, obj) + def test_change_host_maintenance_mode_failed(self): + with self.no_rights_user_logged_in: + response = self.client.get( + path=reverse(viewname="host-details", kwargs={"host_id": self.host.id}), content_type=APPLICATION_JSON + ) + + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + with self.assertRaises(PermissionDenied): + check_custom_perm( + user=self.no_rights_user, + action_type="change_maintenance_mode", + model=self.host._meta.model_name, + obj=self.host, + ) + + def test_change_component_maintenance_mode_failed(self): + with self.no_rights_user_logged_in: + response = self.client.get( + path=reverse(viewname="component-details", kwargs={"component_id": self.component.id}), + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + with self.assertRaises(PermissionDenied): + check_custom_perm( + user=self.no_rights_user, + action_type="change_maintenance_mode", + model=self.component._meta.model_name, + obj=self.component, + ) + + def test_change_service_maintenance_mode_failed(self): + with self.no_rights_user_logged_in: + response = self.client.get( + path=reverse(viewname="service-details", kwargs={"service_id": self.service.id}), + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + with self.assertRaises(PermissionDenied): + check_custom_perm( + user=self.no_rights_user, + action_type="change_maintenance_mode", + model=self.service._meta.model_name, + obj=self.service, + ) def test_mm_host_role(self): policy_create(name="mm host policy", object=[self.host], role=self.mm_role_host, user=[self.test_user]) From ea50791381e6c59bdef68cb0c32d73ed5f3dbad6 Mon Sep 17 00:00:00 2001 From: Daniil Skrynnik Date: Mon, 17 Apr 2023 13:35:16 +0000 Subject: [PATCH 2/9] ADCM-3767: Add unittest for apply Cluster Administrator role --- python/rbac/tests/files/provider.tar | Bin 0 -> 3072 bytes .../test_cluster_for_cluster_admin_role.tar | Bin 0 -> 6144 bytes python/rbac/tests/test_policy.py | 374 ++++++++++++++++++ 3 files changed, 374 insertions(+) create mode 100644 python/rbac/tests/files/provider.tar create mode 100644 python/rbac/tests/files/test_cluster_for_cluster_admin_role.tar diff --git a/python/rbac/tests/files/provider.tar b/python/rbac/tests/files/provider.tar new file mode 100644 index 0000000000000000000000000000000000000000..57bdebb8248e927a0abe1d314a37582851ffdac9 GIT binary patch literal 3072 zcmeHHZEM0X5cYF_#o>nuLS1TKg#3;D0Eum^-D=V$X_<)seU~P>im<_;;D;W9m%HoT z<>~V@mttR)s}nocv$2d2l4S|Z2#Gc+8UAAiElpyGl6W0qK1nhl(K^lI8IZ9S8uo3q zVG36(m>7m1{J-+8!MSPaJAhdd9Y4M`xP)ue|QmH#s6meK?<7ZSYWMT zt>#YK_J|273TzRjeL)BX-?imW!BLkK=I3+2Qpc2r%Vi!6)}P!7feC>LfeC?sBJd4E Coyooc literal 0 HcmV?d00001 diff --git a/python/rbac/tests/files/test_cluster_for_cluster_admin_role.tar b/python/rbac/tests/files/test_cluster_for_cluster_admin_role.tar new file mode 100644 index 0000000000000000000000000000000000000000..240e30a8b23f2ce0f285b500e0c45f4ba88c7045 GIT binary patch literal 6144 zcmeH~$!^;)5QcNDpJI?h1EjS}Qlte4d5GSNf?y=tQLDujDFZ=}ckfWE3ol_7!#zkB zi!&pV^Kr)b8JEQ>T`wM0o?V^mA%uis2v>yAP>|hU^bo?ATma>qE*TSqb0Cz7FuVeC zt_|(ZTN|q?+*+cFUGv`iOa3O6COP*8d*A=3hWUIxn}dDa=m_ErVd!cGP^cWoRvRlj zqg<8M@UD_PEo4<@8e_M*GHF>v@VR?~8@eDj=+Q}P8&6#3d0nLT5sfN#HiO~LS{AZ1 zpsROEDoq{7+U!Ni)aty6gl^APnctnk#8tYnChCR&^Dd8OGM&J$@_z6EwBZpfzHO?s zu=1facB?AW^uk`8Y!Ru#r1wqQZo61I$Y@+pZB?2n^hclym+X)clPh%fk#~0y-2RjP zZemCCa8sN}!@0dT-%^*sNcz}@x=8LFDPD?b3@_w zp0RN8C^u!Hi@x}VF;+6&&9e*JJ#C!d*h*mqM(_j3*Dvt1%jd*`F5ROWb*_FPD%n_S zqj_kf(L`n79m%~T#nH&U17a6BAm$FJKhcrK0YJueO_ofPh+*LSC@o7tSM#u)c(RBd!Ggi%6kTp+<&oIZ1s zJ<<2|di3XJ9l`a}(=ZJ@KVQ!dBr=)aL}0iyyyly8n-HKT literal 0 HcmV?d00001 diff --git a/python/rbac/tests/test_policy.py b/python/rbac/tests/test_policy.py index 69b34c47e2..5877f5193a 100644 --- a/python/rbac/tests/test_policy.py +++ b/python/rbac/tests/test_policy.py @@ -10,17 +10,27 @@ # See the License for the specific language governing permissions and # limitations under the License. +from pathlib import Path + from cm import api from cm.models import ( + Bundle, Cluster, ClusterObject, Host, HostProvider, + ObjectType, Prototype, ServiceComponent, ) +from django.conf import settings +from django.urls import reverse from rbac.models import Group, Policy, User from rbac.tests.test_base import RBACBaseTestCase +from rest_framework.response import Response +from rest_framework.status import HTTP_200_OK, HTTP_201_CREATED, HTTP_404_NOT_FOUND + +from adcm.tests.base import APPLICATION_JSON, BaseTestCase class PolicyTestRBAC(RBACBaseTestCase): # pylint: disable=too-many-instance-attributes @@ -501,3 +511,367 @@ def test_add_hc(self): self.assertTrue(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_12)) self.assertTrue(self.user.has_perm("cm.change_config_of_host", host1)) self.assertTrue(self.user.has_perm("cm.change_config_of_host", host2)) + + +class TestPolicyWithClusterAdminRole(BaseTestCase): + def setUp(self) -> None: + super().setUp() + + files_dir = settings.BASE_DIR / "python" / "rbac" / "tests" / "files" + provider_bundle_filename = files_dir / "provider.tar" + cluster_bundle_filename = files_dir / "test_cluster_for_cluster_admin_role.tar" + + provider = self._make_adcm_entity( + entity_type=ObjectType.PROVIDER, bundle_filename=provider_bundle_filename, name="Test Provider" + ) + self.cluster = self._make_adcm_entity( + entity_type=ObjectType.CLUSTER, bundle_filename=cluster_bundle_filename, name="Test Cluster" + ) + + self.host_pks = self._make_hosts(num=5, provider_id=provider.pk, cluster_id=self.cluster.pk) + self.service_pks = self._make_services(cluster_id=self.cluster.pk) + self.assertEqual(len(self.host_pks), len(self.service_pks)) + + self.component_pks = self._save_hc_map(host_pks=self.host_pks, service_pks=self.service_pks) + + def _save_hc_map(self, host_pks: list[int], service_pks: list[int]) -> list[int]: + component_pks = [] + hc_data = [] + + for host_pk, service_pk in zip(host_pks, service_pks): + for component in ServiceComponent.objects.filter(service=ClusterObject.objects.get(pk=service_pk)): + component_pks.append(component.pk) + hc_data.append({"component_id": component.pk, "host_id": host_pk, "service_id": service_pk}) + + response: Response = self.client.post( + path=reverse(viewname="host-component", kwargs={"cluster_id": self.cluster.pk}), + data={"cluster_id": self.cluster.pk, "hc": hc_data}, + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_201_CREATED) + + return component_pks + + def _make_adcm_entity(self, entity_type: ObjectType, bundle_filename: Path, name: str) -> Cluster | HostProvider: + if entity_type == ObjectType.CLUSTER: + model = Cluster + viewname = "cluster" + elif entity_type == ObjectType.PROVIDER: + model = HostProvider + viewname = "provider" + else: + raise NotImplementedError + + bundle = self.upload_and_load_bundle(path=bundle_filename) + + response: Response = self.client.post( + path=reverse(viewname=viewname), + data={ + "prototype_id": Prototype.objects.get(bundle=bundle, type=entity_type).pk, + "name": name, + "display_name": name, + "bundle_id": bundle.pk, + }, + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_201_CREATED) + + return model.objects.get(pk=response.json()["id"]) + + def _get_role_request_data(self, role_display_name: str) -> dict | None: + response: Response = self.client.get( + path=reverse(viewname="rbac:role-list"), + data={"ordering": "name", "type": "role", "view": "interface"}, + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_200_OK) + + target_role_data = None + for role_data in response.json()["results"]: + if role_data["display_name"] == role_display_name: + target_role_data = role_data + break + + return target_role_data + + def _get_user_request_data(self, username: str) -> dict | None: + response: Response = self.client.get( + path=reverse(viewname="rbac:user-list"), + data={"ordering": "username", "view": "interface"}, + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_200_OK) + + target_user_data = None + for user_data in response.json()["results"]: + if user_data["username"] == username: + target_user_data = user_data + break + + return target_user_data + + def _apply_policy(self, role_display_name: str, username: str) -> None: + role_data = self._get_role_request_data(role_display_name=role_display_name) + self.assertIsNotNone(role_data) + + user_data = self._get_user_request_data(username=username) + self.assertIsNotNone(user_data) + + policy_request_data = { + "name": "test_policy_cluster_admin", + "role": {"id": role_data["id"]}, + "user": [ + {"id": user_data["id"]}, + ], + "group": [], + "object": [ + {"name": self.cluster.name, "type": "cluster", "id": self.cluster.pk}, + ], + } + response: Response = self.client.post( + path=reverse(viewname="rbac:policy-list"), + data=policy_request_data, + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_201_CREATED) + + def _make_hosts(self, num: int, provider_id: int, cluster_id: int | None = None) -> list[int]: + host_pks = [] + + for host_num in range(num): + fqdn = f"host-{host_num}" + + response: Response = self.client.post( + path=reverse(viewname="host", kwargs={"provider_id": provider_id}), + data={ + "fqdn": fqdn, + }, + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_201_CREATED) + host_id = response.json()["id"] + host_pks.append(host_id) + + if not cluster_id: + continue + + response: Response = self.client.post( + path=reverse(viewname="host", kwargs={"cluster_id": cluster_id}), + data={ + "host_id": host_id, + }, + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_201_CREATED) + + return host_pks + + def _make_services(self, cluster_id: int) -> list[int]: + service_pks = [] + + service_proto_pks = ( + Prototype.objects.filter( + bundle=Bundle.objects.get(name="test_cluster_for_cluster_admin_role"), type=ObjectType.SERVICE + ) + .order_by("name") + .values_list("pk", flat=True) + ) + for service_proto_pk in service_proto_pks: + response = self.client.post( + path=reverse(viewname="service", kwargs={"cluster_id": cluster_id}), + data={ + "prototype_id": service_proto_pk, + }, + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_201_CREATED) + service_pks.append(response.json()["id"]) + + return service_pks + + def test_view_perm_for_cluster_and_all_descendants_success(self): + # pylint: disable=too-many-statements + with self.no_rights_user_logged_in: + response: Response = self.client.get( + path=reverse(viewname="cluster-details", kwargs={"cluster_id": self.cluster.pk}), + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + response: Response = self.client.get( + path=reverse(viewname="service-details", kwargs={"service_id": self.service_pks[0]}), + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + response: Response = self.client.get( + path=reverse(viewname="component-details", kwargs={"component_id": self.component_pks[0]}), + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + response: Response = self.client.get( + path=reverse(viewname="host-details", kwargs={"host_id": self.host_pks[0]}), + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + response: Response = self.client.get( + path=reverse(viewname="host", kwargs={"cluster_id": self.cluster.pk}), + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + response: Response = self.client.get( + path=reverse( + viewname="config-current", + kwargs={"cluster_id": self.cluster.pk, "object_type": "cluster", "version": "current"}, + ), + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + response: Response = self.client.get( + path=reverse( + viewname="config-current", + kwargs={ + "cluster_id": self.cluster.pk, + "service_id": self.service_pks[0], + "object_type": "service", + "version": "current", + }, + ), + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + response: Response = self.client.get( + path=reverse( + viewname="config-current", + kwargs={"component_id": self.component_pks[0], "object_type": "component", "version": "current"}, + ), + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + response: Response = self.client.get( + path=reverse( + viewname="object-action", kwargs={"cluster_id": self.cluster.pk, "object_type": "cluster"} + ), + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_200_OK) + self.assertEqual(response.json(), []) + + response: Response = self.client.get( + path=reverse( + viewname="object-action", kwargs={"service_id": self.service_pks[0], "object_type": "service"} + ), + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_200_OK) + self.assertEqual(response.json(), []) + + response: Response = self.client.get( + path=reverse( + viewname="object-action", kwargs={"component_id": self.component_pks[0], "object_type": "component"} + ), + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_200_OK) + self.assertEqual(response.json(), []) + + self._apply_policy(role_display_name="Cluster Administrator", username=self.no_rights_user_username) + + with self.no_rights_user_logged_in: + response: Response = self.client.get( + path=reverse(viewname="cluster-details", kwargs={"cluster_id": self.cluster.pk}), + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_200_OK) + + response: Response = self.client.get( + path=reverse(viewname="service-details", kwargs={"service_id": self.service_pks[0]}), + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_200_OK) + + response: Response = self.client.get( + path=reverse(viewname="component-details", kwargs={"component_id": self.component_pks[0]}), + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_200_OK) + + response: Response = self.client.get( + path=reverse(viewname="host-details", kwargs={"host_id": self.host_pks[0]}), + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_200_OK) + + response: Response = self.client.get( + path=reverse(viewname="host", kwargs={"cluster_id": self.cluster.pk}), + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_200_OK) + + response: Response = self.client.get( + path=reverse( + viewname="config-current", + kwargs={"cluster_id": self.cluster.pk, "object_type": "cluster", "version": "current"}, + ), + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_200_OK) + + response: Response = self.client.get( + path=reverse( + viewname="config-current", + kwargs={ + "cluster_id": self.cluster.pk, + "service_id": self.service_pks[0], + "object_type": "service", + "version": "current", + }, + ), + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_200_OK) + + response: Response = self.client.get( + path=reverse( + viewname="config-current", + kwargs={"component_id": self.component_pks[0], "object_type": "component", "version": "current"}, + ), + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_200_OK) + + response: Response = self.client.get( + path=reverse( + viewname="object-action", kwargs={"cluster_id": self.cluster.pk, "object_type": "cluster"} + ), + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_200_OK) + self.assertIsInstance(response.json(), list) + self.assertTrue(len(response.json()) > 0) + + response: Response = self.client.get( + path=reverse( + viewname="object-action", kwargs={"service_id": self.service_pks[0], "object_type": "service"} + ), + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_200_OK) + self.assertIsInstance(response.json(), list) + self.assertTrue(len(response.json()) > 0) + + response: Response = self.client.get( + path=reverse( + viewname="object-action", kwargs={"component_id": self.component_pks[0], "object_type": "component"} + ), + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_200_OK) + self.assertIsInstance(response.json(), list) + self.assertTrue(len(response.json()) > 0) From fbff45bdbff2e9f7947eccf38a8ceb87c1681ff4 Mon Sep 17 00:00:00 2001 From: Konstantin Shestakov Date: Tue, 18 Apr 2023 06:58:09 +0000 Subject: [PATCH 3/9] ADCM-3775 Add tests for Provider Admin role policy --- python/adcm/tests/base.py | 10 +- python/rbac/tests/files/provider_2.tar | Bin 0 -> 10240 bytes python/rbac/tests/test_policy.py | 245 ++++++++++++++++++++++++- 3 files changed, 252 insertions(+), 3 deletions(-) create mode 100644 python/rbac/tests/files/provider_2.tar diff --git a/python/adcm/tests/base.py b/python/adcm/tests/base.py index 13ba3fbfab..829a823f0c 100644 --- a/python/adcm/tests/base.py +++ b/python/adcm/tests/base.py @@ -30,8 +30,6 @@ class BaseTestCase(TestCase): # pylint: disable=too-many-instance-attributes def setUp(self) -> None: - init_roles() - self.test_user_username = "test_user" self.test_user_password = "test_user_password" @@ -51,6 +49,14 @@ def setUp(self) -> None: self.client = Client(HTTP_USER_AGENT="Mozilla/5.0") self.login() + @classmethod + def setUpClass(cls): + init_roles() + + @classmethod + def tearDownClass(cls): + ... + def tearDown(self) -> None: dirs_to_clear = ( *Path(settings.BUNDLE_DIR).iterdir(), diff --git a/python/rbac/tests/files/provider_2.tar b/python/rbac/tests/files/provider_2.tar new file mode 100644 index 0000000000000000000000000000000000000000..c1cb689ede6c0ea31532feda72a31c0ca6b186f7 GIT binary patch literal 10240 zcmeH{QE!4k499u)Q#3xz!~}Q81QQ80-VpQ=>!|ETXV zuIu&hw>azqFKxfiltbMXScl!_{cOx2_2B2U65Za|^^UihvzvCpK8N%kBzjez7dZz9 z(GnT!{O_VayFHFh05NEI1r@RZ!c^-6l{rj@*mN>}1+j&I5D)@FKnMr{As_^VfDjM@ oLO=)z0U;m+gn$qb0zyCt2mv7=1cZPP5CTF#2nYco@UIDc0;>?xMgRZ+ literal 0 HcmV?d00001 diff --git a/python/rbac/tests/test_policy.py b/python/rbac/tests/test_policy.py index 5877f5193a..2a5416d789 100644 --- a/python/rbac/tests/test_policy.py +++ b/python/rbac/tests/test_policy.py @@ -11,12 +11,14 @@ # limitations under the License. from pathlib import Path +from unittest.mock import patch from cm import api from cm.models import ( Bundle, Cluster, ClusterObject, + ConfigLog, Host, HostProvider, ObjectType, @@ -24,11 +26,17 @@ ServiceComponent, ) from django.conf import settings +from django.db.models import ObjectDoesNotExist from django.urls import reverse from rbac.models import Group, Policy, User from rbac.tests.test_base import RBACBaseTestCase from rest_framework.response import Response -from rest_framework.status import HTTP_200_OK, HTTP_201_CREATED, HTTP_404_NOT_FOUND +from rest_framework.status import ( + HTTP_200_OK, + HTTP_201_CREATED, + HTTP_204_NO_CONTENT, + HTTP_404_NOT_FOUND, +) from adcm.tests.base import APPLICATION_JSON, BaseTestCase @@ -875,3 +883,238 @@ def test_view_perm_for_cluster_and_all_descendants_success(self): self.assertEqual(response.status_code, HTTP_200_OK) self.assertIsInstance(response.json(), list) self.assertTrue(len(response.json()) > 0) + + +class TestPolicyWithProviderAdminRole(BaseTestCase): + def setUp(self) -> None: + super().setUp() + + self.new_user = self._get_new_user() + self.provider = self._get_provider() + self._create_policy() + + def _get_provider(self) -> HostProvider: + bundle = self.upload_and_load_bundle( + path=settings.BASE_DIR / "python" / "rbac" / "tests" / "files" / "provider_2.tar", + ) + + response: Response = self.client.post( + path=reverse(viewname="provider"), + data={ + "prototype_id": Prototype.objects.get(bundle=bundle, type=ObjectType.PROVIDER).pk, + "name": "Test Provider", + "display_name": "Test Provider", + "bundle_id": bundle.pk, + }, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_201_CREATED) + + return HostProvider.objects.get(pk=response.json()["id"]) + + def _get_new_user(self) -> User: + response: Response = self.client.post( + path=reverse(viewname="rbac:user-list"), + data={"username": "new_user", "password": "new_user_password"}, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_201_CREATED) + + return User.objects.get(pk=response.json()["id"]) + + def _get_role_pk(self) -> int: + response: Response = self.client.get( + path=reverse(viewname="rbac:role-list"), + data={"ordering": "name", "type": "role", "view": "interface"}, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + + return [ + role_data["id"] for role_data in response.json()["results"] if role_data["name"] == "Provider Administrator" + ][0] + + def _create_policy(self) -> None: + response: Response = self.client.post( + path=reverse(viewname="rbac:policy-list"), + data={ + "name": "test_policy_provider_admin", + "role": {"id": self._get_role_pk()}, + "user": [{"id": self.new_user.pk}], + "group": [], + "object": [{"name": self.provider.name, "type": "provider", "id": self.provider.pk}], + }, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_201_CREATED) + + def _retrieve_provider_action(self) -> Response: + response: Response = self.client.get( + path=reverse(viewname="object-action", kwargs={"provider_id": self.provider.pk}), + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + self.assertEqual(len(response.data), 1) + + return response + + def _create_host(self) -> Host: + response: Response = self.client.post( + path=reverse("host", kwargs={"provider_id": self.provider.pk}), + data={"fqdn": "test-host"}, + ) + + self.assertEqual(response.status_code, HTTP_201_CREATED) + + return Host.objects.get(pk=response.data["id"]) + + def _retrieve_host_action(self, host_pk: int) -> Response: + response: Response = self.client.get( + path=reverse(viewname="object-action", kwargs={"host_id": host_pk}), + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + self.assertEqual(len(response.data), 1) + + return response + + def test_policy_add_required_perms_success(self): + required_perms = {perm.codename for perm in self.new_user.user_permissions.all()} + required_perms.update({perm.permission.codename for perm in self.new_user.userobjectpermission_set.all()}) + + self.assertEqual( + required_perms, + { + "delete_bundle", + "add_groupconfig", + "change_objectconfig", + "add_bundle", + "change_groupconfig", + "add_configlog", + "delete_groupconfig", + "add_prototype", + "change_bundle", + "change_configlog", + "add_host", + "view_configlog", + "view_hostprovider", + "do_upgrade_of_hostprovider", + "change_config_of_hostprovider", + "view_action", + "view_upgrade_of_hostprovider", + "view_objectconfig", + "add_host_to_hostprovider", + "run_action_bd938c688f49b77c7fc537c6b9222e2c97ebddd63076b87f2feaec66fb9c05d0", + }, + ) + + def test_retrieve_provider_success(self): + response: Response = self.client.get( + path=reverse(viewname="provider-details", kwargs={"provider_id": self.provider.pk}), + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + self.assertEqual(response.data["name"], self.provider.name) + + def test_retrieve_provider_config_success(self): + response: Response = self.client.get( + path=reverse(viewname="object-config", kwargs={"provider_id": self.provider.pk}), + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + + def test_update_provider_config_success(self): + new_string = "new_string" + + response: Response = self.client.post( + path=reverse(viewname="config-history", kwargs={"provider_id": self.provider.pk}), + data={"config": {"string": new_string}}, + content_type=APPLICATION_JSON, + ) + + self.provider.refresh_from_db() + config_log = ConfigLog.objects.get(pk=self.provider.config.current) + + self.assertEqual(response.status_code, HTTP_201_CREATED) + self.assertEqual(config_log.config["string"], new_string) + + def test_retrieve_provider_actions_success(self): + self._retrieve_provider_action() + + def test_run_provider_actions_success(self): + response: Response = self._retrieve_provider_action() + + with patch("api.action.views.create", return_value=Response(status=HTTP_201_CREATED)): + response: Response = self.client.post( + path=reverse( + viewname="run-task", + kwargs={"provider_id": self.provider.pk, "action_id": response.data[0]["id"]}, + ), + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_201_CREATED) + + def test_create_host_success(self): + self._create_host() + + def test_retrieve_host_success(self): + host: Host = self._create_host() + response: Response = self.client.get(path=reverse("host-details", kwargs={"host_id": host.pk})) + + self.assertEqual(response.status_code, HTTP_200_OK) + self.assertEqual(response.data["id"], host.pk) + + def test_retrieve_host_config_success(self): + response: Response = self.client.get( + path=reverse(viewname="object-config", kwargs={"host_id": self._create_host().pk}), + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + + def test_update_host_config_success(self): + host: Host = self._create_host() + new_string = "new_string" + + response: Response = self.client.post( + path=reverse(viewname="config-history", kwargs={"host_id": host.pk}), + data={"config": {"string": new_string}}, + content_type=APPLICATION_JSON, + ) + + host.refresh_from_db() + config_log = ConfigLog.objects.get(pk=host.config.current) + + self.assertEqual(response.status_code, HTTP_201_CREATED) + self.assertEqual(config_log.config["string"], new_string) + + def test_retrieve_host_actions_success(self): + self._retrieve_host_action(host_pk=self._create_host().pk) + + def test_run_host_actions_success(self): + host: Host = self._create_host() + response: Response = self._retrieve_host_action(host_pk=host.pk) + + with patch("api.action.views.create", return_value=Response(status=HTTP_201_CREATED)): + response: Response = self.client.post( + path=reverse( + viewname="run-task", + kwargs={"host_id": host.pk, "action_id": response.data[0]["id"]}, + ), + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_201_CREATED) + + def test_delete_host_success(self): + host: Host = self._create_host() + + response: Response = self.client.delete(path=reverse("host-details", kwargs={"host_id": host.pk})) + + self.assertEqual(response.status_code, HTTP_204_NO_CONTENT) + with self.assertRaises(ObjectDoesNotExist): + host.refresh_from_db() From 8554b6529936e16cdcc34143c294823fb11fc431 Mon Sep 17 00:00:00 2001 From: Daniil Skrynnik Date: Tue, 18 Apr 2023 10:35:23 +0000 Subject: [PATCH 4/9] ADCM-3767 more tests for `Cluster administrator` policy --- python/rbac/tests/files/provider.tar | Bin 3072 -> 10240 bytes .../test_cluster_for_cluster_admin_role.tar | Bin 6144 -> 6656 bytes python/rbac/tests/test_policy.py | 240 ++++++++++++++++++ 3 files changed, 240 insertions(+) diff --git a/python/rbac/tests/files/provider.tar b/python/rbac/tests/files/provider.tar index 57bdebb8248e927a0abe1d314a37582851ffdac9..c1cb689ede6c0ea31532feda72a31c0ca6b186f7 100644 GIT binary patch literal 10240 zcmeH{QE!4k499u)Q#3xz!~}Q81QQ80-VpQ=>!|ETXV zuIu&hw>azqFKxfiltbMXScl!_{cOx2_2B2U65Za|^^UihvzvCpK8N%kBzjez7dZz9 z(GnT!{O_VayFHFh05NEI1r@RZ!c^-6l{rj@*mN>}1+j&I5D)@FKnMr{As_^VfDjM@ oLO=)z0U;m+gn$qb0zyCt2mv7=1cZPP5CTF#2nYco@UIDc0;>?xMgRZ+ literal 3072 zcmeHHZEM0X5cYF_#o>nuLS1TKg#3;D0Eum^-D=V$X_<)seU~P>im<_;;D;W9m%HoT z<>~V@mttR)s}nocv$2d2l4S|Z2#Gc+8UAAiElpyGl6W0qK1nhl(K^lI8IZ9S8uo3q zVG36(m>7m1{J-+8!MSPaJAhdd9Y4M`xP)ue|QmH#s6meK?<7ZSYWMT zt>#YK_J|273TzRjeL)BX-?imW!BLkK=I3+2Qpc2r%Vi!6)}P!7feC>LfeC?sBJd4E Coyooc diff --git a/python/rbac/tests/files/test_cluster_for_cluster_admin_role.tar b/python/rbac/tests/files/test_cluster_for_cluster_admin_role.tar index 240e30a8b23f2ce0f285b500e0c45f4ba88c7045..c84458cc28d4cae0bacec244a57ad678ce92c99f 100644 GIT binary patch delta 108 zcmZoLXfT=3EM;n9Y@}dlVq$1+Vq$J!W~g9bXk=z!z@T6-Igv4CGb5uTqjX|UPJVfO zZenI$NornVUUF)DZhlItl|o5T>1I2obk52AoPs=NKm~cFi8=9!DJhc`IZHP` None: entity_type=ObjectType.CLUSTER, bundle_filename=cluster_bundle_filename, name="Test Cluster" ) + self.service_6_proto = Prototype.objects.get( + bundle=Bundle.objects.get(name="test_cluster_for_cluster_admin_role"), + name="service_6_manual_add", + type=ObjectType.SERVICE, + ) + self.host_pks = self._make_hosts(num=5, provider_id=provider.pk, cluster_id=self.cluster.pk) self.service_pks = self._make_services(cluster_id=self.cluster.pk) self.assertEqual(len(self.host_pks), len(self.service_pks)) @@ -681,6 +692,7 @@ def _make_services(self, cluster_id: int) -> list[int]: Prototype.objects.filter( bundle=Bundle.objects.get(name="test_cluster_for_cluster_admin_role"), type=ObjectType.SERVICE ) + .exclude(pk=self.service_6_proto.pk) .order_by("name") .values_list("pk", flat=True) ) @@ -884,6 +896,234 @@ def test_view_perm_for_cluster_and_all_descendants_success(self): self.assertIsInstance(response.json(), list) self.assertTrue(len(response.json()) > 0) + def test_edit_perm_for_cluster_and_all_descendants_success(self): + initial_hc_count = HostComponent.objects.count() + + with self.no_rights_user_logged_in: + response: Response = self.client.post( + path=reverse(viewname="host-component", kwargs={"cluster_id": self.cluster.pk}), + data={ + "cluster_id": self.cluster.pk, + "hc": [ + { + "component_id": self.component_pks[0], + "host_id": self.host_pks[0], + "service_id": self.service_pks[0], + } + ], + }, + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + response: Response = self.client.post( + path=reverse( + viewname="config-history", kwargs={"cluster_id": self.cluster.pk, "object_type": "cluster"} + ), + data={"attr": {}, "config": {"float": 3.3}}, + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_403_FORBIDDEN) + + response: Response = self.client.post( + path=reverse( + viewname="config-history", + kwargs={"cluster_id": self.cluster.pk, "service_id": self.service_pks[0], "object_type": "service"}, + ), + data={"attr": {}, "config": {"float": 3.3}}, + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_403_FORBIDDEN) + + response: Response = self.client.post( + path=reverse( + viewname="config-history", + kwargs={"component_id": self.component_pks[0], "object_type": "component"}, + ), + data={"attr": {}, "config": {"float": 3.3}}, + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_403_FORBIDDEN) + + response: Response = self.client.post( + path=reverse( + viewname="config-history", + kwargs={"host_id": self.host_pks[0], "object_type": "host"}, + ), + data={"attr": {}, "config": {"string": "new_srting"}}, + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_403_FORBIDDEN) + + response: Response = self.client.post( + path=reverse( + viewname="config-history", + kwargs={"cluster_id": self.cluster.pk, "host_id": self.host_pks[0], "object_type": "host"}, + ), + data={"attr": {}, "config": {"string": "new_srting"}}, + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_403_FORBIDDEN) + + response = self.client.post( + path=reverse(viewname="service", kwargs={"cluster_id": self.cluster.pk}), + data={ + "prototype_id": self.service_6_proto.pk, + }, + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_403_FORBIDDEN) + + response = self.client.delete( + path=reverse( + viewname="service-details", + kwargs={"cluster_id": self.cluster.pk, "service_id": self.service_pks[-1]}, + ), + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + response = self.client.post( + path=reverse(viewname="service-maintenance-mode", kwargs={"service_id": self.service_pks[0]}), + data={ + "maintenance_mode": MaintenanceMode.ON, + }, + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + response = self.client.post( + path=reverse(viewname="component-maintenance-mode", kwargs={"component_id": self.component_pks[0]}), + data={ + "maintenance_mode": MaintenanceMode.ON, + }, + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + response = self.client.post( + path=reverse(viewname="host-maintenance-mode", kwargs={"host_id": self.host_pks[0]}), + data={ + "maintenance_mode": MaintenanceMode.ON, + }, + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + self._apply_policy(role_display_name="Cluster Administrator", username=self.no_rights_user_username) + + with self.no_rights_user_logged_in: + response: Response = self.client.post( + path=reverse(viewname="host-component", kwargs={"cluster_id": self.cluster.pk}), + data={ + "cluster_id": self.cluster.pk, + "hc": [ + { + "component_id": self.component_pks[0], + "host_id": self.host_pks[0], + "service_id": self.service_pks[0], + } + ], + }, + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_201_CREATED) + self.assertTrue(initial_hc_count != HostComponent.objects.count()) + + response: Response = self.client.post( + path=reverse( + viewname="config-history", kwargs={"cluster_id": self.cluster.pk, "object_type": "cluster"} + ), + data={"attr": {}, "config": {"float": 3.3}}, + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_201_CREATED) + + response: Response = self.client.post( + path=reverse( + viewname="config-history", + kwargs={"cluster_id": self.cluster.pk, "service_id": self.service_pks[0], "object_type": "service"}, + ), + data={"attr": {}, "config": {"float": 3.3}}, + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_201_CREATED) + + response: Response = self.client.post( + path=reverse( + viewname="config-history", + kwargs={"component_id": self.component_pks[0], "object_type": "component"}, + ), + data={"attr": {}, "config": {"float": 3.3}}, + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_201_CREATED) + + response: Response = self.client.post( + path=reverse( + viewname="config-history", + kwargs={"host_id": self.host_pks[0], "object_type": "host"}, + ), + data={"attr": {}, "config": {"string": "new_srting"}}, + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_201_CREATED) + + response: Response = self.client.post( + path=reverse( + viewname="config-history", + kwargs={"cluster_id": self.cluster.pk, "host_id": self.host_pks[0], "object_type": "host"}, + ), + data={"attr": {}, "config": {"string": "new_srting"}}, + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_201_CREATED) + + response = self.client.post( + path=reverse(viewname="service", kwargs={"cluster_id": self.cluster.pk}), + data={ + "prototype_id": self.service_6_proto.pk, + }, + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_201_CREATED) + + response = self.client.delete( + path=reverse( + viewname="service-details", + kwargs={"cluster_id": self.cluster.pk, "service_id": self.service_pks[-1]}, + ), + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_204_NO_CONTENT) + + response = self.client.post( + path=reverse(viewname="service-maintenance-mode", kwargs={"service_id": self.service_pks[0]}), + data={ + "maintenance_mode": MaintenanceMode.ON, + }, + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_200_OK) + + response = self.client.post( + path=reverse(viewname="component-maintenance-mode", kwargs={"component_id": self.component_pks[0]}), + data={ + "maintenance_mode": MaintenanceMode.ON, + }, + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_200_OK) + + response = self.client.post( + path=reverse(viewname="host-maintenance-mode", kwargs={"host_id": self.host_pks[0]}), + data={ + "maintenance_mode": MaintenanceMode.ON, + }, + content_type=APPLICATION_JSON, + ) + self.assertEqual(response.status_code, HTTP_200_OK) + class TestPolicyWithProviderAdminRole(BaseTestCase): def setUp(self) -> None: From 5fe33e9f5407e604fc952988826c37bb275dd72d Mon Sep 17 00:00:00 2001 From: Konstantin Shestakov Date: Tue, 18 Apr 2023 17:38:14 +0700 Subject: [PATCH 5/9] ADCM-3778 rename protected test case methods to public --- python/rbac/tests/test_policy.py | 74 ++++++++++++++++---------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/python/rbac/tests/test_policy.py b/python/rbac/tests/test_policy.py index 59473780a7..431ac9b2cc 100644 --- a/python/rbac/tests/test_policy.py +++ b/python/rbac/tests/test_policy.py @@ -534,10 +534,10 @@ def setUp(self) -> None: provider_bundle_filename = files_dir / "provider.tar" cluster_bundle_filename = files_dir / "test_cluster_for_cluster_admin_role.tar" - provider = self._make_adcm_entity( + provider = self.make_adcm_entity( entity_type=ObjectType.PROVIDER, bundle_filename=provider_bundle_filename, name="Test Provider" ) - self.cluster = self._make_adcm_entity( + self.cluster = self.make_adcm_entity( entity_type=ObjectType.CLUSTER, bundle_filename=cluster_bundle_filename, name="Test Cluster" ) @@ -547,13 +547,13 @@ def setUp(self) -> None: type=ObjectType.SERVICE, ) - self.host_pks = self._make_hosts(num=5, provider_id=provider.pk, cluster_id=self.cluster.pk) - self.service_pks = self._make_services(cluster_id=self.cluster.pk) + self.host_pks = self.make_hosts(num=5, provider_id=provider.pk, cluster_id=self.cluster.pk) + self.service_pks = self.make_services(cluster_id=self.cluster.pk) self.assertEqual(len(self.host_pks), len(self.service_pks)) - self.component_pks = self._save_hc_map(host_pks=self.host_pks, service_pks=self.service_pks) + self.component_pks = self.save_hc_map(host_pks=self.host_pks, service_pks=self.service_pks) - def _save_hc_map(self, host_pks: list[int], service_pks: list[int]) -> list[int]: + def save_hc_map(self, host_pks: list[int], service_pks: list[int]) -> list[int]: component_pks = [] hc_data = [] @@ -571,7 +571,7 @@ def _save_hc_map(self, host_pks: list[int], service_pks: list[int]) -> list[int] return component_pks - def _make_adcm_entity(self, entity_type: ObjectType, bundle_filename: Path, name: str) -> Cluster | HostProvider: + def make_adcm_entity(self, entity_type: ObjectType, bundle_filename: Path, name: str) -> Cluster | HostProvider: if entity_type == ObjectType.CLUSTER: model = Cluster viewname = "cluster" @@ -597,7 +597,7 @@ def _make_adcm_entity(self, entity_type: ObjectType, bundle_filename: Path, name return model.objects.get(pk=response.json()["id"]) - def _get_role_request_data(self, role_display_name: str) -> dict | None: + def get_role_request_data(self, role_display_name: str) -> dict | None: response: Response = self.client.get( path=reverse(viewname="rbac:role-list"), data={"ordering": "name", "type": "role", "view": "interface"}, @@ -613,7 +613,7 @@ def _get_role_request_data(self, role_display_name: str) -> dict | None: return target_role_data - def _get_user_request_data(self, username: str) -> dict | None: + def get_user_request_data(self, username: str) -> dict | None: response: Response = self.client.get( path=reverse(viewname="rbac:user-list"), data={"ordering": "username", "view": "interface"}, @@ -629,11 +629,11 @@ def _get_user_request_data(self, username: str) -> dict | None: return target_user_data - def _apply_policy(self, role_display_name: str, username: str) -> None: - role_data = self._get_role_request_data(role_display_name=role_display_name) + def apply_policy(self, role_display_name: str, username: str) -> None: + role_data = self.get_role_request_data(role_display_name=role_display_name) self.assertIsNotNone(role_data) - user_data = self._get_user_request_data(username=username) + user_data = self.get_user_request_data(username=username) self.assertIsNotNone(user_data) policy_request_data = { @@ -654,7 +654,7 @@ def _apply_policy(self, role_display_name: str, username: str) -> None: ) self.assertEqual(response.status_code, HTTP_201_CREATED) - def _make_hosts(self, num: int, provider_id: int, cluster_id: int | None = None) -> list[int]: + def make_hosts(self, num: int, provider_id: int, cluster_id: int | None = None) -> list[int]: host_pks = [] for host_num in range(num): @@ -685,7 +685,7 @@ def _make_hosts(self, num: int, provider_id: int, cluster_id: int | None = None) return host_pks - def _make_services(self, cluster_id: int) -> list[int]: + def make_services(self, cluster_id: int) -> list[int]: service_pks = [] service_proto_pks = ( @@ -801,7 +801,7 @@ def test_view_perm_for_cluster_and_all_descendants_success(self): self.assertEqual(response.status_code, HTTP_200_OK) self.assertEqual(response.json(), []) - self._apply_policy(role_display_name="Cluster Administrator", username=self.no_rights_user_username) + self.apply_policy(role_display_name="Cluster Administrator", username=self.no_rights_user_username) with self.no_rights_user_logged_in: response: Response = self.client.get( @@ -1010,7 +1010,7 @@ def test_edit_perm_for_cluster_and_all_descendants_success(self): ) self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) - self._apply_policy(role_display_name="Cluster Administrator", username=self.no_rights_user_username) + self.apply_policy(role_display_name="Cluster Administrator", username=self.no_rights_user_username) with self.no_rights_user_logged_in: response: Response = self.client.post( @@ -1129,11 +1129,11 @@ class TestPolicyWithProviderAdminRole(BaseTestCase): def setUp(self) -> None: super().setUp() - self.new_user = self._get_new_user() - self.provider = self._get_provider() - self._create_policy() + self.new_user = self.get_new_user() + self.provider = self.get_provider() + self.create_policy() - def _get_provider(self) -> HostProvider: + def get_provider(self) -> HostProvider: bundle = self.upload_and_load_bundle( path=settings.BASE_DIR / "python" / "rbac" / "tests" / "files" / "provider_2.tar", ) @@ -1153,7 +1153,7 @@ def _get_provider(self) -> HostProvider: return HostProvider.objects.get(pk=response.json()["id"]) - def _get_new_user(self) -> User: + def get_new_user(self) -> User: response: Response = self.client.post( path=reverse(viewname="rbac:user-list"), data={"username": "new_user", "password": "new_user_password"}, @@ -1164,7 +1164,7 @@ def _get_new_user(self) -> User: return User.objects.get(pk=response.json()["id"]) - def _get_role_pk(self) -> int: + def get_role_pk(self) -> int: response: Response = self.client.get( path=reverse(viewname="rbac:role-list"), data={"ordering": "name", "type": "role", "view": "interface"}, @@ -1177,12 +1177,12 @@ def _get_role_pk(self) -> int: role_data["id"] for role_data in response.json()["results"] if role_data["name"] == "Provider Administrator" ][0] - def _create_policy(self) -> None: + def create_policy(self) -> None: response: Response = self.client.post( path=reverse(viewname="rbac:policy-list"), data={ "name": "test_policy_provider_admin", - "role": {"id": self._get_role_pk()}, + "role": {"id": self.get_role_pk()}, "user": [{"id": self.new_user.pk}], "group": [], "object": [{"name": self.provider.name, "type": "provider", "id": self.provider.pk}], @@ -1192,7 +1192,7 @@ def _create_policy(self) -> None: self.assertEqual(response.status_code, HTTP_201_CREATED) - def _retrieve_provider_action(self) -> Response: + def retrieve_provider_action(self) -> Response: response: Response = self.client.get( path=reverse(viewname="object-action", kwargs={"provider_id": self.provider.pk}), ) @@ -1202,7 +1202,7 @@ def _retrieve_provider_action(self) -> Response: return response - def _create_host(self) -> Host: + def create_host(self) -> Host: response: Response = self.client.post( path=reverse("host", kwargs={"provider_id": self.provider.pk}), data={"fqdn": "test-host"}, @@ -1212,7 +1212,7 @@ def _create_host(self) -> Host: return Host.objects.get(pk=response.data["id"]) - def _retrieve_host_action(self, host_pk: int) -> Response: + def retrieve_host_action(self, host_pk: int) -> Response: response: Response = self.client.get( path=reverse(viewname="object-action", kwargs={"host_id": host_pk}), ) @@ -1283,10 +1283,10 @@ def test_update_provider_config_success(self): self.assertEqual(config_log.config["string"], new_string) def test_retrieve_provider_actions_success(self): - self._retrieve_provider_action() + self.retrieve_provider_action() def test_run_provider_actions_success(self): - response: Response = self._retrieve_provider_action() + response: Response = self.retrieve_provider_action() with patch("api.action.views.create", return_value=Response(status=HTTP_201_CREATED)): response: Response = self.client.post( @@ -1300,10 +1300,10 @@ def test_run_provider_actions_success(self): self.assertEqual(response.status_code, HTTP_201_CREATED) def test_create_host_success(self): - self._create_host() + self.create_host() def test_retrieve_host_success(self): - host: Host = self._create_host() + host: Host = self.create_host() response: Response = self.client.get(path=reverse("host-details", kwargs={"host_id": host.pk})) self.assertEqual(response.status_code, HTTP_200_OK) @@ -1311,13 +1311,13 @@ def test_retrieve_host_success(self): def test_retrieve_host_config_success(self): response: Response = self.client.get( - path=reverse(viewname="object-config", kwargs={"host_id": self._create_host().pk}), + path=reverse(viewname="object-config", kwargs={"host_id": self.create_host().pk}), ) self.assertEqual(response.status_code, HTTP_200_OK) def test_update_host_config_success(self): - host: Host = self._create_host() + host: Host = self.create_host() new_string = "new_string" response: Response = self.client.post( @@ -1333,11 +1333,11 @@ def test_update_host_config_success(self): self.assertEqual(config_log.config["string"], new_string) def test_retrieve_host_actions_success(self): - self._retrieve_host_action(host_pk=self._create_host().pk) + self.retrieve_host_action(host_pk=self.create_host().pk) def test_run_host_actions_success(self): - host: Host = self._create_host() - response: Response = self._retrieve_host_action(host_pk=host.pk) + host: Host = self.create_host() + response: Response = self.retrieve_host_action(host_pk=host.pk) with patch("api.action.views.create", return_value=Response(status=HTTP_201_CREATED)): response: Response = self.client.post( @@ -1351,7 +1351,7 @@ def test_run_host_actions_success(self): self.assertEqual(response.status_code, HTTP_201_CREATED) def test_delete_host_success(self): - host: Host = self._create_host() + host: Host = self.create_host() response: Response = self.client.delete(path=reverse("host-details", kwargs={"host_id": host.pk})) From 79d14bcbf640872904333f010b5c1a11cd8aedd4 Mon Sep 17 00:00:00 2001 From: Aleksandr Alferov Date: Wed, 19 Apr 2023 19:42:15 +0000 Subject: [PATCH 6/9] ADCM-3769 Rework apply policy --- python/cm/api.py | 27 ++++++++------ python/cm/signals.py | 22 ++--------- python/cm/status_api.py | 1 - python/cm/upgrade.py | 21 +++++++---- python/job_runner.py | 3 ++ python/rbac/roles.py | 15 +++----- python/rbac/upgrade/role_spec.yaml | 60 ++++++++++++++++++++---------- 7 files changed, 81 insertions(+), 68 deletions(-) diff --git a/python/cm/api.py b/python/cm/api.py index 248589272a..e793e5d6c0 100644 --- a/python/cm/api.py +++ b/python/cm/api.py @@ -681,9 +681,10 @@ def save_hc(cluster, host_comp_list): # pylint: disable=too-many-locals hc_queryset = HostComponent.objects.filter(cluster=cluster).order_by("id") - service_map = {hc.service for hc in hc_queryset} + service_set = {hc.service for hc in hc_queryset} old_hosts = {i.host for i in hc_queryset.select_related("host")} new_hosts = {i[1] for i in host_comp_list} + for removed_host in old_hosts.difference(new_hosts): removed_host.remove_from_concerns(CTX.lock) @@ -692,6 +693,7 @@ def save_hc(cluster, host_comp_list): still_hc = still_existed_hc(cluster, host_comp_list) host_service_of_still_hc = {(hc.host, hc.service) for hc in still_hc} + for removed_hc in set(hc_queryset) - set(still_hc): groupconfigs = GroupConfig.objects.filter( object_type__model__in=["clusterobject", "servicecomponent"], @@ -706,32 +708,35 @@ def save_hc(cluster, host_comp_list): group_config.hosts.remove(removed_hc.host) hc_queryset.delete() - result = [] + host_component_list = [] + for proto, host, comp in host_comp_list: - hostcomponent = HostComponent( + host_component = HostComponent( cluster=cluster, service=proto, host=host, component=comp, ) - hostcomponent.save() - result.append(hostcomponent) + host_component.save() + host_component_list.append(host_component) CTX.event.send_state() post_event(event="change_hostcomponentmap", obj=cluster) update_hierarchy_issues(cluster) - for provider in [host.provider for host in Host.objects.filter(cluster=cluster)]: + + for provider in {host.provider for host in Host.objects.filter(cluster=cluster)}: update_hierarchy_issues(provider) update_issue_after_deleting() load_service_map() - for service in service_map: - re_apply_object_policy(service) - for hostcomponent in result: - re_apply_object_policy(hostcomponent.service) + for host_component_item in host_component_list: + service_set.add(host_component_item.service) - return result + for service in service_set: + re_apply_object_policy(apply_object=service) + + return host_component_list def add_hc(cluster, hc_in): diff --git a/python/cm/signals.py b/python/cm/signals.py index 01556356d7..a61c1b978c 100644 --- a/python/cm/signals.py +++ b/python/cm/signals.py @@ -13,7 +13,6 @@ import casestyle from audit.models import MODEL_TO_AUDIT_OBJECT_TYPE_MAP, AuditObject from audit.utils import mark_deleted_audit_object -from cm.logger import logger from cm.models import ( ADCM, ADCMEntity, @@ -109,16 +108,12 @@ def model_change(sender, **kwargs): if kwargs["raw"]: return - name, module, obj = get_names(sender, **kwargs) - if "filter_out" in kwargs: - if kwargs["filter_out"](module, name, obj): - return + _, module, obj = get_names(sender, **kwargs) action = "update" if kwargs.get("created"): action = "create" - logger.info("%s %s %s #%s", action, module, name, obj.pk) _post_event(action=action, module=module, obj=obj) @@ -128,15 +123,8 @@ def model_change(sender, **kwargs): @receiver(post_delete, sender=Role) @receiver(post_delete, sender=GroupConfig) def model_delete(sender, **kwargs): - name, module, obj = get_names(sender, **kwargs) - - if "filter_out" in kwargs: - if kwargs["filter_out"](module, name, obj): - return - - action = "delete" - logger.info("%s %s %s #%s", action, module, name, obj.pk) - _post_event(action=action, module=module, obj=obj) + _, module, obj = get_names(sender, **kwargs) + _post_event(action="delete", module=module, obj=obj) @receiver(m2m_changed, sender=GroupConfig) @@ -147,9 +135,6 @@ def model_delete(sender, **kwargs): @receiver(m2m_changed, sender=Group) def m2m_change(sender, **kwargs): name, module, obj = get_names(sender, **kwargs) - if "filter_out" in kwargs: - if kwargs["filter_out"](module, name, obj): - return if kwargs["action"] == "post_add": action = "add" @@ -158,5 +143,4 @@ def m2m_change(sender, **kwargs): else: return - logger.info("%s %s %s #%s", action, module, name, obj.pk) _post_event(action=action, module=module, obj=obj, model_name=name) diff --git a/python/cm/status_api.py b/python/cm/status_api.py index 3d3c7ef069..bf1f54005e 100644 --- a/python/cm/status_api.py +++ b/python/cm/status_api.py @@ -118,7 +118,6 @@ def post_event(event: str, obj, details: dict = None) -> Response | None: }, } - logger.debug("post_event %s", data) return api_request(method="post", url="event/", data=data) diff --git a/python/cm/upgrade.py b/python/cm/upgrade.py index e9e3903b4c..bae1a4a52b 100644 --- a/python/cm/upgrade.py +++ b/python/cm/upgrade.py @@ -48,8 +48,9 @@ Upgrade, ) from cm.status_api import post_event +from django.contrib.contenttypes.models import ContentType from django.db import transaction -from rbac.models import re_apply_object_policy +from rbac.models import Policy from version_utils import rpm @@ -308,18 +309,21 @@ def rpm_cmp_reverse(obj1, obj2): return res -def re_apply_following_policies(obj: Cluster | HostProvider) -> None: - re_apply_object_policy(apply_object=obj) +def re_apply_policy_for_upgrade(obj: Cluster | HostProvider) -> None: + obj_type_map = {obj: ContentType.objects.get_for_model(obj)} if isinstance(obj, Cluster): for service in ClusterObject.objects.filter(cluster=obj): - re_apply_object_policy(apply_object=service) + obj_type_map[service] = ContentType.objects.get_for_model(service) for component in ServiceComponent.objects.filter(cluster=obj, service=service): - re_apply_object_policy(apply_object=component) - + obj_type_map[component] = ContentType.objects.get_for_model(component) elif isinstance(obj, HostProvider): for host in Host.objects.filter(provider=obj): - re_apply_object_policy(apply_object=host) + obj_type_map[host] = ContentType.objects.get_for_model(host) + + for policy_object, content_type in obj_type_map.items(): + for policy in Policy.objects.filter(object__object_id=policy_object.id, object__content_type=content_type): + policy.apply() def update_components_after_bundle_switch(cluster: Cluster, upgrade: Upgrade) -> None: @@ -501,7 +505,6 @@ def do_upgrade( task_id = task.id obj.refresh_from_db() - re_apply_following_policies(obj=obj) return {"id": obj.id, "upgradable": bool(get_upgrade(obj)), "task_id": task_id} @@ -532,5 +535,7 @@ def bundle_switch(obj: Cluster | HostProvider, upgrade: Upgrade) -> None: if isinstance(obj, Cluster): update_components_after_bundle_switch(obj, upgrade) + obj.refresh_from_db() + re_apply_policy_for_upgrade(obj=obj) logger.info("upgrade %s OK to version %s", obj_ref(obj), obj.prototype.version) post_event(event="upgrade", obj=obj, details={"type": "version", "value": str(obj.prototype.version)}) diff --git a/python/job_runner.py b/python/job_runner.py index 0165556f81..c8e9a674dd 100755 --- a/python/job_runner.py +++ b/python/job_runner.py @@ -31,6 +31,7 @@ from cm.status_api import Event, post_event from cm.upgrade import bundle_revert, bundle_switch from cm.utils import get_env_with_venv_path +from rbac.roles import re_apply_policy_for_jobs def open_file(root, tag, job_id): @@ -184,6 +185,8 @@ def run_upgrade(job): bundle_revert(obj=job.task.task_object) switch_hc(task=job.task, action=job.action) + re_apply_policy_for_jobs(action_object=job.task.task_object, task=job.task) + except AdcmEx as e: err_file.write(e.msg) cm.job.set_job_status(job.id, JobStatus.FAILED, event) diff --git a/python/rbac/roles.py b/python/rbac/roles.py index 1a18c287e0..5e15685978 100644 --- a/python/rbac/roles.py +++ b/python/rbac/roles.py @@ -182,7 +182,7 @@ def get_perm_for_model(model, action: str = "view") -> Permission: return perm -def apply_jobs(task: TaskLog, policy: Policy, user: User, group: Group = None) -> None: +def apply_jobs(task: TaskLog, policy: Policy, user: User | None = None, group: Group | None = None) -> None: assign_user_or_group_perm(user=user, group=group, policy=policy, perm=get_perm_for_model(model=TaskLog), obj=task) assign_user_or_group_perm( user=user, @@ -242,7 +242,7 @@ def re_apply_policy_for_jobs(action_object, task): # noqa: C901 obj=action_object, ): policy.role.child.add(task_role) - apply_jobs(task=task, policy=policy, user=user, group=None) + apply_jobs(task=task, policy=policy, user=user) for group in policy.group.all(): try: @@ -261,7 +261,7 @@ def re_apply_policy_for_jobs(action_object, task): # noqa: C901 if group_obj_perm in policy.group_object_perm.all() and model_view_gop: policy.role.child.add(task_role) - apply_jobs(task=task, policy=policy, user=None, group=group) + apply_jobs(task=task, policy=policy, group=group) def apply_policy_for_new_config(config_object: ADCMEntity, config_log: ConfigLog) -> None: # noqa: C901 @@ -383,9 +383,6 @@ def apply( # noqa: C901 parametrized_by.update(set(child_role.parametrized_by_type)) for obj in policy.get_objects(param_obj=param_obj): - view_cluster_perm = Permission.objects.get(codename="view_cluster") - view_service_perm = Permission.objects.get(codename="view_clusterobject") - self.find_and_apply(obj=obj, policy=policy, role=role, user=user, group=group) if obj.prototype.type == "cluster": @@ -412,7 +409,7 @@ def apply( # noqa: C901 user=user, group=group, policy=policy, - perm=view_cluster_perm, + perm=Permission.objects.get(codename="view_cluster"), obj=obj.cluster, ) elif obj.prototype.type == "component": @@ -428,14 +425,14 @@ def apply( # noqa: C901 user=user, group=group, policy=policy, - perm=view_cluster_perm, + perm=Permission.objects.get(codename="view_cluster"), obj=obj.cluster, ) assign_user_or_group_perm( user=user, group=group, policy=policy, - perm=view_service_perm, + perm=Permission.objects.get(codename="view_clusterobject"), obj=obj.service, ) elif obj.prototype.type == "provider": diff --git a/python/rbac/upgrade/role_spec.yaml b/python/rbac/upgrade/role_spec.yaml index cb8129a644..0542079c7d 100644 --- a/python/rbac/upgrade/role_spec.yaml +++ b/python/rbac/upgrade/role_spec.yaml @@ -1,6 +1,6 @@ --- -version: 4 +version: 5 roles: - name: Add host @@ -1430,14 +1430,17 @@ roles: module_name: rbac.roles class_name: ParentRole child: - - Get service object + - Edit component config + - Edit config + - Edit service config - Get component object - Get host object - - View host configurations - - Edit service configurations - - Edit component configurations - - View host-components + - Get service object - Manage service imports + - View component config + - View host config + - View host-components hidden + - View service config - name: Cluster Administrator type: role @@ -1449,23 +1452,40 @@ roles: module_name: rbac.roles class_name: ParentRole child: - - Service Administrator - - Edit cluster configurations - - Edit host configurations - - Edit host-components + - Accept license + - Add bundle + - Add host + - Add host to provider + - Add service hidden + - Create service + - Delete host + - Edit cluster config + - Edit component config + - Edit config + - Edit host config + - Edit host-components hidden + - Edit service config + - Get cluster object + - Get component object + - Get host + - Get host object + - Get service object - Manage cluster imports - - Add service - - Remove service - - Remove hosts - - Map hosts - - Unmap hosts - - Upgrade cluster bundle - - Create host - - Upload bundle - - Remove bundle + - Manage component Maintenance mode - Manage host Maintenance mode hidden - Manage service Maintenance mode - - Manage component Maintenance mode + - Manage service imports + - Map hosts hidden + - Remove bundle + - Remove hosts hidden + - Remove service + - Unmap hosts hidden + - Upgrade cluster bundle + - View cluster config + - View component config + - View host config + - View host-components hidden + - View service config - name: Provider Administrator type: role From 99bcaabb7604f123d24bf6e6ca563a0edf250d35 Mon Sep 17 00:00:00 2001 From: Aleksandr Alferov Date: Thu, 20 Apr 2023 11:10:43 +0000 Subject: [PATCH 7/9] ADCM-3778 Add tests for add/remove host for Cluster Administrator role --- python/adcm/tests/base.py | 18 +- python/rbac/tests/files/provider_2.tar | Bin 10240 -> 0 bytes python/rbac/tests/test_policy.py | 1360 ----------------- python/rbac/tests/test_policy/__init__.py | 11 + python/rbac/tests/test_policy/base.py | 202 +++ .../test_policy_cluster_admin_role.py | 386 +++++ ...est_policy_no_right_user_have_no_access.py | 263 ++++ .../test_policy_provider_admin_role.py | 166 ++ .../tests/test_policy/test_policy_rbac.py | 503 ++++++ 9 files changed, 1545 insertions(+), 1364 deletions(-) delete mode 100644 python/rbac/tests/files/provider_2.tar delete mode 100644 python/rbac/tests/test_policy.py create mode 100644 python/rbac/tests/test_policy/__init__.py create mode 100644 python/rbac/tests/test_policy/base.py create mode 100644 python/rbac/tests/test_policy/test_policy_cluster_admin_role.py create mode 100644 python/rbac/tests/test_policy/test_policy_no_right_user_have_no_access.py create mode 100644 python/rbac/tests/test_policy/test_policy_provider_admin_role.py create mode 100644 python/rbac/tests/test_policy/test_policy_rbac.py diff --git a/python/adcm/tests/base.py b/python/adcm/tests/base.py index 829a823f0c..8b5c0f0242 100644 --- a/python/adcm/tests/base.py +++ b/python/adcm/tests/base.py @@ -51,11 +51,9 @@ def setUp(self) -> None: @classmethod def setUpClass(cls): - init_roles() + super().setUpClass() - @classmethod - def tearDownClass(cls): - ... + init_roles() def tearDown(self) -> None: dirs_to_clear = ( @@ -115,6 +113,18 @@ def another_user_logged_in(self, username: str, password: str): self.login() + def another_user_log_in(self, username: str, password: str): + self.client.post(path=reverse("rbac:logout")) + response: Response = self.client.post( + path=reverse("rbac:token"), + data={ + "username": username, + "password": password, + }, + content_type=APPLICATION_JSON, + ) + self.client.defaults["Authorization"] = f"Token {response.data['token']}" + def upload_bundle(self, path: Path) -> None: with open(path, encoding=settings.ENCODING_UTF_8) as f: response: Response = self.client.post( diff --git a/python/rbac/tests/files/provider_2.tar b/python/rbac/tests/files/provider_2.tar deleted file mode 100644 index c1cb689ede6c0ea31532feda72a31c0ca6b186f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10240 zcmeH{QE!4k499u)Q#3xz!~}Q81QQ80-VpQ=>!|ETXV zuIu&hw>azqFKxfiltbMXScl!_{cOx2_2B2U65Za|^^UihvzvCpK8N%kBzjez7dZz9 z(GnT!{O_VayFHFh05NEI1r@RZ!c^-6l{rj@*mN>}1+j&I5D)@FKnMr{As_^VfDjM@ oLO=)z0U;m+gn$qb0zyCt2mv7=1cZPP5CTF#2nYco@UIDc0;>?xMgRZ+ diff --git a/python/rbac/tests/test_policy.py b/python/rbac/tests/test_policy.py deleted file mode 100644 index 431ac9b2cc..0000000000 --- a/python/rbac/tests/test_policy.py +++ /dev/null @@ -1,1360 +0,0 @@ -# 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. - -# pylint: disable=too-many-lines - -from pathlib import Path -from unittest.mock import patch - -from cm import api -from cm.models import ( - Bundle, - Cluster, - ClusterObject, - ConfigLog, - Host, - HostComponent, - HostProvider, - MaintenanceMode, - ObjectType, - Prototype, - ServiceComponent, -) -from django.conf import settings -from django.db.models import ObjectDoesNotExist -from django.urls import reverse -from rbac.models import Group, Policy, User -from rbac.tests.test_base import RBACBaseTestCase -from rest_framework.response import Response -from rest_framework.status import ( - HTTP_200_OK, - HTTP_201_CREATED, - HTTP_204_NO_CONTENT, - HTTP_403_FORBIDDEN, - HTTP_404_NOT_FOUND, -) - -from adcm.tests.base import APPLICATION_JSON, BaseTestCase - - -class PolicyTestRBAC(RBACBaseTestCase): # pylint: disable=too-many-instance-attributes - """Tests for applying policy with different combination of roles and object""" - - def setUp(self) -> None: - super().setUp() - - self.user = User.objects.create(username="user", is_active=True, is_superuser=False) - self.cluster = Cluster.objects.create(name="Cluster_1", prototype=self.clp) - self.service_1 = ClusterObject.objects.create(cluster=self.cluster, prototype=self.sp_1) - self.service_2 = ClusterObject.objects.create(cluster=self.cluster, prototype=self.sp_2) - self.component_11 = ServiceComponent.objects.create( - cluster=self.cluster, - service=self.service_1, - prototype=self.cop_11, - ) - self.component_12 = ServiceComponent.objects.create( - cluster=self.cluster, - service=self.service_1, - prototype=self.cop_12, - ) - self.component_21 = ServiceComponent.objects.create( - cluster=self.cluster, - service=self.service_2, - prototype=self.cop_21, - ) - - def get_hosts_and_provider(self): - provider, _ = HostProvider.objects.get_or_create(name="provider", prototype=self.provider_prototype) - host1 = Host.objects.create(prototype=self.host_prototype, provider=provider, fqdn="host_1") - host2 = Host.objects.create(prototype=self.host_prototype, provider=provider, fqdn="host_2") - - return provider, host1, host2 - - @staticmethod - def clear_perm_cache(user): - if hasattr(user, "_perm_cache"): - delattr(user, "_perm_cache") - - if hasattr(user, "_user_perm_cache"): - delattr(user, "_user_perm_cache") - - if hasattr(user, "_group_perm_cache"): - delattr(user, "_group_perm_cache") - - def test_model_policy(self): - policy = Policy.objects.create(name="MyPolicy", role=self.model_role()) - policy.user.add(self.user) - - self.assertNotIn(self.add_host_perm, self.user.user_permissions.all()) - self.assertFalse(self.user.has_perm("cm.add_host")) - self.clear_perm_cache(self.user) - - policy.apply() - - self.assertIn(self.add_host_perm, self.user.user_permissions.all()) - self.assertTrue(self.user.has_perm("cm.add_host")) - - self.clear_perm_cache(self.user) - policy.apply() - - self.assertTrue(self.user.has_perm("cm.add_host")) - - def test_model_policy4group(self): - group = Group.objects.create(name="group") - group.user_set.add(self.user) - - policy = Policy.objects.create(name="MyPolicy", role=self.model_role()) - policy.group.add(group) - - self.assertNotIn(self.add_host_perm, group.permissions.all()) - self.assertFalse(self.user.has_perm("cm.add_host")) - - self.clear_perm_cache(self.user) - policy.apply() - - self.assertIn(self.add_host_perm, group.permissions.all()) - self.assertTrue(self.user.has_perm("cm.add_host")) - - self.clear_perm_cache(self.user) - policy.apply() - - self.assertTrue(self.user.has_perm("cm.add_host")) - - def test_object_policy(self): - cluster2 = Cluster.objects.create(name="Cluster_2", prototype=self.clp) - policy = Policy.objects.create(name="MyPolicy", role=self.object_role_view_perm_cluster()) - policy.user.add(self.user) - - self.assertFalse(self.user.has_perm("cm.view_cluster", self.cluster)) - self.assertFalse(self.user.has_perm("cm.view_cluster", cluster2)) - - policy.add_object(self.cluster) - policy.apply() - - self.assertTrue(self.user.has_perm("cm.view_cluster", self.cluster)) - self.assertFalse(self.user.has_perm("cm.view_cluster", cluster2)) - - def test_object_policy_remove_user(self): - cluster2 = Cluster.objects.create(name="Cluster_2", prototype=self.clp) - - policy = Policy.objects.create(name="MyPolicy", role=self.object_role()) - policy.user.add(self.user) - policy.add_object(self.cluster) - - self.assertFalse(self.user.has_perm("cm.view_cluster", self.cluster)) - - policy.apply() - - self.assertTrue(self.user.has_perm("cm.view_cluster", self.cluster)) - self.assertFalse(self.user.has_perm("cm.view_cluster", cluster2)) - - policy.user.remove(self.user) - policy.apply() - - self.assertFalse(self.user.has_perm("cm.view_cluster", self.cluster)) - self.assertFalse(self.user.has_perm("cm.view_cluster", cluster2)) - - def test_object_policy4group(self): - cluster2 = Cluster.objects.create(name="Cluster_2", prototype=self.clp) - group = Group.objects.create(name="group") - group.user_set.add(self.user) - - policy = Policy.objects.create(name="MyPolicy", role=self.object_role()) - policy.group.add(group) - - policy.add_object(self.cluster) - - self.assertFalse(self.user.has_perm("cm.view_cluster", self.cluster)) - - policy.apply() - - self.assertTrue(self.user.has_perm("cm.view_cluster", self.cluster)) - self.assertFalse(self.user.has_perm("cm.view_cluster", cluster2)) - - def test_parent_policy4cluster(self): - policy = Policy.objects.create(role=self.object_role_custom_perm_cluster_service_component()) - policy.user.add(self.user) - policy.add_object(self.cluster) - - self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) - self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) - self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_2)) - self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) - self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_21)) - - policy.apply() - - self.assertTrue(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) - self.assertTrue(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) - self.assertTrue(self.user.has_perm("cm.change_config_of_clusterobject", self.service_2)) - self.assertTrue(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) - self.assertTrue(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_21)) - - def test_parent_policy4service(self): - policy = Policy.objects.create(role=self.object_role_custom_perm_cluster_service_component()) - policy.user.add(self.user) - policy.add_object(self.service_1) - - self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) - self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) - self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_2)) - self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) - self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_21)) - - policy.apply() - - self.assertTrue(self.user.has_perm("cm.view_cluster", self.cluster)) - self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) - self.assertTrue(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) - self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_2)) - self.assertTrue(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) - self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_21)) - - def test_parent_policy4service2(self): - policy = Policy.objects.create(role=self.object_role_custom_perm_cluster_service_component()) - policy.user.add(self.user) - policy.add_object(self.service_2) - - self.assertFalse(self.user.has_perm("cm.view_cluster", self.cluster)) - self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) - self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) - self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_2)) - self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) - self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_21)) - - policy.apply() - - self.assertTrue(self.user.has_perm("cm.view_cluster", self.cluster)) - self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) - self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) - self.assertTrue(self.user.has_perm("cm.change_config_of_clusterobject", self.service_2)) - self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) - self.assertTrue(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_21)) - - def test_parent_policy4component(self): - policy = Policy.objects.create(role=self.object_role_custom_perm_cluster_service_component()) - policy.user.add(self.user) - policy.add_object(self.component_11) - - self.assertFalse(self.user.has_perm("cm.view_cluster", self.cluster)) - self.assertFalse(self.user.has_perm("cm.view_clusterobject", self.service_1)) - self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) - self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) - self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_2)) - self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) - self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_21)) - - policy.apply() - - self.assertTrue(self.user.has_perm("cm.view_cluster", self.cluster)) - self.assertTrue(self.user.has_perm("cm.view_clusterobject", self.service_1)) - self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) - self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) - self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_2)) - self.assertTrue(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) - self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_21)) - - def test_parent_policy4host_in_cluster(self): - provider, host1, host2 = self.get_hosts_and_provider() - host3 = Host.objects.create(provider=provider, prototype=self.host_prototype, fqdn="host_3") - api.add_host_to_cluster(self.cluster, host1) - api.add_host_to_cluster(self.cluster, host2) - policy = Policy.objects.create(role=self.object_role_custom_perm_cluster_host()) - policy.user.add(self.user) - policy.add_object(self.cluster) - - self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) - self.assertFalse(self.user.has_perm("cm.change_config_of_host", host1)) - self.assertFalse(self.user.has_perm("cm.change_config_of_host", host2)) - self.assertFalse(self.user.has_perm("cm.change_config_of_host", host3)) - - policy.apply() - - self.assertTrue(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) - self.assertTrue(self.user.has_perm("cm.change_config_of_host", host1)) - self.assertTrue(self.user.has_perm("cm.change_config_of_host", host2)) - self.assertFalse(self.user.has_perm("cm.change_config_of_host", host3)) - - def test_parent_policy4host_in_service(self): - _, host1, host2 = self.get_hosts_and_provider() - api.add_host_to_cluster(self.cluster, host1) - api.add_host_to_cluster(self.cluster, host2) - api.add_hc( - self.cluster, - [ - { - "service_id": self.service_1.id, - "component_id": self.component_11.id, - "host_id": host1.id, - }, - { - "service_id": self.service_2.id, - "component_id": self.component_21.id, - "host_id": host2.id, - }, - ], - ) - policy = Policy.objects.create(role=self.object_role_custom_perm_cluster_service_component_host()) - policy.user.add(self.user) - policy.add_object(self.service_1) - - self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) - self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) - self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_2)) - self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) - self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_21)) - self.assertFalse(self.user.has_perm("cm.change_config_of_host", host1)) - self.assertFalse(self.user.has_perm("cm.change_config_of_host", host2)) - - policy.apply() - - self.assertFalse(self.user.has_perm("cm.change_confing_of_cluster", self.cluster)) - self.assertTrue(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) - self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_2)) - self.assertTrue(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) - self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_21)) - self.assertTrue(self.user.has_perm("cm.change_config_of_host", host1)) - self.assertFalse(self.user.has_perm("cm.change_config_of_host", host2)) - - def test_parent_policy4host_in_component(self): - provider, host1, host2 = self.get_hosts_and_provider() - host3 = Host.objects.create(provider=provider, prototype=self.host_prototype, fqdn="host_3") - api.add_host_to_cluster(self.cluster, host1) - api.add_host_to_cluster(self.cluster, host2) - api.add_host_to_cluster(self.cluster, host3) - api.add_hc( - self.cluster, - [ - { - "service_id": self.service_2.id, - "component_id": self.component_21.id, - "host_id": host1.id, - }, - { - "service_id": self.service_2.id, - "component_id": self.component_21.id, - "host_id": host2.id, - }, - { - "service_id": self.service_1.id, - "component_id": self.component_11.id, - "host_id": host3.id, - }, - ], - ) - - policy = Policy.objects.create(role=self.object_role_custom_perm_cluster_service_component_host()) - policy.user.add(self.user) - policy.add_object(self.component_21) - - self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) - self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) - self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_2)) - self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) - self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_21)) - self.assertFalse(self.user.has_perm("cm.change_config_of_host", host1)) - self.assertFalse(self.user.has_perm("cm.change_config_of_host", host2)) - self.assertFalse(self.user.has_perm("cm.change_config_of_host", host3)) - - policy.apply() - - self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) - self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) - self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_2)) - self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) - self.assertTrue(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_21)) - self.assertTrue(self.user.has_perm("cm.change_config_of_host", host1)) - self.assertTrue(self.user.has_perm("cm.change_config_of_host", host2)) - self.assertFalse(self.user.has_perm("cm.change_config_of_host", host3)) - - def test_parent_policy4provider(self): - provider, host1, host2 = self.get_hosts_and_provider() - policy = Policy.objects.create(role=self.object_role_custom_perm_provider_host()) - policy.user.add(self.user) - policy.add_object(provider) - - self.assertFalse(self.user.has_perm("cm.change_config_of_hostprovider", provider)) - self.assertFalse(self.user.has_perm("cm.change_config_of_host", host1)) - self.assertFalse(self.user.has_perm("cm.change_config_of_host", host2)) - - policy.apply() - - self.assertTrue(self.user.has_perm("cm.change_config_of_hostprovider", provider)) - self.assertTrue(self.user.has_perm("cm.change_config_of_host", host1)) - self.assertTrue(self.user.has_perm("cm.change_config_of_host", host2)) - - def test_simple_parent_policy(self): - policy = Policy.objects.create(role=self.model_role_view_cluster_service_component_perm()) - policy.user.add(self.user) - - self.assertFalse(self.user.has_perm("cm.view_cluster")) - self.assertFalse(self.user.has_perm("cm.view_clusterobject")) - self.assertFalse(self.user.has_perm("cm.view_servicecomponent")) - - self.clear_perm_cache(self.user) - policy.apply() - - self.assertTrue(self.user.has_perm("cm.view_cluster")) - self.assertTrue(self.user.has_perm("cm.view_clusterobject")) - self.assertTrue(self.user.has_perm("cm.view_servicecomponent")) - - def test_add_service(self): - sp_3 = Prototype.obj.create(bundle=self.bundle_1, type="service", name="service_3") - - policy = Policy.objects.create(role=self.object_role_custom_perm_cluster_service()) - policy.user.add(self.user) - policy.add_object(self.cluster) - - self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) - self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) - self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_2)) - - policy.apply() - - self.assertTrue(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) - self.assertTrue(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) - self.assertTrue(self.user.has_perm("cm.change_config_of_clusterobject", self.service_2)) - - service3 = api.add_service_to_cluster(self.cluster, sp_3) - - self.assertTrue(self.user.has_perm("cm.change_config_of_clusterobject", service3)) - - def test_add_host(self): - _, host1, host2 = self.get_hosts_and_provider() - api.add_host_to_cluster(self.cluster, host1) - api.add_hc( - self.cluster, - [ - { - "service_id": self.service_1.id, - "component_id": self.component_11.id, - "host_id": host1.id, - }, - ], - ) - - policy = Policy.objects.create(role=self.object_role_custom_perm_cluster_service_component_host()) - policy.user.add(self.user) - policy.add_object(self.cluster) - - self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) - self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) - self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) - self.assertFalse(self.user.has_perm("cm.change_config_of_host", host1)) - self.assertFalse(self.user.has_perm("cm.change_config_of_host", host2)) - - policy.apply() - - self.assertTrue(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) - self.assertTrue(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) - self.assertTrue(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) - self.assertTrue(self.user.has_perm("cm.change_config_of_host", host1)) - self.assertFalse(self.user.has_perm("cm.change_config_of_host", host2)) - - api.add_host_to_cluster(self.cluster, host2) - - self.assertTrue(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) - self.assertTrue(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) - self.assertTrue(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) - self.assertTrue(self.user.has_perm("cm.change_config_of_host", host1)) - self.assertTrue(self.user.has_perm("cm.change_config_of_host", host2)) - - def test_add_hc(self): - _, host1, host2 = self.get_hosts_and_provider() - api.add_host_to_cluster(self.cluster, host1) - api.add_hc( - self.cluster, - [ - { - "service_id": self.service_1.id, - "component_id": self.component_11.id, - "host_id": host1.id, - }, - ], - ) - policy = Policy.objects.create(role=self.object_role_custom_perm_service_component_host()) - policy.user.add(self.user) - policy.add_object(self.service_1) - - self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) - self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) - self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) - self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_12)) - self.assertFalse(self.user.has_perm("cm.change_config_of_host", host1)) - self.assertFalse(self.user.has_perm("cm.change_config_of_host", host2)) - - policy.apply() - - self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) - self.assertTrue(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) - self.assertTrue(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) - self.assertTrue(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_12)) - self.assertTrue(self.user.has_perm("cm.change_config_of_host", host1)) - self.assertFalse(self.user.has_perm("cm.change_config_of_host", host2)) - - api.add_host_to_cluster(self.cluster, host2) - api.add_hc( - self.cluster, - [ - { - "service_id": self.service_1.id, - "component_id": self.component_11.id, - "host_id": host1.id, - }, - { - "service_id": self.service_1.id, - "component_id": self.component_12.id, - "host_id": host2.id, - }, - ], - ) - - self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) - self.assertTrue(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) - self.assertTrue(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) - self.assertTrue(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_12)) - self.assertTrue(self.user.has_perm("cm.change_config_of_host", host1)) - self.assertTrue(self.user.has_perm("cm.change_config_of_host", host2)) - - -class TestPolicyWithClusterAdminRole(BaseTestCase): - def setUp(self) -> None: - super().setUp() - - files_dir = settings.BASE_DIR / "python" / "rbac" / "tests" / "files" - provider_bundle_filename = files_dir / "provider.tar" - cluster_bundle_filename = files_dir / "test_cluster_for_cluster_admin_role.tar" - - provider = self.make_adcm_entity( - entity_type=ObjectType.PROVIDER, bundle_filename=provider_bundle_filename, name="Test Provider" - ) - self.cluster = self.make_adcm_entity( - entity_type=ObjectType.CLUSTER, bundle_filename=cluster_bundle_filename, name="Test Cluster" - ) - - self.service_6_proto = Prototype.objects.get( - bundle=Bundle.objects.get(name="test_cluster_for_cluster_admin_role"), - name="service_6_manual_add", - type=ObjectType.SERVICE, - ) - - self.host_pks = self.make_hosts(num=5, provider_id=provider.pk, cluster_id=self.cluster.pk) - self.service_pks = self.make_services(cluster_id=self.cluster.pk) - self.assertEqual(len(self.host_pks), len(self.service_pks)) - - self.component_pks = self.save_hc_map(host_pks=self.host_pks, service_pks=self.service_pks) - - def save_hc_map(self, host_pks: list[int], service_pks: list[int]) -> list[int]: - component_pks = [] - hc_data = [] - - for host_pk, service_pk in zip(host_pks, service_pks): - for component in ServiceComponent.objects.filter(service=ClusterObject.objects.get(pk=service_pk)): - component_pks.append(component.pk) - hc_data.append({"component_id": component.pk, "host_id": host_pk, "service_id": service_pk}) - - response: Response = self.client.post( - path=reverse(viewname="host-component", kwargs={"cluster_id": self.cluster.pk}), - data={"cluster_id": self.cluster.pk, "hc": hc_data}, - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_201_CREATED) - - return component_pks - - def make_adcm_entity(self, entity_type: ObjectType, bundle_filename: Path, name: str) -> Cluster | HostProvider: - if entity_type == ObjectType.CLUSTER: - model = Cluster - viewname = "cluster" - elif entity_type == ObjectType.PROVIDER: - model = HostProvider - viewname = "provider" - else: - raise NotImplementedError - - bundle = self.upload_and_load_bundle(path=bundle_filename) - - response: Response = self.client.post( - path=reverse(viewname=viewname), - data={ - "prototype_id": Prototype.objects.get(bundle=bundle, type=entity_type).pk, - "name": name, - "display_name": name, - "bundle_id": bundle.pk, - }, - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_201_CREATED) - - return model.objects.get(pk=response.json()["id"]) - - def get_role_request_data(self, role_display_name: str) -> dict | None: - response: Response = self.client.get( - path=reverse(viewname="rbac:role-list"), - data={"ordering": "name", "type": "role", "view": "interface"}, - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_200_OK) - - target_role_data = None - for role_data in response.json()["results"]: - if role_data["display_name"] == role_display_name: - target_role_data = role_data - break - - return target_role_data - - def get_user_request_data(self, username: str) -> dict | None: - response: Response = self.client.get( - path=reverse(viewname="rbac:user-list"), - data={"ordering": "username", "view": "interface"}, - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_200_OK) - - target_user_data = None - for user_data in response.json()["results"]: - if user_data["username"] == username: - target_user_data = user_data - break - - return target_user_data - - def apply_policy(self, role_display_name: str, username: str) -> None: - role_data = self.get_role_request_data(role_display_name=role_display_name) - self.assertIsNotNone(role_data) - - user_data = self.get_user_request_data(username=username) - self.assertIsNotNone(user_data) - - policy_request_data = { - "name": "test_policy_cluster_admin", - "role": {"id": role_data["id"]}, - "user": [ - {"id": user_data["id"]}, - ], - "group": [], - "object": [ - {"name": self.cluster.name, "type": "cluster", "id": self.cluster.pk}, - ], - } - response: Response = self.client.post( - path=reverse(viewname="rbac:policy-list"), - data=policy_request_data, - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_201_CREATED) - - def make_hosts(self, num: int, provider_id: int, cluster_id: int | None = None) -> list[int]: - host_pks = [] - - for host_num in range(num): - fqdn = f"host-{host_num}" - - response: Response = self.client.post( - path=reverse(viewname="host", kwargs={"provider_id": provider_id}), - data={ - "fqdn": fqdn, - }, - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_201_CREATED) - host_id = response.json()["id"] - host_pks.append(host_id) - - if not cluster_id: - continue - - response: Response = self.client.post( - path=reverse(viewname="host", kwargs={"cluster_id": cluster_id}), - data={ - "host_id": host_id, - }, - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_201_CREATED) - - return host_pks - - def make_services(self, cluster_id: int) -> list[int]: - service_pks = [] - - service_proto_pks = ( - Prototype.objects.filter( - bundle=Bundle.objects.get(name="test_cluster_for_cluster_admin_role"), type=ObjectType.SERVICE - ) - .exclude(pk=self.service_6_proto.pk) - .order_by("name") - .values_list("pk", flat=True) - ) - for service_proto_pk in service_proto_pks: - response = self.client.post( - path=reverse(viewname="service", kwargs={"cluster_id": cluster_id}), - data={ - "prototype_id": service_proto_pk, - }, - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_201_CREATED) - service_pks.append(response.json()["id"]) - - return service_pks - - def test_view_perm_for_cluster_and_all_descendants_success(self): - # pylint: disable=too-many-statements - with self.no_rights_user_logged_in: - response: Response = self.client.get( - path=reverse(viewname="cluster-details", kwargs={"cluster_id": self.cluster.pk}), - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) - - response: Response = self.client.get( - path=reverse(viewname="service-details", kwargs={"service_id": self.service_pks[0]}), - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) - - response: Response = self.client.get( - path=reverse(viewname="component-details", kwargs={"component_id": self.component_pks[0]}), - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) - - response: Response = self.client.get( - path=reverse(viewname="host-details", kwargs={"host_id": self.host_pks[0]}), - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) - - response: Response = self.client.get( - path=reverse(viewname="host", kwargs={"cluster_id": self.cluster.pk}), - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) - - response: Response = self.client.get( - path=reverse( - viewname="config-current", - kwargs={"cluster_id": self.cluster.pk, "object_type": "cluster", "version": "current"}, - ), - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) - - response: Response = self.client.get( - path=reverse( - viewname="config-current", - kwargs={ - "cluster_id": self.cluster.pk, - "service_id": self.service_pks[0], - "object_type": "service", - "version": "current", - }, - ), - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) - - response: Response = self.client.get( - path=reverse( - viewname="config-current", - kwargs={"component_id": self.component_pks[0], "object_type": "component", "version": "current"}, - ), - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) - - response: Response = self.client.get( - path=reverse( - viewname="object-action", kwargs={"cluster_id": self.cluster.pk, "object_type": "cluster"} - ), - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_200_OK) - self.assertEqual(response.json(), []) - - response: Response = self.client.get( - path=reverse( - viewname="object-action", kwargs={"service_id": self.service_pks[0], "object_type": "service"} - ), - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_200_OK) - self.assertEqual(response.json(), []) - - response: Response = self.client.get( - path=reverse( - viewname="object-action", kwargs={"component_id": self.component_pks[0], "object_type": "component"} - ), - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_200_OK) - self.assertEqual(response.json(), []) - - self.apply_policy(role_display_name="Cluster Administrator", username=self.no_rights_user_username) - - with self.no_rights_user_logged_in: - response: Response = self.client.get( - path=reverse(viewname="cluster-details", kwargs={"cluster_id": self.cluster.pk}), - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_200_OK) - - response: Response = self.client.get( - path=reverse(viewname="service-details", kwargs={"service_id": self.service_pks[0]}), - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_200_OK) - - response: Response = self.client.get( - path=reverse(viewname="component-details", kwargs={"component_id": self.component_pks[0]}), - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_200_OK) - - response: Response = self.client.get( - path=reverse(viewname="host-details", kwargs={"host_id": self.host_pks[0]}), - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_200_OK) - - response: Response = self.client.get( - path=reverse(viewname="host", kwargs={"cluster_id": self.cluster.pk}), - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_200_OK) - - response: Response = self.client.get( - path=reverse( - viewname="config-current", - kwargs={"cluster_id": self.cluster.pk, "object_type": "cluster", "version": "current"}, - ), - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_200_OK) - - response: Response = self.client.get( - path=reverse( - viewname="config-current", - kwargs={ - "cluster_id": self.cluster.pk, - "service_id": self.service_pks[0], - "object_type": "service", - "version": "current", - }, - ), - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_200_OK) - - response: Response = self.client.get( - path=reverse( - viewname="config-current", - kwargs={"component_id": self.component_pks[0], "object_type": "component", "version": "current"}, - ), - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_200_OK) - - response: Response = self.client.get( - path=reverse( - viewname="object-action", kwargs={"cluster_id": self.cluster.pk, "object_type": "cluster"} - ), - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_200_OK) - self.assertIsInstance(response.json(), list) - self.assertTrue(len(response.json()) > 0) - - response: Response = self.client.get( - path=reverse( - viewname="object-action", kwargs={"service_id": self.service_pks[0], "object_type": "service"} - ), - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_200_OK) - self.assertIsInstance(response.json(), list) - self.assertTrue(len(response.json()) > 0) - - response: Response = self.client.get( - path=reverse( - viewname="object-action", kwargs={"component_id": self.component_pks[0], "object_type": "component"} - ), - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_200_OK) - self.assertIsInstance(response.json(), list) - self.assertTrue(len(response.json()) > 0) - - def test_edit_perm_for_cluster_and_all_descendants_success(self): - initial_hc_count = HostComponent.objects.count() - - with self.no_rights_user_logged_in: - response: Response = self.client.post( - path=reverse(viewname="host-component", kwargs={"cluster_id": self.cluster.pk}), - data={ - "cluster_id": self.cluster.pk, - "hc": [ - { - "component_id": self.component_pks[0], - "host_id": self.host_pks[0], - "service_id": self.service_pks[0], - } - ], - }, - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) - - response: Response = self.client.post( - path=reverse( - viewname="config-history", kwargs={"cluster_id": self.cluster.pk, "object_type": "cluster"} - ), - data={"attr": {}, "config": {"float": 3.3}}, - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_403_FORBIDDEN) - - response: Response = self.client.post( - path=reverse( - viewname="config-history", - kwargs={"cluster_id": self.cluster.pk, "service_id": self.service_pks[0], "object_type": "service"}, - ), - data={"attr": {}, "config": {"float": 3.3}}, - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_403_FORBIDDEN) - - response: Response = self.client.post( - path=reverse( - viewname="config-history", - kwargs={"component_id": self.component_pks[0], "object_type": "component"}, - ), - data={"attr": {}, "config": {"float": 3.3}}, - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_403_FORBIDDEN) - - response: Response = self.client.post( - path=reverse( - viewname="config-history", - kwargs={"host_id": self.host_pks[0], "object_type": "host"}, - ), - data={"attr": {}, "config": {"string": "new_srting"}}, - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_403_FORBIDDEN) - - response: Response = self.client.post( - path=reverse( - viewname="config-history", - kwargs={"cluster_id": self.cluster.pk, "host_id": self.host_pks[0], "object_type": "host"}, - ), - data={"attr": {}, "config": {"string": "new_srting"}}, - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_403_FORBIDDEN) - - response = self.client.post( - path=reverse(viewname="service", kwargs={"cluster_id": self.cluster.pk}), - data={ - "prototype_id": self.service_6_proto.pk, - }, - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_403_FORBIDDEN) - - response = self.client.delete( - path=reverse( - viewname="service-details", - kwargs={"cluster_id": self.cluster.pk, "service_id": self.service_pks[-1]}, - ), - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) - - response = self.client.post( - path=reverse(viewname="service-maintenance-mode", kwargs={"service_id": self.service_pks[0]}), - data={ - "maintenance_mode": MaintenanceMode.ON, - }, - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) - - response = self.client.post( - path=reverse(viewname="component-maintenance-mode", kwargs={"component_id": self.component_pks[0]}), - data={ - "maintenance_mode": MaintenanceMode.ON, - }, - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) - - response = self.client.post( - path=reverse(viewname="host-maintenance-mode", kwargs={"host_id": self.host_pks[0]}), - data={ - "maintenance_mode": MaintenanceMode.ON, - }, - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) - - self.apply_policy(role_display_name="Cluster Administrator", username=self.no_rights_user_username) - - with self.no_rights_user_logged_in: - response: Response = self.client.post( - path=reverse(viewname="host-component", kwargs={"cluster_id": self.cluster.pk}), - data={ - "cluster_id": self.cluster.pk, - "hc": [ - { - "component_id": self.component_pks[0], - "host_id": self.host_pks[0], - "service_id": self.service_pks[0], - } - ], - }, - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_201_CREATED) - self.assertTrue(initial_hc_count != HostComponent.objects.count()) - - response: Response = self.client.post( - path=reverse( - viewname="config-history", kwargs={"cluster_id": self.cluster.pk, "object_type": "cluster"} - ), - data={"attr": {}, "config": {"float": 3.3}}, - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_201_CREATED) - - response: Response = self.client.post( - path=reverse( - viewname="config-history", - kwargs={"cluster_id": self.cluster.pk, "service_id": self.service_pks[0], "object_type": "service"}, - ), - data={"attr": {}, "config": {"float": 3.3}}, - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_201_CREATED) - - response: Response = self.client.post( - path=reverse( - viewname="config-history", - kwargs={"component_id": self.component_pks[0], "object_type": "component"}, - ), - data={"attr": {}, "config": {"float": 3.3}}, - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_201_CREATED) - - response: Response = self.client.post( - path=reverse( - viewname="config-history", - kwargs={"host_id": self.host_pks[0], "object_type": "host"}, - ), - data={"attr": {}, "config": {"string": "new_srting"}}, - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_201_CREATED) - - response: Response = self.client.post( - path=reverse( - viewname="config-history", - kwargs={"cluster_id": self.cluster.pk, "host_id": self.host_pks[0], "object_type": "host"}, - ), - data={"attr": {}, "config": {"string": "new_srting"}}, - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_201_CREATED) - - response = self.client.post( - path=reverse(viewname="service", kwargs={"cluster_id": self.cluster.pk}), - data={ - "prototype_id": self.service_6_proto.pk, - }, - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_201_CREATED) - - response = self.client.delete( - path=reverse( - viewname="service-details", - kwargs={"cluster_id": self.cluster.pk, "service_id": self.service_pks[-1]}, - ), - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_204_NO_CONTENT) - - response = self.client.post( - path=reverse(viewname="service-maintenance-mode", kwargs={"service_id": self.service_pks[0]}), - data={ - "maintenance_mode": MaintenanceMode.ON, - }, - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_200_OK) - - response = self.client.post( - path=reverse(viewname="component-maintenance-mode", kwargs={"component_id": self.component_pks[0]}), - data={ - "maintenance_mode": MaintenanceMode.ON, - }, - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_200_OK) - - response = self.client.post( - path=reverse(viewname="host-maintenance-mode", kwargs={"host_id": self.host_pks[0]}), - data={ - "maintenance_mode": MaintenanceMode.ON, - }, - content_type=APPLICATION_JSON, - ) - self.assertEqual(response.status_code, HTTP_200_OK) - - -class TestPolicyWithProviderAdminRole(BaseTestCase): - def setUp(self) -> None: - super().setUp() - - self.new_user = self.get_new_user() - self.provider = self.get_provider() - self.create_policy() - - def get_provider(self) -> HostProvider: - bundle = self.upload_and_load_bundle( - path=settings.BASE_DIR / "python" / "rbac" / "tests" / "files" / "provider_2.tar", - ) - - response: Response = self.client.post( - path=reverse(viewname="provider"), - data={ - "prototype_id": Prototype.objects.get(bundle=bundle, type=ObjectType.PROVIDER).pk, - "name": "Test Provider", - "display_name": "Test Provider", - "bundle_id": bundle.pk, - }, - content_type=APPLICATION_JSON, - ) - - self.assertEqual(response.status_code, HTTP_201_CREATED) - - return HostProvider.objects.get(pk=response.json()["id"]) - - def get_new_user(self) -> User: - response: Response = self.client.post( - path=reverse(viewname="rbac:user-list"), - data={"username": "new_user", "password": "new_user_password"}, - content_type=APPLICATION_JSON, - ) - - self.assertEqual(response.status_code, HTTP_201_CREATED) - - return User.objects.get(pk=response.json()["id"]) - - def get_role_pk(self) -> int: - response: Response = self.client.get( - path=reverse(viewname="rbac:role-list"), - data={"ordering": "name", "type": "role", "view": "interface"}, - content_type=APPLICATION_JSON, - ) - - self.assertEqual(response.status_code, HTTP_200_OK) - - return [ - role_data["id"] for role_data in response.json()["results"] if role_data["name"] == "Provider Administrator" - ][0] - - def create_policy(self) -> None: - response: Response = self.client.post( - path=reverse(viewname="rbac:policy-list"), - data={ - "name": "test_policy_provider_admin", - "role": {"id": self.get_role_pk()}, - "user": [{"id": self.new_user.pk}], - "group": [], - "object": [{"name": self.provider.name, "type": "provider", "id": self.provider.pk}], - }, - content_type=APPLICATION_JSON, - ) - - self.assertEqual(response.status_code, HTTP_201_CREATED) - - def retrieve_provider_action(self) -> Response: - response: Response = self.client.get( - path=reverse(viewname="object-action", kwargs={"provider_id": self.provider.pk}), - ) - - self.assertEqual(response.status_code, HTTP_200_OK) - self.assertEqual(len(response.data), 1) - - return response - - def create_host(self) -> Host: - response: Response = self.client.post( - path=reverse("host", kwargs={"provider_id": self.provider.pk}), - data={"fqdn": "test-host"}, - ) - - self.assertEqual(response.status_code, HTTP_201_CREATED) - - return Host.objects.get(pk=response.data["id"]) - - def retrieve_host_action(self, host_pk: int) -> Response: - response: Response = self.client.get( - path=reverse(viewname="object-action", kwargs={"host_id": host_pk}), - ) - - self.assertEqual(response.status_code, HTTP_200_OK) - self.assertEqual(len(response.data), 1) - - return response - - def test_policy_add_required_perms_success(self): - required_perms = {perm.codename for perm in self.new_user.user_permissions.all()} - required_perms.update({perm.permission.codename for perm in self.new_user.userobjectpermission_set.all()}) - - self.assertEqual( - required_perms, - { - "delete_bundle", - "add_groupconfig", - "change_objectconfig", - "add_bundle", - "change_groupconfig", - "add_configlog", - "delete_groupconfig", - "add_prototype", - "change_bundle", - "change_configlog", - "add_host", - "view_configlog", - "view_hostprovider", - "do_upgrade_of_hostprovider", - "change_config_of_hostprovider", - "view_action", - "view_upgrade_of_hostprovider", - "view_objectconfig", - "add_host_to_hostprovider", - "run_action_bd938c688f49b77c7fc537c6b9222e2c97ebddd63076b87f2feaec66fb9c05d0", - }, - ) - - def test_retrieve_provider_success(self): - response: Response = self.client.get( - path=reverse(viewname="provider-details", kwargs={"provider_id": self.provider.pk}), - ) - - self.assertEqual(response.status_code, HTTP_200_OK) - self.assertEqual(response.data["name"], self.provider.name) - - def test_retrieve_provider_config_success(self): - response: Response = self.client.get( - path=reverse(viewname="object-config", kwargs={"provider_id": self.provider.pk}), - ) - - self.assertEqual(response.status_code, HTTP_200_OK) - - def test_update_provider_config_success(self): - new_string = "new_string" - - response: Response = self.client.post( - path=reverse(viewname="config-history", kwargs={"provider_id": self.provider.pk}), - data={"config": {"string": new_string}}, - content_type=APPLICATION_JSON, - ) - - self.provider.refresh_from_db() - config_log = ConfigLog.objects.get(pk=self.provider.config.current) - - self.assertEqual(response.status_code, HTTP_201_CREATED) - self.assertEqual(config_log.config["string"], new_string) - - def test_retrieve_provider_actions_success(self): - self.retrieve_provider_action() - - def test_run_provider_actions_success(self): - response: Response = self.retrieve_provider_action() - - with patch("api.action.views.create", return_value=Response(status=HTTP_201_CREATED)): - response: Response = self.client.post( - path=reverse( - viewname="run-task", - kwargs={"provider_id": self.provider.pk, "action_id": response.data[0]["id"]}, - ), - content_type=APPLICATION_JSON, - ) - - self.assertEqual(response.status_code, HTTP_201_CREATED) - - def test_create_host_success(self): - self.create_host() - - def test_retrieve_host_success(self): - host: Host = self.create_host() - response: Response = self.client.get(path=reverse("host-details", kwargs={"host_id": host.pk})) - - self.assertEqual(response.status_code, HTTP_200_OK) - self.assertEqual(response.data["id"], host.pk) - - def test_retrieve_host_config_success(self): - response: Response = self.client.get( - path=reverse(viewname="object-config", kwargs={"host_id": self.create_host().pk}), - ) - - self.assertEqual(response.status_code, HTTP_200_OK) - - def test_update_host_config_success(self): - host: Host = self.create_host() - new_string = "new_string" - - response: Response = self.client.post( - path=reverse(viewname="config-history", kwargs={"host_id": host.pk}), - data={"config": {"string": new_string}}, - content_type=APPLICATION_JSON, - ) - - host.refresh_from_db() - config_log = ConfigLog.objects.get(pk=host.config.current) - - self.assertEqual(response.status_code, HTTP_201_CREATED) - self.assertEqual(config_log.config["string"], new_string) - - def test_retrieve_host_actions_success(self): - self.retrieve_host_action(host_pk=self.create_host().pk) - - def test_run_host_actions_success(self): - host: Host = self.create_host() - response: Response = self.retrieve_host_action(host_pk=host.pk) - - with patch("api.action.views.create", return_value=Response(status=HTTP_201_CREATED)): - response: Response = self.client.post( - path=reverse( - viewname="run-task", - kwargs={"host_id": host.pk, "action_id": response.data[0]["id"]}, - ), - content_type=APPLICATION_JSON, - ) - - self.assertEqual(response.status_code, HTTP_201_CREATED) - - def test_delete_host_success(self): - host: Host = self.create_host() - - response: Response = self.client.delete(path=reverse("host-details", kwargs={"host_id": host.pk})) - - self.assertEqual(response.status_code, HTTP_204_NO_CONTENT) - with self.assertRaises(ObjectDoesNotExist): - host.refresh_from_db() diff --git a/python/rbac/tests/test_policy/__init__.py b/python/rbac/tests/test_policy/__init__.py new file mode 100644 index 0000000000..824dd6c8fe --- /dev/null +++ b/python/rbac/tests/test_policy/__init__.py @@ -0,0 +1,11 @@ +# 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. diff --git a/python/rbac/tests/test_policy/base.py b/python/rbac/tests/test_policy/base.py new file mode 100644 index 0000000000..3e74fd7066 --- /dev/null +++ b/python/rbac/tests/test_policy/base.py @@ -0,0 +1,202 @@ +# 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 cm.models import ( + Bundle, + Cluster, + ClusterObject, + Host, + HostProvider, + ObjectType, + Prototype, + ServiceComponent, +) +from django.conf import settings +from django.urls import reverse +from rbac.models import User +from rest_framework.response import Response +from rest_framework.status import HTTP_200_OK, HTTP_201_CREATED + +from adcm.tests.base import APPLICATION_JSON, BaseTestCase + + +class PolicyBaseTestCase(BaseTestCase): # pylint: disable=too-many-instance-attributes + def setUp(self) -> None: + super().setUp() + + self.new_user_password = "new_user_password" + self.new_user = self.get_new_user(password=self.new_user_password) + self.cluster = self.create_cluster() + self.provider = self.get_provider() + host_ids = self.create_hosts() + self.first_host_pk = host_ids[0] + self.last_host_pk = host_ids[-1] + self.add_hosts_to_cluster(host_ids=host_ids) + self.service_6_proto = Prototype.objects.get( + bundle=Bundle.objects.get(name="test_cluster_for_cluster_admin_role"), + name="service_6_manual_add", + type=ObjectType.SERVICE, + ) + service_ids = self.get_services() + self.last_service_pk = service_ids[-1] + self.host_component = self.get_host_components() + self.last_component_pk = self.host_component[-1]["component_id"] + + def create_cluster(self) -> Cluster: + bundle = self.upload_and_load_bundle( + path=settings.BASE_DIR / "python" / "rbac" / "tests" / "files" / "test_cluster_for_cluster_admin_role.tar" + ) + + response: Response = self.client.post( + path=reverse(viewname="cluster"), + data={ + "prototype_id": Prototype.objects.get(bundle=bundle, type=ObjectType.CLUSTER).pk, + "name": "Test Cluster", + "display_name": "Test Cluster", + "bundle_id": bundle.pk, + }, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_201_CREATED) + + return Cluster.objects.get(pk=response.json()["id"]) + + def create_hosts(self) -> list[int]: + host_ids = [] + + for host_num in range(5): + fqdn = f"host-{host_num}" + + response: Response = self.client.post( + path=reverse(viewname="host", kwargs={"provider_id": self.provider.pk}), + data={"fqdn": fqdn}, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_201_CREATED) + host_ids.append(response.json()["id"]) + + return host_ids + + def add_hosts_to_cluster(self, host_ids: list[int]) -> None: + for host_id in host_ids: + response: Response = self.client.post( + path=reverse(viewname="host", kwargs={"cluster_id": self.cluster.pk}), + data={"host_id": host_id}, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_201_CREATED) + + def get_services(self) -> list[int]: + service_proto_pks = ( + Prototype.objects.filter( + bundle=Bundle.objects.get(name="test_cluster_for_cluster_admin_role"), type=ObjectType.SERVICE + ) + .exclude(pk=self.service_6_proto.pk) + .order_by("name") + .values_list("pk", flat=True) + ) + service_ids = [] + + for service_proto_pk in service_proto_pks: + response = self.client.post( + path=reverse(viewname="service", kwargs={"cluster_id": self.cluster.pk}), + data={"prototype_id": service_proto_pk}, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_201_CREATED) + service_ids.append(response.json()["id"]) + + return service_ids + + def get_host_components(self) -> list[dict]: + host_pks = [host.pk for host in Host.objects.all()] + services = list(ClusterObject.objects.all()) + hc_data = [] + + for host_pk, service in zip(host_pks, services): + for component in ServiceComponent.objects.filter(service=service): + hc_data.append({"component_id": component.pk, "host_id": host_pk, "service_id": service.pk}) + + response: Response = self.client.post( + path=reverse(viewname="host-component", kwargs={"cluster_id": self.cluster.pk}), + data={"cluster_id": self.cluster.pk, "hc": hc_data}, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_201_CREATED) + + return hc_data + + def get_provider(self) -> HostProvider: + bundle = self.upload_and_load_bundle( + path=settings.BASE_DIR / "python" / "rbac" / "tests" / "files" / "provider.tar", + ) + + response: Response = self.client.post( + path=reverse(viewname="provider"), + data={ + "prototype_id": Prototype.objects.get(bundle=bundle, type=ObjectType.PROVIDER).pk, + "name": "Test Provider", + "display_name": "Test Provider", + "bundle_id": bundle.pk, + }, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_201_CREATED) + + return HostProvider.objects.get(pk=response.json()["id"]) + + def get_new_user(self, password: str) -> User: + response: Response = self.client.post( + path=reverse(viewname="rbac:user-list"), + data={"username": "new_user", "password": password}, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_201_CREATED) + + return User.objects.get(pk=response.json()["id"]) + + def get_role_data(self, role_name: str) -> dict: + response: Response = self.client.get( + path=reverse(viewname="rbac:role-list"), + data={"ordering": "name", "type": "role", "view": "interface"}, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + + return [role_data for role_data in response.json()["results"] if role_data["name"] == role_name][0] + + def create_policy(self, role_name: str) -> None: + role_data = self.get_role_data(role_name=role_name) + object_type = role_data["parametrized_by_type"][0] + obj = getattr(self, object_type) + + response: Response = self.client.post( + path=reverse(viewname="rbac:policy-list"), + data={ + "name": f"test_policy_{object_type}_admin", + "role": {"id": role_data["id"]}, + "user": [{"id": self.new_user.pk}], + "group": [], + "object": [{"name": obj.name, "type": object_type, "id": obj.pk}], + }, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_201_CREATED) diff --git a/python/rbac/tests/test_policy/test_policy_cluster_admin_role.py b/python/rbac/tests/test_policy/test_policy_cluster_admin_role.py new file mode 100644 index 0000000000..3367436d4b --- /dev/null +++ b/python/rbac/tests/test_policy/test_policy_cluster_admin_role.py @@ -0,0 +1,386 @@ +# 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 unittest.mock import patch + +from cm.models import Action, Host, MaintenanceMode +from django.urls import reverse +from rbac.tests.test_policy.base import PolicyBaseTestCase +from rest_framework.response import Response +from rest_framework.status import ( + HTTP_200_OK, + HTTP_201_CREATED, + HTTP_204_NO_CONTENT, + HTTP_403_FORBIDDEN, + HTTP_404_NOT_FOUND, +) + +from adcm.tests.base import APPLICATION_JSON + + +class PolicyWithClusterAdminRoleTestCase(PolicyBaseTestCase): + def setUp(self) -> None: + super().setUp() + + self.create_policy(role_name="Cluster Administrator") + + self.another_user_log_in(username=self.new_user.username, password=self.new_user_password) + + def test_policy_with_cluster_admin_role(self): # pylint: disable=too-many-statements + required_perms = {perm.codename for perm in self.new_user.user_permissions.all()} + required_perms.update({perm.permission.codename for perm in self.new_user.userobjectpermission_set.all()}) + + self.assertEqual( + required_perms, + { + "add_bundle", + "change_maintenance_mode_servicecomponent", + "run_action_633e1c7d3008add9f296b760ef59217bdf23ecda0d58cad0108aff4d59f3dec1", + "change_maintenance_mode_clusterobject", + "delete_bundle", + "add_configlog", + "run_action_e4c717c28210c2a5937ca61c3da64cb4207842ef849eb4c0722ac9de41929348", + "change_bundle", + "view_upgrade_of_cluster", + "change_config_of_host", + "view_import_of_cluster", + "add_host", + "view_action", + "change_import_of_clusterobject", + "view_cluster", + "change_config_of_cluster", + "change_configlog", + "add_clusterobject", + "unmap_host_from_cluster", + "change_objectconfig", + "change_config_of_clusterobject", + "change_groupconfig", + "view_clusterobject", + "view_host", + "edit_host_components_of_cluster", + "view_servicecomponent", + "change_config_of_servicecomponent", + "view_import_of_clusterobject", + "delete_host", + "add_prototype", + "delete_clusterobject", + "view_configlog", + "change_maintenance_mode_host", + "remove_host", + "view_objectconfig", + "run_action_0673a0096dff0ec7006ee273e441fff030b0d9f895b8ee57c9a1d02bdc338f67", + "view_host_components_of_cluster", + "do_upgrade_of_cluster", + "map_host_to_cluster", + "delete_groupconfig", + "change_import_of_cluster", + "run_action_eed3575d5dd631c3528cb110d024d73a00f628b124dbcb7d78c82ee33a358410", + "run_action_d96211279e42aa024d783a3c107df602883f0d569bf4d30f376a19d46a7106c3", + "add_service_to_cluster", + "run_action_bd938c688f49b77c7fc537c6b9222e2c97ebddd63076b87f2feaec66fb9c05d0", + "add_groupconfig", + }, + ) + + response: Response = self.client.get( + path=reverse(viewname="cluster-details", kwargs={"cluster_id": self.cluster.pk}), + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + + response: Response = self.client.get( + path=reverse(viewname="service-details", kwargs={"service_id": self.last_service_pk}), + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + + response: Response = self.client.get( + path=reverse(viewname="component-details", kwargs={"component_id": self.last_component_pk}), + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + + response: Response = self.client.get( + path=reverse(viewname="host-details", kwargs={"host_id": self.last_host_pk}), + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + + response: Response = self.client.get( + path=reverse(viewname="host", kwargs={"cluster_id": self.cluster.pk}), + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + + response: Response = self.client.get( + path=reverse( + viewname="config-current", + kwargs={"cluster_id": self.cluster.pk, "object_type": "cluster", "version": "current"}, + ), + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + + response: Response = self.client.get( + path=reverse( + viewname="config-current", + kwargs={ + "cluster_id": self.cluster.pk, + "service_id": self.last_service_pk, + "object_type": "service", + "version": "current", + }, + ), + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + + response: Response = self.client.get( + path=reverse( + viewname="config-current", + kwargs={"component_id": self.last_component_pk, "object_type": "component", "version": "current"}, + ), + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + + response: Response = self.client.get( + path=reverse( + viewname="config-current", + kwargs={"host_id": self.last_host_pk, "object_type": "host", "version": "current"}, + ), + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + + response: Response = self.client.get( + path=reverse(viewname="object-action", kwargs={"cluster_id": self.cluster.pk, "object_type": "cluster"}), + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + self.assertTrue(response.json()) + + response: Response = self.client.get( + path=reverse( + viewname="object-action", kwargs={"service_id": self.last_service_pk, "object_type": "service"} + ), + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + self.assertTrue(response.json()) + + response: Response = self.client.get( + path=reverse( + viewname="object-action", kwargs={"component_id": self.last_component_pk, "object_type": "component"} + ), + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + self.assertTrue(response.json()) + + response: Response = self.client.post( + path=reverse(viewname="host-component", kwargs={"cluster_id": self.cluster.pk}), + data={ + "cluster_id": self.cluster.pk, + "hc": [ + { + "component_id": self.last_component_pk, + "host_id": self.last_host_pk, + "service_id": self.last_service_pk, + } + ], + }, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_201_CREATED) + + response: Response = self.client.post( + path=reverse(viewname="config-history", kwargs={"cluster_id": self.cluster.pk, "object_type": "cluster"}), + data={"config": {"float": 3.3}}, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_201_CREATED) + + response: Response = self.client.post( + path=reverse( + viewname="config-history", + kwargs={"cluster_id": self.cluster.pk, "service_id": self.last_service_pk, "object_type": "service"}, + ), + data={"config": {"float": 3.3}}, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_201_CREATED) + + response: Response = self.client.post( + path=reverse( + viewname="config-history", + kwargs={"component_id": self.last_component_pk, "object_type": "component"}, + ), + data={"config": {"float": 3.3}}, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_201_CREATED) + + response: Response = self.client.post( + path=reverse( + viewname="config-history", + kwargs={"host_id": self.last_host_pk, "object_type": "host"}, + ), + data={"config": {"string": "new_string"}}, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_201_CREATED) + + response: Response = self.client.post( + path=reverse( + viewname="config-history", + kwargs={"cluster_id": self.cluster.pk, "host_id": self.last_host_pk, "object_type": "host"}, + ), + data={"config": {"string": "new_string"}}, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_201_CREATED) + + response = self.client.post( + path=reverse(viewname="service", kwargs={"cluster_id": self.cluster.pk}), + data={ + "prototype_id": self.service_6_proto.pk, + }, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_201_CREATED) + + response = self.client.delete( + path=reverse( + viewname="service-details", + kwargs={"cluster_id": self.cluster.pk, "service_id": response.json()["id"]}, + ), + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_204_NO_CONTENT) + + response = self.client.post( + path=reverse(viewname="service-maintenance-mode", kwargs={"service_id": self.last_service_pk}), + data={ + "maintenance_mode": MaintenanceMode.ON, + }, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + + response = self.client.post( + path=reverse(viewname="component-maintenance-mode", kwargs={"component_id": self.last_component_pk}), + data={ + "maintenance_mode": MaintenanceMode.ON, + }, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + + response = self.client.post( + path=reverse(viewname="host-maintenance-mode", kwargs={"host_id": self.last_host_pk}), + data={ + "maintenance_mode": MaintenanceMode.ON, + }, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + + response = self.client.post( + path=reverse(viewname="host-maintenance-mode", kwargs={"host_id": self.last_host_pk}), + data={ + "maintenance_mode": MaintenanceMode.OFF, + }, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + + response: Response = self.client.get( + path=reverse(viewname="object-action", kwargs={"host_id": self.last_host_pk}), + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + self.assertEqual(len(response.data), 1) + + with patch("api.action.views.create", return_value=Response(status=HTTP_201_CREATED)): + response: Response = self.client.post( + path=reverse( + viewname="run-task", + kwargs={"host_id": self.last_host_pk, "action_id": response.json()[0]["id"]}, + ), + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_201_CREATED) + + response: Response = self.client.delete( + path=reverse( + viewname="host-details", + kwargs={"host_id": self.first_host_pk, "cluster_id": self.cluster.pk}, + ), + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_204_NO_CONTENT) + + response: Response = self.client.get( + path=reverse( + viewname="config-current", + kwargs={"host_id": self.first_host_pk, "object_type": "host", "version": "current"}, + ), + ) + + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + response: Response = self.client.post( + path=reverse( + viewname="config-history", + kwargs={"cluster_id": self.cluster.pk, "host_id": self.first_host_pk, "object_type": "host"}, + ), + data={"config": {"string": "new_string"}}, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_403_FORBIDDEN) + + response: Response = self.client.get( + path=reverse(viewname="object-action", kwargs={"host_id": self.first_host_pk}), + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + self.assertFalse(response.json()) + + first_host = Host.objects.get(id=self.first_host_pk) + first_host_action = Action.objects.filter(prototype=first_host.prototype).first() + + with patch("api.action.views.create", return_value=Response(status=HTTP_201_CREATED)): + response: Response = self.client.post( + path=reverse( + viewname="run-task", + kwargs={"host_id": self.first_host_pk, "action_id": first_host_action.pk}, + ), + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_403_FORBIDDEN) diff --git a/python/rbac/tests/test_policy/test_policy_no_right_user_have_no_access.py b/python/rbac/tests/test_policy/test_policy_no_right_user_have_no_access.py new file mode 100644 index 0000000000..891b6e35f5 --- /dev/null +++ b/python/rbac/tests/test_policy/test_policy_no_right_user_have_no_access.py @@ -0,0 +1,263 @@ +# 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 cm.models import MaintenanceMode +from django.urls import reverse +from rbac.tests.test_policy.base import PolicyBaseTestCase +from rest_framework.response import Response +from rest_framework.status import HTTP_200_OK, HTTP_403_FORBIDDEN, HTTP_404_NOT_FOUND + +from adcm.tests.base import APPLICATION_JSON + + +class PolicyNoRightsUserHaveNoAccessTestCase(PolicyBaseTestCase): + def setUp(self) -> None: + super().setUp() + + self.another_user_log_in(username=self.new_user.username, password=self.new_user_password) + + def test_no_rights_user_have_no_access(self): + response: Response = self.client.get( + path=reverse(viewname="cluster-details", kwargs={"cluster_id": self.cluster.pk}), + ) + + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + response: Response = self.client.get( + path=reverse(viewname="service-details", kwargs={"service_id": self.last_service_pk}), + ) + + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + response: Response = self.client.get( + path=reverse(viewname="component-details", kwargs={"component_id": self.last_component_pk}), + ) + + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + response: Response = self.client.get( + path=reverse(viewname="provider-details", kwargs={"provider_id": self.provider.pk}), + ) + + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + response: Response = self.client.get( + path=reverse(viewname="host-details", kwargs={"host_id": self.last_host_pk}), + ) + + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + response: Response = self.client.get( + path=reverse(viewname="host", kwargs={"cluster_id": self.cluster.pk}), + ) + + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + response: Response = self.client.get( + path=reverse( + viewname="config-current", + kwargs={"cluster_id": self.cluster.pk, "object_type": "cluster", "version": "current"}, + ), + ) + + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + response: Response = self.client.get( + path=reverse( + viewname="config-current", + kwargs={ + "cluster_id": self.cluster.pk, + "service_id": self.last_service_pk, + "object_type": "service", + "version": "current", + }, + ), + ) + + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + response: Response = self.client.get( + path=reverse( + viewname="config-current", + kwargs={"component_id": self.last_component_pk, "object_type": "component", "version": "current"}, + ), + ) + + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + response: Response = self.client.get( + path=reverse( + viewname="config-current", + kwargs={"provider_id": self.provider.pk, "object_type": "provider", "version": "current"}, + ), + ) + + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + response: Response = self.client.get( + path=reverse(viewname="object-action", kwargs={"cluster_id": self.cluster.pk, "object_type": "cluster"}), + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + self.assertEqual(response.json(), []) + + response: Response = self.client.get( + path=reverse( + viewname="object-action", kwargs={"service_id": self.last_service_pk, "object_type": "service"} + ), + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + self.assertEqual(response.json(), []) + + response: Response = self.client.get( + path=reverse( + viewname="object-action", kwargs={"component_id": self.last_component_pk, "object_type": "component"} + ), + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + self.assertEqual(response.json(), []) + + response: Response = self.client.get( + path=reverse(viewname="object-action", kwargs={"provider_id": self.provider.pk}), + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + self.assertEqual(response.json(), []) + + response: Response = self.client.post( + path=reverse(viewname="host-component", kwargs={"cluster_id": self.cluster.pk}), + data={ + "cluster_id": self.cluster.pk, + "hc": [ + { + "component_id": self.last_component_pk, + "host_id": self.last_host_pk, + "service_id": self.last_service_pk, + } + ], + }, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + response: Response = self.client.post( + path=reverse(viewname="config-history", kwargs={"cluster_id": self.cluster.pk, "object_type": "cluster"}), + data={"config": {"float": 3.3}}, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_403_FORBIDDEN) + + response: Response = self.client.post( + path=reverse( + viewname="config-history", + kwargs={"cluster_id": self.cluster.pk, "service_id": self.last_service_pk, "object_type": "service"}, + ), + data={"config": {"float": 3.3}}, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_403_FORBIDDEN) + + response: Response = self.client.post( + path=reverse( + viewname="config-history", + kwargs={"component_id": self.last_component_pk, "object_type": "component"}, + ), + data={"config": {"float": 3.3}}, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_403_FORBIDDEN) + + response: Response = self.client.post( + path=reverse(viewname="config-history", kwargs={"provider_id": self.provider.pk}), + data={"config": {"string": "new_string"}}, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_403_FORBIDDEN) + + response: Response = self.client.post( + path=reverse( + viewname="config-history", + kwargs={"host_id": self.last_host_pk, "object_type": "host"}, + ), + data={"config": {"string": "new_string"}}, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_403_FORBIDDEN) + + response: Response = self.client.post( + path=reverse( + viewname="config-history", + kwargs={"cluster_id": self.cluster.pk, "host_id": self.last_host_pk, "object_type": "host"}, + ), + data={"attr": {}, "config": {"string": "new_string"}}, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_403_FORBIDDEN) + + response = self.client.post( + path=reverse(viewname="service", kwargs={"cluster_id": self.cluster.pk}), + data={ + "prototype_id": self.service_6_proto.pk, + }, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_403_FORBIDDEN) + + response = self.client.delete( + path=reverse( + viewname="service-details", + kwargs={"cluster_id": self.cluster.pk, "service_id": self.last_service_pk}, + ), + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + response = self.client.post( + path=reverse(viewname="service-maintenance-mode", kwargs={"service_id": self.last_service_pk}), + data={ + "maintenance_mode": MaintenanceMode.ON, + }, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + response = self.client.post( + path=reverse(viewname="component-maintenance-mode", kwargs={"component_id": self.last_component_pk}), + data={ + "maintenance_mode": MaintenanceMode.ON, + }, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) + + response = self.client.post( + path=reverse(viewname="host-maintenance-mode", kwargs={"host_id": self.last_host_pk}), + data={ + "maintenance_mode": MaintenanceMode.ON, + }, + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_404_NOT_FOUND) diff --git a/python/rbac/tests/test_policy/test_policy_provider_admin_role.py b/python/rbac/tests/test_policy/test_policy_provider_admin_role.py new file mode 100644 index 0000000000..3ddf9efd91 --- /dev/null +++ b/python/rbac/tests/test_policy/test_policy_provider_admin_role.py @@ -0,0 +1,166 @@ +# 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 unittest.mock import patch + +from cm.models import ConfigLog, Host +from django.db.models import ObjectDoesNotExist +from django.urls import reverse +from rbac.tests.test_policy.base import PolicyBaseTestCase +from rest_framework.response import Response +from rest_framework.status import HTTP_200_OK, HTTP_201_CREATED, HTTP_204_NO_CONTENT + +from adcm.tests.base import APPLICATION_JSON + + +class PolicyWithProviderAdminRole(PolicyBaseTestCase): + def setUp(self) -> None: + super().setUp() + + self.create_policy(role_name="Provider Administrator") + + def test_policy_with_provider_admin_role(self): + required_perms = {perm.codename for perm in self.new_user.user_permissions.all()} + required_perms.update({perm.permission.codename for perm in self.new_user.userobjectpermission_set.all()}) + + self.assertEqual( + required_perms, + { + "add_configlog", + "do_upgrade_of_hostprovider", + "change_config_of_hostprovider", + "change_bundle", + "add_bundle", + "delete_groupconfig", + "change_config_of_host", + "add_prototype", + "change_objectconfig", + "view_hostprovider", + "add_host", + "delete_bundle", + "view_host", + "change_configlog", + "add_host_to_hostprovider", + "delete_host", + "run_action_bd938c688f49b77c7fc537c6b9222e2c97ebddd63076b87f2feaec66fb9c05d0", + "remove_host", + "view_action", + "add_groupconfig", + "change_groupconfig", + "view_upgrade_of_hostprovider", + "view_configlog", + "view_objectconfig", + }, + ) + + response: Response = self.client.get( + path=reverse(viewname="provider-details", kwargs={"provider_id": self.provider.pk}), + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + self.assertEqual(response.data["name"], self.provider.name) + + response: Response = self.client.get( + path=reverse(viewname="object-config", kwargs={"provider_id": self.provider.pk}), + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + + new_string = "new_string" + + response: Response = self.client.post( + path=reverse(viewname="config-history", kwargs={"provider_id": self.provider.pk}), + data={"config": {"string": new_string}}, + content_type=APPLICATION_JSON, + ) + + self.provider.refresh_from_db() + config_log = ConfigLog.objects.get(pk=self.provider.config.current) + + self.assertEqual(response.status_code, HTTP_201_CREATED) + self.assertEqual(config_log.config["string"], new_string) + + response: Response = self.client.get( + path=reverse(viewname="object-action", kwargs={"provider_id": self.provider.pk}), + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + self.assertEqual(len(response.data), 1) + + with patch("api.action.views.create", return_value=Response(status=HTTP_201_CREATED)): + response: Response = self.client.post( + path=reverse( + viewname="run-task", + kwargs={"provider_id": self.provider.pk, "action_id": response.json()[0]["id"]}, + ), + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_201_CREATED) + + response: Response = self.client.post( + path=reverse("host", kwargs={"provider_id": self.provider.pk}), + data={"fqdn": "test-host"}, + ) + + self.assertEqual(response.status_code, HTTP_201_CREATED) + + host_pk = response.json()["id"] + + response: Response = self.client.get(path=reverse("host-details", kwargs={"host_id": host_pk})) + + self.assertEqual(response.status_code, HTTP_200_OK) + self.assertEqual(response.data["id"], host_pk) + + response: Response = self.client.get( + path=reverse(viewname="object-config", kwargs={"host_id": host_pk}), + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + + new_string = "new_string" + + response: Response = self.client.post( + path=reverse(viewname="config-history", kwargs={"host_id": host_pk}), + data={"config": {"string": new_string}}, + content_type=APPLICATION_JSON, + ) + + host = Host.objects.get(pk=host_pk) + config_log = ConfigLog.objects.get(pk=host.config.current) + + self.assertEqual(response.status_code, HTTP_201_CREATED) + self.assertEqual(config_log.config["string"], new_string) + + response: Response = self.client.get( + path=reverse(viewname="object-action", kwargs={"host_id": host_pk}), + ) + + self.assertEqual(response.status_code, HTTP_200_OK) + self.assertEqual(len(response.data), 1) + + with patch("api.action.views.create", return_value=Response(status=HTTP_201_CREATED)): + response: Response = self.client.post( + path=reverse( + viewname="run-task", + kwargs={"host_id": host.pk, "action_id": response.json()[0]["id"]}, + ), + content_type=APPLICATION_JSON, + ) + + self.assertEqual(response.status_code, HTTP_201_CREATED) + + response: Response = self.client.delete(path=reverse("host-details", kwargs={"host_id": host.pk})) + + self.assertEqual(response.status_code, HTTP_204_NO_CONTENT) + with self.assertRaises(ObjectDoesNotExist): + host.refresh_from_db() diff --git a/python/rbac/tests/test_policy/test_policy_rbac.py b/python/rbac/tests/test_policy/test_policy_rbac.py new file mode 100644 index 0000000000..0034e00507 --- /dev/null +++ b/python/rbac/tests/test_policy/test_policy_rbac.py @@ -0,0 +1,503 @@ +# 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 cm.api import add_hc, add_host_to_cluster, add_service_to_cluster +from cm.models import ( + Cluster, + ClusterObject, + Host, + HostProvider, + Prototype, + ServiceComponent, +) +from rbac.models import Group, Policy, User +from rbac.tests.test_base import RBACBaseTestCase + + +class PolicyRBACTestCase(RBACBaseTestCase): # pylint: disable=too-many-instance-attributes + """Tests for applying policy with different combination of roles and object""" + + def setUp(self) -> None: + super().setUp() + + self.user = User.objects.create(username="user", is_active=True, is_superuser=False) + self.cluster = Cluster.objects.create(name="Cluster_1", prototype=self.clp) + self.service_1 = ClusterObject.objects.create(cluster=self.cluster, prototype=self.sp_1) + self.service_2 = ClusterObject.objects.create(cluster=self.cluster, prototype=self.sp_2) + self.component_11 = ServiceComponent.objects.create( + cluster=self.cluster, + service=self.service_1, + prototype=self.cop_11, + ) + self.component_12 = ServiceComponent.objects.create( + cluster=self.cluster, + service=self.service_1, + prototype=self.cop_12, + ) + self.component_21 = ServiceComponent.objects.create( + cluster=self.cluster, + service=self.service_2, + prototype=self.cop_21, + ) + + def get_hosts_and_provider(self): + provider, _ = HostProvider.objects.get_or_create(name="provider", prototype=self.provider_prototype) + host1 = Host.objects.create(prototype=self.host_prototype, provider=provider, fqdn="host_1") + host2 = Host.objects.create(prototype=self.host_prototype, provider=provider, fqdn="host_2") + + return provider, host1, host2 + + @staticmethod + def clear_perm_cache(user): + if hasattr(user, "_perm_cache"): + delattr(user, "_perm_cache") + + if hasattr(user, "_user_perm_cache"): + delattr(user, "_user_perm_cache") + + if hasattr(user, "_group_perm_cache"): + delattr(user, "_group_perm_cache") + + def test_model_policy(self): + policy = Policy.objects.create(name="MyPolicy", role=self.model_role()) + policy.user.add(self.user) + + self.assertNotIn(self.add_host_perm, self.user.user_permissions.all()) + self.assertFalse(self.user.has_perm("cm.add_host")) + self.clear_perm_cache(self.user) + + policy.apply() + + self.assertIn(self.add_host_perm, self.user.user_permissions.all()) + self.assertTrue(self.user.has_perm("cm.add_host")) + + self.clear_perm_cache(self.user) + policy.apply() + + self.assertTrue(self.user.has_perm("cm.add_host")) + + def test_model_policy4group(self): + group = Group.objects.create(name="group") + group.user_set.add(self.user) + + policy = Policy.objects.create(name="MyPolicy", role=self.model_role()) + policy.group.add(group) + + self.assertNotIn(self.add_host_perm, group.permissions.all()) + self.assertFalse(self.user.has_perm("cm.add_host")) + + self.clear_perm_cache(self.user) + policy.apply() + + self.assertIn(self.add_host_perm, group.permissions.all()) + self.assertTrue(self.user.has_perm("cm.add_host")) + + self.clear_perm_cache(self.user) + policy.apply() + + self.assertTrue(self.user.has_perm("cm.add_host")) + + def test_object_policy(self): + cluster2 = Cluster.objects.create(name="Cluster_2", prototype=self.clp) + policy = Policy.objects.create(name="MyPolicy", role=self.object_role_view_perm_cluster()) + policy.user.add(self.user) + + self.assertFalse(self.user.has_perm("cm.view_cluster", self.cluster)) + self.assertFalse(self.user.has_perm("cm.view_cluster", cluster2)) + + policy.add_object(self.cluster) + policy.apply() + + self.assertTrue(self.user.has_perm("cm.view_cluster", self.cluster)) + self.assertFalse(self.user.has_perm("cm.view_cluster", cluster2)) + + def test_object_policy_remove_user(self): + cluster2 = Cluster.objects.create(name="Cluster_2", prototype=self.clp) + + policy = Policy.objects.create(name="MyPolicy", role=self.object_role()) + policy.user.add(self.user) + policy.add_object(self.cluster) + + self.assertFalse(self.user.has_perm("cm.view_cluster", self.cluster)) + + policy.apply() + + self.assertTrue(self.user.has_perm("cm.view_cluster", self.cluster)) + self.assertFalse(self.user.has_perm("cm.view_cluster", cluster2)) + + policy.user.remove(self.user) + policy.apply() + + self.assertFalse(self.user.has_perm("cm.view_cluster", self.cluster)) + self.assertFalse(self.user.has_perm("cm.view_cluster", cluster2)) + + def test_object_policy4group(self): + cluster2 = Cluster.objects.create(name="Cluster_2", prototype=self.clp) + group = Group.objects.create(name="group") + group.user_set.add(self.user) + + policy = Policy.objects.create(name="MyPolicy", role=self.object_role()) + policy.group.add(group) + + policy.add_object(self.cluster) + + self.assertFalse(self.user.has_perm("cm.view_cluster", self.cluster)) + + policy.apply() + + self.assertTrue(self.user.has_perm("cm.view_cluster", self.cluster)) + self.assertFalse(self.user.has_perm("cm.view_cluster", cluster2)) + + def test_parent_policy4cluster(self): + policy = Policy.objects.create(role=self.object_role_custom_perm_cluster_service_component()) + policy.user.add(self.user) + policy.add_object(self.cluster) + + self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) + self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) + self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_2)) + self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) + self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_21)) + + policy.apply() + + self.assertTrue(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) + self.assertTrue(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) + self.assertTrue(self.user.has_perm("cm.change_config_of_clusterobject", self.service_2)) + self.assertTrue(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) + self.assertTrue(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_21)) + + def test_parent_policy4service(self): + policy = Policy.objects.create(role=self.object_role_custom_perm_cluster_service_component()) + policy.user.add(self.user) + policy.add_object(self.service_1) + + self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) + self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) + self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_2)) + self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) + self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_21)) + + policy.apply() + + self.assertTrue(self.user.has_perm("cm.view_cluster", self.cluster)) + self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) + self.assertTrue(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) + self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_2)) + self.assertTrue(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) + self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_21)) + + def test_parent_policy4service2(self): + policy = Policy.objects.create(role=self.object_role_custom_perm_cluster_service_component()) + policy.user.add(self.user) + policy.add_object(self.service_2) + + self.assertFalse(self.user.has_perm("cm.view_cluster", self.cluster)) + self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) + self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) + self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_2)) + self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) + self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_21)) + + policy.apply() + + self.assertTrue(self.user.has_perm("cm.view_cluster", self.cluster)) + self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) + self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) + self.assertTrue(self.user.has_perm("cm.change_config_of_clusterobject", self.service_2)) + self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) + self.assertTrue(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_21)) + + def test_parent_policy4component(self): + policy = Policy.objects.create(role=self.object_role_custom_perm_cluster_service_component()) + policy.user.add(self.user) + policy.add_object(self.component_11) + + self.assertFalse(self.user.has_perm("cm.view_cluster", self.cluster)) + self.assertFalse(self.user.has_perm("cm.view_clusterobject", self.service_1)) + self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) + self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) + self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_2)) + self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) + self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_21)) + + policy.apply() + + self.assertTrue(self.user.has_perm("cm.view_cluster", self.cluster)) + self.assertTrue(self.user.has_perm("cm.view_clusterobject", self.service_1)) + self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) + self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) + self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_2)) + self.assertTrue(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) + self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_21)) + + def test_parent_policy4host_in_cluster(self): + provider, host1, host2 = self.get_hosts_and_provider() + host3 = Host.objects.create(provider=provider, prototype=self.host_prototype, fqdn="host_3") + add_host_to_cluster(self.cluster, host1) + add_host_to_cluster(self.cluster, host2) + policy = Policy.objects.create(role=self.object_role_custom_perm_cluster_host()) + policy.user.add(self.user) + policy.add_object(self.cluster) + + self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) + self.assertFalse(self.user.has_perm("cm.change_config_of_host", host1)) + self.assertFalse(self.user.has_perm("cm.change_config_of_host", host2)) + self.assertFalse(self.user.has_perm("cm.change_config_of_host", host3)) + + policy.apply() + + self.assertTrue(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) + self.assertTrue(self.user.has_perm("cm.change_config_of_host", host1)) + self.assertTrue(self.user.has_perm("cm.change_config_of_host", host2)) + self.assertFalse(self.user.has_perm("cm.change_config_of_host", host3)) + + def test_parent_policy4host_in_service(self): + _, host1, host2 = self.get_hosts_and_provider() + add_host_to_cluster(self.cluster, host1) + add_host_to_cluster(self.cluster, host2) + add_hc( + self.cluster, + [ + { + "service_id": self.service_1.id, + "component_id": self.component_11.id, + "host_id": host1.id, + }, + { + "service_id": self.service_2.id, + "component_id": self.component_21.id, + "host_id": host2.id, + }, + ], + ) + policy = Policy.objects.create(role=self.object_role_custom_perm_cluster_service_component_host()) + policy.user.add(self.user) + policy.add_object(self.service_1) + + self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) + self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) + self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_2)) + self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) + self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_21)) + self.assertFalse(self.user.has_perm("cm.change_config_of_host", host1)) + self.assertFalse(self.user.has_perm("cm.change_config_of_host", host2)) + + policy.apply() + + self.assertFalse(self.user.has_perm("cm.change_confing_of_cluster", self.cluster)) + self.assertTrue(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) + self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_2)) + self.assertTrue(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) + self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_21)) + self.assertTrue(self.user.has_perm("cm.change_config_of_host", host1)) + self.assertFalse(self.user.has_perm("cm.change_config_of_host", host2)) + + def test_parent_policy4host_in_component(self): + provider, host1, host2 = self.get_hosts_and_provider() + host3 = Host.objects.create(provider=provider, prototype=self.host_prototype, fqdn="host_3") + add_host_to_cluster(self.cluster, host1) + add_host_to_cluster(self.cluster, host2) + add_host_to_cluster(self.cluster, host3) + add_hc( + self.cluster, + [ + { + "service_id": self.service_2.id, + "component_id": self.component_21.id, + "host_id": host1.id, + }, + { + "service_id": self.service_2.id, + "component_id": self.component_21.id, + "host_id": host2.id, + }, + { + "service_id": self.service_1.id, + "component_id": self.component_11.id, + "host_id": host3.id, + }, + ], + ) + + policy = Policy.objects.create(role=self.object_role_custom_perm_cluster_service_component_host()) + policy.user.add(self.user) + policy.add_object(self.component_21) + + self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) + self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) + self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_2)) + self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) + self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_21)) + self.assertFalse(self.user.has_perm("cm.change_config_of_host", host1)) + self.assertFalse(self.user.has_perm("cm.change_config_of_host", host2)) + self.assertFalse(self.user.has_perm("cm.change_config_of_host", host3)) + + policy.apply() + + self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) + self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) + self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_2)) + self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) + self.assertTrue(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_21)) + self.assertTrue(self.user.has_perm("cm.change_config_of_host", host1)) + self.assertTrue(self.user.has_perm("cm.change_config_of_host", host2)) + self.assertFalse(self.user.has_perm("cm.change_config_of_host", host3)) + + def test_parent_policy4provider(self): + provider, host1, host2 = self.get_hosts_and_provider() + policy = Policy.objects.create(role=self.object_role_custom_perm_provider_host()) + policy.user.add(self.user) + policy.add_object(provider) + + self.assertFalse(self.user.has_perm("cm.change_config_of_hostprovider", provider)) + self.assertFalse(self.user.has_perm("cm.change_config_of_host", host1)) + self.assertFalse(self.user.has_perm("cm.change_config_of_host", host2)) + + policy.apply() + + self.assertTrue(self.user.has_perm("cm.change_config_of_hostprovider", provider)) + self.assertTrue(self.user.has_perm("cm.change_config_of_host", host1)) + self.assertTrue(self.user.has_perm("cm.change_config_of_host", host2)) + + def test_simple_parent_policy(self): + policy = Policy.objects.create(role=self.model_role_view_cluster_service_component_perm()) + policy.user.add(self.user) + + self.assertFalse(self.user.has_perm("cm.view_cluster")) + self.assertFalse(self.user.has_perm("cm.view_clusterobject")) + self.assertFalse(self.user.has_perm("cm.view_servicecomponent")) + + self.clear_perm_cache(self.user) + policy.apply() + + self.assertTrue(self.user.has_perm("cm.view_cluster")) + self.assertTrue(self.user.has_perm("cm.view_clusterobject")) + self.assertTrue(self.user.has_perm("cm.view_servicecomponent")) + + def test_add_service(self): + sp_3 = Prototype.obj.create(bundle=self.bundle_1, type="service", name="service_3") + + policy = Policy.objects.create(role=self.object_role_custom_perm_cluster_service()) + policy.user.add(self.user) + policy.add_object(self.cluster) + + self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) + self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) + self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_2)) + + policy.apply() + + self.assertTrue(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) + self.assertTrue(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) + self.assertTrue(self.user.has_perm("cm.change_config_of_clusterobject", self.service_2)) + + service3 = add_service_to_cluster(self.cluster, sp_3) + + self.assertTrue(self.user.has_perm("cm.change_config_of_clusterobject", service3)) + + def test_add_host(self): + _, host1, host2 = self.get_hosts_and_provider() + add_host_to_cluster(self.cluster, host1) + add_hc( + self.cluster, + [ + { + "service_id": self.service_1.id, + "component_id": self.component_11.id, + "host_id": host1.id, + }, + ], + ) + + policy = Policy.objects.create(role=self.object_role_custom_perm_cluster_service_component_host()) + policy.user.add(self.user) + policy.add_object(self.cluster) + + self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) + self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) + self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) + self.assertFalse(self.user.has_perm("cm.change_config_of_host", host1)) + self.assertFalse(self.user.has_perm("cm.change_config_of_host", host2)) + + policy.apply() + + self.assertTrue(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) + self.assertTrue(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) + self.assertTrue(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) + self.assertTrue(self.user.has_perm("cm.change_config_of_host", host1)) + self.assertFalse(self.user.has_perm("cm.change_config_of_host", host2)) + + add_host_to_cluster(self.cluster, host2) + + self.assertTrue(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) + self.assertTrue(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) + self.assertTrue(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) + self.assertTrue(self.user.has_perm("cm.change_config_of_host", host1)) + self.assertTrue(self.user.has_perm("cm.change_config_of_host", host2)) + + def test_add_hc(self): + _, host1, host2 = self.get_hosts_and_provider() + add_host_to_cluster(self.cluster, host1) + add_hc( + self.cluster, + [ + { + "service_id": self.service_1.id, + "component_id": self.component_11.id, + "host_id": host1.id, + }, + ], + ) + policy = Policy.objects.create(role=self.object_role_custom_perm_service_component_host()) + policy.user.add(self.user) + policy.add_object(self.service_1) + + self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) + self.assertFalse(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) + self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) + self.assertFalse(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_12)) + self.assertFalse(self.user.has_perm("cm.change_config_of_host", host1)) + self.assertFalse(self.user.has_perm("cm.change_config_of_host", host2)) + + policy.apply() + + self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) + self.assertTrue(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) + self.assertTrue(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) + self.assertTrue(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_12)) + self.assertTrue(self.user.has_perm("cm.change_config_of_host", host1)) + self.assertFalse(self.user.has_perm("cm.change_config_of_host", host2)) + + add_host_to_cluster(self.cluster, host2) + add_hc( + self.cluster, + [ + { + "service_id": self.service_1.id, + "component_id": self.component_11.id, + "host_id": host1.id, + }, + { + "service_id": self.service_1.id, + "component_id": self.component_12.id, + "host_id": host2.id, + }, + ], + ) + + self.assertFalse(self.user.has_perm("cm.change_config_of_cluster", self.cluster)) + self.assertTrue(self.user.has_perm("cm.change_config_of_clusterobject", self.service_1)) + self.assertTrue(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_11)) + self.assertTrue(self.user.has_perm("cm.change_config_of_servicecomponent", self.component_12)) + self.assertTrue(self.user.has_perm("cm.change_config_of_host", host1)) + self.assertTrue(self.user.has_perm("cm.change_config_of_host", host2)) From b0f479e792f2e3e30863a7ff8e1745d7b72f6e6f Mon Sep 17 00:00:00 2001 From: Ella Kurginyan Date: Thu, 20 Apr 2023 17:18:04 +0000 Subject: [PATCH 8/9] ADCM-3705 Added bulk remove permission --- python/cm/api.py | 49 +++++++++++++++++++++++++------------------ python/rbac/models.py | 23 +++++++++++++++----- python/rbac/roles.py | 15 ++++++++++--- 3 files changed, 59 insertions(+), 28 deletions(-) diff --git a/python/cm/api.py b/python/cm/api.py index e793e5d6c0..7c0c3d3c10 100644 --- a/python/cm/api.py +++ b/python/cm/api.py @@ -56,8 +56,11 @@ from cm.status_api import api_request, post_event from django.core.exceptions import MultipleObjectsReturned from django.db import transaction -from rbac.models import re_apply_object_policy -from rbac.roles import apply_policy_for_new_config +from rbac.models import apply_objects_policies_on_node, re_apply_object_policy +from rbac.roles import ( + apply_policy_for_new_config, + bulk_remove_user_or_group_permissions_of_node, +) from version_utils import rpm @@ -211,8 +214,8 @@ def add_host(proto, provider, fqdn, desc=""): host.config = obj_conf host.save() host.add_to_concerns(CTX.lock) - update_hierarchy_issues(host.provider) - re_apply_object_policy(provider) + update_hierarchy_issues(provider) + apply_objects_policies_on_node(list_of_policy_objects=[provider], node=host) CTX.event.send_state() post_event(event="create", obj=host, details={"type": "provider", "value": str(provider.pk)}) @@ -261,7 +264,9 @@ def delete_host_provider(provider, cancel_tasks=True): provider_pk = provider.pk post_event(event="delete", obj=provider) - provider.delete() + with transaction.atomic(): + provider.delete() + bulk_remove_user_or_group_permissions_of_node(provider) logger.info("host provider #%s is deleted", provider_pk) @@ -277,7 +282,7 @@ def add_host_to_cluster(cluster, host): host.save() host.add_to_concerns(CTX.lock) update_hierarchy_issues(host) - re_apply_object_policy(cluster) + re_apply_object_policy(apply_object=cluster) post_event(event="add", obj=host, details={"type": "cluster", "value": str(cluster.pk)}) load_service_map() @@ -336,9 +341,11 @@ def delete_host(host, cancel_tasks=True): host_pk = host.pk post_event(event="delete", obj=host) - host.delete() + with transaction.atomic(): + host.delete() + update_issue_after_deleting() + bulk_remove_user_or_group_permissions_of_node(host) load_service_map() - update_issue_after_deleting() logger.info("host #%s is deleted", host_pk) @@ -375,9 +382,8 @@ def delete_service_by_pk(service_pk): This is intended for use in adcm_delete_service ansible plugin only """ - + service = ClusterObject.obj.get(pk=service_pk) with transaction.atomic(): - service = ClusterObject.obj.get(pk=service_pk) _clean_up_related_hc(service) ClusterBind.objects.filter(source_service=service).delete() delete_service(service=service) @@ -390,8 +396,8 @@ def delete_service_by_name(service_name, cluster_pk): This is intended for use in adcm_delete_service ansible plugin only """ + service = ClusterObject.obj.get(cluster__pk=cluster_pk, prototype__name=service_name) with transaction.atomic(): - service = ClusterObject.obj.get(cluster__pk=cluster_pk, prototype__name=service_name) _clean_up_related_hc(service) ClusterBind.objects.filter(source_service=service).delete() delete_service(service=service) @@ -400,10 +406,11 @@ def delete_service_by_name(service_name, cluster_pk): def delete_service(service: ClusterObject) -> None: service_pk = service.pk post_event(event="delete", obj=service) - service.delete() - update_issue_after_deleting() - update_hierarchy_issues(service.cluster) - re_apply_object_policy(service.cluster) + with transaction.atomic(): + service.delete() + update_issue_after_deleting() + update_hierarchy_issues(service.cluster) + bulk_remove_user_or_group_permissions_of_node(service) load_service_map() logger.info("service #%s is deleted", service_pk) @@ -423,8 +430,10 @@ def delete_cluster(cluster, cancel_tasks=True): ", ".join(host_pks), ) post_event(event="delete", obj=cluster) - cluster.delete() - update_issue_after_deleting() + with transaction.atomic(): + cluster.delete() + update_issue_after_deleting() + bulk_remove_user_or_group_permissions_of_node(cluster) load_service_map() @@ -444,8 +453,8 @@ def remove_host_from_cluster(host: Host) -> Host: for group in cluster.group_config.order_by("id"): group.hosts.remove(host) - update_hierarchy_issues(obj=host) + update_hierarchy_issues(obj=host) host.remove_from_concerns(CTX.lock) update_hierarchy_issues(obj=cluster) re_apply_object_policy(apply_object=cluster) @@ -488,7 +497,7 @@ def add_service_to_cluster(cluster, proto): service.save(update_fields=["config"]) add_components_to_service(cluster=cluster, service=service) update_hierarchy_issues(obj=service) - re_apply_object_policy(apply_object=cluster) + apply_objects_policies_on_node(list_of_policy_objects=[cluster], node=service) post_event(event="add", obj=service, details={"type": "cluster", "value": str(cluster.pk)}) load_service_map() @@ -722,12 +731,12 @@ def save_hc(cluster, host_comp_list): CTX.event.send_state() post_event(event="change_hostcomponentmap", obj=cluster) + update_issue_after_deleting() update_hierarchy_issues(cluster) for provider in {host.provider for host in Host.objects.filter(cluster=cluster)}: update_hierarchy_issues(provider) - update_issue_after_deleting() load_service_map() for host_component_item in host_component_list: diff --git a/python/rbac/models.py b/python/rbac/models.py index 0320d0d4ed..10dcb0a678 100644 --- a/python/rbac/models.py +++ b/python/rbac/models.py @@ -285,12 +285,12 @@ def delete(self, using=None, keep_parents=False): return super().delete(using, keep_parents) @atomic - def apply_without_deletion(self): + def apply_without_deletion(self, apply_node): for user in self.user.order_by("id"): - self.role.apply(self, user, None) + self.role.apply(self, user, None, obj=apply_node) for group in self.group.all(): - self.role.apply(self, None, group=group) + self.role.apply(self, None, group=group, obj=apply_node) @atomic def apply(self): @@ -302,7 +302,7 @@ def apply(self): self.role.apply(self, None, group=group) -def get_objects_for_policy(obj: ADCMEntity) -> dict[ADCMEntity, ContentType]: +def get_objects_hierarchy_for_policy(obj: ADCMEntity) -> dict[ADCMEntity, ContentType]: obj_type_map = {} obj_type = obj.prototype.type @@ -317,6 +317,8 @@ def get_objects_for_policy(obj: ADCMEntity) -> dict[ADCMEntity, ContentType]: for hostcomponent in HostComponent.objects.filter(cluster=obj.cluster, host=obj): object_list.append(hostcomponent.service) object_list.append(hostcomponent.component) + + object_list = list(set(object_list)) else: object_list = [obj, obj.provider] else: @@ -333,12 +335,23 @@ def re_apply_object_policy(apply_object): This function search for polices linked with specified object and re apply them """ - obj_type_map = get_objects_for_policy(apply_object) + obj_type_map = get_objects_hierarchy_for_policy(apply_object) for obj, content_type in obj_type_map.items(): for policy in Policy.objects.filter(object__object_id=obj.id, object__content_type=content_type): policy.apply() +def apply_objects_policies_on_node(list_of_policy_objects: list, node: ADCMEntity): + """ + This function search for polices of objects and apply them on node + """ + + for obj in list_of_policy_objects: + content_type = ContentType.objects.get_for_model(obj) + for policy in Policy.objects.filter(object__object_id=obj.id, object__content_type=content_type): + policy.apply_without_deletion(node) + + def re_apply_all_polices(): """ This function re apply all polices diff --git a/python/rbac/roles.py b/python/rbac/roles.py index 5e15685978..33858fb31f 100644 --- a/python/rbac/roles.py +++ b/python/rbac/roles.py @@ -38,7 +38,7 @@ Role, RoleTypes, User, - get_objects_for_policy, + get_objects_hierarchy_for_policy, ) @@ -91,6 +91,15 @@ def assign_user_or_group_perm(user: User | None, group: Group | None, policy: Po policy.group_object_perm.add(gop) +def bulk_remove_user_or_group_permissions_of_node(node: ADCMEntity): + content_type = ContentType.objects.get_for_model(model=node) + user_permission_queryset = UserObjectPermission.objects.filter(object_pk=node.pk, content_type=content_type) + group_permission_queryset = UserObjectPermission.objects.filter(object_pk=node.pk, content_type=content_type) + with transaction.atomic(): + user_permission_queryset.delete() + group_permission_queryset.delete() + + class ObjectRole(AbstractRole): """This Role apply django-guardian object level permissions""" @@ -210,7 +219,7 @@ def apply_jobs(task: TaskLog, policy: Policy, user: User | None = None, group: G def re_apply_policy_for_jobs(action_object, task): # noqa: C901 - obj_type_map = get_objects_for_policy(action_object) + obj_type_map = get_objects_hierarchy_for_policy(action_object) object_model = action_object.__class__.__name__.lower() task_role, _ = Role.objects.get_or_create( name=f"View role for task {task.id}", @@ -265,7 +274,7 @@ def re_apply_policy_for_jobs(action_object, task): # noqa: C901 def apply_policy_for_new_config(config_object: ADCMEntity, config_log: ConfigLog) -> None: # noqa: C901 - obj_type_map = get_objects_for_policy(obj=config_object) + obj_type_map = get_objects_hierarchy_for_policy(obj=config_object) object_model = config_object.__class__.__name__.lower() for obj, content_type in obj_type_map.items(): From 7d08cec9c1fa25784a230846393dfcbf9ba49def Mon Sep 17 00:00:00 2001 From: Alexandr Alferov Date: Fri, 21 Apr 2023 00:51:38 +0300 Subject: [PATCH 9/9] Revert "ADCM-3705 Added bulk remove permission" This reverts commit b0f479e792f2e3e30863a7ff8e1745d7b72f6e6f. --- python/cm/api.py | 49 ++++++++++++++++++------------------------- python/rbac/models.py | 23 +++++--------------- python/rbac/roles.py | 15 +++---------- 3 files changed, 28 insertions(+), 59 deletions(-) diff --git a/python/cm/api.py b/python/cm/api.py index 7c0c3d3c10..e793e5d6c0 100644 --- a/python/cm/api.py +++ b/python/cm/api.py @@ -56,11 +56,8 @@ from cm.status_api import api_request, post_event from django.core.exceptions import MultipleObjectsReturned from django.db import transaction -from rbac.models import apply_objects_policies_on_node, re_apply_object_policy -from rbac.roles import ( - apply_policy_for_new_config, - bulk_remove_user_or_group_permissions_of_node, -) +from rbac.models import re_apply_object_policy +from rbac.roles import apply_policy_for_new_config from version_utils import rpm @@ -214,8 +211,8 @@ def add_host(proto, provider, fqdn, desc=""): host.config = obj_conf host.save() host.add_to_concerns(CTX.lock) - update_hierarchy_issues(provider) - apply_objects_policies_on_node(list_of_policy_objects=[provider], node=host) + update_hierarchy_issues(host.provider) + re_apply_object_policy(provider) CTX.event.send_state() post_event(event="create", obj=host, details={"type": "provider", "value": str(provider.pk)}) @@ -264,9 +261,7 @@ def delete_host_provider(provider, cancel_tasks=True): provider_pk = provider.pk post_event(event="delete", obj=provider) - with transaction.atomic(): - provider.delete() - bulk_remove_user_or_group_permissions_of_node(provider) + provider.delete() logger.info("host provider #%s is deleted", provider_pk) @@ -282,7 +277,7 @@ def add_host_to_cluster(cluster, host): host.save() host.add_to_concerns(CTX.lock) update_hierarchy_issues(host) - re_apply_object_policy(apply_object=cluster) + re_apply_object_policy(cluster) post_event(event="add", obj=host, details={"type": "cluster", "value": str(cluster.pk)}) load_service_map() @@ -341,11 +336,9 @@ def delete_host(host, cancel_tasks=True): host_pk = host.pk post_event(event="delete", obj=host) - with transaction.atomic(): - host.delete() - update_issue_after_deleting() - bulk_remove_user_or_group_permissions_of_node(host) + host.delete() load_service_map() + update_issue_after_deleting() logger.info("host #%s is deleted", host_pk) @@ -382,8 +375,9 @@ def delete_service_by_pk(service_pk): This is intended for use in adcm_delete_service ansible plugin only """ - service = ClusterObject.obj.get(pk=service_pk) + with transaction.atomic(): + service = ClusterObject.obj.get(pk=service_pk) _clean_up_related_hc(service) ClusterBind.objects.filter(source_service=service).delete() delete_service(service=service) @@ -396,8 +390,8 @@ def delete_service_by_name(service_name, cluster_pk): This is intended for use in adcm_delete_service ansible plugin only """ - service = ClusterObject.obj.get(cluster__pk=cluster_pk, prototype__name=service_name) with transaction.atomic(): + service = ClusterObject.obj.get(cluster__pk=cluster_pk, prototype__name=service_name) _clean_up_related_hc(service) ClusterBind.objects.filter(source_service=service).delete() delete_service(service=service) @@ -406,11 +400,10 @@ def delete_service_by_name(service_name, cluster_pk): def delete_service(service: ClusterObject) -> None: service_pk = service.pk post_event(event="delete", obj=service) - with transaction.atomic(): - service.delete() - update_issue_after_deleting() - update_hierarchy_issues(service.cluster) - bulk_remove_user_or_group_permissions_of_node(service) + service.delete() + update_issue_after_deleting() + update_hierarchy_issues(service.cluster) + re_apply_object_policy(service.cluster) load_service_map() logger.info("service #%s is deleted", service_pk) @@ -430,10 +423,8 @@ def delete_cluster(cluster, cancel_tasks=True): ", ".join(host_pks), ) post_event(event="delete", obj=cluster) - with transaction.atomic(): - cluster.delete() - update_issue_after_deleting() - bulk_remove_user_or_group_permissions_of_node(cluster) + cluster.delete() + update_issue_after_deleting() load_service_map() @@ -453,8 +444,8 @@ def remove_host_from_cluster(host: Host) -> Host: for group in cluster.group_config.order_by("id"): group.hosts.remove(host) + update_hierarchy_issues(obj=host) - update_hierarchy_issues(obj=host) host.remove_from_concerns(CTX.lock) update_hierarchy_issues(obj=cluster) re_apply_object_policy(apply_object=cluster) @@ -497,7 +488,7 @@ def add_service_to_cluster(cluster, proto): service.save(update_fields=["config"]) add_components_to_service(cluster=cluster, service=service) update_hierarchy_issues(obj=service) - apply_objects_policies_on_node(list_of_policy_objects=[cluster], node=service) + re_apply_object_policy(apply_object=cluster) post_event(event="add", obj=service, details={"type": "cluster", "value": str(cluster.pk)}) load_service_map() @@ -731,12 +722,12 @@ def save_hc(cluster, host_comp_list): CTX.event.send_state() post_event(event="change_hostcomponentmap", obj=cluster) - update_issue_after_deleting() update_hierarchy_issues(cluster) for provider in {host.provider for host in Host.objects.filter(cluster=cluster)}: update_hierarchy_issues(provider) + update_issue_after_deleting() load_service_map() for host_component_item in host_component_list: diff --git a/python/rbac/models.py b/python/rbac/models.py index 10dcb0a678..0320d0d4ed 100644 --- a/python/rbac/models.py +++ b/python/rbac/models.py @@ -285,12 +285,12 @@ def delete(self, using=None, keep_parents=False): return super().delete(using, keep_parents) @atomic - def apply_without_deletion(self, apply_node): + def apply_without_deletion(self): for user in self.user.order_by("id"): - self.role.apply(self, user, None, obj=apply_node) + self.role.apply(self, user, None) for group in self.group.all(): - self.role.apply(self, None, group=group, obj=apply_node) + self.role.apply(self, None, group=group) @atomic def apply(self): @@ -302,7 +302,7 @@ def apply(self): self.role.apply(self, None, group=group) -def get_objects_hierarchy_for_policy(obj: ADCMEntity) -> dict[ADCMEntity, ContentType]: +def get_objects_for_policy(obj: ADCMEntity) -> dict[ADCMEntity, ContentType]: obj_type_map = {} obj_type = obj.prototype.type @@ -317,8 +317,6 @@ def get_objects_hierarchy_for_policy(obj: ADCMEntity) -> dict[ADCMEntity, Conten for hostcomponent in HostComponent.objects.filter(cluster=obj.cluster, host=obj): object_list.append(hostcomponent.service) object_list.append(hostcomponent.component) - - object_list = list(set(object_list)) else: object_list = [obj, obj.provider] else: @@ -335,23 +333,12 @@ def re_apply_object_policy(apply_object): This function search for polices linked with specified object and re apply them """ - obj_type_map = get_objects_hierarchy_for_policy(apply_object) + obj_type_map = get_objects_for_policy(apply_object) for obj, content_type in obj_type_map.items(): for policy in Policy.objects.filter(object__object_id=obj.id, object__content_type=content_type): policy.apply() -def apply_objects_policies_on_node(list_of_policy_objects: list, node: ADCMEntity): - """ - This function search for polices of objects and apply them on node - """ - - for obj in list_of_policy_objects: - content_type = ContentType.objects.get_for_model(obj) - for policy in Policy.objects.filter(object__object_id=obj.id, object__content_type=content_type): - policy.apply_without_deletion(node) - - def re_apply_all_polices(): """ This function re apply all polices diff --git a/python/rbac/roles.py b/python/rbac/roles.py index 33858fb31f..5e15685978 100644 --- a/python/rbac/roles.py +++ b/python/rbac/roles.py @@ -38,7 +38,7 @@ Role, RoleTypes, User, - get_objects_hierarchy_for_policy, + get_objects_for_policy, ) @@ -91,15 +91,6 @@ def assign_user_or_group_perm(user: User | None, group: Group | None, policy: Po policy.group_object_perm.add(gop) -def bulk_remove_user_or_group_permissions_of_node(node: ADCMEntity): - content_type = ContentType.objects.get_for_model(model=node) - user_permission_queryset = UserObjectPermission.objects.filter(object_pk=node.pk, content_type=content_type) - group_permission_queryset = UserObjectPermission.objects.filter(object_pk=node.pk, content_type=content_type) - with transaction.atomic(): - user_permission_queryset.delete() - group_permission_queryset.delete() - - class ObjectRole(AbstractRole): """This Role apply django-guardian object level permissions""" @@ -219,7 +210,7 @@ def apply_jobs(task: TaskLog, policy: Policy, user: User | None = None, group: G def re_apply_policy_for_jobs(action_object, task): # noqa: C901 - obj_type_map = get_objects_hierarchy_for_policy(action_object) + obj_type_map = get_objects_for_policy(action_object) object_model = action_object.__class__.__name__.lower() task_role, _ = Role.objects.get_or_create( name=f"View role for task {task.id}", @@ -274,7 +265,7 @@ def re_apply_policy_for_jobs(action_object, task): # noqa: C901 def apply_policy_for_new_config(config_object: ADCMEntity, config_log: ConfigLog) -> None: # noqa: C901 - obj_type_map = get_objects_hierarchy_for_policy(obj=config_object) + obj_type_map = get_objects_for_policy(obj=config_object) object_model = config_object.__class__.__name__.lower() for obj, content_type in obj_type_map.items():