Allow users to set a combined certificte and key file for additional certificates in the SSL context

This commit is contained in:
Trenton H
2023-08-21 13:21:02 -07:00
parent d1ae82c5c2
commit 16adddc803
5 changed files with 83 additions and 7 deletions

View File

@@ -177,6 +177,23 @@ def settings_values_check(app_configs, **kwargs):
)
return msgs
def _email_certificate_validate():
msgs = []
# Existence checks
if (
settings.EMAIL_CERTIFICATE_FILE is not None
and not settings.EMAIL_CERTIFICATE_FILE.is_file()
):
msgs.append(
Error(
f"Email cert {settings.EMAIL_CERTIFICATE_FILE} is not a file",
),
)
return msgs
return (
_ocrmypdf_settings_check() + _timezone_validate() + _barcode_scanner_validate()
_ocrmypdf_settings_check()
+ _timezone_validate()
+ _barcode_scanner_validate()
+ _email_certificate_validate()
)

View File

@@ -67,11 +67,20 @@ def __get_float(key: str, default: float) -> float:
return float(os.getenv(key, default))
def __get_path(key: str, default: Union[PathLike, str]) -> Path:
def __get_path(
key: str,
default: Optional[Union[PathLike, str]] = None,
) -> Optional[Path]:
"""
Return a normalized, absolute path based on the environment variable or a default
Return a normalized, absolute path based on the environment variable or a default,
if provided. If not set and no default, returns None
"""
return Path(os.environ.get(key, default)).resolve()
if key in os.environ:
return Path(os.environ[key]).resolve()
elif default is not None:
return Path(default).resolve()
else:
return None
def __get_list(
@@ -477,6 +486,8 @@ CSRF_COOKIE_NAME = f"{COOKIE_PREFIX}csrftoken"
SESSION_COOKIE_NAME = f"{COOKIE_PREFIX}sessionid"
LANGUAGE_COOKIE_NAME = f"{COOKIE_PREFIX}django_language"
EMAIL_CERTIFICATE_FILE = __get_path("PAPERLESS_EMAIL_CERTIFICATE_FILE")
###############################################################################
# Database #

View File

@@ -1,9 +1,11 @@
import os
from pathlib import Path
from django.test import TestCase
from django.test import override_settings
from documents.tests.utils import DirectoriesMixin
from documents.tests.utils import FileSystemAssertsMixin
from paperless.checks import binaries_check
from paperless.checks import debug_mode_check
from paperless.checks import paths_check
@@ -57,7 +59,7 @@ class TestChecks(DirectoriesMixin, TestCase):
self.assertEqual(len(debug_mode_check(None)), 1)
class TestSettingsChecks(DirectoriesMixin, TestCase):
class TestSettingsChecksAgainstDefaults(DirectoriesMixin, TestCase):
def test_all_valid(self):
"""
GIVEN:
@@ -70,6 +72,8 @@ class TestSettingsChecks(DirectoriesMixin, TestCase):
msgs = settings_values_check(None)
self.assertEqual(len(msgs), 0)
class TestOcrSettingsChecks(DirectoriesMixin, TestCase):
@override_settings(OCR_OUTPUT_TYPE="notapdf")
def test_invalid_output_type(self):
"""
@@ -160,6 +164,8 @@ class TestSettingsChecks(DirectoriesMixin, TestCase):
self.assertIn('OCR clean mode "cleanme"', msg.msg)
class TestTimezoneSettingsChecks(DirectoriesMixin, TestCase):
@override_settings(TIME_ZONE="TheMoon\\MyCrater")
def test_invalid_timezone(self):
"""
@@ -178,6 +184,8 @@ class TestSettingsChecks(DirectoriesMixin, TestCase):
self.assertIn('Timezone "TheMoon\\MyCrater"', msg.msg)
class TestBarcodeSettingsChecks(DirectoriesMixin, TestCase):
@override_settings(CONSUMER_BARCODE_SCANNER="Invalid")
def test_barcode_scanner_invalid(self):
msgs = settings_values_check(None)
@@ -200,3 +208,26 @@ class TestSettingsChecks(DirectoriesMixin, TestCase):
def test_barcode_scanner_valid(self):
msgs = settings_values_check(None)
self.assertEqual(len(msgs), 0)
class TestEmailCertSettingsChecks(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
@override_settings(EMAIL_CERTIFICATE_FILE=Path("/tmp/not_actually_here.pem"))
def test_not_valid_file(self):
"""
GIVEN:
- Default settings
- Email certificate is set
WHEN:
- Email certificate file doesn't exist
THEN:
- system check error reported for email certificate
"""
self.assertIsNotFile("/tmp/not_actually_here.pem")
msgs = settings_values_check(None)
self.assertEqual(len(msgs), 1)
msg = msgs[0]
self.assertIn("Email cert /tmp/not_actually_here.pem is not a file", msg.msg)

View File

@@ -395,12 +395,16 @@ def get_mailbox(server, port, security) -> MailBox:
"""
Returns the correct MailBox instance for the given configuration.
"""
ssl_context = ssl.create_default_context()
if settings.EMAIL_CERTIFICATE_FILE is not None: # pragma: nocover
ssl_context.load_cert_chain(certfile=settings.EMAIL_CERTIFICATE_FILE)
if security == MailAccount.ImapSecurity.NONE:
mailbox = MailBoxUnencrypted(server, port)
elif security == MailAccount.ImapSecurity.STARTTLS:
mailbox = MailBoxTls(server, port, ssl_context=ssl.create_default_context())
mailbox = MailBoxTls(server, port, ssl_context=ssl_context)
elif security == MailAccount.ImapSecurity.SSL:
mailbox = MailBox(server, port, ssl_context=ssl.create_default_context())
mailbox = MailBox(server, port, ssl_context=ssl_context)
else:
raise NotImplementedError("Unknown IMAP security") # pragma: nocover
return mailbox