ACLs: Make get_inherited_permissions recursive
Update .get_inherited_permissions() to grab the permissions of an object up the parent tree. Also add the role permissions. Finally filter all the permissions by those that apply to the object. Signed-off-by: Roberto Rosario <Roberto.Rosario@mayan-edms.com>
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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')
|
||||
|
||||
Reference in New Issue
Block a user