mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-03-21 16:32:45 +00:00
Fix: require view permission for more-like search
This commit is contained in:
@@ -772,6 +772,58 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase):
|
||||
self.assertEqual(results[0]["id"], d3.id)
|
||||
self.assertEqual(results[1]["id"], d1.id)
|
||||
|
||||
def test_search_more_like_requires_view_permission_on_seed_document(self):
|
||||
"""
|
||||
GIVEN:
|
||||
- A user can search documents they own
|
||||
- Another user's private document exists with similar content
|
||||
WHEN:
|
||||
- The user requests more-like-this for the private seed document
|
||||
THEN:
|
||||
- The request is rejected
|
||||
"""
|
||||
owner = User.objects.create_user("owner")
|
||||
attacker = User.objects.create_user("attacker")
|
||||
attacker.user_permissions.add(
|
||||
Permission.objects.get(codename="view_document"),
|
||||
)
|
||||
|
||||
private_seed = Document.objects.create(
|
||||
title="private bank statement",
|
||||
content="quarterly treasury bank statement wire transfer",
|
||||
checksum="seed",
|
||||
owner=owner,
|
||||
pk=10,
|
||||
)
|
||||
visible_doc = Document.objects.create(
|
||||
title="attacker-visible match",
|
||||
content="quarterly treasury bank statement wire transfer summary",
|
||||
checksum="visible",
|
||||
owner=attacker,
|
||||
pk=11,
|
||||
)
|
||||
other_doc = Document.objects.create(
|
||||
title="unrelated",
|
||||
content="completely different topic",
|
||||
checksum="other",
|
||||
owner=attacker,
|
||||
pk=12,
|
||||
)
|
||||
|
||||
with AsyncWriter(index.open_index()) as writer:
|
||||
index.update_document(writer, private_seed)
|
||||
index.update_document(writer, visible_doc)
|
||||
index.update_document(writer, other_doc)
|
||||
|
||||
self.client.force_authenticate(user=attacker)
|
||||
|
||||
response = self.client.get(
|
||||
f"/api/documents/?more_like_id={private_seed.id}",
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||
self.assertEqual(response.content, b"Insufficient permissions.")
|
||||
|
||||
def test_search_filtering(self):
|
||||
t = Tag.objects.create(name="tag")
|
||||
t2 = Tag.objects.create(name="tag2")
|
||||
|
||||
@@ -49,6 +49,7 @@ from django.utils import timezone
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.utils.timezone import make_aware
|
||||
from django.utils.translation import get_language
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views import View
|
||||
from django.views.decorators.cache import cache_control
|
||||
from django.views.decorators.http import condition
|
||||
@@ -70,6 +71,7 @@ from rest_framework import parsers
|
||||
from rest_framework import serializers
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.exceptions import NotFound
|
||||
from rest_framework.exceptions import PermissionDenied
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.filters import OrderingFilter
|
||||
from rest_framework.filters import SearchFilter
|
||||
@@ -1369,11 +1371,28 @@ class UnifiedSearchViewSet(DocumentViewSet):
|
||||
filtered_queryset = super().filter_queryset(queryset)
|
||||
|
||||
if self._is_search_request():
|
||||
from documents import index
|
||||
|
||||
if "query" in self.request.query_params:
|
||||
from documents import index
|
||||
|
||||
query_class = index.DelayedFullTextQuery
|
||||
elif "more_like_id" in self.request.query_params:
|
||||
try:
|
||||
more_like_doc_id = int(self.request.query_params["more_like_id"])
|
||||
more_like_doc = Document.objects.select_related("owner").get(
|
||||
pk=more_like_doc_id,
|
||||
)
|
||||
except (TypeError, ValueError, Document.DoesNotExist):
|
||||
raise PermissionDenied(_("Invalid more_like_id"))
|
||||
|
||||
if not has_perms_owner_aware(
|
||||
self.request.user,
|
||||
"view_document",
|
||||
more_like_doc,
|
||||
):
|
||||
raise PermissionDenied(_("Insufficient permissions."))
|
||||
|
||||
from documents import index
|
||||
|
||||
query_class = index.DelayedMoreLikeThisQuery
|
||||
else:
|
||||
raise ValueError
|
||||
@@ -1409,6 +1428,8 @@ class UnifiedSearchViewSet(DocumentViewSet):
|
||||
return response
|
||||
except NotFound:
|
||||
raise
|
||||
except PermissionDenied as e:
|
||||
return HttpResponseForbidden(str(e.detail))
|
||||
except Exception as e:
|
||||
logger.warning(f"An error occurred listing search results: {e!s}")
|
||||
return HttpResponseBadRequest(
|
||||
|
||||
Reference in New Issue
Block a user