Add settings for email decryption preprocessor
This commit is contained in:
parent
243daf7469
commit
c134ce9025
@ -1171,6 +1171,15 @@ if DEBUG: # pragma: no cover
|
|||||||
EMAIL_BACKEND = "django.core.mail.backends.filebased.EmailBackend"
|
EMAIL_BACKEND = "django.core.mail.backends.filebased.EmailBackend"
|
||||||
EMAIL_FILE_PATH = BASE_DIR / "sent_emails"
|
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
|
# Soft Delete
|
||||||
|
@ -10,7 +10,6 @@ from datetime import timedelta
|
|||||||
from fnmatch import fnmatch
|
from fnmatch import fnmatch
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from typing import Callable
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from typing import Union
|
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 MailRule
|
||||||
from paperless_mail.models import ProcessedMail
|
from paperless_mail.models import ProcessedMail
|
||||||
from paperless_mail.preprocessor import MailMessageDecryptor
|
from paperless_mail.preprocessor import MailMessageDecryptor
|
||||||
|
from paperless_mail.preprocessor import MailMessagePreprocessor
|
||||||
|
|
||||||
# Apple Mail sets multiple IMAP KEYWORD and the general "\Flagged" FLAG
|
# Apple Mail sets multiple IMAP KEYWORD and the general "\Flagged" FLAG
|
||||||
# imaplib => conn.fetch(b"<message_id>", "FLAGS")
|
# imaplib => conn.fetch(b"<message_id>", "FLAGS")
|
||||||
@ -428,12 +428,22 @@ class MailAccountHandler(LoggingMixin):
|
|||||||
|
|
||||||
logging_name = "paperless_mail"
|
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__()
|
super().__init__()
|
||||||
self._message_preprocessors.append(MailMessageDecryptor(*gpgArgs, **gpgkwArgs))
|
|
||||||
self.renew_logging_group()
|
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]:
|
def _correspondent_from_name(self, name: str) -> Optional[Correspondent]:
|
||||||
try:
|
try:
|
||||||
@ -542,7 +552,7 @@ class MailAccountHandler(LoggingMixin):
|
|||||||
|
|
||||||
def _preprocess_message(self, message: MailMessage):
|
def _preprocess_message(self, message: MailMessage):
|
||||||
for preprocessor in self._message_preprocessors:
|
for preprocessor in self._message_preprocessors:
|
||||||
message = preprocessor(message)
|
message = preprocessor.run(message)
|
||||||
return message
|
return message
|
||||||
|
|
||||||
def _handle_mail_rule(
|
def _handle_mail_rule(
|
||||||
|
@ -1,22 +1,53 @@
|
|||||||
|
import abc
|
||||||
from email import message_from_bytes
|
from email import message_from_bytes
|
||||||
from email import policy
|
from email import policy
|
||||||
from email.message import Message
|
from email.message import Message
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from gnupg import GPG
|
from gnupg import GPG
|
||||||
from imap_tools import MailMessage
|
from imap_tools import MailMessage
|
||||||
|
|
||||||
from documents.loggers import LoggingMixin
|
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"
|
logging_name = "paperless_mail_message_decryptor"
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
NAME = "MailMessageDecryptor"
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.renew_logging_group()
|
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"):
|
if not hasattr(message, "obj"):
|
||||||
self.log.debug("Message does not have 'obj' attribute")
|
self.log.debug("Message does not have 'obj' attribute")
|
||||||
return message
|
return message
|
||||||
|
@ -16,6 +16,7 @@ import pytest
|
|||||||
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 imap_tools import NOT
|
from imap_tools import NOT
|
||||||
from imap_tools import EmailAddress
|
from imap_tools import EmailAddress
|
||||||
from imap_tools import FolderInfo
|
from imap_tools import FolderInfo
|
||||||
@ -273,10 +274,11 @@ class TestMail(
|
|||||||
self.reset_bogus_mailbox()
|
self.reset_bogus_mailbox()
|
||||||
|
|
||||||
self.messageEncryptor = MessageEncryptor()
|
self.messageEncryptor = MessageEncryptor()
|
||||||
|
with override_settings(
|
||||||
self.mail_account_handler = MailAccountHandler(
|
EMAIL_GNUPG_HOME=self.messageEncryptor.gpg_home,
|
||||||
gnupghome=self.messageEncryptor.gpg_home,
|
EMAIL_ENABLE_GPG_DECRYPTOR=True,
|
||||||
)
|
):
|
||||||
|
self.mail_account_handler = MailAccountHandler()
|
||||||
|
|
||||||
super().setUp()
|
super().setUp()
|
||||||
|
|
||||||
@ -1368,10 +1370,13 @@ class TestMail(
|
|||||||
self.assertEqual(encrypted_message.attachments[0].filename, "encrypted.asc")
|
self.assertEqual(encrypted_message.attachments[0].filename, "encrypted.asc")
|
||||||
self.assertEqual(encrypted_message.text, "")
|
self.assertEqual(encrypted_message.text, "")
|
||||||
|
|
||||||
message_decryptor = MailMessageDecryptor(
|
with override_settings(
|
||||||
gnupghome=self.messageEncryptor.gpg_home,
|
EMAIL_ENABLE_GPG_DECRYPTOR=True,
|
||||||
)
|
EMAIL_GNUPG_HOME=self.messageEncryptor.gpg_home,
|
||||||
decrypted_message = message_decryptor(encrypted_message)
|
):
|
||||||
|
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(len(decrypted_message.attachments), 2)
|
||||||
self.assertEqual(decrypted_message.attachments[0].filename, "f1.pdf")
|
self.assertEqual(decrypted_message.attachments[0].filename, "f1.pdf")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user