feat: add include_deleted param to permitted_document_ids

Widens permitted_document_ids to accept an include_deleted keyword-only
flag (default False, preserving current behavior) so later call sites
that need visibility into soft-deleted documents (e.g. trash restore)
can reuse this permission check instead of duplicating it.
This commit is contained in:
stumpylog
2026-07-27 19:39:20 -07:00
parent 2ece405b6d
commit 4352a8be0f
2 changed files with 37 additions and 5 deletions
+11 -5
View File
@@ -163,14 +163,20 @@ def set_permissions_for_object(
)
def permitted_document_ids(user):
def permitted_document_ids(user, *, include_deleted: bool = False):
"""
Return a queryset of document IDs the user may view, limited to non-deleted
documents. This intentionally avoids ``get_objects_for_user`` to keep the
subquery small and index-friendly.
Return a queryset of document IDs the user may view. By default limited
to non-deleted documents; pass ``include_deleted=True`` for callers that
need to check permission on soft-deleted documents (e.g. trash restore).
This intentionally avoids ``get_objects_for_user`` to keep the subquery
small and index-friendly.
"""
base_docs = Document.objects.filter(deleted_at__isnull=True).only("id", "owner")
manager = Document.global_objects if include_deleted else Document.objects
base_docs = (
manager.all() if include_deleted else manager.filter(deleted_at__isnull=True)
)
base_docs = base_docs.only("id", "owner")
if user is None or not getattr(user, "is_authenticated", False):
# Just Anonymous user e.g. for drf-spectacular
@@ -119,3 +119,29 @@ class TestPermittedDocumentIdsSecurity:
expected_visible=[unowned.pk],
expected_hidden=[owned.pk],
)
@pytest.mark.django_db
class TestPermittedDocumentIdsIncludeDeleted:
def test_include_deleted_true_reveals_soft_deleted_owned_document(self):
owner = User.objects.create_user(username="owner")
doc = DocumentFactory(owner=owner)
doc.delete()
assert_visible_document_ids(
permitted_document_ids(owner, include_deleted=True),
expected_visible=[doc.pk],
expected_hidden=[],
)
def test_include_deleted_true_still_respects_permission_boundary(self):
owner = User.objects.create_user(username="owner")
stranger = User.objects.create_user(username="mallory")
doc = DocumentFactory(owner=owner)
doc.delete()
assert_visible_document_ids(
permitted_document_ids(stranger, include_deleted=True),
expected_visible=[],
expected_hidden=[doc.pk],
)