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:
Roberto Rosario
2019-03-02 01:51:23 -04:00
parent 48aad4f356
commit 0cbd9e0d45
4 changed files with 183 additions and 34 deletions

View File

@@ -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)

View File

@@ -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,

View File

@@ -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
}

View File

@@ -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')