diff --git a/aleksis/core/util/core_helpers.py b/aleksis/core/util/core_helpers.py index d188f6cf29c6909809ec03f81a7601dbcc41c4f9..8e424174c6658feb19cf02295400c4c4946dbd38 100644 --- a/aleksis/core/util/core_helpers.py +++ b/aleksis/core/util/core_helpers.py @@ -9,7 +9,7 @@ from typing import Any, Callable, Optional, Sequence, Union from uuid import uuid4 from django.conf import settings -from django.db.models import Model +from django.db.models import Model, QuerySet from django.http import HttpRequest from django.shortcuts import get_object_or_404 from django.utils import timezone @@ -17,6 +17,8 @@ from django.utils.functional import lazy from django_global_request.middleware import get_request +from cache_memoize import cache_memoize + from aleksis.core.util import messages @@ -357,3 +359,20 @@ def handle_uploaded_file(f, filename: str): with open(filename, "wb+") as destination: for chunk in f.chunks(): destination.write(chunk) + + +@cache_memoize(3600) +def queryset_rules_filter( + obj: Union[HttpRequest, Model], queryset: QuerySet, perm: str +) -> QuerySet: + """Filter queryset by user and permission.""" + + wanted_objects = set() + if isinstance(obj, HttpRequest) and hasattr(obj, "user"): + obj = obj.user + + for item in queryset: + if obj.has_perm(perm, item): + wanted_objects.add(item.pk) + + return queryset.filter(pk__in=wanted_objects) diff --git a/aleksis/core/util/predicates.py b/aleksis/core/util/predicates.py index 975273d7d934ae41df8ecefb494b121ed69b2a94..6e50a6b38acd04db565faacaec92c627b0cbb53f 100644 --- a/aleksis/core/util/predicates.py +++ b/aleksis/core/util/predicates.py @@ -1,5 +1,6 @@ from django.contrib.auth.backends import ModelBackend from django.contrib.auth.models import User +from django.contrib.contenttypes.models import ContentType from django.db.models import Model from django.http import HttpRequest @@ -8,7 +9,7 @@ from guardian.shortcuts import get_objects_for_user from rules import predicate from ..models import Group -from .core_helpers import get_site_preferences +from .core_helpers import get_site_preferences, queryset_rules_filter from .core_helpers import has_person as has_person_helper @@ -57,15 +58,17 @@ def has_any_object(perm: str, klass): """Check if has any object. Build predicate which checks whether a user has access - to objects with the provided permission. + to objects with the provided permission or rule. """ name = f"has_any_object:{perm}" @predicate(name) def fn(user: User) -> bool: - objs = get_objects_for_user(user, perm, klass) - return len(objs) > 0 - + ct_perm = ContentType.objects.get(app_label=perm.split('.', 1)[0], permission__codename=perm.split('.', 1)[1]) + if ct_perm and ct_perm.model_class() == klass: + return get_objects_for_user(user, perm, klass).exists() + else: + return queryset_rules_filter(user, klass.objects.all(), perm).exists() return fn