mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-09-11 20:28:01 +00:00
Merge branch 'main' into dev
# Conflicts: # docs/setup.md # src-ui/src/main.ts # src/documents/tests/test_api_bulk_edit.py # src/documents/tests/test_api_custom_fields.py # src/documents/tests/test_api_search.py # src/documents/tests/test_api_status.py # src/documents/tests/test_workflows.py # src/paperless_mail/tests/test_api.py
This commit is contained in:
@@ -702,6 +702,40 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase):
|
||||
|
||||
self.assertEqual(correction, None)
|
||||
|
||||
def test_search_spelling_suggestion_suppressed_for_private_terms(self):
|
||||
owner = User.objects.create_user("owner")
|
||||
attacker = User.objects.create_user("attacker")
|
||||
attacker.user_permissions.add(
|
||||
Permission.objects.get(codename="view_document"),
|
||||
)
|
||||
|
||||
with AsyncWriter(index.open_index()) as writer:
|
||||
for i in range(55):
|
||||
private_doc = Document.objects.create(
|
||||
checksum=f"p{i}",
|
||||
pk=100 + i,
|
||||
title=f"Private Document {i + 1}",
|
||||
content=f"treasury document {i + 1}",
|
||||
owner=owner,
|
||||
)
|
||||
visible_doc = Document.objects.create(
|
||||
checksum=f"v{i}",
|
||||
pk=200 + i,
|
||||
title=f"Visible Document {i + 1}",
|
||||
content=f"public ledger {i + 1}",
|
||||
owner=attacker,
|
||||
)
|
||||
index.update_document(writer, private_doc)
|
||||
index.update_document(writer, visible_doc)
|
||||
|
||||
self.client.force_authenticate(user=attacker)
|
||||
|
||||
response = self.client.get("/api/documents/?query=treasurx")
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data["count"], 0)
|
||||
self.assertIsNone(response.data["corrected_query"])
|
||||
|
||||
@mock.patch(
|
||||
"whoosh.searching.Searcher.correct_query",
|
||||
side_effect=Exception("Test error"),
|
||||
@@ -772,6 +806,60 @@ 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,
|
||||
) -> None:
|
||||
"""
|
||||
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) -> None:
|
||||
t = Tag.objects.create(name="tag")
|
||||
t2 = Tag.objects.create(name="tag2")
|
||||
@@ -1356,6 +1444,83 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase):
|
||||
self.assertEqual(results["custom_fields"][0]["id"], custom_field1.id)
|
||||
self.assertEqual(results["workflows"][0]["id"], workflow1.id)
|
||||
|
||||
def test_global_search_filters_owned_mail_objects(self) -> None:
|
||||
user1 = User.objects.create_user("mail-search-user")
|
||||
user2 = User.objects.create_user("other-mail-search-user")
|
||||
user1.user_permissions.add(
|
||||
Permission.objects.get(codename="view_mailaccount"),
|
||||
Permission.objects.get(codename="view_mailrule"),
|
||||
)
|
||||
|
||||
own_account = MailAccount.objects.create(
|
||||
name="bank owned account",
|
||||
username="owner@example.com",
|
||||
password="secret",
|
||||
imap_server="imap.owner.example.com",
|
||||
imap_port=993,
|
||||
imap_security=MailAccount.ImapSecurity.SSL,
|
||||
character_set="UTF-8",
|
||||
owner=user1,
|
||||
)
|
||||
other_account = MailAccount.objects.create(
|
||||
name="bank other account",
|
||||
username="other@example.com",
|
||||
password="secret",
|
||||
imap_server="imap.other.example.com",
|
||||
imap_port=993,
|
||||
imap_security=MailAccount.ImapSecurity.SSL,
|
||||
character_set="UTF-8",
|
||||
owner=user2,
|
||||
)
|
||||
unowned_account = MailAccount.objects.create(
|
||||
name="bank shared account",
|
||||
username="shared@example.com",
|
||||
password="secret",
|
||||
imap_server="imap.shared.example.com",
|
||||
imap_port=993,
|
||||
imap_security=MailAccount.ImapSecurity.SSL,
|
||||
character_set="UTF-8",
|
||||
)
|
||||
own_rule = MailRule.objects.create(
|
||||
name="bank owned rule",
|
||||
account=own_account,
|
||||
action=MailRule.MailAction.MOVE,
|
||||
owner=user1,
|
||||
)
|
||||
other_rule = MailRule.objects.create(
|
||||
name="bank other rule",
|
||||
account=other_account,
|
||||
action=MailRule.MailAction.MOVE,
|
||||
owner=user2,
|
||||
)
|
||||
unowned_rule = MailRule.objects.create(
|
||||
name="bank shared rule",
|
||||
account=unowned_account,
|
||||
action=MailRule.MailAction.MOVE,
|
||||
)
|
||||
|
||||
self.client.force_authenticate(user1)
|
||||
|
||||
response = self.client.get("/api/search/?query=bank")
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertCountEqual(
|
||||
[account["id"] for account in response.data["mail_accounts"]],
|
||||
[own_account.id, unowned_account.id],
|
||||
)
|
||||
self.assertCountEqual(
|
||||
[rule["id"] for rule in response.data["mail_rules"]],
|
||||
[own_rule.id, unowned_rule.id],
|
||||
)
|
||||
self.assertNotIn(
|
||||
other_account.id,
|
||||
[account["id"] for account in response.data["mail_accounts"]],
|
||||
)
|
||||
self.assertNotIn(
|
||||
other_rule.id,
|
||||
[rule["id"] for rule in response.data["mail_rules"]],
|
||||
)
|
||||
|
||||
def test_global_search_bad_request(self) -> None:
|
||||
"""
|
||||
WHEN:
|
||||
|
||||
Reference in New Issue
Block a user