diff options
author | Alexander Sulfrian <alexander@sulfrian.net> | 2016-01-23 19:24:42 +0100 |
---|---|---|
committer | Alexander Sulfrian <alexander@sulfrian.net> | 2016-01-25 01:56:48 +0100 |
commit | ea3983d891bc6e34a827902ac8cf15734923e14c (patch) | |
tree | 87abdc6d4c5976341b3d12ddc94af7a55e7c7d95 | |
parent | 64ab1d6c61905df0c1c81ca8b7fb2135bb5692ee (diff) | |
download | web-ea3983d891bc6e34a827902ac8cf15734923e14c.tar.gz web-ea3983d891bc6e34a827902ac8cf15734923e14c.tar.bz2 web-ea3983d891bc6e34a827902ac8cf15734923e14c.zip |
backend/mail: Allow different backends for mail
-rw-r--r-- | accounts/__init__.py | 13 | ||||
-rw-r--r-- | accounts/backend/__init__.py | 0 | ||||
-rw-r--r-- | accounts/backend/mail/__init__.py | 9 | ||||
-rw-r--r-- | accounts/backend/mail/dummy.py | 48 | ||||
-rw-r--r-- | accounts/backend/mail/sendmail.py | 35 | ||||
-rw-r--r-- | accounts/default_settings.py | 2 | ||||
-rw-r--r-- | accounts/utils.py | 42 | ||||
-rw-r--r-- | accounts/views/admin/__init__.py | 4 |
8 files changed, 115 insertions, 38 deletions
diff --git a/accounts/__init__.py b/accounts/__init__.py index 8fbcdd0..09aa875 100644 --- a/accounts/__init__.py +++ b/accounts/__init__.py @@ -18,6 +18,7 @@ if 'SPLINE_ACCOUNT_WEB_SETTINGS' in os.environ: app.all_services = account.SERVICES #TODO: take that from our json file or so app.username_blacklist = list() +app.mail_backend = get_backend(app.config['MAIL_BACKEND'], app) @app.before_request def session_permanent(): @@ -118,7 +119,7 @@ def register_complete(token): assert login_user(user.uid, user.password) if app.config.get('MAIL_REGISTER_NOTIFY'): - send_mail( + app.mail_backend.send( app.config['MAIL_REGISTER_NOTIFY'], u'[accounts] Neuer Benutzer %s erstellt' % username, u'Benutzername: %s\nE-Mail: %s\n\nSpammer? Deaktivieren: ' @@ -151,8 +152,9 @@ def lost_password(): body = render_template('mail/lost_password.txt', username=form.user.uid, link=confirm_link) - send_mail(form.user.attributes['mail'], u'Passwort vergessen', body, - sender=app.config.get('MAIL_CONFIRM_SENDER')) + app.mail_backend.send( + form.user.attributes['mail'], u'Passwort vergessen', body, + sender=app.config.get('MAIL_CONFIRM_SENDER')) flash(u'Wir haben dir eine E-Mail mit einem Link zum Passwort ändern ' u'geschickt. Bitte folge den Anweisungen in der E-Mail.', 'success') @@ -210,8 +212,9 @@ def settings(): body = render_template('mail/change_mail.txt', username=g.user.uid, mail=form.mail.data, link=confirm_link) - send_mail(form.mail.data, u'E-Mail-Adresse bestätigen', body, - sender=app.config.get('MAIL_CONFIRM_SENDER')) + app.mail_backend.send( + form.mail.data, u'E-Mail-Adresse bestätigen', body, + sender=app.config.get('MAIL_CONFIRM_SENDER')) flash(u'Es wurde eine E-Mail an die angegebene Adresse geschickt, ' u'um diese zu überprüfen. Bitte folge den Anweisungen in der ' diff --git a/accounts/backend/__init__.py b/accounts/backend/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/accounts/backend/__init__.py diff --git a/accounts/backend/mail/__init__.py b/accounts/backend/mail/__init__.py new file mode 100644 index 0000000..c22b037 --- /dev/null +++ b/accounts/backend/mail/__init__.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- + +class Backend(object): + + def __init__(self, app): + self.app = app + + def send(self, recipient, subject, body, sender=None): + raise NotImplementedError() diff --git a/accounts/backend/mail/dummy.py b/accounts/backend/mail/dummy.py new file mode 100644 index 0000000..d62a66b --- /dev/null +++ b/accounts/backend/mail/dummy.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import + +from . import Backend +from accounts.utils import ensure_utf8 + + +FANCY_FORMAT = '''\ +,--------------------------------------------------------------------------- +| Subject: {subject} +| To: {to} +| From: {sender} +|--------------------------------------------------------------------------- +| {body} +`---------------------------------------------------------------------------''' + + +PLAIN_FORMAT = '''Subject: {subject} +To: {to} +From: {sender} + +{body}''' + + +class DummyBackend(Backend): + + def __init__(self, app, plain=False, format=None): + super(DummyBackend, self).__init__(app) + self.plain = plain + + if format is None: + if self.plain: + self.format = PLAIN_FORMAT + else: + self.format = FANCY_FORMAT + else: + self.format = format + + def send(self, recipient, subject, body, sender=None): + if sender is None: + sender = self.app.config['MAIL_DEFAULT_SENDER'] + + if not self.plain: + body = "\n| ".join(body.split("\n")) + + print(self.format.format( + subject=ensure_utf8(subject), to=ensure_utf8(recipient), + sender=ensure_utf8(sender), body=ensure_utf8(body))) diff --git a/accounts/backend/mail/sendmail.py b/accounts/backend/mail/sendmail.py new file mode 100644 index 0000000..92b354e --- /dev/null +++ b/accounts/backend/mail/sendmail.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import + +import subprocess +from email.mime.text import MIMEText +from email.utils import parseaddr + +from . import Backend + + +class SendmailBackend(Backend): + + def send(self, recipient, subject, body, sender=None): + if sender is None: + sender = self.app.config['MAIL_DEFAULT_SENDER'] + + safe = lambda s: s.split('\n', 1)[0] + + msg = MIMEText(body, _charset='utf-8') + msg['Subject'] = safe(subject) + msg['To'] = safe(recipient) + msg['From'] = safe(sender) + + envelope = [] + _, address = parseaddr(safe(sender)) + if address != '': + envelope = ['-f', address] + + p = subprocess.Popen([self.app.config['SENDMAIL_COMMAND']] + + envelope + ['-t'], stdin=subprocess.PIPE) + p.stdin.write(msg.as_string()) + p.stdin.close() + + if p.wait() != 0: + raise RuntimeError('sendmail terminated with %d' % p.returncode) diff --git a/accounts/default_settings.py b/accounts/default_settings.py index b51cbb3..538a8ed 100644 --- a/accounts/default_settings.py +++ b/accounts/default_settings.py @@ -22,3 +22,5 @@ LDAP_ADMIN_USER = 'admin' LDAP_ADMIN_PASS = 'admin' PREFERRED_URL_SCHEME = 'https' + +MAIL_BACKEND = 'accounts.backend.mail.dummy' diff --git a/accounts/utils.py b/accounts/utils.py index 86991b3..87a2ba8 100644 --- a/accounts/utils.py +++ b/accounts/utils.py @@ -1,14 +1,12 @@ # -*- coding: utf-8 -*- import hmac +import importlib import ldap import pickle import re import struct -import subprocess from base64 import urlsafe_b64encode, urlsafe_b64decode from Crypto.Cipher import AES -from email.mime.text import MIMEText -from email.utils import parseaddr from functools import wraps from flask import current_app, flash, g, redirect, render_template, request, session from flask import url_for as flask_url_for @@ -20,7 +18,6 @@ from werkzeug.exceptions import Forbidden from wtforms.validators import Regexp, ValidationError - _username_re = re.compile(r'^[a-zA-Z][a-zA-Z0-9-]{1,15}$') _username_exclude_re = re.compile(r'^(admin|root)') @@ -194,31 +191,6 @@ def constant_time_compare(val1, val2): return result == 0 -def send_mail(recipient, subject, body, sender=None): - if sender is None: - sender = current_app.config['MAIL_DEFAULT_SENDER'] - - safe = lambda s: s.split('\n', 1)[0] - - msg = MIMEText(body, _charset='utf-8') - msg['Subject'] = safe(subject) - msg['To'] = safe(recipient) - msg['From'] = safe(sender) - - envelope = [] - (name, address) = parseaddr(safe(sender)) - if address != '': - envelope = ['-f', address] - - p = subprocess.Popen([current_app.config['SENDMAIL_COMMAND']] + envelope + ['-t'], - stdin=subprocess.PIPE) - p.stdin.write(msg.as_string()) - p.stdin.close() - - if p.wait() != 0: - raise RuntimeError('sendmail terminated with %d' % p.returncode) - - class Service(object): def __init__(self, id, name, url): self.id = id @@ -243,8 +215,9 @@ def send_register_confirmation_mail(username, mail): body = render_template('mail/register.txt', username=username, mail=mail, link=confirm_link) - send_mail(mail, u'E-Mail-Adresse bestätigen', body, - sender=current_app.config.get('MAIL_CONFIRM_SENDER')) + current_app.mail_backend.send( + mail, u'E-Mail-Adresse bestätigen', body, + sender=current_app.config.get('MAIL_CONFIRM_SENDER')) class NotRegexp(Regexp): @@ -272,3 +245,10 @@ def url_for(endpoint, **values): # used when we encounter inconsistent data etc class ShouldNotHappen(RuntimeError): pass + + +def get_backend(path, app): + module = path.rsplit(".", 1).pop() + class_name = '%sBackend' % module.title() + backend_class = getattr(importlib.import_module(path), class_name) + return backend_class(app) diff --git a/accounts/views/admin/__init__.py b/accounts/views/admin/__init__.py index 998bf8b..02a2da1 100644 --- a/accounts/views/admin/__init__.py +++ b/accounts/views/admin/__init__.py @@ -6,7 +6,7 @@ from flask import current_app, redirect, request, g, flash, url_for from uuid import uuid4 from werkzeug.exceptions import Forbidden -from accounts.utils import templated, send_register_confirmation_mail, send_mail +from accounts.utils import templated, send_register_confirmation_mail from accounts.forms import AdminCreateAccountForm, AdminDisableAccountForm @@ -78,7 +78,7 @@ def disable_account(): u'gesetzt.' % mail, 'success') if current_app.config.get('MAIL_REGISTER_NOTIFY'): - send_mail( + current_app.mail_backend.send( current_app.config['MAIL_REGISTER_NOTIFY'], u'[accounts] Benutzer %s deaktiviert' % form.user.uid, 'Benutzername: %s\nE-Mail war: %s\n\ndurch: %s\n' % \ |