Chore: Add shared helpers for granting test permissions and use them

This commit is contained in:
stumpylog
2026-09-21 08:08:49 -07:00
parent 2aff194bd3
commit afc151f78d
29 changed files with 319 additions and 430 deletions
+4 -4
View File
@@ -5,7 +5,6 @@ import pytest
from django.contrib.auth.models import Group
from django.db import connection
from django.test.utils import CaptureQueriesContext
from guardian.shortcuts import assign_perm
from pytest_mock import MockerFixture
from documents.models import CustomField
@@ -23,6 +22,7 @@ from paperless_testing.factories import DocumentFactory
from paperless_testing.factories import DocumentTypeFactory
from paperless_testing.factories import TagFactory
from paperless_testing.factories import UserFactory
from paperless_testing.permissions import grant_object
pytestmark = [pytest.mark.search, pytest.mark.django_db]
@@ -188,7 +188,7 @@ class TestAddOrUpdateIds:
pk=1,
owner=owner,
)
assign_perm("view_document", user, doc)
grant_object(user, doc, "view_document")
with backend.batch_update() as batch:
batch.add_or_update_ids([doc.pk])
@@ -208,7 +208,7 @@ class TestAddOrUpdateIds:
pk=1,
owner=owner,
)
assign_perm("view_document", group, doc)
grant_object(group, doc, "view_document")
with backend.batch_update() as batch:
batch.add_or_update_ids([doc.pk])
@@ -842,7 +842,7 @@ class TestRebuild:
content="group secret keyword",
owner=owner,
)
assign_perm("view_document", group, doc)
grant_object(group, doc, "view_document")
backend.rebuild(Document.objects.all())
@@ -18,7 +18,6 @@ from typing import TYPE_CHECKING
import pytest
from django.contrib.auth.models import Group
from django.contrib.auth.models import User
from guardian.shortcuts import assign_perm
from documents.models import Correspondent
from documents.models import Document
@@ -26,6 +25,7 @@ from documents.models import DocumentType
from documents.models import StoragePath
from documents.models import Tag
from paperless_testing.factories import UserFactory
from paperless_testing.permissions import grant_object
if TYPE_CHECKING:
from documents.search._backend import TantivyBackend
@@ -128,7 +128,7 @@ class TestPermissionFilteringOnIndexedDocuments:
checksum="perm-shared-user",
owner=owner,
)
assign_perm("view_document", viewer, doc)
grant_object(viewer, doc, "view_document")
backend.add_or_update(doc)
assert backend.search_ids("invoice", user=viewer) == [doc.pk]
@@ -158,7 +158,7 @@ class TestPermissionFilteringOnIndexedDocuments:
checksum="perm-shared-group",
owner=owner,
)
assign_perm("view_document", group_member.groups.first(), doc)
grant_object(group_member.groups.first(), doc, "view_document")
backend.add_or_update(doc)
assert backend.search_ids("invoice", user=group_member) == [doc.pk]
@@ -4,7 +4,6 @@ import json
import shutil
import zipfile
from django.contrib.auth.models import Permission
from django.test import override_settings
from django.utils import timezone
from rest_framework import status
@@ -17,6 +16,7 @@ from documents.tests.utils import SampleDirMixin
from documents.tests.utils import read_streaming_response
from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.factories import UserFactory
from paperless_testing.permissions import grant_global
class TestBulkDownload(DirectoriesMixin, SampleDirMixin, APITestCase):
@@ -327,9 +327,7 @@ class TestBulkDownload(DirectoriesMixin, SampleDirMixin, APITestCase):
def test_download_insufficient_permissions(self) -> None:
user = UserFactory(username="temp_user")
user.user_permissions.add(
Permission.objects.get(codename="view_document"),
)
grant_global(user, "view_document")
self.client.force_authenticate(user=user)
self.doc2.owner = self.user
+17 -26
View File
@@ -2,10 +2,8 @@ import json
from unittest import mock
from auditlog.models import LogEntry
from django.contrib.auth.models import Permission
from django.contrib.auth.models import User
from django.test import override_settings
from guardian.shortcuts import assign_perm
from rest_framework import status
from rest_framework.test import APITestCase
@@ -17,6 +15,9 @@ from documents.models import StoragePath
from documents.models import Tag
from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.factories import UserFactory
from paperless_testing.permissions import grant_all_global
from paperless_testing.permissions import grant_global
from paperless_testing.permissions import grant_object
class TestBulkEditAPI(DirectoriesMixin, APITestCase):
@@ -286,7 +287,7 @@ class TestBulkEditAPI(DirectoriesMixin, APITestCase):
) -> None:
self.setup_mock(m, "modify_custom_fields")
user = UserFactory(username="doc-owner")
user.user_permissions.add(Permission.objects.get(codename="change_document"))
grant_global(user, "change_document")
other_user = UserFactory(username="other-user")
source_doc = Document.objects.create(
checksum="source",
@@ -789,9 +790,7 @@ class TestBulkEditAPI(DirectoriesMixin, APITestCase):
def test_api_bulk_edit_with_all_true_resolves_owned_duplicates(self, m) -> None:
self.setup_mock(m, "set_storage_path")
user = UserFactory(username="duplicate-owner")
user.user_permissions.add(
Permission.objects.get(codename="change_document"),
)
grant_global(user, "change_document")
first_duplicate = Document.objects.create(
checksum="owned-duplicate",
title="First duplicate",
@@ -1179,7 +1178,7 @@ class TestBulkEditAPI(DirectoriesMixin, APITestCase):
user1 = User.objects.create(username="user1")
self.client.force_authenticate(user=user1)
assign_perm("view_document", user1, self.doc2)
grant_object(user1, self.doc2, "view_document")
response = self.client.post(
"/api/documents/selection_data/",
@@ -1189,9 +1188,7 @@ class TestBulkEditAPI(DirectoriesMixin, APITestCase):
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
user1.user_permissions.add(
Permission.objects.get(codename="view_document"),
)
grant_global(user1, "view_document")
user1 = User.objects.get(pk=user1.pk)
self.client.force_authenticate(user=user1)
response = self.client.post(
@@ -1534,7 +1531,7 @@ class TestBulkEditAPI(DirectoriesMixin, APITestCase):
self.doc1.owner = User.objects.get(username="temp_admin")
self.doc1.save()
user1 = User.objects.create(username="user1")
user1.user_permissions.add(*Permission.objects.all())
grant_all_global(user1)
user1.save()
self.client.force_authenticate(user=user1)
@@ -1588,8 +1585,8 @@ class TestBulkEditAPI(DirectoriesMixin, APITestCase):
self.doc1.owner = User.objects.get(username="temp_admin")
self.doc1.save()
user1 = User.objects.create(username="user1")
assign_perm("view_document", user1, self.doc1)
user1.user_permissions.add(*Permission.objects.all())
grant_object(user1, self.doc1, "view_document")
grant_all_global(user1)
user1.save()
self.client.force_authenticate(user=user1)
@@ -1610,7 +1607,7 @@ class TestBulkEditAPI(DirectoriesMixin, APITestCase):
m.assert_not_called()
self.assertEqual(response.content, b"Insufficient permissions")
assign_perm("change_document", user1, self.doc1)
grant_object(user1, self.doc1, "change_document")
response = self.client.post(
"/api/documents/bulk_edit/",
@@ -1820,7 +1817,7 @@ class TestBulkEditAPI(DirectoriesMixin, APITestCase):
self.doc1.owner = User.objects.get(username="temp_admin")
self.doc1.save()
user1 = User.objects.create(username="user1")
user1.user_permissions.add(*Permission.objects.all())
grant_all_global(user1)
user1.save()
self.client.force_authenticate(user=user1)
@@ -1881,7 +1878,7 @@ class TestBulkEditAPI(DirectoriesMixin, APITestCase):
self.doc1.owner = User.objects.get(username="temp_admin")
self.doc1.save()
user1 = User.objects.create(username="user1")
user1.user_permissions.add(*Permission.objects.all())
grant_all_global(user1)
user1.save()
self.client.force_authenticate(user=user1)
@@ -1921,10 +1918,7 @@ class TestBulkEditAPI(DirectoriesMixin, APITestCase):
def test_merge_and_delete_requires_change_permission(self, m) -> None:
self.setup_mock(m, "merge")
user = UserFactory(username="no-change")
user.user_permissions.add(
Permission.objects.get(codename="add_document"),
Permission.objects.get(codename="delete_document"),
)
grant_global(user, "add_document", "delete_document")
self.client.force_authenticate(user=user)
response = self.client.post(
@@ -2311,7 +2305,7 @@ class TestBulkEditAPI(DirectoriesMixin, APITestCase):
self.doc1.owner = User.objects.get(username="temp_admin")
self.doc1.save()
user1 = User.objects.create(username="user1")
user1.user_permissions.add(*Permission.objects.all())
grant_all_global(user1)
user1.save()
self.client.force_authenticate(user=user1)
@@ -2374,10 +2368,7 @@ class TestBulkEditAPI(DirectoriesMixin, APITestCase):
self.setup_mock(edit_pdf_mock, "edit_pdf")
self.setup_mock(remove_password_mock, "remove_password")
user = UserFactory(username="no-delete")
user.user_permissions.add(
Permission.objects.get(codename="add_document"),
Permission.objects.get(codename="change_document"),
)
grant_global(user, "add_document", "change_document")
self.client.force_authenticate(user=user)
cases = [
@@ -2464,7 +2455,7 @@ class TestBulkEditAPI(DirectoriesMixin, APITestCase):
self.doc1.owner = User.objects.get(username="temp_admin")
self.doc1.save()
user1 = User.objects.create(username="user1")
user1.user_permissions.add(*Permission.objects.all())
grant_all_global(user1)
user1.save()
self.client.force_authenticate(user=user1)
+3 -5
View File
@@ -4,15 +4,15 @@ from typing import TYPE_CHECKING
from unittest import mock
import pytest
from django.contrib.auth.models import Permission
from django.contrib.auth.models import User
from rest_framework import status
from rest_framework.test import APIClient
from rest_framework.test import APITestCase
from paperless_testing.factories import UserFactory
from paperless_testing.permissions import grant_global
if TYPE_CHECKING:
from django.contrib.auth.models import User
from pytest_mock import MockerFixture
@@ -115,9 +115,7 @@ class TestChatStreamingViewUnrestrictedFlag:
needs to reach the view at all. Model-level only: says nothing
about which documents (if any) this user can actually see.
"""
regular_user.user_permissions.add(
*Permission.objects.filter(codename="view_document"),
)
grant_global(regular_user, "view_document")
return user_client
@pytest.mark.parametrize(
@@ -5,7 +5,6 @@ from unittest.mock import ANY
from django.contrib.auth.models import Permission
from django.test import override_settings
from guardian.shortcuts import assign_perm
from rest_framework import status
from rest_framework.test import APITestCase
@@ -14,6 +13,8 @@ from documents.models import CustomFieldInstance
from documents.models import Document
from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.factories import UserFactory
from paperless_testing.permissions import grant_global
from paperless_testing.permissions import grant_object
class TestCustomFieldsAPI(DirectoriesMixin, APITestCase):
@@ -1175,9 +1176,7 @@ class TestCustomFieldsAPI(DirectoriesMixin, APITestCase):
self,
) -> None:
source_owner = UserFactory(username="source-owner")
source_owner.user_permissions.add(
Permission.objects.get(codename="change_document"),
)
grant_global(source_owner, "change_document")
other_user = UserFactory(username="other-user")
source_doc = Document.objects.create(
@@ -1222,9 +1221,7 @@ class TestCustomFieldsAPI(DirectoriesMixin, APITestCase):
self,
) -> None:
source_owner = UserFactory(username="source-owner")
source_owner.user_permissions.add(
Permission.objects.get(codename="change_document"),
)
grant_global(source_owner, "change_document")
other_user = UserFactory(username="other-user")
source_doc = Document.objects.create(
@@ -1244,7 +1241,7 @@ class TestCustomFieldsAPI(DirectoriesMixin, APITestCase):
data_type=CustomField.FieldDataType.DOCUMENTLINK,
)
assign_perm("change_document", source_owner, target_doc)
grant_object(source_owner, target_doc, "change_document")
self.client.force_authenticate(user=source_owner)
resp = self.client.patch(
@@ -5,7 +5,6 @@ from typing import TYPE_CHECKING
from unittest import mock
from auditlog.models import LogEntry # type: ignore[import-untyped]
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import TestCase as DjangoTestCase
@@ -22,6 +21,7 @@ from documents.versioning import annotate_effective_content
from documents.views import DocumentSelectionMixin
from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.factories import UserFactory
from paperless_testing.permissions import grant_global
if TYPE_CHECKING:
from pathlib import Path
@@ -91,9 +91,7 @@ class TestDocumentVersioningApi(DirectoriesMixin, APITestCase):
def test_root_endpoint_returns_403_when_user_lacks_permission(self) -> None:
owner = UserFactory(username="owner")
viewer = UserFactory(username="viewer")
viewer.user_permissions.add(
Permission.objects.get(codename="view_document"),
)
grant_global(viewer, "view_document")
root = Document.objects.create(
title="root",
checksum="root",
@@ -285,9 +283,7 @@ class TestDocumentVersioningApi(DirectoriesMixin, APITestCase):
def test_delete_version_returns_403_without_permission(self) -> None:
owner = UserFactory(username="owner")
other = UserFactory(username="other")
other.user_permissions.add(
Permission.objects.get(codename="delete_document"),
)
grant_global(other, "delete_document")
root = Document.objects.create(
title="root",
checksum="root",
@@ -373,9 +369,7 @@ class TestDocumentVersioningApi(DirectoriesMixin, APITestCase):
def test_update_version_label_returns_403_without_permission(self) -> None:
owner = UserFactory(username="owner")
other = UserFactory(username="other")
other.user_permissions.add(
Permission.objects.get(codename="change_document"),
)
grant_global(other, "change_document")
root = Document.objects.create(
title="root",
checksum="root",
@@ -555,9 +549,7 @@ class TestDocumentVersioningApi(DirectoriesMixin, APITestCase):
def test_metadata_returns_403_when_user_lacks_permission(self) -> None:
owner = UserFactory(username="owner")
other = UserFactory(username="other")
other.user_permissions.add(
Permission.objects.get(codename="view_document"),
)
grant_global(other, "view_document")
doc = Document.objects.create(
title="root",
checksum="root",
@@ -673,7 +665,7 @@ class TestDocumentVersioningApi(DirectoriesMixin, APITestCase):
def test_update_version_requires_global_change_permission(self) -> None:
user = UserFactory(username="add-only")
user.user_permissions.add(Permission.objects.get(codename="add_document"))
grant_global(user, "add_document")
root = Document.objects.create(
title="root",
checksum="root",
+39 -53
View File
@@ -23,7 +23,6 @@ from django.core.files.uploadedfile import SimpleUploadedFile
from django.db import DataError
from django.test import override_settings
from django.utils import timezone
from guardian.shortcuts import assign_perm
from rest_framework import status
from rest_framework.test import APITestCase
@@ -54,6 +53,9 @@ from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.factories import DocumentFactory
from paperless_testing.factories import TagFactory
from paperless_testing.factories import UserFactory
from paperless_testing.permissions import grant_all_global
from paperless_testing.permissions import grant_global
from paperless_testing.permissions import grant_object
class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
@@ -360,8 +362,8 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
user1 = UserFactory(username="test1")
user2 = UserFactory(username="test2")
user1.user_permissions.add(*Permission.objects.filter(codename="view_document"))
user2.user_permissions.add(*Permission.objects.filter(codename="view_document"))
grant_global(user1, "view_document")
grant_global(user2, "view_document")
self.client.force_authenticate(user2)
@@ -384,7 +386,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
response = self.client.get(f"/api/documents/{doc.pk}/thumb/")
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
assign_perm("view_document", user2, doc)
grant_object(user2, doc, "view_document")
response = self.client.get(f"/api/documents/{doc.pk}/download/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
@@ -762,7 +764,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
"""
# No auditlog permissions
user = UserFactory(username="test")
user.user_permissions.add(*Permission.objects.filter(codename="view_document"))
grant_global(user, "view_document")
self.client.force_authenticate(user=user)
doc = Document.objects.create(
title="First title",
@@ -1076,9 +1078,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
def test_has_duplicates_filter_respects_document_permissions(self) -> None:
owner = UserFactory(username="duplicate-owner")
requester = UserFactory(username="duplicate-requester")
requester.user_permissions.add(
Permission.objects.get(codename="view_document"),
)
grant_global(requester, "view_document")
visible_document = Document.objects.create(
title="visible document",
checksum="permission-match",
@@ -1097,7 +1097,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
[document["id"] for document in response.data["results"]],
)
assign_perm("view_document", requester, hidden_duplicate)
grant_object(requester, hidden_duplicate, "view_document")
response = self.client.get("/api/documents/?has_duplicates=true")
self.assertIn(
visible_document.id,
@@ -1320,8 +1320,8 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
"""
u1 = UserFactory(username="user1")
u2 = UserFactory(username="user2")
u1.user_permissions.add(*Permission.objects.filter(codename="view_document"))
u2.user_permissions.add(*Permission.objects.filter(codename="view_document"))
grant_global(u1, "view_document")
grant_global(u2, "view_document")
u1_doc1 = Document.objects.create(
title="none1",
@@ -1354,7 +1354,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
)
self.client.force_authenticate(user=u1)
assign_perm("view_document", u1, u2_doc2)
grant_object(u1, u2_doc2, "view_document")
# Will not show any u1 docs or u2_doc1 which isn't shared
response = self.client.get(f"/api/documents/?owner__id__none={u1.id}")
@@ -1401,7 +1401,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
[u1_doc1.id, u1_doc2.id, u2_doc2.id],
)
assign_perm("view_document", u2, u1_doc1)
grant_object(u2, u1_doc1, "view_document")
# Will show only documents shared by user
response = self.client.get(f"/api/documents/?shared_by__id={u1.id}")
@@ -1426,7 +1426,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
(regression test for https://github.com/paperless-ngx/paperless-ngx/issues/13331)
"""
user = UserFactory(username="user1")
user.user_permissions.add(*Permission.objects.filter(codename="view_document"))
grant_global(user, "view_document")
group = Group.objects.create(name="group1")
user.groups.add(group)
@@ -1434,7 +1434,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
tag2 = TagFactory()
doc = DocumentFactory(title="shared", owner=user)
doc.tags.add(tag1, tag2)
assign_perm("view_document", group, doc)
grant_object(group, doc, "view_document")
self.client.force_authenticate(user=user)
response = self.client.get(
@@ -1455,9 +1455,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
"""
owner = UserFactory(username="owner1")
stranger = UserFactory(username="stranger1")
stranger.user_permissions.add(
*Permission.objects.filter(codename="view_document"),
)
grant_global(stranger, "view_document")
DocumentFactory(title="private", owner=owner)
@@ -1479,13 +1477,13 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
member = UserFactory(username="member1")
non_member = UserFactory(username="nonmember1")
for u in (member, non_member):
u.user_permissions.add(*Permission.objects.filter(codename="view_document"))
grant_global(u, "view_document")
group = Group.objects.create(name="group2")
member.groups.add(group)
doc = DocumentFactory(title="shared2", owner=owner)
assign_perm("view_document", group, doc)
grant_object(group, doc, "view_document")
self.client.force_authenticate(user=member)
response = self.client.get("/api/documents/")
@@ -1819,9 +1817,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
def test_statistics_with_statistics_permission(self) -> None:
owner = UserFactory(username="owner")
stats_user = UserFactory(username="stats-user")
stats_user.user_permissions.add(
Permission.objects.get(codename="view_global_statistics"),
)
grant_global(stats_user, "view_global_statistics")
inbox_tag = Tag.objects.create(
name="stats_inbox",
@@ -2808,9 +2804,9 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
sort_field="",
)
assign_perm("view_savedview", u1, v2)
assign_perm("change_savedview", u1, v2)
assign_perm("view_savedview", u1, v3)
grant_object(u1, v2, "view_savedview")
grant_object(u1, v2, "change_savedview")
grant_object(u1, v3, "view_savedview")
self.client.force_authenticate(user=u1)
@@ -3570,7 +3566,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
- Notes are neither created nor deleted
"""
user1 = UserFactory(username="test1")
user1.user_permissions.add(*Permission.objects.all())
grant_all_global(user1)
user1.save()
user2 = UserFactory(username="test2")
@@ -3593,7 +3589,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
self.assertEqual(resp.content, b"Insufficient permissions to view notes")
self.assertEqual(resp.status_code, status.HTTP_403_FORBIDDEN)
assign_perm("view_document", user1, doc)
grant_object(user1, doc, "view_document")
resp = self.client.post(
f"/api/documents/{doc.pk}/notes/",
@@ -3618,11 +3614,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
def test_notes_require_global_document_permissions(self) -> None:
user = UserFactory(username="note_editor")
user.user_permissions.add(
*Permission.objects.filter(
codename__in=["view_note", "add_note", "delete_note"],
),
)
grant_global(user, "view_note", "add_note", "delete_note")
doc = Document.objects.create(
title="test",
mime_type="application/pdf",
@@ -3635,9 +3627,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
response = self.client.get(f"/api/documents/{doc.pk}/notes/")
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
user.user_permissions.add(
Permission.objects.get(codename="view_document"),
)
grant_global(user, "view_document")
user = User.objects.get(pk=user.pk)
self.client.force_authenticate(user)
response = self.client.get(f"/api/documents/{doc.pk}/notes/")
@@ -3649,9 +3639,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
user.user_permissions.add(
Permission.objects.get(codename="change_document"),
)
grant_global(user, "change_document")
user = User.objects.get(pk=user.pk)
self.client.force_authenticate(user)
response = self.client.post(
@@ -3799,11 +3787,11 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
- Non-unique items are not allowed
"""
user1 = UserFactory(username="test1")
user1.user_permissions.add(*Permission.objects.filter(codename="add_tag"))
grant_global(user1, "add_tag")
user1.save()
user2 = UserFactory(username="test2")
user2.user_permissions.add(*Permission.objects.filter(codename="add_tag"))
grant_global(user2, "add_tag")
user2.save()
# User 1 creates tag 1 owned by user 1 by default
@@ -3859,11 +3847,11 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
- Non-unique items are not allowed on update
"""
user1 = UserFactory(username="test1")
user1.user_permissions.add(*Permission.objects.filter(codename="change_tag"))
grant_global(user1, "change_tag")
user1.save()
user2 = UserFactory(username="test2")
user2.user_permissions.add(*Permission.objects.filter(codename="change_tag"))
grant_global(user2, "change_tag")
user2.save()
# Create name tag 1 owned by user 1
@@ -3995,7 +3983,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
- Links only shown if user has permissions
"""
user1 = UserFactory(username="test1")
user1.user_permissions.add(*Permission.objects.all())
grant_all_global(user1)
user1.save()
user2 = UserFactory(username="test2")
@@ -4018,7 +4006,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
self.assertEqual(resp.content, b"Insufficient permissions to add share link")
self.assertEqual(resp.status_code, status.HTTP_403_FORBIDDEN)
assign_perm("change_document", user1, doc)
grant_object(user1, doc, "change_document")
resp = self.client.get(
f"/api/documents/{doc.pk}/share_links/",
@@ -4036,7 +4024,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
- Share link creation is denied until view permission is granted
"""
user1 = UserFactory(username="test1")
user1.user_permissions.add(*Permission.objects.filter(codename="add_sharelink"))
grant_global(user1, "add_sharelink")
user1.save()
user2 = UserFactory(username="test2")
@@ -4061,7 +4049,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
)
self.assertEqual(create_resp.status_code, status.HTTP_403_FORBIDDEN)
assign_perm("view_document", user1, doc)
grant_object(user1, doc, "view_document")
create_resp = self.client.post(
"/api/share_links/",
@@ -4073,9 +4061,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
)
self.assertEqual(create_resp.status_code, status.HTTP_403_FORBIDDEN)
user1.user_permissions.add(
Permission.objects.get(codename="view_document"),
)
grant_global(user1, "view_document")
user1 = User.objects.get(pk=user1.pk)
self.client.force_authenticate(user1)
create_resp = self.client.post(
@@ -4099,7 +4085,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
- ASN +1 from user2's doc is returned for user1
"""
user1 = UserFactory(username="test1")
user1.user_permissions.add(*Permission.objects.all())
grant_all_global(user1)
user1.save()
user2 = UserFactory(username="test2")
@@ -4143,7 +4129,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
- ASN 1 is returned
"""
user1 = UserFactory(username="test1")
user1.user_permissions.add(*Permission.objects.all())
grant_all_global(user1)
user1.save()
doc1 = Document.objects.create(
@@ -4350,7 +4336,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
- Error response is returned
"""
user1 = UserFactory(username="test1")
user1.user_permissions.add(*Permission.objects.all())
grant_all_global(user1)
user1.save()
doc = Document.objects.create(
+3 -3
View File
@@ -2,7 +2,6 @@ import json
import shutil
from unittest import mock
from django.contrib.auth.models import Permission
from django.core import mail
from django.test import override_settings
from rest_framework import status
@@ -12,6 +11,7 @@ from documents.models import Document
from documents.tests.utils import SampleDirMixin
from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.factories import UserFactory
from paperless_testing.permissions import grant_global
class TestEmail(DirectoriesMixin, SampleDirMixin, APITestCase):
@@ -304,7 +304,7 @@ class TestEmail(DirectoriesMixin, SampleDirMixin, APITestCase):
- Forbidden response is returned
"""
user1 = UserFactory(username="test1")
user1.user_permissions.add(*Permission.objects.filter(codename="view_document"))
grant_global(user1, "view_document")
doc_owned = Document.objects.create(
title="owned_doc",
@@ -339,7 +339,7 @@ class TestEmail(DirectoriesMixin, SampleDirMixin, APITestCase):
- Request succeeds
"""
user1 = UserFactory(username="test1")
user1.user_permissions.add(*Permission.objects.filter(codename="view_document"))
grant_global(user1, "view_document")
self.client.force_authenticate(user1)
+16 -36
View File
@@ -3,12 +3,10 @@ import json
from unittest import mock
from django.contrib.auth.models import Group
from django.contrib.auth.models import Permission
from django.contrib.auth.models import User
from django.db import connection
from django.test import override_settings
from django.test.utils import CaptureQueriesContext
from guardian.shortcuts import assign_perm
from guardian.shortcuts import get_groups_with_perms
from guardian.shortcuts import get_users_with_perms
from rest_framework import status
@@ -23,6 +21,8 @@ from documents.models import StoragePath
from documents.models import Tag
from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.factories import UserFactory
from paperless_testing.permissions import grant_global
from paperless_testing.permissions import grant_object
class TestApiObjects(DirectoriesMixin, APITestCase):
@@ -164,9 +164,7 @@ class TestApiObjects(DirectoriesMixin, APITestCase):
)
user = UserFactory(username="regular")
user.user_permissions.add(
Permission.objects.get(codename="view_correspondent"),
)
grant_global(user, "view_correspondent")
self.client.force_authenticate(user=user)
response = self.client.get("/api/correspondents/?last_correspondence=true")
@@ -458,9 +456,7 @@ class TestApiStoragePaths(DirectoriesMixin, APITestCase):
def test_test_storage_path_requires_document_view_permission(self) -> None:
owner = UserFactory(username="owner")
unprivileged = UserFactory(username="unprivileged")
unprivileged.user_permissions.add(
Permission.objects.get(codename="view_document"),
)
grant_global(unprivileged, "view_document")
document = Document.objects.create(
mime_type="application/pdf",
owner=owner,
@@ -490,7 +486,7 @@ class TestApiStoragePaths(DirectoriesMixin, APITestCase):
title="Shared",
checksum="123",
)
assign_perm("view_document", viewer, document)
grant_object(viewer, document, "view_document")
self.client.force_authenticate(user=viewer)
response = self.client.post(
@@ -505,9 +501,7 @@ class TestApiStoragePaths(DirectoriesMixin, APITestCase):
)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
viewer.user_permissions.add(
Permission.objects.get(codename="view_document"),
)
grant_global(viewer, "view_document")
viewer = User.objects.get(pk=viewer.pk)
self.client.force_authenticate(user=viewer)
response = self.client.post(
@@ -551,9 +545,7 @@ class TestApiStoragePaths(DirectoriesMixin, APITestCase):
password="password",
email="owner@example.com",
)
owner.user_permissions.add(
Permission.objects.get(codename="view_document"),
)
grant_global(owner, "view_document")
document = Document.objects.create(
mime_type="application/pdf",
owner=owner,
@@ -628,10 +620,8 @@ class TestApiStoragePaths(DirectoriesMixin, APITestCase):
title="Document",
checksum="123",
)
assign_perm("view_document", viewer, document)
viewer.user_permissions.add(
Permission.objects.get(codename="view_document"),
)
grant_object(viewer, document, "view_document")
grant_global(viewer, "view_document")
self.client.force_authenticate(user=viewer)
response = self.client.post(
@@ -718,10 +708,8 @@ class TestApiStoragePaths(DirectoriesMixin, APITestCase):
checksum="123",
)
document.tags.add(private_tag)
assign_perm("view_document", viewer, document)
viewer.user_permissions.add(
Permission.objects.get(codename="view_document"),
)
grant_object(viewer, document, "view_document")
grant_global(viewer, "view_document")
self.client.force_authenticate(user=viewer)
response = self.client.post(
@@ -774,10 +762,8 @@ class TestApiStoragePaths(DirectoriesMixin, APITestCase):
field=custom_field,
value_int=42,
)
assign_perm("view_document", viewer, document)
viewer.user_permissions.add(
Permission.objects.get(codename="view_document"),
)
grant_object(viewer, document, "view_document")
grant_global(viewer, "view_document")
self.client.force_authenticate(user=viewer)
response = self.client.post(
@@ -1031,9 +1017,7 @@ class TestBulkEditObjects(APITestCase):
THEN:
- User is able to delete objects
"""
self.user1.user_permissions.add(
*Permission.objects.filter(codename="delete_tag"),
)
grant_global(self.user1, "delete_tag")
self.user1.save()
self.client.force_authenticate(user=self.user1)
@@ -1063,9 +1047,7 @@ class TestBulkEditObjects(APITestCase):
self.t2.owner = User.objects.get(username="temp_admin")
self.t2.save()
self.user1.user_permissions.add(
*Permission.objects.filter(codename="delete_tag"),
)
grant_global(self.user1, "delete_tag")
self.user1.save()
self.client.force_authenticate(user=self.user1)
@@ -1098,9 +1080,7 @@ class TestBulkEditObjects(APITestCase):
self.t2.owner = User.objects.get(username="temp_admin")
self.t2.save()
self.user1.user_permissions.add(
*Permission.objects.filter(codename="delete_tag"),
)
grant_global(self.user1, "delete_tag")
self.user1.save()
self.client.force_authenticate(user=self.user1)
+32 -47
View File
@@ -5,9 +5,7 @@ from unittest import mock
from allauth.mfa.models import Authenticator
from allauth.mfa.totp.internal import auth as totp_auth
from django.contrib.auth.models import Group
from django.contrib.auth.models import Permission
from django.contrib.auth.models import User
from guardian.shortcuts import assign_perm
from guardian.shortcuts import get_perms
from guardian.shortcuts import get_users_with_perms
from rest_framework import status
@@ -21,6 +19,9 @@ from documents.models import StoragePath
from documents.models import Tag
from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.factories import UserFactory
from paperless_testing.permissions import grant_all_global
from paperless_testing.permissions import grant_global
from paperless_testing.permissions import grant_object
class TestApiAuth(DirectoriesMixin, APITestCase):
@@ -139,7 +140,7 @@ class TestApiAuth(DirectoriesMixin, APITestCase):
def test_api_sufficient_permissions(self) -> None:
user = UserFactory(username="test")
user.user_permissions.add(*Permission.objects.all())
grant_all_global(user)
user.is_staff = True
self.client.force_authenticate(user)
@@ -169,7 +170,7 @@ class TestApiAuth(DirectoriesMixin, APITestCase):
def test_api_get_object_permissions(self) -> None:
user1 = UserFactory(username="test1")
user2 = UserFactory(username="test2")
user1.user_permissions.add(*Permission.objects.filter(codename="view_document"))
grant_global(user1, "view_document")
self.client.force_authenticate(user1)
self.assertEqual(
@@ -420,10 +421,10 @@ class TestApiAuth(DirectoriesMixin, APITestCase):
doc.owner = user1
doc.save()
assign_perm("view_document", user2, doc)
assign_perm("change_document", user2, doc)
assign_perm("view_document", group1, doc)
assign_perm("change_document", group1, doc)
grant_object(user2, doc, "view_document")
grant_object(user2, doc, "change_document")
grant_object(group1, doc, "view_document")
grant_object(group1, doc, "change_document")
self.client.force_authenticate(user1)
@@ -449,9 +450,7 @@ class TestApiAuth(DirectoriesMixin, APITestCase):
def test_document_permissions_change_requires_owner(self) -> None:
owner = UserFactory(username="owner")
editor = UserFactory(username="editor")
editor.user_permissions.add(
*Permission.objects.all(),
)
grant_all_global(editor)
doc = Document.objects.create(
title="Ownered doc",
@@ -461,8 +460,8 @@ class TestApiAuth(DirectoriesMixin, APITestCase):
owner=owner,
)
assign_perm("view_document", editor, doc)
assign_perm("change_document", editor, doc)
grant_object(editor, doc, "view_document")
grant_object(editor, doc, "change_document")
self.client.force_authenticate(editor)
response = self.client.patch(
@@ -501,7 +500,7 @@ class TestApiAuth(DirectoriesMixin, APITestCase):
def test_dynamic_permissions_fields(self) -> None:
user1 = UserFactory(username="user1")
user1.user_permissions.add(*Permission.objects.filter(codename="view_document"))
grant_global(user1, "view_document")
user2 = UserFactory(username="user2")
Document.objects.create(title="Test", content="content 1", checksum="1")
@@ -524,10 +523,10 @@ class TestApiAuth(DirectoriesMixin, APITestCase):
owner=user1,
)
assign_perm("view_document", user1, doc2)
assign_perm("view_document", user1, doc3)
assign_perm("change_document", user1, doc3)
assign_perm("view_document", user2, doc4)
grant_object(user1, doc2, "view_document")
grant_object(user1, doc3, "view_document")
grant_object(user1, doc3, "change_document")
grant_object(user2, doc4, "view_document")
self.client.force_authenticate(user1)
@@ -860,9 +859,7 @@ class TestApiUser(DirectoriesMixin, APITestCase):
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
regular_user = UserFactory(username="regular_user")
regular_user.user_permissions.add(
*Permission.objects.all(),
)
grant_all_global(regular_user)
self.client.force_authenticate(regular_user)
Authenticator.objects.create(
user=user1,
@@ -887,7 +884,7 @@ class TestApiUser(DirectoriesMixin, APITestCase):
"""
user1 = UserFactory(username="user1")
user1.user_permissions.add(*Permission.objects.all())
grant_all_global(user1)
user2 = UserFactory(username="user2", superuser=True)
self.client.force_authenticate(user1)
@@ -974,7 +971,7 @@ class TestApiUser(DirectoriesMixin, APITestCase):
"""
user1 = UserFactory(username="user1")
user1.user_permissions.add(*Permission.objects.all())
grant_all_global(user1)
user2 = UserFactory(username="user2", superuser=True)
self.client.force_authenticate(user1)
@@ -1277,7 +1274,7 @@ class TestBulkEditObjectPermissions(APITestCase):
},
}
assign_perm("view_tag", self.user3, self.t1)
grant_object(self.user3, self.t1, "view_tag")
self.t1.owner = self.user3
self.t1.save()
@@ -1374,13 +1371,9 @@ class TestBulkEditObjectPermissions(APITestCase):
"""
self.t1.owner = self.user2
self.t1.save()
assign_perm("view_tag", self.user1, self.t1)
assign_perm("change_tag", self.user1, self.t1)
self.user1.user_permissions.add(
*Permission.objects.filter(
codename__in=["view_tag", "change_tag"],
),
)
grant_object(self.user1, self.t1, "view_tag")
grant_object(self.user1, self.t1, "change_tag")
grant_global(self.user1, "view_tag", "change_tag")
user1 = User.objects.get(pk=self.user1.pk)
self.client.force_authenticate(user=user1)
@@ -1427,13 +1420,9 @@ class TestBulkEditObjectPermissions(APITestCase):
"""
owned = Tag.objects.create(name="owned", owner=self.user1)
shared = Tag.objects.create(name="shared", owner=self.user2)
assign_perm("view_tag", self.user1, shared)
assign_perm("change_tag", self.user1, shared)
self.user1.user_permissions.add(
*Permission.objects.filter(
codename__in=["view_tag", "change_tag"],
),
)
grant_object(self.user1, shared, "view_tag")
grant_object(self.user1, shared, "change_tag")
grant_global(self.user1, "view_tag", "change_tag")
user1 = User.objects.get(pk=self.user1.pk)
self.client.force_authenticate(user=user1)
@@ -1474,14 +1463,10 @@ class TestBulkEditObjectPermissions(APITestCase):
"""
self.t1.owner = self.user2
self.t1.save()
assign_perm("view_tag", self.user1, self.t1)
assign_perm("change_tag", self.user1, self.t1)
assign_perm("delete_tag", self.user1, self.t1)
self.user1.user_permissions.add(
*Permission.objects.filter(
codename__in=["view_tag", "change_tag", "delete_tag"],
),
)
grant_object(self.user1, self.t1, "view_tag")
grant_object(self.user1, self.t1, "change_tag")
grant_object(self.user1, self.t1, "delete_tag")
grant_global(self.user1, "view_tag", "change_tag", "delete_tag")
user1 = User.objects.get(pk=self.user1.pk)
self.client.force_authenticate(user=user1)
@@ -1586,7 +1571,7 @@ class TestBulkEditObjectPermissions(APITestCase):
- Request succeeds and null is treated as an empty user list,
so the existing view permission is removed
"""
assign_perm("view_tag", self.user1, self.t1)
grant_object(self.user1, self.t1, "view_tag")
response = self.client.post(
"/api/bulk_edit_objects/",
+17 -23
View File
@@ -9,7 +9,6 @@ from django.contrib.auth.models import Group
from django.contrib.auth.models import Permission
from django.test import override_settings
from django.utils import timezone
from guardian.shortcuts import assign_perm
from rest_framework import status
from rest_framework.test import APITestCase
@@ -31,6 +30,8 @@ from paperless_mail.models import MailRule
from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.factories import DocumentFactory
from paperless_testing.factories import UserFactory
from paperless_testing.permissions import grant_global
from paperless_testing.permissions import grant_object
pytestmark = pytest.mark.search
@@ -951,7 +952,7 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase):
"""
u1 = UserFactory(username="user1")
u2 = UserFactory(username="user2")
u1.user_permissions.add(Permission.objects.get(codename="view_document"))
grant_global(u1, "view_document")
self.client.force_authenticate(user=u1)
@@ -991,7 +992,7 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase):
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data, ["applebaum", "apples"])
assign_perm("view_document", u1, d3)
grant_object(u1, d3, "view_document")
backend.add_or_update(d3)
response = self.client.get("/api/search/autocomplete/?term=app")
@@ -1002,7 +1003,7 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase):
user = UserFactory(username="group-user")
owner = UserFactory(username="document-owner")
group = Group.objects.create(name="temporary-viewers")
user.user_permissions.add(Permission.objects.get(codename="view_document"))
grant_global(user, "view_document")
user.groups.add(group)
document = Document.objects.create(
@@ -1011,7 +1012,7 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase):
checksum="group-revocation",
owner=owner,
)
assign_perm("view_document", group, document)
grant_object(group, document, "view_document")
get_backend().add_or_update(document)
self.client.force_authenticate(user=user)
@@ -1093,9 +1094,7 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase):
def test_search_spelling_suggestion_suppressed_for_private_terms(self) -> None:
owner = UserFactory(username="owner")
attacker = UserFactory(username="attacker")
attacker.user_permissions.add(
Permission.objects.get(codename="view_document"),
)
grant_global(attacker, "view_document")
backend = get_backend()
for i in range(5):
@@ -1224,9 +1223,7 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase):
"""
owner = UserFactory(username="owner")
attacker = UserFactory(username="attacker")
attacker.user_permissions.add(
Permission.objects.get(codename="view_document"),
)
grant_global(attacker, "view_document")
private_seed = Document.objects.create(
title="private bank statement",
@@ -1537,8 +1534,8 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase):
superuser = UserFactory(username="superuser", superuser=True)
u1 = UserFactory(username="user1")
u2 = UserFactory(username="user2")
u1.user_permissions.add(*Permission.objects.filter(codename="view_document"))
u2.user_permissions.add(*Permission.objects.filter(codename="view_document"))
grant_global(u1, "view_document")
grant_global(u2, "view_document")
Document.objects.create(checksum="1", content="test 1", owner=u1)
Document.objects.create(checksum="2", content="test 2", owner=u2)
@@ -1590,8 +1587,8 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase):
"""
u1 = UserFactory(username="user1")
u2 = UserFactory(username="user2")
u1.user_permissions.add(*Permission.objects.filter(codename="view_document"))
u2.user_permissions.add(*Permission.objects.filter(codename="view_document"))
grant_global(u1, "view_document")
grant_global(u2, "view_document")
d1 = Document.objects.create(checksum="1", content="test 1", owner=u1)
d2 = Document.objects.create(checksum="2", content="test 2", owner=u2)
@@ -1616,9 +1613,9 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase):
r = self.client.get("/api/documents/?query=test&owner__isnull=true")
self.assertEqual(r.data["count"], 1)
assign_perm("view_document", u1, d2)
assign_perm("view_document", u1, d3)
assign_perm("view_document", u2, d1)
grant_object(u1, d2, "view_document")
grant_object(u1, d3, "view_document")
grant_object(u2, d1, "view_document")
backend.add_or_update(d1)
backend.add_or_update(d2)
@@ -1925,7 +1922,7 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase):
sort_field="",
owner=user2,
)
assign_perm("view_savedview", user1, shared_view)
grant_object(user1, shared_view, "view_savedview")
mail_account1 = MailAccount.objects.create(name="bank mail account 1")
mail_account2 = MailAccount.objects.create(name="mail account 2")
mail_rule1 = MailRule.objects.create(
@@ -2020,10 +2017,7 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase):
def test_global_search_filters_owned_mail_objects(self) -> None:
user1 = UserFactory(username="mail-search-user")
user2 = UserFactory(username="other-mail-search-user")
user1.user_permissions.add(
Permission.objects.get(codename="view_mailaccount"),
Permission.objects.get(codename="view_mailrule"),
)
grant_global(user1, "view_mailaccount", "view_mailrule")
own_account = MailAccount.objects.create(
name="bank owned account",
+2 -4
View File
@@ -5,7 +5,6 @@ from datetime import timedelta
from pathlib import Path
from unittest import mock
from django.contrib.auth.models import Permission
from django.test import override_settings
from django.utils import timezone
from rest_framework import status
@@ -16,6 +15,7 @@ from documents.permissions import has_system_status_permission
from paperless import version
from paperless_testing.factories import PaperlessTaskFactory
from paperless_testing.factories import UserFactory
from paperless_testing.permissions import grant_global
class TestSystemStatus(APITestCase):
@@ -106,9 +106,7 @@ class TestSystemStatus(APITestCase):
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
user = UserFactory(username="status_user")
user.user_permissions.add(
Permission.objects.get(codename="view_system_monitoring"),
)
grant_global(user, "view_system_monitoring")
self.client.force_login(user)
response = self.client.get(self.ENDPOINT)
+10 -24
View File
@@ -11,10 +11,8 @@ from datetime import timedelta
from unittest import mock
import pytest
from django.contrib.auth.models import Permission
from django.contrib.auth.models import User
from django.utils import timezone
from guardian.shortcuts import assign_perm
from rest_framework import status
from rest_framework.test import APIClient
@@ -22,6 +20,8 @@ from documents.filters import PaperlessTaskFilterSet
from documents.models import PaperlessTask
from paperless_testing.factories import DocumentFactory
from paperless_testing.factories import PaperlessTaskFactory
from paperless_testing.permissions import grant_global
from paperless_testing.permissions import grant_object
pytestmark = pytest.mark.api
@@ -348,9 +348,7 @@ class TestGetTasksV10:
user_client: APIClient,
) -> None:
"""Regular users see their own tasks and unowned (system) tasks; other users' tasks are hidden."""
regular_user.user_permissions.add(
Permission.objects.get(codename="view_paperlesstask"),
)
grant_global(regular_user, "view_paperlesstask")
PaperlessTaskFactory(owner=admin_user) # other user — not visible
unowned_task = PaperlessTaskFactory() # unowned (system task) — visible
@@ -586,9 +584,7 @@ class TestGetTasksV9:
regular_user: User,
) -> None:
"""Non-staff users see their own tasks plus unowned tasks via v9 API."""
regular_user.user_permissions.add(
Permission.objects.get(codename="view_paperlesstask"),
)
grant_global(regular_user, "view_paperlesstask")
client = APIClient()
client.force_authenticate(user=regular_user)
@@ -734,9 +730,7 @@ class TestAcknowledge:
user_client: APIClient,
) -> None:
"""Users granted change_paperlesstask permission can acknowledge tasks."""
regular_user.user_permissions.add(
Permission.objects.get(codename="change_paperlesstask"),
)
grant_global(regular_user, "change_paperlesstask")
regular_user.save()
task = PaperlessTaskFactory()
@@ -803,9 +797,7 @@ class TestSummaryPermissions:
regular_user,
) -> None:
"""A user with view_system_monitoring but no document permissions can access summary/."""
regular_user.user_permissions.add(
Permission.objects.get(codename="view_system_monitoring"),
)
grant_global(regular_user, "view_system_monitoring")
response = user_client.get(ENDPOINT + "summary/")
@@ -818,9 +810,7 @@ class TestSummaryPermissions:
admin_user,
) -> None:
"""Monitoring user sees aggregate data for all tasks, not just unowned ones."""
regular_user.user_permissions.add(
Permission.objects.get(codename="view_system_monitoring"),
)
grant_global(regular_user, "view_system_monitoring")
PaperlessTaskFactory(
owner=admin_user,
task_type=PaperlessTask.TaskType.CONSUME_FILE,
@@ -841,9 +831,7 @@ class TestSummaryPermissions:
) -> None:
"""A regular user with view_paperlesstask but not view_system_monitoring sees only
their own tasks and unowned tasks in the summary, not other users' tasks."""
regular_user.user_permissions.add(
Permission.objects.get(codename="view_paperlesstask"),
)
grant_global(regular_user, "view_paperlesstask")
PaperlessTaskFactory(
owner=regular_user,
@@ -1008,9 +996,7 @@ class TestDuplicateDocumentsPermissions:
@pytest.fixture()
def user_v9_client(self, regular_user: User) -> APIClient:
regular_user.user_permissions.add(
Permission.objects.get(codename="view_paperlesstask"),
)
grant_global(regular_user, "view_paperlesstask")
client = APIClient()
client.force_authenticate(user=regular_user)
client.credentials(HTTP_ACCEPT=ACCEPT_V9)
@@ -1081,7 +1067,7 @@ class TestDuplicateDocumentsPermissions:
) -> None:
"""A user with explicit guardian view_document permission sees the duplicate_of document."""
doc = DocumentFactory(owner=admin_user, title="Granted Doc")
assign_perm("view_document", regular_user, doc)
grant_object(regular_user, doc, "view_document")
PaperlessTaskFactory(
owner=regular_user,
status=PaperlessTask.Status.SUCCESS,
+2 -2
View File
@@ -1,6 +1,5 @@
from datetime import date
from django.contrib.auth.models import Permission
from django.core.cache import cache
from rest_framework import status
from rest_framework.test import APITestCase
@@ -8,6 +7,7 @@ from rest_framework.test import APITestCase
from documents.models import Document
from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.factories import UserFactory
from paperless_testing.permissions import grant_all_global
class TestTrashAPI(DirectoriesMixin, APITestCase):
@@ -15,7 +15,7 @@ class TestTrashAPI(DirectoriesMixin, APITestCase):
super().setUp()
self.user = UserFactory(username="temp_admin")
self.user.user_permissions.add(*Permission.objects.all())
grant_all_global(self.user)
self.client.force_authenticate(user=self.user)
cache.clear()
+5 -5
View File
@@ -10,7 +10,6 @@ from django.contrib.auth.models import User
from django.db import connection
from django.test import TestCase
from django.test.utils import CaptureQueriesContext
from guardian.shortcuts import assign_perm
from guardian.shortcuts import get_groups_with_perms
from guardian.shortcuts import get_users_with_perms
@@ -24,6 +23,7 @@ from documents.models import StoragePath
from documents.models import Tag
from documents.permissions import set_permissions_for_objects
from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.permissions import grant_object
class TestBulkEdit(DirectoriesMixin, TestCase):
@@ -440,7 +440,7 @@ class TestBulkEdit(DirectoriesMixin, TestCase):
def test_set_permissions(self, m) -> None:
doc_ids = [self.doc1.id, self.doc2.id, self.doc3.id]
assign_perm("view_document", self.group1, self.doc1)
grant_object(self.group1, self.doc1, "view_document")
permissions = {
"view": {
@@ -482,8 +482,8 @@ class TestBulkEdit(DirectoriesMixin, TestCase):
self.doc1.owner = self.user1
self.doc1.save()
assign_perm("view_document", self.user1, self.doc1)
assign_perm("view_document", self.group1, self.doc1)
grant_object(self.user1, self.doc1, "view_document")
grant_object(self.group1, self.doc1, "view_document")
permissions = {
"view": {
@@ -609,7 +609,7 @@ class TestBulkEdit(DirectoriesMixin, TestCase):
self.doc1.owner = self.user1
self.doc1.save()
self.user1.groups.add(self.group1)
assign_perm("view_document", self.group1, self.doc1)
grant_object(self.group1, self.doc1, "view_document")
bulk_edit.set_permissions(
[self.doc1.id],
@@ -26,7 +26,6 @@ from django.test import override_settings
from django.utils import timezone
from guardian.models import GroupObjectPermission
from guardian.models import UserObjectPermission
from guardian.shortcuts import assign_perm
from documents.management.commands import document_exporter
from documents.models import Correspondent
@@ -51,6 +50,7 @@ from documents.tests.utils import SampleDirMixin
from paperless_mail.models import MailAccount
from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.dirs import paperless_environment
from paperless_testing.permissions import grant_object
@pytest.mark.management
@@ -105,8 +105,8 @@ class TestExportImport(
user=self.user,
)
assign_perm("view_document", self.user2, self.d2)
assign_perm("view_document", self.group1, self.d3)
grant_object(self.user2, self.d2, "view_document")
grant_object(self.group1, self.d3, "view_document")
self.t1 = Tag.objects.create(name="t")
self.dt1 = DocumentType.objects.create(name="dt")
@@ -2,7 +2,6 @@ import json
from unittest import mock
from auditlog.models import LogEntry
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from rest_framework import status
@@ -12,6 +11,7 @@ from documents.bulk_edit import merge_as_versions
from documents.models import Document
from documents.serialisers import MergeDocumentsAsVersionsSerializer
from paperless_testing.factories import UserFactory
from paperless_testing.permissions import grant_global
class TestMergeDocumentsAsVersionsSerializer(TestCase):
@@ -336,11 +336,7 @@ class TestMergeDocumentsAsVersions(TestCase):
class TestMergeDocumentsAsVersionsAPI(APITestCase):
def setUp(self) -> None:
self.user = UserFactory(username="user")
self.user.user_permissions.add(
Permission.objects.get(codename="change_document"),
Permission.objects.get(codename="view_document"),
Permission.objects.get(codename="delete_document"),
)
grant_global(self.user, "change_document", "view_document", "delete_document")
self.doc1 = Document.objects.create(
checksum="A",
title="A",
@@ -406,10 +402,7 @@ class TestMergeDocumentsAsVersionsAPI(APITestCase):
merge_mock.__name__ = "merge_as_versions"
# Owns them and may change them, but may not make them stop being documents
user = UserFactory(username="no-delete")
user.user_permissions.add(
Permission.objects.get(codename="change_document"),
Permission.objects.get(codename="view_document"),
)
grant_global(user, "change_document", "view_document")
for doc in (self.doc1, self.doc2):
doc.owner = user
doc.save()
@@ -7,9 +7,7 @@ from unittest.mock import patch
import pytest
from django.contrib.auth.models import AnonymousUser
from django.contrib.auth.models import Group
from django.contrib.auth.models import Permission
from django.test import override_settings
from guardian.shortcuts import assign_perm
from rest_framework.test import APIClient
from documents.matching import match_correspondents
@@ -30,6 +28,8 @@ from paperless_testing.factories import DocumentTypeFactory
from paperless_testing.factories import StoragePathFactory
from paperless_testing.factories import TagFactory
from paperless_testing.factories import UserFactory
from paperless_testing.permissions import grant_global
from paperless_testing.permissions import grant_object
if TYPE_CHECKING:
from paperless_testing.dirs import PaperlessDirs
@@ -80,7 +80,7 @@ class TestPermittedDocumentIdsSecurity:
owner = UserFactory(username="owner")
shared = DocumentFactory(owner=owner)
not_shared = DocumentFactory(owner=owner)
assign_perm("view_document", grantee, shared)
grant_object(grantee, shared, "view_document")
assert_visible_document_ids(
permitted_document_ids(grantee),
@@ -100,7 +100,7 @@ class TestPermittedDocumentIdsSecurity:
group = Group.objects.create(name="finance")
member.groups.add(group)
shared = DocumentFactory(owner=owner)
assign_perm("view_document", group, shared)
grant_object(group, shared, "view_document")
assert_visible_document_ids(
permitted_document_ids(member),
@@ -197,12 +197,10 @@ class TestAiChatAllDocumentsPermissionBoundary:
owner = UserFactory(username="owner")
asker = UserFactory(username="asker")
asker.user_permissions.add(
*Permission.objects.filter(codename="view_document"),
)
grant_global(asker, "view_document")
shared = DocumentFactory(owner=owner)
not_shared = DocumentFactory(owner=owner)
assign_perm("view_document", asker, shared)
grant_object(asker, shared, "view_document")
client = APIClient()
client.force_authenticate(user=asker)
@@ -229,7 +227,7 @@ class TestDuplicateDocumentsPermissionBoundary:
dup_visible = DocumentFactory(owner=owner, checksum="dupe-checksum")
dup_hidden = DocumentFactory(owner=owner, checksum="dupe-checksum")
dup_hidden.delete() # soft delete, should still be found (include_deleted=True)
assign_perm("view_document", stranger, dup_visible)
grant_object(stranger, dup_visible, "view_document")
result_owner = _get_viewable_duplicates(original, owner)
assert {d.pk for d in result_owner} == {dup_visible.pk, dup_hidden.pk}
@@ -245,9 +243,9 @@ class TestPermittedDocumentIdsArbitraryPermission:
viewer_only = UserFactory(username="viewer")
editor = UserFactory(username="editor")
doc = DocumentFactory(owner=owner)
assign_perm("view_document", viewer_only, doc)
assign_perm("change_document", editor, doc)
assign_perm("view_document", editor, doc)
grant_object(viewer_only, doc, "view_document")
grant_object(editor, doc, "change_document")
grant_object(editor, doc, "view_document")
assert_visible_document_ids(
permitted_document_ids(editor, perm="change_document"),
@@ -264,7 +262,7 @@ class TestPermittedDocumentIdsArbitraryPermission:
owner = UserFactory(username="owner")
editor = UserFactory(username="editor")
doc = DocumentFactory(owner=owner)
assign_perm("change_document", editor, doc)
grant_object(editor, doc, "change_document")
assert_visible_document_ids(
permitted_document_ids(editor, perm="documents.change_document"),
@@ -277,7 +275,7 @@ class TestPermittedDocumentIdsArbitraryPermission:
stranger = UserFactory(username="mallory")
view_only = UserFactory(username="viewer")
doc = DocumentFactory(owner=owner)
assign_perm("view_document", view_only, doc)
grant_object(view_only, doc, "view_document")
doc.delete()
assert_visible_document_ids(
@@ -313,9 +311,7 @@ class TestEmailDocumentPermissionBoundary:
):
owner = UserFactory(username="owner")
requester = UserFactory(username="requester")
requester.user_permissions.add(
Permission.objects.get(codename="view_document"),
)
grant_global(requester, "view_document")
rest_api_client.force_authenticate(user=requester)
hidden = DocumentFactory(owner=owner)
@@ -347,15 +343,13 @@ class TestBulkEditChangePermissionBoundary:
requester = UserFactory(username="requester")
# grant the global change_document permission so the object-level
# check (not the global has_perm check) is what's under test
requester.user_permissions.add(
Permission.objects.get(codename="change_document"),
)
grant_global(requester, "change_document")
rest_api_client.force_authenticate(user=requester)
changeable = DocumentFactory(owner=owner)
assign_perm("view_document", requester, changeable)
assign_perm("change_document", requester, changeable) # fully permitted
grant_object(requester, changeable, "view_document")
grant_object(requester, changeable, "change_document") # fully permitted
target = DocumentFactory(owner=owner)
assign_perm("view_document", requester, target) # view only, NOT change
grant_object(requester, target, "view_document") # view only, NOT change
response = rest_api_client.post(
"/api/documents/bulk_edit/",
@@ -380,7 +374,7 @@ class TestBulkDownloadPermissionChecksRootDocument:
requester = UserFactory(username="requester")
root = DocumentFactory(owner=owner)
root.source_path.write_bytes(b"%PDF-1.4 test")
assign_perm("view_document", requester, root)
grant_object(requester, root, "view_document")
rest_api_client.force_authenticate(user=requester)
response = rest_api_client.post(
@@ -398,15 +392,13 @@ class TestBulkDownloadPermissionChecksRootDocument:
) -> None:
owner = UserFactory(username="owner")
requester = UserFactory(username="requester")
requester.user_permissions.add(
Permission.objects.get(codename="view_document"),
)
grant_global(requester, "view_document")
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
grant_object(requester, root, "view_document") # granted on ROOT only
response = rest_api_client.post(
"/api/documents/bulk_download/",
@@ -425,10 +417,8 @@ class TestBulkDownloadPermissionChecksRootDocument:
# `stranger` case) can't tell the two apart, since they're denied
# either way.
version_only_grantee = UserFactory(username="version_only_grantee")
version_only_grantee.user_permissions.add(
Permission.objects.get(codename="view_document"),
)
assign_perm("view_document", version_only_grantee, version)
grant_global(version_only_grantee, "view_document")
grant_object(version_only_grantee, version, "view_document")
rest_api_client.force_authenticate(user=version_only_grantee)
response = rest_api_client.post(
"/api/documents/bulk_download/",
@@ -449,12 +439,10 @@ class TestTrashRestorePermissionBoundary:
):
owner = UserFactory(username="owner")
requester = UserFactory(username="requester")
requester.user_permissions.add(
Permission.objects.get(codename="delete_document"),
)
grant_global(requester, "delete_document")
rest_api_client.force_authenticate(user=requester)
doc = DocumentFactory(owner=owner)
assign_perm("view_document", requester, doc) # view only, NOT delete
grant_object(requester, doc, "view_document") # view only, NOT delete
doc.delete()
response = rest_api_client.post(
@@ -470,12 +458,10 @@ class TestTrashRestorePermissionBoundary:
):
owner = UserFactory(username="owner")
requester = UserFactory(username="requester")
requester.user_permissions.add(
Permission.objects.get(codename="delete_document"),
)
grant_global(requester, "delete_document")
rest_api_client.force_authenticate(user=requester)
doc = DocumentFactory(owner=owner)
assign_perm("delete_document", requester, doc)
grant_object(requester, doc, "delete_document")
doc.delete()
response = rest_api_client.post(
@@ -490,7 +476,7 @@ class TestTrashRestorePermissionBoundary:
requester = UserFactory(username="requester")
rest_api_client.force_authenticate(user=requester)
doc = DocumentFactory(owner=owner)
assign_perm("delete_document", requester, doc)
grant_object(requester, doc, "delete_document")
doc.delete()
response = rest_api_client.post(
@@ -517,12 +503,10 @@ class TestTrashViewExcludesExplicitlyGrantedDocuments:
def test_explicit_grant_does_not_leak_trashed_document(self, rest_api_client):
owner = UserFactory(username="trash_owner")
grantee = UserFactory(username="trash_grantee")
grantee.user_permissions.add(
Permission.objects.get(codename="view_document"),
)
grant_global(grantee, "view_document")
doc = DocumentFactory(owner=owner)
doc.delete() # soft delete
assign_perm("view_document", grantee, doc)
grant_object(grantee, doc, "view_document")
rest_api_client.force_authenticate(user=grantee)
response = rest_api_client.get("/api/trash/")
@@ -565,7 +549,7 @@ class TestPermittedObjectIdsGenericModels:
)
other = UserFactory(username=f"other_{suffix}")
granted = factory(owner=other)
assign_perm(perm, user, granted)
grant_object(user, granted, perm)
assert_visible_document_ids(
permitted_object_ids(user, model, perm),
@@ -593,7 +577,7 @@ class TestPermittedObjectIdsGenericModels:
stranger = UserFactory(username=f"stranger2_{model.__name__}")
shared = factory(owner=owner)
not_shared = factory(owner=owner)
assign_perm(perm, grantee, shared)
grant_object(grantee, shared, perm)
assert_visible_document_ids(
permitted_object_ids(grantee, model, perm),
@@ -618,7 +602,7 @@ class TestPermittedObjectIdsGenericModels:
group = Group.objects.create(name=f"group_{model.__name__}")
member.groups.add(group)
shared = factory(owner=owner)
assign_perm(perm, group, shared)
grant_object(group, shared, perm)
assert_visible_document_ids(
permitted_object_ids(member, model, perm),
@@ -658,7 +642,7 @@ class TestMatchingRespectsObjectPermissions:
match="invoice",
matching_algorithm=Tag.MATCH_LITERAL,
)
assign_perm("view_tag", classifying_user, visible_tag)
grant_object(classifying_user, visible_tag, "view_tag")
doc = DocumentFactory(owner=classifying_user, content="an invoice document")
matched = match_tags(doc, classifier=None, user=classifying_user)
@@ -679,7 +663,7 @@ class TestMatchingRespectsObjectPermissions:
match="invoice",
matching_algorithm=Correspondent.MATCH_LITERAL,
)
assign_perm("view_correspondent", classifying_user, visible_correspondent)
grant_object(classifying_user, visible_correspondent, "view_correspondent")
doc = DocumentFactory(owner=classifying_user, content="an invoice document")
matched = match_correspondents(doc, classifier=None, user=classifying_user)
@@ -700,7 +684,7 @@ class TestMatchingRespectsObjectPermissions:
match="invoice",
matching_algorithm=DocumentType.MATCH_LITERAL,
)
assign_perm("view_documenttype", classifying_user, visible_document_type)
grant_object(classifying_user, visible_document_type, "view_documenttype")
doc = DocumentFactory(owner=classifying_user, content="an invoice document")
matched = match_document_types(doc, classifier=None, user=classifying_user)
@@ -721,7 +705,7 @@ class TestMatchingRespectsObjectPermissions:
match="invoice",
matching_algorithm=StoragePath.MATCH_LITERAL,
)
assign_perm("view_storagepath", classifying_user, visible_storage_path)
grant_object(classifying_user, visible_storage_path, "view_storagepath")
doc = DocumentFactory(owner=classifying_user, content="an invoice document")
matched = match_storage_paths(doc, classifier=None, user=classifying_user)
@@ -738,9 +722,7 @@ class TestBulkEditObjectsApplyToAllPermissionBoundary:
new_owner = UserFactory(username="tags_new_owner")
# grant the global change_tag permission so the object-level
# filtering (not the global has_perm check) is what's under test
requester.user_permissions.add(
Permission.objects.get(codename="change_tag"),
)
grant_global(requester, "change_tag")
rest_api_client.force_authenticate(user=requester)
visible = TagFactory(owner=requester)
hidden = TagFactory(owner=owner)
@@ -775,14 +757,12 @@ class TestBulkEditObjectsApplyToAllPermissionBoundary:
"""
owner = UserFactory(username="shared_tags_owner")
requester = UserFactory(username="shared_tags_requester")
requester.user_permissions.add(
Permission.objects.get(codename="change_tag"),
)
grant_global(requester, "change_tag")
rest_api_client.force_authenticate(user=requester)
owned = TagFactory(owner=requester)
shared = TagFactory(owner=owner)
assign_perm("view_tag", requester, shared)
assign_perm("change_tag", requester, shared)
grant_object(requester, shared, "view_tag")
grant_object(requester, shared, "change_tag")
response = rest_api_client.post(
"/api/bulk_edit_objects/",
@@ -838,9 +818,7 @@ class TestBulkEditObjectsTagDescendantPartialPermission:
new_owner = UserFactory(username="tag_hierarchy_new_owner")
# global change_tag permission so the has_perm() gate passes and the
# object-level permitted_object_ids filtering is what's under test
requester.user_permissions.add(
Permission.objects.get(codename="change_tag"),
)
grant_global(requester, "change_tag")
rest_api_client.force_authenticate(user=requester)
parent = TagFactory(owner=requester, name="parent-tag")
@@ -1,11 +1,11 @@
import pytest
from guardian.shortcuts import assign_perm
from rest_framework.test import APIRequestFactory
from documents.filters import PermittedObjectsFilter
from documents.models import Tag
from paperless_testing.factories import TagFactory
from paperless_testing.factories import UserFactory
from paperless_testing.permissions import grant_object
class _DummyView:
@@ -35,7 +35,7 @@ class TestPermittedObjectsFilter:
unowned = TagFactory(owner=None)
granted = TagFactory(owner=owner)
hidden = TagFactory(owner=owner)
assign_perm("view_tag", grantee, granted)
grant_object(grantee, granted, "view_tag")
request = APIRequestFactory().get("/")
request.user = grantee
@@ -53,7 +53,7 @@ class TestPermittedObjectsFilter:
grantee = UserFactory(username="grantee2")
owned = TagFactory(owner=grantee)
granted = TagFactory(owner=owner)
assign_perm("view_tag", grantee, granted)
grant_object(grantee, granted, "view_tag")
request = APIRequestFactory().get("/")
request.user = grantee
@@ -82,7 +82,7 @@ class TestPermittedObjectsFilter:
TagFactory(owner=None)
TagFactory(owner=user)
granted = TagFactory(owner=UserFactory(username=f"o_{username}"))
assign_perm("view_tag", user, granted)
grant_object(user, granted, "view_tag")
request = APIRequestFactory().get("/")
request.user = user
@@ -6,10 +6,8 @@ from pathlib import Path
from unittest import mock
from django.conf import settings
from django.contrib.auth.models import Permission
from django.contrib.auth.models import User
from django.utils import timezone
from guardian.shortcuts import assign_perm
from rest_framework import serializers
from rest_framework import status
from rest_framework.test import APITestCase
@@ -23,6 +21,8 @@ from documents.tasks import cleanup_expired_share_link_bundles
from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.factories import DocumentFactory
from paperless_testing.factories import UserFactory
from paperless_testing.permissions import grant_global
from paperless_testing.permissions import grant_object
class ShareLinkBundleAPITests(DirectoriesMixin, APITestCase):
@@ -58,11 +58,9 @@ class ShareLinkBundleAPITests(DirectoriesMixin, APITestCase):
) -> None:
owner = UserFactory(username="document_owner")
requester = UserFactory(username="bundle_creator")
requester.user_permissions.add(
Permission.objects.get(codename="add_sharelinkbundle"),
)
grant_global(requester, "add_sharelinkbundle")
document = DocumentFactory.create(owner=owner)
assign_perm("view_document", requester, document)
grant_object(requester, document, "view_document")
self.client.force_authenticate(requester)
payload = {
"document_ids": [document.pk],
@@ -73,9 +71,7 @@ class ShareLinkBundleAPITests(DirectoriesMixin, APITestCase):
response = self.client.post(self.ENDPOINT, payload, format="json")
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
requester.user_permissions.add(
Permission.objects.get(codename="view_document"),
)
grant_global(requester, "view_document")
requester = User.objects.get(pk=requester.pk)
self.client.force_authenticate(requester)
response = self.client.post(self.ENDPOINT, payload, format="json")
+2 -4
View File
@@ -1,6 +1,5 @@
from unittest import mock
from django.contrib.auth.models import Permission
from rest_framework import status
from rest_framework.test import APITestCase
@@ -14,15 +13,14 @@ from documents.serialisers import TagSerializer
from documents.signals.handlers import run_workflows
from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.factories import UserFactory
from paperless_testing.permissions import grant_global
class TestTagHierarchyPermissions(APITestCase):
def test_children_only_include_visible_tags(self) -> None:
owner = UserFactory(username="owner")
requester = UserFactory(username="requester")
requester.user_permissions.add(
Permission.objects.get(codename="view_tag"),
)
grant_global(requester, "view_tag")
parent = Tag.objects.create(name="Visible parent", owner=requester)
hidden_child = Tag.objects.create(
name="Hidden child",
+17 -30
View File
@@ -15,7 +15,6 @@ from django.test import TestCase
from django.test import override_settings
from django.test.utils import CaptureQueriesContext
from django.utils import timezone
from guardian.shortcuts import assign_perm
from rest_framework import status
from documents.caching import get_llm_suggestion_cache
@@ -35,6 +34,8 @@ from paperless_ai.exceptions import LLMProviderError
from paperless_ai.exceptions import LLMTimeoutError
from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.factories import UserFactory
from paperless_testing.permissions import grant_global
from paperless_testing.permissions import grant_object
class TestViews(DirectoriesMixin, TestCase):
@@ -142,9 +143,7 @@ class TestViews(DirectoriesMixin, TestCase):
codename__contains="sharelink",
)
self.user.user_permissions.add(*sharelink_permissions)
self.user.user_permissions.add(
Permission.objects.get(codename="view_document"),
)
grant_global(self.user, "view_document")
self.user.save()
self.client.force_login(self.user)
@@ -206,9 +205,7 @@ class TestViews(DirectoriesMixin, TestCase):
codename__contains="sharelink",
)
self.user.user_permissions.add(*sharelink_permissions)
self.user.user_permissions.add(
Permission.objects.get(codename="view_document"),
)
grant_global(self.user, "view_document")
self.client.force_login(self.user)
create_response = self.client.post(
@@ -241,16 +238,16 @@ class TestViews(DirectoriesMixin, TestCase):
group2 = Group.objects.create(name="group2")
group3 = Group.objects.create(name="group3")
t1 = Tag.objects.create(name="invoice", pk=1)
assign_perm("view_tag", self.user, t1)
assign_perm("view_tag", user2, t1)
assign_perm("view_tag", user3, t1)
assign_perm("view_tag", group1, t1)
assign_perm("view_tag", group2, t1)
assign_perm("view_tag", group3, t1)
assign_perm("change_tag", self.user, t1)
assign_perm("change_tag", user2, t1)
assign_perm("change_tag", group1, t1)
assign_perm("change_tag", group2, t1)
grant_object(self.user, t1, "view_tag")
grant_object(user2, t1, "view_tag")
grant_object(user3, t1, "view_tag")
grant_object(group1, t1, "view_tag")
grant_object(group2, t1, "view_tag")
grant_object(group3, t1, "view_tag")
grant_object(self.user, t1, "change_tag")
grant_object(user2, t1, "change_tag")
grant_object(group1, t1, "change_tag")
grant_object(group2, t1, "change_tag")
Tag.objects.create(name="bank statement", pk=2)
d1 = Document.objects.create(
@@ -429,11 +426,7 @@ class TestAISuggestions(DirectoriesMixin, TestCase):
tag_owner = UserFactory(username="cache_tag_owner")
invisible_tag = Tag.objects.create(name="cache_restricted", owner=tag_owner)
requester = UserFactory(username="cache_requester")
requester.user_permissions.add(
*Permission.objects.filter(
codename__in=["view_document", "change_document", "view_tag"],
),
)
grant_global(requester, "view_document", "change_document", "view_tag")
mock_get_cache.return_value = MagicMock(
suggestions={
"title": "Untitled",
@@ -879,11 +872,7 @@ class TestAISuggestions(DirectoriesMixin, TestCase):
tag_owner = UserFactory(username="tagowner")
invisible_tag = Tag.objects.create(name="restricted", owner=tag_owner)
requester = UserFactory(username="requester")
requester.user_permissions.add(
*Permission.objects.filter(
codename__in=["view_document", "change_document", "view_tag"],
),
)
grant_global(requester, "view_document", "change_document", "view_tag")
mock_get_ai_classification.return_value = {
"title": "Untitled",
@@ -967,9 +956,7 @@ class TestAIChatStreamingView(DirectoriesMixin, TestCase):
super().setUp()
def grant_view_document_permission(self) -> None:
self.user.user_permissions.add(
*Permission.objects.filter(codename="view_document"),
)
grant_global(self.user, "view_document")
@override_settings(AI_ENABLED=False)
def test_post_ai_disabled(self) -> None:
+13 -13
View File
@@ -17,7 +17,6 @@ from django.contrib.auth.models import User
from django.core import mail
from django.test import override_settings
from django.utils import timezone
from guardian.shortcuts import assign_perm
from guardian.shortcuts import get_groups_with_perms
from guardian.shortcuts import get_users_with_perms
from httpx import ConnectError
@@ -72,6 +71,7 @@ from paperless_mail.models import MailAccount
from paperless_mail.models import MailRule
from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.factories import UserFactory
from paperless_testing.permissions import grant_object
class TestWorkflows(
@@ -2130,10 +2130,10 @@ class TestWorkflows(
original_filename="sample.pdf",
)
assign_perm("documents.view_document", self.user2, doc)
assign_perm("documents.change_document", self.user2, doc)
assign_perm("documents.view_document", self.group1, doc)
assign_perm("documents.change_document", self.group1, doc)
grant_object(self.user2, doc, "documents.view_document")
grant_object(self.user2, doc, "documents.change_document")
grant_object(self.group1, doc, "documents.view_document")
grant_object(self.group1, doc, "documents.change_document")
superuser = UserFactory(username="superuser", superuser=True)
self.client.force_authenticate(user=superuser)
@@ -2901,10 +2901,10 @@ class TestWorkflows(
doc.tags.set([self.t1, self.t2])
CustomFieldInstance.objects.create(document=doc, field=self.cf1)
doc.save()
assign_perm("documents.view_document", self.user3, doc)
assign_perm("documents.change_document", self.user3, doc)
assign_perm("documents.view_document", self.group1, doc)
assign_perm("documents.change_document", self.group1, doc)
grant_object(self.user3, doc, "documents.view_document")
grant_object(self.user3, doc, "documents.change_document")
grant_object(self.group1, doc, "documents.view_document")
grant_object(self.group1, doc, "documents.change_document")
superuser = UserFactory(username="superuser", superuser=True)
self.client.force_authenticate(user=superuser)
@@ -3098,10 +3098,10 @@ class TestWorkflows(
doc.tags.set([self.t1, self.t2])
CustomFieldInstance.objects.create(document=doc, field=self.cf1)
doc.save()
assign_perm("documents.view_document", self.user3, doc)
assign_perm("documents.change_document", self.user3, doc)
assign_perm("documents.view_document", self.group1, doc)
assign_perm("documents.change_document", self.group1, doc)
grant_object(self.user3, doc, "documents.view_document")
grant_object(self.user3, doc, "documents.change_document")
grant_object(self.group1, doc, "documents.view_document")
grant_object(self.group1, doc, "documents.change_document")
superuser = UserFactory(username="superuser", superuser=True)
self.client.force_authenticate(user=superuser)
+3 -3
View File
@@ -7,7 +7,6 @@ from unittest.mock import patch
import pytest
import pytest_mock
from django.test import override_settings
from guardian.shortcuts import assign_perm
from guardian.shortcuts import remove_perm
from documents.models import Document
@@ -27,6 +26,7 @@ from paperless_ai.taxonomy import TaxonomyCandidates
from paperless_testing.factories import DocumentFactory
from paperless_testing.factories import TagFactory
from paperless_testing.factories import UserFactory
from paperless_testing.permissions import grant_object
@pytest.fixture
@@ -815,8 +815,8 @@ class TestFulltextSimilarDocuments:
content="shared content phrase",
owner=owner,
)
assign_perm("view_document", viewer, permitted)
assign_perm("view_document", viewer, now_private)
grant_object(viewer, permitted, "view_document")
grant_object(viewer, now_private, "view_document")
fulltext_backend.add_or_update(source)
fulltext_backend.add_or_update(permitted)
fulltext_backend.add_or_update(now_private)
+15 -18
View File
@@ -1,9 +1,7 @@
import json
from unittest import mock
from django.contrib.auth.models import Permission
from django.contrib.auth.models import User
from guardian.shortcuts import assign_perm
from rest_framework import status
from rest_framework.test import APITestCase
@@ -19,6 +17,9 @@ from paperless_testing.factories import CorrespondentFactory
from paperless_testing.factories import DocumentTypeFactory
from paperless_testing.factories import TagFactory
from paperless_testing.factories import UserFactory
from paperless_testing.permissions import grant_all_global
from paperless_testing.permissions import grant_global
from paperless_testing.permissions import grant_object
class TestAPIMailAccounts(DirectoriesMixin, APITestCase):
@@ -35,7 +36,7 @@ class TestAPIMailAccounts(DirectoriesMixin, APITestCase):
super().setUp()
self.user = UserFactory(username="temp_admin")
self.user.user_permissions.add(*Permission.objects.all())
grant_all_global(self.user)
self.user.save()
self.client.force_authenticate(user=self.user)
@@ -331,7 +332,7 @@ class TestAPIMailAccounts(DirectoriesMixin, APITestCase):
owner=owner,
)
user = UserFactory(username="object_perms_only")
assign_perm("change_mailaccount", user, account)
grant_object(user, account, "change_mailaccount")
self.client.force_authenticate(user=user)
response = self.client.post(
@@ -384,7 +385,7 @@ class TestAPIMailAccounts(DirectoriesMixin, APITestCase):
account2 = MailAccountFactory(name="Email2", owner=self.user)
_account3 = MailAccountFactory(name="Email3", owner=user2)
account4 = MailAccountFactory(name="Email4", owner=user2)
assign_perm("view_mailaccount", self.user, account4)
grant_object(self.user, account4, "view_mailaccount")
response = self.client.get(self.ENDPOINT)
@@ -402,7 +403,7 @@ class TestAPIMailRules(DirectoriesMixin, APITestCase):
super().setUp()
self.user = UserFactory(username="temp_admin")
self.user.user_permissions.add(*Permission.objects.all())
grant_all_global(self.user)
self.user.save()
self.client.force_authenticate(user=self.user)
@@ -636,7 +637,7 @@ class TestAPIMailRules(DirectoriesMixin, APITestCase):
) -> None:
other_user = UserFactory(username="mail-owner")
foreign_account = MailAccountFactory(name="ForeignEmail", owner=other_user)
assign_perm("change_mailaccount", self.user, foreign_account)
grant_object(self.user, foreign_account, "change_mailaccount")
response = self.client.post(
self.ENDPOINT,
@@ -688,7 +689,7 @@ class TestAPIMailRules(DirectoriesMixin, APITestCase):
rule2 = MailRuleFactory(account=account1, order=1, owner=self.user)
MailRuleFactory(account=account1, order=2, owner=user2)
rule4 = MailRuleFactory(account=account1, order=3, owner=user2)
assign_perm("view_mailrule", self.user, rule4)
grant_object(self.user, rule4, "view_mailrule")
response = self.client.get(self.ENDPOINT)
@@ -739,7 +740,7 @@ class TestAPIProcessedMails(DirectoriesMixin, APITestCase):
super().setUp()
self.user = UserFactory(username="temp_admin")
self.user.user_permissions.add(*Permission.objects.all())
grant_all_global(self.user)
self.user.save()
self.client.force_authenticate(user=self.user)
@@ -763,7 +764,7 @@ class TestAPIProcessedMails(DirectoriesMixin, APITestCase):
)
ProcessedMailFactory(rule=rule, owner=user2)
pm4 = ProcessedMailFactory(rule=rule, owner=user2)
assign_perm("view_processedmail", self.user, pm4)
grant_object(self.user, pm4, "view_processedmail")
response = self.client.get(self.ENDPOINT)
@@ -814,7 +815,7 @@ class TestAPIProcessedMails(DirectoriesMixin, APITestCase):
owner=self.user,
)
pm_granted = ProcessedMailFactory(rule=rule, owner=user2)
assign_perm("delete_processedmail", self.user, pm_granted)
grant_object(self.user, pm_granted, "delete_processedmail")
pm_forbidden = ProcessedMailFactory(rule=rule, owner=user2)
# Success for allowed items
@@ -858,11 +859,9 @@ class TestAPIProcessedMails(DirectoriesMixin, APITestCase):
def test_bulk_delete_requires_global_delete_permission(self) -> None:
owner = UserFactory(username="mail_owner")
requester = UserFactory(username="mail_deleter")
requester.user_permissions.add(
Permission.objects.get(codename="add_processedmail"),
)
grant_global(requester, "add_processedmail")
mail = ProcessedMailFactory(owner=owner)
assign_perm("delete_processedmail", requester, mail)
grant_object(requester, mail, "delete_processedmail")
self.client.force_authenticate(requester)
response = self.client.post(
@@ -872,9 +871,7 @@ class TestAPIProcessedMails(DirectoriesMixin, APITestCase):
)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
requester.user_permissions.add(
Permission.objects.get(codename="delete_processedmail"),
)
grant_global(requester, "delete_processedmail")
requester = User.objects.get(pk=requester.pk)
self.client.force_authenticate(requester)
response = self.client.post(
+2 -5
View File
@@ -43,6 +43,7 @@ from paperless_mail.tests.factories import MailRuleFactory
from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.factories import CorrespondentFactory
from paperless_testing.factories import UserFactory
from paperless_testing.permissions import grant_global
@dataclasses.dataclass
@@ -2264,11 +2265,7 @@ class TestMailAccountTestView(APITestCase):
self.mailMocker.setUp()
self.addCleanup(self.mailMocker.doCleanups)
self.user = UserFactory(username="testuser", password="testpassword")
self.user.user_permissions.add(
*Permission.objects.filter(
codename__in=["add_mailaccount", "change_mailaccount"],
),
)
grant_global(self.user, "add_mailaccount", "change_mailaccount")
self.user.save()
self.client.force_authenticate(user=self.user)
self.url = "/api/mail_accounts/test/"
+2 -7
View File
@@ -14,6 +14,7 @@ from paperless_mail.models import MailAccount
from paperless_mail.oauth import PaperlessMailOAuth2Manager
from paperless_mail.tests.factories import MailAccountFactory
from paperless_testing.factories import UserFactory
from paperless_testing.permissions import grant_global
@override_settings(
@@ -28,13 +29,7 @@ class TestMailOAuth(
):
def setUp(self) -> None:
self.user = UserFactory(username="testuser")
self.user.user_permissions.add(
*Permission.objects.filter(
codename__in=[
"add_mailaccount",
],
),
)
grant_global(self.user, "add_mailaccount")
self.user.save()
self.client.force_login(self.user)
self.mail_account_handler = MailAccountHandler()
+43
View File
@@ -0,0 +1,43 @@
"""Helpers that grant permissions to users and groups in tests.
Two mechanisms, two names. A global permission is a Django model permission held
by a user. An object permission is a guardian permission held on one object by a
user or a group. They are not interchangeable.
"""
from __future__ import annotations
from typing import TYPE_CHECKING
from django.contrib.auth.models import Permission
from guardian.shortcuts import assign_perm
if TYPE_CHECKING:
from django.contrib.auth.models import Group
from django.contrib.auth.models import User
from django.db.models import Model
def grant_global(user: User, *perms: str) -> None:
"""Grant model-level permissions.
Each of perms is a codename or "app_label.codename". An unknown name raises
Permission.DoesNotExist.
"""
for perm in perms:
app_label, _, codename = perm.rpartition(".")
lookup = {"codename": codename}
if app_label:
lookup["content_type__app_label"] = app_label
user.user_permissions.add(Permission.objects.get(**lookup))
def grant_all_global(user: User) -> None:
"""Grant every existing model-level permission without making a superuser."""
user.user_permissions.add(*Permission.objects.all())
def grant_object(target: User | Group, obj: Model, *perms: str) -> None:
"""Grant object-level permissions on obj to a user or a group."""
for perm in perms:
assign_perm(perm, target, obj)