diff --git a/aleksis/core/tasks.py b/aleksis/core/tasks.py index f391fe05bd6dfd82eaf3e84ddb222bf322881b5f..863bf7e42839a7b79e674f7175b242228256d2d3 100644 --- a/aleksis/core/tasks.py +++ b/aleksis/core/tasks.py @@ -19,11 +19,13 @@ def send_notification(notification: int, resend: bool = False) -> None: def backup_data() -> None: """Backup database and media using django-dbbackup.""" # Assemble command-line options for dbbackup management command - db_options = "-z " * settings.DBBACKUP_COMPRESS_DB + "-e" * settings.DBBACKUP_ENCRYPT_DB - media_options = ( - "-z " * settings.DBBACKUP_COMPRESS_MEDIA + "-e" * settings.DBBACKUP_ENCRYPT_MEDIA + db_options = (["-z"] if settings.DBBACKUP_COMPRESS_DB else []) + ( + ["-e"] if settings.DBBACKUP_ENCRYPT_DB else [] + ) + media_options = (["-z"] if settings.DBBACKUP_COMPRESS_MEDIA else []) + ( + ["-e"] if settings.DBBACKUP_ENCRYPT_MEDIA else [] ) # Hand off to dbbackup's management commands - management.call_command("dbbackup", db_options) - management.call_command("mediabackup", media_options) + management.call_command("dbbackup", *db_options) + management.call_command("mediabackup", *media_options) 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..7fe74e99d4a4618bd8fa0384fd6319ab588055d4 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,20 @@ 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 - + try: + ct_perm = ContentType.objects.get(app_label=perm.split('.', 1)[0], permission__codename=perm.split('.', 1)[1]) + except ContentType.DoesNotExist: + ct_perm = None + 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 diff --git a/aleksis/core/views.py b/aleksis/core/views.py index 968077c9bbe1224972cdd399b72ed118591fe0f0..1ceb588ac9e4ebd984a0b2cb16a186f9d9d3adb4 100644 --- a/aleksis/core/views.py +++ b/aleksis/core/views.py @@ -368,6 +368,7 @@ class SystemStatus(MainView, PermissionRequiredMixin): def get(self, request, *args, **kwargs): status_code = 500 if self.errors else 200 + task_results = [] if "django_celery_results" in settings.INSTALLED_APPS: from django_celery_results.models import TaskResult # noqa @@ -375,11 +376,10 @@ class SystemStatus(MainView, PermissionRequiredMixin): if inspect().registered_tasks(): job_list = list(inspect().registered_tasks().values())[0] - results = [] for job in job_list: - results.append(TaskResult.objects.filter(task_name=job).last()) + task_results.append(TaskResult.objects.filter(task_name=job).order_by("date_done").last()) - context = {"plugins": self.plugins, "status_code": status_code} + context = {"plugins": self.plugins, "status_code": status_code, "tasks": task_results} return self.render_to_response(context, status=status_code) diff --git a/poetry.lock b/poetry.lock index 86a814e190cd2ab13930c35044e129b0828756c7..8d27cbbad95d95e55a3849871117ad816ea6dc6d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -690,7 +690,7 @@ description = "A pluggable framework for adding two-factor authentication to Dja name = "django-otp" optional = false python-versions = "*" -version = "0.9.3" +version = "0.9.4" [package.dependencies] django = ">=1.11" @@ -2154,7 +2154,7 @@ celery = ["Celery", "django-celery-results", "django-celery-beat", "django-celer ldap = ["django-auth-ldap"] [metadata] -content-hash = "a3a9d462489de57ed0d52f922959a885c5502329f81283fd5e29eecfef0c8545" +content-hash = "c8c6c8720cf5493adfea9b718feb249d200e295d42071f7f2663a37811e707e5" python-versions = "^3.7" [metadata.files] @@ -2413,8 +2413,8 @@ django-middleware-global-request = [ {file = "django-middleware-global-request-0.1.2.tar.gz", hash = "sha256:f6490759bc9f7dbde4001709554e29ca715daf847f2222914b4e47117dca9313"}, ] django-otp = [ - {file = "django-otp-0.9.3.tar.gz", hash = "sha256:d2390e61794bc10dea2fd949cbcfb7946e9ae4fb248df5494ccc4ef9ac50427e"}, - {file = "django_otp-0.9.3-py3-none-any.whl", hash = "sha256:97849f7bf1b50c4c36a5845ab4d2e11dd472fa8e6bcc34fe18b6d3af6e4aa449"}, + {file = "django-otp-0.9.4.tar.gz", hash = "sha256:50e54bc09bc435e2ad88f0aa7008718079c3529c422b469b3991a97d28b147bb"}, + {file = "django_otp-0.9.4-py3-none-any.whl", hash = "sha256:6b92c69021558765e80411479a01788977106d5696c391d2e5342074c1dd74d1"}, ] django-otp-yubikey = [ {file = "django-otp-yubikey-0.5.2.tar.gz", hash = "sha256:f0b1881562fb42ee9f12c28d284cbdb90d1f0383f2d53a595373b080a19bc261"}, diff --git a/pyproject.toml b/pyproject.toml index f040f012a853acc82ad7f9ad53e058b7ef31edc2..47436ffe25c058ffa328ffc8a76dad96e4275483 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -72,7 +72,7 @@ django-celery-beat = {version="^2.0.0", optional=true} django-celery-email = {version="^3.0.0", optional=true} django-jsonstore = "^0.4.1" django-polymorphic = "^2.1.2" -django-otp = "0.9.3" +django-otp = "0.9.4" django-colorfield = "^0.3.0" django-bleach = "^0.6.1" django-guardian = "^2.2.0"