Compare commits

..

1 Commits

Author SHA1 Message Date
Trenton H
376af81b9c Fix: Resolve another TC assuming an object has been created somewhere (#12503) 2026-04-02 14:58:28 -07:00
6 changed files with 16 additions and 52 deletions

View File

@@ -1420,14 +1420,6 @@ ports.
## Incoming Mail {#incoming_mail} ## Incoming Mail {#incoming_mail}
#### [`PAPERLESS_EMAIL_ALLOW_INTERNAL_HOSTS=<bool>`](#PAPERLESS_EMAIL_ALLOW_INTERNAL_HOSTS) {#PAPERLESS_EMAIL_ALLOW_INTERNAL_HOSTS}
: If set to false, incoming mail account connections are blocked when the
configured IMAP hostname resolves to a non-public address (for example,
localhost, link-local, or RFC1918 private ranges).
Defaults to true, which allows internal hosts.
### Email OAuth {#email_oauth} ### Email OAuth {#email_oauth}
#### [`PAPERLESS_OAUTH_CALLBACK_BASE_URL=<str>`](#PAPERLESS_OAUTH_CALLBACK_BASE_URL) {#PAPERLESS_OAUTH_CALLBACK_BASE_URL} #### [`PAPERLESS_OAUTH_CALLBACK_BASE_URL=<str>`](#PAPERLESS_OAUTH_CALLBACK_BASE_URL) {#PAPERLESS_OAUTH_CALLBACK_BASE_URL}

View File

@@ -31,6 +31,11 @@ from paperless.models import ApplicationConfiguration
class TestViews(DirectoriesMixin, TestCase): class TestViews(DirectoriesMixin, TestCase):
@classmethod
def setUpTestData(cls) -> None:
super().setUpTestData()
ApplicationConfiguration.objects.get_or_create()
def setUp(self) -> None: def setUp(self) -> None:
self.user = User.objects.create_user("testuser") self.user = User.objects.create_user("testuser")
super().setUp() super().setUp()

View File

@@ -497,10 +497,6 @@ SESSION_COOKIE_NAME = f"{COOKIE_PREFIX}sessionid"
LANGUAGE_COOKIE_NAME = f"{COOKIE_PREFIX}django_language" LANGUAGE_COOKIE_NAME = f"{COOKIE_PREFIX}django_language"
EMAIL_CERTIFICATE_FILE = get_path_from_env("PAPERLESS_EMAIL_CERTIFICATE_LOCATION") EMAIL_CERTIFICATE_FILE = get_path_from_env("PAPERLESS_EMAIL_CERTIFICATE_LOCATION")
EMAIL_ALLOW_INTERNAL_HOSTS = get_bool_from_env(
"PAPERLESS_EMAIL_ALLOW_INTERNAL_HOSTS",
"true",
)
############################################################################### ###############################################################################

View File

@@ -39,8 +39,6 @@ from documents.loggers import LoggingMixin
from documents.models import Correspondent from documents.models import Correspondent
from documents.parsers import is_mime_type_supported from documents.parsers import is_mime_type_supported
from documents.tasks import consume_file from documents.tasks import consume_file
from paperless.network import is_public_ip
from paperless.network import resolve_hostname_ips
from paperless_mail.models import MailAccount from paperless_mail.models import MailAccount
from paperless_mail.models import MailRule from paperless_mail.models import MailRule
from paperless_mail.models import ProcessedMail from paperless_mail.models import ProcessedMail
@@ -414,13 +412,6 @@ def get_mailbox(server, port, security) -> MailBox:
""" """
Returns the correct MailBox instance for the given configuration. Returns the correct MailBox instance for the given configuration.
""" """
if not settings.EMAIL_ALLOW_INTERNAL_HOSTS:
for ip_str in resolve_hostname_ips(server):
if not is_public_ip(ip_str):
raise MailError(
f"Connection blocked: {server} resolves to a non-public address",
)
ssl_context = ssl.create_default_context() ssl_context = ssl.create_default_context()
if settings.EMAIL_CERTIFICATE_FILE is not None: # pragma: no cover if settings.EMAIL_CERTIFICATE_FILE is not None: # pragma: no cover
ssl_context.load_verify_locations(cafile=settings.EMAIL_CERTIFICATE_FILE) ssl_context.load_verify_locations(cafile=settings.EMAIL_CERTIFICATE_FILE)

View File

@@ -13,7 +13,6 @@ from django.contrib.auth.models import User
from django.core.management import call_command from django.core.management import call_command
from django.db import DatabaseError from django.db import DatabaseError
from django.test import TestCase from django.test import TestCase
from django.test import override_settings
from django.utils import timezone from django.utils import timezone
from imap_tools import NOT from imap_tools import NOT
from imap_tools import EmailAddress from imap_tools import EmailAddress
@@ -1847,25 +1846,6 @@ class TestMailAccountTestView(APITestCase):
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.content.decode(), "Unable to connect to server") self.assertEqual(response.content.decode(), "Unable to connect to server")
@override_settings(EMAIL_ALLOW_INTERNAL_HOSTS=False)
@mock.patch("paperless_mail.mail.resolve_hostname_ips", return_value=["127.0.0.1"])
def test_mail_account_test_view_blocks_internal_host_when_disabled(
self,
_mock_resolve_hostname_ips,
) -> None:
data = {
"imap_server": "internal.example",
"imap_port": 993,
"imap_security": MailAccount.ImapSecurity.SSL,
"username": "admin",
"password": "secret",
"account_type": MailAccount.MailAccountType.IMAP,
"is_token": False,
}
response = self.client.post(self.url, data, format="json")
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.content.decode(), "Unable to connect to server")
@mock.patch( @mock.patch(
"paperless_mail.oauth.PaperlessMailOAuth2Manager.refresh_account_oauth_token", "paperless_mail.oauth.PaperlessMailOAuth2Manager.refresh_account_oauth_token",
) )

View File

@@ -120,12 +120,12 @@ class MailAccountViewSet(ModelViewSet, PassUserMixin):
serializer.validated_data["expiration"] = existing_account.expiration serializer.validated_data["expiration"] = existing_account.expiration
account = MailAccount(**serializer.validated_data) account = MailAccount(**serializer.validated_data)
try: with get_mailbox(
with get_mailbox( account.imap_server,
account.imap_server, account.imap_port,
account.imap_port, account.imap_security,
account.imap_security, ) as M:
) as M: try:
if ( if (
existing_account is not None existing_account is not None
and account.is_token and account.is_token
@@ -145,11 +145,11 @@ class MailAccountViewSet(ModelViewSet, PassUserMixin):
mailbox_login(M, account) mailbox_login(M, account)
return Response({"success": True}) return Response({"success": True})
except MailError: except MailError:
logger.error( logger.error(
"Mail account connectivity test failed", "Mail account connectivity test failed",
) )
return HttpResponseBadRequest("Unable to connect to server") return HttpResponseBadRequest("Unable to connect to server")
@action(methods=["post"], detail=True) @action(methods=["post"], detail=True)
def process(self, request, pk=None): def process(self, request, pk=None):