diff --git a/mayan/apps/acls/classes.py b/mayan/apps/acls/classes.py index 94ee4c6f69..c95ab4e562 100644 --- a/mayan/apps/acls/classes.py +++ b/mayan/apps/acls/classes.py @@ -40,20 +40,10 @@ class ModelPermission(object): app_label='permissions', model_name='StoredPermission' ) - permissions = [] - - class_permissions = cls.get_for_class(klass=type(instance)) - - if class_permissions: - permissions.extend(class_permissions) - - proxy = cls._proxies.get(type(instance)) - - if proxy: - permissions.extend(cls._registry.get(proxy)) + permissions = cls.get_for_class(klass=type(instance)) pks = [ - permission.stored_permission.pk for permission in set(permissions) + permission.stored_permission.pk for permission in permissions ] return StoredPermission.objects.filter(pk__in=pks) diff --git a/mayan/apps/acls/managers.py b/mayan/apps/acls/managers.py index d113be9254..64bb239dd3 100644 --- a/mayan/apps/acls/managers.py +++ b/mayan/apps/acls/managers.py @@ -40,9 +40,9 @@ class AccessControlListManager(models.Manager): # 2: Related field # 3: Related field that is Generic Foreign Key # 4: No related field, but has an inherited related field, solved by - # 5: Inherited field of a related field # recursion, branches to #2 or #3. - # Not addressed yet + # 5: Inherited field of a related field + # -- Not addressed yet -- # 6: Inherited field of a related field that is Generic Foreign Key result = [] @@ -152,41 +152,56 @@ class AccessControlListManager(models.Manager): raise PermissionDenied def get_inherited_permissions(self, obj, role): - try: - instance = obj.first() - except AttributeError: - instance = obj - else: - if not instance: - return StoredPermission.objects.none() + queryset = self._get_inherited_object_permissions(obj=obj, role=role) + + queryset = queryset | role.permissions.all() + + # Filter the permissions to the ones that apply to the model + queryset = ModelPermission.get_for_instance( + instance=obj + ).filter( + pk__in=queryset + ) + + return queryset + + def _get_inherited_object_permissions(self, obj, role): + queryset = StoredPermission.objects.none() + + if not obj: + return queryset try: parent_accessor = ModelPermission.get_inheritance( - model=type(instance) + model=type(obj) ) except KeyError: - return StoredPermission.objects.none() + pass else: try: parent_object = resolve_attribute( - obj=instance, attribute=parent_accessor + obj=obj, attribute=parent_accessor ) except AttributeError: # Parent accessor is not an attribute, try it as a related # field. parent_object = return_related( - instance=instance, related_field=parent_accessor + instance=obj, related_field=parent_accessor ) content_type = ContentType.objects.get_for_model(model=parent_object) try: - return self.get( + queryset = queryset | self.get( content_type=content_type, object_id=parent_object.pk, role=role ).permissions.all() except self.model.DoesNotExist: - return StoredPermission.objects.none() + pass - def grant(self, permission, role, obj): + queryset = queryset | self._get_inherited_object_permissions(obj=parent_object, role=role) + + return queryset + + def grant(self, obj, permission, role): class_permissions = ModelPermission.get_for_class(klass=obj.__class__) if permission not in class_permissions: raise PermissionNotValidForClass @@ -224,7 +239,7 @@ class AccessControlListManager(models.Manager): # is staff. Return the entire queryset. return queryset - def revoke(self, permission, role, obj): + def revoke(self, obj, permission, role): content_type = ContentType.objects.get_for_model(model=obj) acl, created = self.get_or_create( content_type=content_type, object_id=obj.pk, diff --git a/mayan/apps/acls/tests/mixins.py b/mayan/apps/acls/tests/mixins.py index 7ea4237294..1f7186b4bd 100644 --- a/mayan/apps/acls/tests/mixins.py +++ b/mayan/apps/acls/tests/mixins.py @@ -40,12 +40,11 @@ class ACLTestMixin(object): if self.auto_create_test_role: self._create_test_role() - self.test_object = self.document - - content_type = ContentType.objects.get_for_model(self.test_object) + def _inject_test_object_content_type(self): + self.test_object_content_type = ContentType.objects.get_for_model(self.test_object) self.test_content_object_view_kwargs = { - 'app_label': content_type.app_label, - 'model': content_type.model, + 'app_label': self.test_object_content_type.app_label, + 'model_name': self.test_object_content_type.model, 'object_id': self.test_object.pk } diff --git a/mayan/apps/acls/tests/test_models.py b/mayan/apps/acls/tests/test_models.py index e7657094a1..ab2c8e144b 100644 --- a/mayan/apps/acls/tests/test_models.py +++ b/mayan/apps/acls/tests/test_models.py @@ -1,16 +1,22 @@ from __future__ import absolute_import, unicode_literals from django.core.exceptions import PermissionDenied +from django.db import models from mayan.apps.common.tests import BaseTestCase +from mayan.apps.common.tests.mixins import TestModelTestMixin from mayan.apps.documents.models import Document, DocumentType from mayan.apps.documents.permissions import permission_document_view from mayan.apps.documents.tests import ( DocumentTestMixin, TEST_DOCUMENT_TYPE_2_LABEL, TEST_DOCUMENT_TYPE_LABEL ) +from mayan.apps.permissions.tests.mixins import PermissionTestMixin, RoleTestMixin +from ..classes import ModelPermission from ..models import AccessControlList +from .mixins import ACLTestMixin + class PermissionTestCase(DocumentTestMixin, BaseTestCase): auto_create_document_type = False @@ -150,3 +156,142 @@ class PermissionTestCase(DocumentTestMixin, BaseTestCase): self.assertTrue(self.test_document_1 in result) self.assertTrue(self.test_document_2 in result) self.assertTrue(self.test_document_3 in result) + + +class InheritedPermissionTestCase(TestModelTestMixin, PermissionTestMixin, RoleTestMixin, ACLTestMixin, BaseTestCase): + def test_retrieve_inherited_role_permission_not_model_applicable(self): + self._create_test_model() + self.test_object = self.TestModel.objects.create() + self._create_test_acl() + self._create_test_permission() + + self.test_role.grant(permission=self.test_permission) + + queryset = AccessControlList.objects.get_inherited_permissions( + obj=self.test_object, role=self.test_role + ) + self.assertTrue(self.test_permission.stored_permission not in queryset) + + def test_retrieve_inherited_role_permission_model_applicable(self): + self._create_test_model() + self.test_object = self.TestModel.objects.create() + self._create_test_acl() + self._create_test_permission() + + ModelPermission.register( + model=self.test_object._meta.model, permissions=( + self.test_permission, + ) + ) + self.test_role.grant(permission=self.test_permission) + + queryset = AccessControlList.objects.get_inherited_permissions( + obj=self.test_object, role=self.test_role + ) + self.assertTrue(self.test_permission.stored_permission in queryset) + + def test_retrieve_inherited_related_parent_child_permission(self): + self._create_test_permission() + + self._create_test_model(model_name='TestModelParent') + self._create_test_model( + fields={ + 'parent': models.ForeignKey( + on_delete=models.CASCADE, related_name='children', + to='TestModelParent', + ) + }, model_name='TestModelChild' + ) + + ModelPermission.register( + model=self.TestModelParent, permissions=( + self.test_permission, + ) + ) + ModelPermission.register( + model=self.TestModelChild, permissions=( + self.test_permission, + ) + ) + ModelPermission.register_inheritance( + model=self.TestModelChild, related='parent', + ) + + parent = self.TestModelParent.objects.create() + child = self.TestModelChild.objects.create(parent=parent) + + AccessControlList.objects.grant( + obj=parent, permission=self.test_permission, role=self.test_role + ) + + queryset = AccessControlList.objects.get_inherited_permissions( + obj=child, role=self.test_role + ) + + self.assertTrue(self.test_permission.stored_permission in queryset) + + #self._delete_test_model(model_name='TestModelParent') + #elf._delete_test_model(model_name='TestModelChild') + + def test_retrieve_inherited_related_grandparent_parent_child_permission(self): + self._create_test_permission() + + self._create_test_model(model_name='TestModelGrandParent') + self._create_test_model( + fields={ + 'parent': models.ForeignKey( + on_delete=models.CASCADE, related_name='children', + to='TestModelGrandParent', + ) + }, model_name='TestModelParent' + ) + self._create_test_model( + fields={ + 'parent': models.ForeignKey( + on_delete=models.CASCADE, related_name='children', + to='TestModelParent', + ) + }, model_name='TestModelChild' + ) + + ModelPermission.register( + model=self.TestModelGrandParent, permissions=( + self.test_permission, + ) + ) + ModelPermission.register( + model=self.TestModelParent, permissions=( + self.test_permission, + ) + ) + ModelPermission.register( + model=self.TestModelChild, permissions=( + self.test_permission, + ) + ) + + ModelPermission.register_inheritance( + model=self.TestModelChild, related='parent', + ) + ModelPermission.register_inheritance( + model=self.TestModelParent, related='parent', + ) + + grandparent = self.TestModelGrandParent.objects.create() + parent = self.TestModelParent.objects.create(parent=grandparent) + child = self.TestModelChild.objects.create(parent=parent) + + AccessControlList.objects.grant( + obj=grandparent, permission=self.test_permission, + role=self.test_role + ) + + queryset = AccessControlList.objects.get_inherited_permissions( + obj=child, role=self.test_role + ) + + self.assertTrue(self.test_permission.stored_permission in queryset) + + #self._delete_test_model(model_name='TestModelGrandParent') + #self._delete_test_model(model_name='TestModelParent') + #self._delete_test_model(model_name='TestModelChild')