Add settings for email decryption preprocessor

This commit is contained in:
Daniel Bankmann 2024-08-18 00:29:58 +02:00 committed by shamoon
parent 243daf7469
commit c134ce9025
4 changed files with 72 additions and 17 deletions

View File

@ -1171,6 +1171,15 @@ if DEBUG: # pragma: no cover
EMAIL_BACKEND = "django.core.mail.backends.filebased.EmailBackend"
EMAIL_FILE_PATH = BASE_DIR / "sent_emails"
###############################################################################
# Email Preprocessors #
###############################################################################
EMAIL_GNUPG_HOME: Final[Optional[str]] = os.getenv("PAPERLESS_EMAIL_GNUPG_HOME")
EMAIL_ENABLE_GPG_DECRYPTOR: Final[bool] = __get_boolean(
"PAPERLESS_ENABLE_GPG_DECRYPTOR",
)
###############################################################################
# Soft Delete

View File

@ -10,7 +10,6 @@ from datetime import timedelta
from fnmatch import fnmatch
from pathlib import Path
from typing import TYPE_CHECKING
from typing import Callable
from typing import Optional
from typing import Union
@ -45,6 +44,7 @@ from paperless_mail.models import MailAccount
from paperless_mail.models import MailRule
from paperless_mail.models import ProcessedMail
from paperless_mail.preprocessor import MailMessageDecryptor
from paperless_mail.preprocessor import MailMessagePreprocessor
# Apple Mail sets multiple IMAP KEYWORD and the general "\Flagged" FLAG
# imaplib => conn.fetch(b"<message_id>", "FLAGS")
@ -428,12 +428,22 @@ class MailAccountHandler(LoggingMixin):
logging_name = "paperless_mail"
_message_preprocessors: list[Callable[[MailMessage], MailMessage]] = []
_message_preprocessor_types: list[type[MailMessagePreprocessor]] = [
MailMessageDecryptor,
]
def __init__(self, *gpgArgs, **gpgkwArgs) -> None:
def __init__(self) -> None:
super().__init__()
self._message_preprocessors.append(MailMessageDecryptor(*gpgArgs, **gpgkwArgs))
self.renew_logging_group()
self._init_preprocessors()
def _init_preprocessors(self):
self._message_preprocessors: list[MailMessagePreprocessor] = []
for preprocessor_type in self._message_preprocessor_types:
if preprocessor_type.able_to_run():
self._message_preprocessors.append(preprocessor_type())
else:
self.log.debug(f"Skipping mail preprocessor {preprocessor_type.NAME}")
def _correspondent_from_name(self, name: str) -> Optional[Correspondent]:
try:
@ -542,7 +552,7 @@ class MailAccountHandler(LoggingMixin):
def _preprocess_message(self, message: MailMessage):
for preprocessor in self._message_preprocessors:
message = preprocessor(message)
message = preprocessor.run(message)
return message
def _handle_mail_rule(

View File

@ -1,22 +1,53 @@
import abc
from email import message_from_bytes
from email import policy
from email.message import Message
from django.conf import settings
from gnupg import GPG
from imap_tools import MailMessage
from documents.loggers import LoggingMixin
class MailMessageDecryptor(LoggingMixin):
class MailMessagePreprocessor(abc.ABC):
"""
Defines the interface for preprocessors that alter messages before they are handled in MailAccountHandler
"""
NAME: str = "MailMessagePreprocessor"
@staticmethod
@abc.abstractmethod
def able_to_run() -> bool:
"""
Return True if the conditions are met for the preprocessor to run, False otherwise
If False, run(message) will not be called
"""
@abc.abstractmethod
def run(self, message: MailMessage) -> MailMessage:
"""
Performs the actual preprocessing task
"""
class MailMessageDecryptor(MailMessagePreprocessor, LoggingMixin):
logging_name = "paperless_mail_message_decryptor"
def __init__(self, *args, **kwargs):
NAME = "MailMessageDecryptor"
def __init__(self):
super().__init__()
self.renew_logging_group()
self._gpg = GPG(*args, **kwargs)
self._gpg = GPG(gnupghome=settings.EMAIL_GNUPG_HOME)
def __call__(self, message: MailMessage) -> MailMessage:
@staticmethod
def able_to_run() -> bool:
return settings.EMAIL_ENABLE_GPG_DECRYPTOR
def run(self, message: MailMessage) -> MailMessage:
if not hasattr(message, "obj"):
self.log.debug("Message does not have 'obj' attribute")
return message

View File

@ -16,6 +16,7 @@ import pytest
from django.core.management import call_command
from django.db import DatabaseError
from django.test import TestCase
from django.test import override_settings
from imap_tools import NOT
from imap_tools import EmailAddress
from imap_tools import FolderInfo
@ -273,10 +274,11 @@ class TestMail(
self.reset_bogus_mailbox()
self.messageEncryptor = MessageEncryptor()
self.mail_account_handler = MailAccountHandler(
gnupghome=self.messageEncryptor.gpg_home,
)
with override_settings(
EMAIL_GNUPG_HOME=self.messageEncryptor.gpg_home,
EMAIL_ENABLE_GPG_DECRYPTOR=True,
):
self.mail_account_handler = MailAccountHandler()
super().setUp()
@ -1368,10 +1370,13 @@ class TestMail(
self.assertEqual(encrypted_message.attachments[0].filename, "encrypted.asc")
self.assertEqual(encrypted_message.text, "")
message_decryptor = MailMessageDecryptor(
gnupghome=self.messageEncryptor.gpg_home,
)
decrypted_message = message_decryptor(encrypted_message)
with override_settings(
EMAIL_ENABLE_GPG_DECRYPTOR=True,
EMAIL_GNUPG_HOME=self.messageEncryptor.gpg_home,
):
message_decryptor = MailMessageDecryptor()
self.assertTrue(message_decryptor.able_to_run())
decrypted_message = message_decryptor.run(encrypted_message)
self.assertEqual(len(decrypted_message.attachments), 2)
self.assertEqual(decrypted_message.attachments[0].filename, "f1.pdf")