diff --git a/aleksis/core/mixins.py b/aleksis/core/mixins.py index 2d06ba0dc84bf79edf74cbdbbb25b7d50da33da0..1d347b802941986a183d6a0c61e7038ff5d157a5 100644 --- a/aleksis/core/mixins.py +++ b/aleksis/core/mixins.py @@ -21,6 +21,7 @@ from django.views.generic.edit import DeleteView, ModelFormMixin import reversion from guardian.admin import GuardedModelAdmin +from guardian.core import ObjectPermissionChecker from jsonstore.fields import IntegerField, JSONFieldMixin from material.base import Layout, LayoutNode from rules.contrib.admin import ObjectPermissionsModelAdmin @@ -332,6 +333,10 @@ class ExtensibleModel(models.Model, metaclass=_ExtensibleModelBase): """Dynamically add a new permission to a model.""" cls.extra_permissions.append((name, verbose_name)) + def set_object_permission_checker(self, checker: ObjectPermissionChecker): + """Annotate a ``ObjectPermissionChecker`` for use with permission system.""" + self._permission_checker = checker + def save(self, *args, **kwargs): """Ensure all functionality of our extensions that needs saving gets it.""" # For auto-created remote syncable fields diff --git a/aleksis/core/util/predicates.py b/aleksis/core/util/predicates.py index d107b425bab206b9a1a0c57d0cb4a4e997a82d34..c456ee329e3560c65abc9953a689ad23053736f3 100644 --- a/aleksis/core/util/predicates.py +++ b/aleksis/core/util/predicates.py @@ -1,3 +1,5 @@ +from typing import Optional + from django.contrib.auth.backends import ModelBackend from django.contrib.auth.models import User from django.db.models import Model @@ -7,6 +9,7 @@ from guardian.backends import ObjectPermissionBackend from guardian.shortcuts import get_objects_for_user from rules import predicate +from ..mixins import ExtensibleModel from ..models import Group from .core_helpers import get_content_type_by_perm, get_site_preferences from .core_helpers import has_person as has_person_helper @@ -25,8 +28,20 @@ def check_global_permission(user: User, perm: str) -> bool: return ModelBackend().has_perm(user, perm) -def check_object_permission(user: User, perm: str, obj: Model) -> bool: - """Check whether a user has a permission on a object.""" +def check_object_permission( + user: User, perm: str, obj: Model, checker_obj: Optional[ExtensibleModel] = None +) -> bool: + """Check whether a user has a permission on an object. + + You can provide a custom ``ObjectPermissionChecker`` for prefetching object permissions + by annotating an extensible model with ``set_object_permission_checker``. + This can be the provided object (``obj``) or a special object + which is only used to get the checker class (``checker_obj``). + """ + if not checker_obj: + checker_obj = obj + if hasattr(checker_obj, "_permission_checker"): + return checker_obj._permission_checker.has_perm(perm, obj) return ObjectPermissionBackend().has_perm(user, perm, obj)