From 1bb4b9b473e102b2e6c6ca37452a5b4ed96c09f7 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Sat, 21 Feb 2026 16:43:28 -0800 Subject: [PATCH] More permissions on mail account test endpoint --- src/paperless_mail/tests/test_api.py | 18 ++++++++++++++++++ src/paperless_mail/tests/test_mail.py | 24 ++++++++++++++++++++++++ src/paperless_mail/views.py | 18 +++++++++++++----- 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/src/paperless_mail/tests/test_api.py b/src/paperless_mail/tests/test_api.py index dd63c67ab..cbfe0f9a4 100644 --- a/src/paperless_mail/tests/test_api.py +++ b/src/paperless_mail/tests/test_api.py @@ -272,6 +272,24 @@ class TestAPIMailAccounts(DirectoriesMixin, APITestCase): self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data["success"], True) + def test_mail_account_test_existing_nonexistent_id_forbidden(self): + response = self.client.post( + f"{self.ENDPOINT}test/", + json.dumps( + { + "id": 999999, + "imap_server": "server.example.com", + "imap_port": 443, + "imap_security": MailAccount.ImapSecurity.SSL, + "username": "admin", + "password": "******", + }, + ), + content_type="application/json", + ) + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(response.content.decode(), "Insufficient permissions") + def test_get_mail_accounts_owner_aware(self): """ GIVEN: diff --git a/src/paperless_mail/tests/test_mail.py b/src/paperless_mail/tests/test_mail.py index c239c9f33..f8ab14bdd 100644 --- a/src/paperless_mail/tests/test_mail.py +++ b/src/paperless_mail/tests/test_mail.py @@ -9,6 +9,7 @@ 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.core.management import call_command from django.db import DatabaseError @@ -1699,6 +1700,10 @@ class TestMailAccountTestView(APITestCase): username="testuser", password="testpassword", ) + self.user.user_permissions.add( + *Permission.objects.filter(codename__in=["add_mailaccount"]), + ) + self.user.save() self.client.force_authenticate(user=self.user) self.url = "/api/mail_accounts/test/" @@ -1844,6 +1849,25 @@ class TestMailAccountTestView(APITestCase): self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response.content.decode(), "Insufficient permissions") + def test_mail_account_test_view_requires_add_permission_without_account_id(self): + self.user.user_permissions.remove( + *Permission.objects.filter(codename__in=["add_mailaccount"]), + ) + self.user.save() + data = { + "imap_server": "imap.example.com", + "imap_port": 993, + "imap_security": MailAccount.ImapSecurity.SSL, + "username": "admin", + "password": "secret", + "is_token": False, + } + + response = self.client.post(self.url, data, format="json") + + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(response.content.decode(), "Insufficient permissions") + class TestMailAccountProcess(APITestCase): def setUp(self): diff --git a/src/paperless_mail/views.py b/src/paperless_mail/views.py index 5fceedd64..b8ac2c485 100644 --- a/src/paperless_mail/views.py +++ b/src/paperless_mail/views.py @@ -89,15 +89,18 @@ class MailAccountViewSet(ModelViewSet, PassUserMixin): existing_account = None account_id = request.data.get("id") - # account exists, use the password from there instead of *** and refresh_token / expiration - if ( - len(serializer.validated_data.get("password").replace("*", "")) == 0 - and account_id is not None + # testing a new connection requires add permission + if account_id is None and not request.user.has_perms( + ["paperless_mail.add_mailaccount"], ): + return HttpResponseForbidden("Insufficient permissions") + + # testing an existing account requires change permission on that account + if account_id is not None: try: existing_account = MailAccount.objects.get(pk=account_id) except (TypeError, ValueError, MailAccount.DoesNotExist): - return HttpResponseBadRequest("Invalid account") + return HttpResponseForbidden("Insufficient permissions") if not has_perms_owner_aware( request.user, @@ -106,6 +109,11 @@ class MailAccountViewSet(ModelViewSet, PassUserMixin): ): return HttpResponseForbidden("Insufficient permissions") + # account exists, use the password from there instead of *** + if ( + len(serializer.validated_data.get("password").replace("*", "")) == 0 + and existing_account is not None + ): serializer.validated_data["password"] = existing_account.password serializer.validated_data["account_type"] = existing_account.account_type serializer.validated_data["refresh_token"] = existing_account.refresh_token