feat(mail): add governed POP3 legacy import
Module Package Release / publish-packages (push) Successful in 11s

This commit is contained in:
2026-08-22 04:52:25 +02:00
parent 93ecedf607
commit 218fef11f1
27 changed files with 3291 additions and 88 deletions
+31 -15
View File
@@ -23,6 +23,8 @@ from govoplan_core.security.secrets import decrypt_secret
from govoplan_mail.backend.config import (
ImapConfig,
ImapServerConfig,
Pop3Config,
Pop3ServerConfig,
SmtpConfig,
SmtpServerConfig,
)
@@ -34,7 +36,7 @@ from govoplan_mail.backend.db.models import (
)
MAIL_SERVER_PROTOCOLS = frozenset({"smtp", "imap"})
MAIL_SERVER_PROTOCOLS = frozenset({"smtp", "imap", "pop3"})
class MailServerHierarchyError(RuntimeError):
@@ -68,7 +70,7 @@ class ResolvedMailTransport:
profile: MailServerProfile
server: MailServerEndpoint | None
credential: CredentialEnvelope | None
config: SmtpConfig | ImapConfig
config: SmtpConfig | ImapConfig | Pop3Config
transport_revision: str
@@ -951,6 +953,10 @@ def resolve_mail_transport(
)
)
if server is None:
if clean_protocol == "pop3":
raise MailServerHierarchyError(
"The selected Mail profile has no active POP3 legacy-import server"
)
return _legacy_resolved_transport(profile, clean_protocol)
binding, credential = _selected_server_credential(
session,
@@ -978,16 +984,17 @@ def resolve_mail_transport(
if clean_protocol == "smtp":
payload["username"] = profile.smtp_username
payload["password"] = decrypt_secret(profile.smtp_password_encrypted)
else:
elif clean_protocol == "imap":
payload["username"] = profile.imap_username
payload["password"] = decrypt_secret(profile.imap_password_encrypted)
config: SmtpConfig | ImapConfig
config: SmtpConfig | ImapConfig | Pop3Config
try:
config = (
SmtpConfig.model_validate(payload)
if clean_protocol == "smtp"
else ImapConfig.model_validate(payload)
)
if clean_protocol == "smtp":
config = SmtpConfig.model_validate(payload)
elif clean_protocol == "imap":
config = ImapConfig.model_validate(payload)
else:
config = Pop3Config.model_validate(payload)
except Exception as exc:
raise MailServerHierarchyError(
f"The selected {clean_protocol.upper()} server configuration is invalid"
@@ -1030,6 +1037,14 @@ def select_mail_transport(
)
)
if server is None:
if clean_protocol == "pop3":
return SelectedMailTransport(
profile=profile,
server=None,
credential=None,
available=False,
transport_revision="unconfigured",
)
legacy_config = (
profile.smtp_config
if clean_protocol == "smtp"
@@ -1302,11 +1317,12 @@ def _validated_server_config(
payload.pop("password", None)
payload.pop("enabled", None)
try:
model = (
SmtpServerConfig.model_validate(payload)
if protocol == "smtp"
else ImapServerConfig.model_validate(payload)
)
if protocol == "smtp":
model = SmtpServerConfig.model_validate(payload)
elif protocol == "imap":
model = ImapServerConfig.model_validate(payload)
else:
model = Pop3ServerConfig.model_validate(payload)
except Exception as exc:
raise MailServerHierarchyError(
f"Invalid {protocol.upper()} server configuration"
@@ -1347,7 +1363,7 @@ def _server_scope(
def _normalize_protocol(value: str) -> str:
clean = str(value or "").strip().casefold()
if clean not in MAIL_SERVER_PROTOCOLS:
raise MailServerHierarchyError("Mail server protocol must be smtp or imap")
raise MailServerHierarchyError("Mail server protocol must be smtp, imap or pop3")
return clean