Merge pull request #1003 from simple-login/ac-full-app

Allow testing sent mails and add migrations and templates in app bundle
This commit is contained in:
Adrià Casajús 2022-05-19 12:47:04 +02:00 committed by GitHub
commit 76e40894e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 32 additions and 23 deletions

View File

@ -37,7 +37,6 @@ from sqlalchemy import func
from app.config import ( from app.config import (
ROOT_DIR, ROOT_DIR,
POSTFIX_SERVER, POSTFIX_SERVER,
NOT_SEND_EMAIL,
DKIM_SELECTOR, DKIM_SELECTOR,
DKIM_PRIVATE_KEY, DKIM_PRIVATE_KEY,
ALIAS_DOMAINS, ALIAS_DOMAINS,
@ -295,15 +294,6 @@ def send_email(
from_addr=None, from_addr=None,
): ):
to_email = sanitize_email(to_email) to_email = sanitize_email(to_email)
if NOT_SEND_EMAIL:
LOG.d(
"send email with subject '%s' to '%s', plaintext: %s, html: %s",
subject,
to_email,
plaintext,
html,
)
return
LOG.d("send email to %s, subject '%s'", to_email, subject) LOG.d("send email to %s, subject '%s'", to_email, subject)

View File

@ -2,17 +2,12 @@ import time
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
from mailbox import Message from mailbox import Message
from smtplib import SMTP, SMTPServerDisconnected, SMTPRecipientsRefused from smtplib import SMTP, SMTPServerDisconnected, SMTPRecipientsRefused
from typing import Optional, Dict from typing import Optional, Dict, List
import newrelic.agent import newrelic.agent
from attr import dataclass from attr import dataclass
from app.config import ( from app import config
NOT_SEND_EMAIL,
POSTFIX_SUBMISSION_TLS,
POSTFIX_PORT,
POSTFIX_SERVER,
)
from app.email import headers from app.email import headers
from app.log import LOG from app.log import LOG
from app.message_utils import message_to_bytes from app.message_utils import message_to_bytes
@ -32,13 +27,26 @@ class SendRequest:
class MailSender: class MailSender:
def __init__(self): def __init__(self):
self._pool: Optional[ThreadPoolExecutor] = None self._pool: Optional[ThreadPoolExecutor] = None
self._store_emails = False
self._emails_sent: List[SendRequest] = []
def store_emails_instead_of_sending(self):
self._store_emails = True
def purge_stored_emails(self):
self._emails_sent = []
def get_stored_emails(self) -> List[SendRequest]:
return self._emails_sent
def enable_background_pool(self, max_workers=10): def enable_background_pool(self, max_workers=10):
self._pool = ThreadPoolExecutor(max_workers=max_workers) self._pool = ThreadPoolExecutor(max_workers=max_workers)
def send(self, send_request: SendRequest, retries: int = 2): def send(self, send_request: SendRequest, retries: int = 2):
"""replace smtp.sendmail""" """replace smtp.sendmail"""
if NOT_SEND_EMAIL: if self._store_emails:
self._emails_sent.append(send_request)
if config.NOT_SEND_EMAIL:
LOG.d( LOG.d(
"send email with subject '%s', from '%s' to '%s'", "send email with subject '%s', from '%s' to '%s'",
send_request.msg[headers.SUBJECT], send_request.msg[headers.SUBJECT],
@ -54,13 +62,13 @@ class MailSender:
def _send_to_smtp(self, send_request: SendRequest, retries: int): def _send_to_smtp(self, send_request: SendRequest, retries: int):
try: try:
start = time.time() start = time.time()
if POSTFIX_SUBMISSION_TLS: if config.POSTFIX_SUBMISSION_TLS:
smtp_port = 587 smtp_port = 587
else: else:
smtp_port = POSTFIX_PORT smtp_port = config.POSTFIX_PORT
with SMTP(POSTFIX_SERVER, smtp_port) as smtp: with SMTP(config.POSTFIX_SERVER, smtp_port) as smtp:
if POSTFIX_SUBMISSION_TLS: if config.POSTFIX_SUBMISSION_TLS:
smtp.starttls() smtp.starttls()
elapsed = time.time() - start elapsed = time.time() - start

View File

@ -27,8 +27,10 @@ license = "MIT"
repository = "https://github.com/simple-login/app" repository = "https://github.com/simple-login/app"
keywords = ["email", "alias", "privacy", "oauth2", "openid"] keywords = ["email", "alias", "privacy", "oauth2", "openid"]
packages = [ packages = [
{ include = "app/" } { include = "app/" },
{ include = "migrations/" },
] ]
include = ["templates/*", "templates/**/*"]
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.7" python = "^3.7"

View File

@ -13,6 +13,7 @@ from app.handler.provider_complaint import (
handle_hotmail_complaint, handle_hotmail_complaint,
handle_yahoo_complaint, handle_yahoo_complaint,
) )
from app.mail_sender import mail_sender
from app.models import ( from app.models import (
Alias, Alias,
ProviderComplaint, ProviderComplaint,
@ -61,6 +62,8 @@ def prepare_complaint(
@pytest.mark.parametrize("handle_ftor,provider", origins) @pytest.mark.parametrize("handle_ftor,provider", origins)
def test_provider_to_user(flask_client, handle_ftor, provider): def test_provider_to_user(flask_client, handle_ftor, provider):
mail_sender.store_emails_instead_of_sending()
mail_sender.purge_stored_emails()
user = create_new_user() user = create_new_user()
alias = Alias.create_new_random(user) alias = Alias.create_new_random(user)
Session.commit() Session.commit()
@ -70,6 +73,8 @@ def test_provider_to_user(flask_client, handle_ftor, provider):
assert len(found) == 0 assert len(found) == 0
alerts = SentAlert.filter_by(user_id=user.id).all() alerts = SentAlert.filter_by(user_id=user.id).all()
assert len(alerts) == 1 assert len(alerts) == 1
sent_mails = mail_sender.get_stored_emails()
assert len(sent_mails) == 1
assert alerts[0].alert_type == f"{ALERT_COMPLAINT_TRANSACTIONAL_PHASE}_{provider}" assert alerts[0].alert_type == f"{ALERT_COMPLAINT_TRANSACTIONAL_PHASE}_{provider}"
@ -89,6 +94,8 @@ def test_provider_forward_phase(flask_client, handle_ftor, provider):
@pytest.mark.parametrize("handle_ftor,provider", origins) @pytest.mark.parametrize("handle_ftor,provider", origins)
def test_provider_reply_phase(flask_client, handle_ftor, provider): def test_provider_reply_phase(flask_client, handle_ftor, provider):
mail_sender.store_emails_instead_of_sending()
mail_sender.purge_stored_emails()
user = create_new_user() user = create_new_user()
alias = Alias.create_new_random(user) alias = Alias.create_new_random(user)
Session.commit() Session.commit()
@ -98,4 +105,6 @@ def test_provider_reply_phase(flask_client, handle_ftor, provider):
assert len(found) == 0 assert len(found) == 0
alerts = SentAlert.filter_by(user_id=user.id).all() alerts = SentAlert.filter_by(user_id=user.id).all()
assert len(alerts) == 1 assert len(alerts) == 1
sent_mails = mail_sender.get_stored_emails()
assert len(sent_mails) == 1
assert alerts[0].alert_type == f"{ALERT_COMPLAINT_FORWARD_PHASE}_{provider}" assert alerts[0].alert_type == f"{ALERT_COMPLAINT_FORWARD_PHASE}_{provider}"