diff --git a/src/documents/tests/test_permission_filtering_security.py b/src/documents/tests/test_permission_filtering_security.py index b688ed29f..7b28bcb05 100644 --- a/src/documents/tests/test_permission_filtering_security.py +++ b/src/documents/tests/test_permission_filtering_security.py @@ -256,3 +256,27 @@ class TestPermittedDocumentIdsArbitraryPermission: expected_visible=[], expected_hidden=[doc.pk], ) + + +@pytest.mark.django_db +class TestEmailDocumentPermissionBoundary: + def test_email_action_rejects_document_without_view_permission( + self, + rest_api_client, + ): + owner = User.objects.create_user(username="owner") + requester = User.objects.create_user(username="requester") + rest_api_client.force_authenticate(user=requester) + hidden = DocumentFactory(owner=owner) + + response = rest_api_client.post( + "/api/documents/email/", + { + "documents": [hidden.pk], + "addresses": "someone@example.com", + "subject": "test", + "message": "test", + }, + format="json", + ) + assert response.status_code == 403 diff --git a/src/documents/tests/test_share_link_bundles.py b/src/documents/tests/test_share_link_bundles.py index 0040c5030..9583e7e47 100644 --- a/src/documents/tests/test_share_link_bundles.py +++ b/src/documents/tests/test_share_link_bundles.py @@ -60,7 +60,7 @@ class ShareLinkBundleAPITests(DirectoriesMixin, APITestCase): self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertIn("document_ids", response.data) - @mock.patch("documents.views.has_perms_owner_aware", return_value=False) + @mock.patch("documents.views.permitted_document_ids", return_value=set()) def test_create_bundle_rejects_insufficient_permissions(self, perms_mock) -> None: payload = { "document_ids": [self.document.pk], diff --git a/src/documents/views.py b/src/documents/views.py index 10987f448..46eff8504 100644 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -1925,12 +1925,9 @@ class DocumentViewSet( use_archive_version = validated_data.get("use_archive_version", True) documents = Document.objects.select_related("owner").filter(pk__in=document_ids) - for document in documents: - if request.user is not None and not has_perms_owner_aware( - request.user, - "view_document", - document, - ): + if request.user is not None: + permitted_ids = set(permitted_document_ids(request.user)) + if not all(document.pk in permitted_ids for document in documents): return HttpResponseForbidden("Insufficient permissions") try: @@ -4505,8 +4502,9 @@ class ShareLinkBundleViewSet(PassUserMixin, ModelViewSet[ShareLinkBundle]): ) documents = list(documents_qs) + permitted_ids = set(permitted_document_ids(request.user)) for document in documents: - if not has_perms_owner_aware(request.user, "view_document", document): + if document.pk not in permitted_ids: raise ValidationError( { "document_ids": _(