perf: resolve permitted_document_ids once for root-document version-listing loop

BulkDownloadView.post() previously called has_perms_owner_aware() per row
inside the loop that resolves each document's root and latest version.
Resolve permitted_document_ids(request.user) once before the loop and check
membership by root_doc.pk instead, consistent with the other consolidated
permission-filtering sites.
This commit is contained in:
stumpylog
2026-07-28 07:55:12 -07:00
parent 75f8e9f611
commit 9edcfa8682
2 changed files with 39 additions and 1 deletions
@@ -1,5 +1,6 @@
from __future__ import annotations
from http import HTTPStatus
from unittest.mock import patch
import pytest
@@ -314,3 +315,39 @@ class TestBulkEditChangePermissionBoundary:
format="json",
)
assert response.status_code == 403
@pytest.mark.django_db
class TestBulkDownloadPermissionChecksRootDocument:
def test_permission_checked_on_root_not_on_version(
self,
rest_api_client,
paperless_dirs,
_media_settings,
):
owner = User.objects.create_user(username="owner")
requester = User.objects.create_user(username="requester")
rest_api_client.force_authenticate(user=requester)
root = DocumentFactory(owner=owner)
# a version of root that the requester has NOT been individually granted
version = DocumentFactory(owner=owner, root_document=root, version_index=1)
version.source_path.write_bytes(b"%PDF-1.4 test")
assign_perm("view_document", requester, root) # granted on ROOT only
response = rest_api_client.post(
"/api/documents/bulk_download/",
{"documents": [version.pk]},
format="json",
)
assert (
response.status_code == HTTPStatus.OK
) # visible because root is permitted
stranger = User.objects.create_user(username="mallory")
rest_api_client.force_authenticate(user=stranger)
response = rest_api_client.post(
"/api/documents/bulk_download/",
{"documents": [version.pk]},
format="json",
)
assert response.status_code == HTTPStatus.FORBIDDEN
+2 -1
View File
@@ -3837,9 +3837,10 @@ class BulkDownloadView(DocumentSelectionMixin, GenericAPIView[Any]):
content = serializer.validated_data.get("content")
follow_filename_format = serializer.validated_data.get("follow_formatting")
permitted_ids = set(permitted_document_ids(request.user))
for document in documents:
root_doc = get_root_document(document)
if not has_perms_owner_aware(request.user, "view_document", root_doc):
if root_doc.pk not in permitted_ids:
return HttpResponseForbidden("Insufficient permissions")
versioned_documents.append(
get_latest_version_for_root(