Skip to content
Snippets Groups Projects
Commit a41048aa authored by Bjarke Madsen's avatar Bjarke Madsen
Browse files

Merge branch 'feature/COMP-257-Send-Email-On-Signup' into 'develop'

Feature/comp 257 send email on signup

See merge request !69
parents ffeaffcb b26221b5
No related branches found
No related tags found
1 merge request!69Feature/comp 257 send email on signup
...@@ -67,6 +67,17 @@ def _create_app_with_db(app_config) -> Flask: ...@@ -67,6 +67,17 @@ def _create_app_with_db(app_config) -> Flask:
# for the publishers # for the publishers
app.config['SQLALCHEMY_BINDS'] = app_config['SQLALCHEMY_BINDS'] app.config['SQLALCHEMY_BINDS'] = app_config['SQLALCHEMY_BINDS']
if 'mail' in app_config:
mail_config = app_config['mail']
app.config['MAIL_ENABLE'] = True
app.config['MAIL_SERVER'] = mail_config['MAIL_SERVER']
app.config['MAIL_PORT'] = mail_config['MAIL_PORT']
app.config['MAIL_SENDER_EMAIL'] = mail_config['MAIL_SENDER_EMAIL'] # email address to send emails from
excluded_admins = mail_config.get('MAIL_EXCLUDED_ADMINS', [])
app.config['MAIL_EXCLUDED_ADMINS'] = excluded_admins # list of admin emails not to send emails to
else:
app.config['MAIL_ENABLE'] = False
db.init_app(app) db.init_app(app)
return app return app
......
...@@ -5,6 +5,7 @@ from datetime import datetime ...@@ -5,6 +5,7 @@ from datetime import datetime
from flask_login import LoginManager, current_user # type: ignore from flask_login import LoginManager, current_user # type: ignore
from compendium_v2.db import session_scope from compendium_v2.db import session_scope
from compendium_v2.db.auth_model import User, ROLES from compendium_v2.db.auth_model import User, ROLES
from compendium_v2.email import send_mail
def admin_required(func): def admin_required(func):
...@@ -45,6 +46,7 @@ def create_user(email: str, fullname: str, oidc_sub: str): ...@@ -45,6 +46,7 @@ def create_user(email: str, fullname: str, oidc_sub: str):
with session_scope() as session: with session_scope() as session:
user = User(email=email, fullname=fullname, oidc_sub=oidc_sub) user = User(email=email, fullname=fullname, oidc_sub=oidc_sub)
session.add(user) session.add(user)
send_mail(f'{fullname} has just signed up with the email {email} and provider ID {oidc_sub}')
return user return user
......
...@@ -17,6 +17,20 @@ CONFIG_SCHEMA = { ...@@ -17,6 +17,20 @@ CONFIG_SCHEMA = {
'required': ['client_id', 'client_secret', 'server_metadata_url'], 'required': ['client_id', 'client_secret', 'server_metadata_url'],
'additionalProperties': False 'additionalProperties': False
}, },
'mail': {
'type': 'object',
'properties': {
'MAIL_SERVER': {'type': 'string'},
'MAIL_PORT': {'type': 'integer'},
'MAIL_SENDER_EMAIL': {'type': 'string', 'format': 'email'},
'MAIL_EXCLUDED_ADMINS': {
'type': 'array',
'items': {'type': 'string', 'format': 'email'}
}
},
'required': ['MAIL_SERVER', 'MAIL_PORT', 'MAIL_SENDER_EMAIL'],
'additionalProperties': False
},
'SECRET_KEY': {'type': 'string'}, 'SECRET_KEY': {'type': 'string'},
}, },
'required': ['SQLALCHEMY_DATABASE_URI', 'SURVEY_DATABASE_URI', 'SECRET_KEY'], 'required': ['SQLALCHEMY_DATABASE_URI', 'SURVEY_DATABASE_URI', 'SECRET_KEY'],
......
import smtplib
import threading
import logging
from typing import Sequence, Union
from sqlalchemy import select
from flask import current_app
from compendium_v2.db import db
from compendium_v2.db.auth_model import User, ROLES
logger = logging.getLogger(__name__)
def _send_mail(smtp_server, port, sender_email, recipients, message):
try:
with smtplib.SMTP(smtp_server, port) as server:
server.sendmail(from_addr=sender_email, to_addrs=recipients, msg=message)
logger.debug('Successfully sent email')
except Exception:
logger.exception('Unable to send email:')
def send_mail(
contents: str,
subject: str = 'New user signed up for Compendium',
recipients: Union[str, Sequence[str]] = ''
):
if not current_app.config['MAIL_ENABLE']:
logger.warning('No mail configuration, cannot send email.')
return
if not contents or not isinstance(contents, str):
raise ValueError('Contents must be a non-empty string.')
excluded_admins = set(email.lower() for email in current_app.config['MAIL_EXCLUDED_ADMINS'])
admins = db.session.scalars(select(User).where(User.roles == ROLES.admin)).unique().all()
admin_emails = [admin.email for admin in admins if admin.email.lower() not in excluded_admins]
if not recipients:
recipients = admin_emails
subject = subject.replace('\n', ' ')
message = f"""Subject: {subject}\n\n{contents}"""
smtp_server = current_app.config['MAIL_SERVER']
port = current_app.config['MAIL_PORT']
sender_email = current_app.config['MAIL_SENDER_EMAIL']
# spin off a thread since this can take some time..
logger.debug('Sending email')
thread = threading.Thread(target=_send_mail, args=(smtp_server, port, sender_email, recipients, message))
thread.start()
...@@ -6,5 +6,13 @@ ...@@ -6,5 +6,13 @@
"client_secret": "<secret>", "client_secret": "<secret>",
"server_metadata_url": "https://accounts.google.com/.well-known/openid-configuration" "server_metadata_url": "https://accounts.google.com/.well-known/openid-configuration"
}, },
"SECRET_KEY": "changeme" "SECRET_KEY": "changeme",
"mail": {
"MAIL_SERVER": "mail.geant.net",
"MAIL_PORT": 25,
"MAIL_SENDER_EMAIL": "compendium@geant.org",
"MAIL_EXCLUDED_ADMINS": [
"bjarke@nordu.net"
]
}
} }
...@@ -26,7 +26,7 @@ def dummy_config(): ...@@ -26,7 +26,7 @@ def dummy_config():
@pytest.fixture @pytest.fixture
def mocked_admin_user(app, mocker): def mocked_admin_user(app, test_survey_data, mocker):
with app.app_context(): with app.app_context():
user = User(email='testemail123@email.local', fullname='testfullname', oidc_sub='fakesub', roles=ROLES.admin) user = User(email='testemail123@email.local', fullname='testfullname', oidc_sub='fakesub', roles=ROLES.admin)
...@@ -42,7 +42,7 @@ def mocked_admin_user(app, mocker): ...@@ -42,7 +42,7 @@ def mocked_admin_user(app, mocker):
@pytest.fixture @pytest.fixture
def mocked_user(app, mocker): def mocked_user(app, test_survey_data, mocker):
with app.app_context(): with app.app_context():
user = User(email='testemail123@email.local', fullname='testfullname', oidc_sub='fakesub') user = User(email='testemail123@email.local', fullname='testfullname', oidc_sub='fakesub')
......
from compendium_v2.email import send_mail
def test_email(app, mocked_admin_user, mocker):
def _send_mail(*args, **kwargs):
pass
mocker.patch('compendium_v2.email._send_mail', _send_mail)
with app.app_context():
app.config['MAIL_ENABLE'] = True
app.config['MAIL_SERVER'] = 'localhost'
app.config['MAIL_PORT'] = 54655
app.config['MAIL_SENDER_EMAIL'] = 'fakesender123@test.local'
app.config['MAIL_EXCLUDED_ADMINS'] = []
send_mail('testmail321')
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment