Update email verification, add Mailgun backend (#380)

Changes config.example.py!
This commit is contained in:
Anna-Maria Meriniemi 2017-10-08 03:31:32 +03:00 committed by Arylide
parent 19eaa2c532
commit 6d09920abd
7 changed files with 156 additions and 30 deletions

View File

@ -19,6 +19,8 @@ MAINTENANCE_MODE_LOGINS = True
# What the site identifies itself as. This affects templates, not database stuff.
SITE_NAME = 'Nyaa'
# What the both sites are labeled under (used for eg. email subjects)
GLOBAL_SITE_NAME = 'Nyaa.si'
# General prefix for running multiple sites, eg. most database tables are site-prefixed
SITE_FLAVOR = 'nyaa' # 'nyaa' or 'sukebei'
@ -49,13 +51,25 @@ else:
SQLALCHEMY_DATABASE_URI = (
'sqlite:///' + os.path.join(BASE_DIR, 'test.db') + '?check_same_thread=False')
# Email server settings
###########
## EMAIL ##
###########
# 'smtp' or 'mailgun'
MAIL_BACKEND = 'mailgun'
MAIL_FROM_ADDRESS = 'Sender Name <sender@domain.com>'
# Mailgun settings
MAILGUN_API_BASE = 'https://api.mailgun.net/v3/YOUR_DOMAIN_NAME'
MAILGUN_API_KEY = 'YOUR_API_KEY'
# SMTP settings
SMTP_SERVER = '***'
SMTP_PORT = 587
MAIL_FROM_ADDRESS = '***'
SMTP_USERNAME = '***'
SMTP_PASSWORD = '***'
# The maximum number of files a torrent can contain
# until the site says "Too many files to display."
MAX_FILES_VIEW = 1000

83
nyaa/email.py Normal file
View File

@ -0,0 +1,83 @@
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from flask import current_app as app
import requests
from nyaa import models
class EmailHolder(object):
''' Holds email subject, recipient and content, so we have a general class for
all mail backends. '''
def __init__(self, subject=None, recipient=None, text=None, html=None):
self.subject = subject
self.recipient = recipient # models.User or string
self.text = text
self.html = html
def format_recipient(self):
if isinstance(self.recipient, models.User):
return '{} <{}>'.format(self.recipient.username, self.recipient.email)
else:
return self.recipient
def recipient_email(self):
if isinstance(self.recipient, models.User):
return self.recipient.email
else:
return self.recipient.email
def as_mimemultipart(self):
msg = MIMEMultipart()
msg['Subject'] = self.subject
msg['From'] = app.config['MAIL_FROM_ADDRESS']
msg['To'] = self.format_recipient()
msg.attach(MIMEText(self.text, 'plain'))
if self.html:
msg.attach(MIMEText(self.html, 'html'))
return msg
def send_email(email_holder):
mail_backend = app.config.get('MAIL_BACKEND')
if mail_backend == 'mailgun':
_send_mailgun(email_holder)
elif mail_backend == 'smtp':
_send_smtp(email_holder)
elif mail_backend:
# TODO: Do this in logging.error when we have that set up
print('Unknown mail backend:', mail_backend)
def _send_mailgun(email_holder):
mailgun_endpoint = app.config['MAILGUN_API_BASE'] + '/messages'
auth = ('api', app.config['MAILGUN_API_KEY'])
data = {
'from': app.config['MAIL_FROM_ADDRESS'],
'to': email_holder.format_recipient(),
'subject': email_holder.subject,
'text': email_holder.text,
'html': email_holder.html
}
r = requests.post(mailgun_endpoint, data=data, auth=auth)
# TODO real error handling?
assert r.status_code == 200
def _send_smtp(email_holder):
# NOTE: Unused, most likely untested! Should work, however.
msg = email_holder.as_mimemultipart()
server = smtplib.SMTP(app.config['SMTP_SERVER'], app.config['SMTP_PORT'])
server.set_debuglevel(1)
server.ehlo()
server.starttls()
server.ehlo()
server.login(app.config['SMTP_USERNAME'], app.config['SMTP_PASSWORD'])
server.sendmail(app.config['SMTP_USERNAME'], email_holder.recipient_email(), msg.as_string())
server.quit()

View File

@ -0,0 +1,24 @@
<html>
<head>
<title>Verify your {{ config.GLOBAL_SITE_NAME }} account</title>
<style type="text/css">
.well {
display: inline-block;
border-radius: 5px;
padding: 10px;
background-color: rgb(240, 240, 240)
}
</style>
</head>
<body>
<div>
{{ user.username }}, please verify your email by clicking the link below:
</div>
<div class="well">
<a href="{{ activation_link }}">{{ activation_link }}</a>
</div>
<div>
If you did not sign up for {{ config.GLOBAL_SITE_NAME }}, feel free to ignore this email.
</div>
</body>
</html>

View File

@ -0,0 +1,6 @@
{{ user.username }}, please verify your email by clicking the link below:
{{ activation_link }}
(if you can't click on the link, copy and paste it to your browser's address bar)
If you did not sign up for {{ config.GLOBAL_SITE_NAME }}, feel free to ignore this email.

View File

@ -1,12 +1,9 @@
import smtplib
from datetime import datetime
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from ipaddress import ip_address
import flask
from nyaa import forms, models
from nyaa import email, forms, models
from nyaa.extensions import db
from nyaa.views.users import get_activation_link
@ -83,8 +80,7 @@ def register():
db.session.commit()
if app.config['USE_EMAIL_VERIFICATION']: # force verification, enable email
activ_link = get_activation_link(user)
send_verification_email(user.email, activ_link)
send_verification_email(user)
return flask.render_template('waiting.html')
else: # disable verification, set user as active and auto log in
user.status = models.UserStatusType.ACTIVE
@ -150,25 +146,19 @@ def redirect_url():
return url
def send_verification_email(to_address, activ_link):
''' this is until we have our own mail server, obviously.
This can be greatly cut down if on same machine.
probably can get rid of all but msg formatting/building,
init line and sendmail line if local SMTP server '''
def send_verification_email(user):
activation_link = get_activation_link(user)
msg_body = 'Please click on: ' + activ_link + ' to activate your account.\n\n\nUnsubscribe:'
tmpl_context = {
'activation_link': activation_link,
'user': user
}
msg = MIMEMultipart()
msg['Subject'] = 'Verification Link'
msg['From'] = app.config['MAIL_FROM_ADDRESS']
msg['To'] = to_address
msg.attach(MIMEText(msg_body, 'plain'))
email_msg = email.EmailHolder(
subject='Verify your {} account'.format(app.config['GLOBAL_SITE_NAME']),
recipient=user,
text=flask.render_template('email/verify.txt', **tmpl_context),
html=flask.render_template('email/verify.html', **tmpl_context),
)
server = smtplib.SMTP(app.config['SMTP_SERVER'], app.config['SMTP_PORT'])
server.set_debuglevel(1)
server.ehlo()
server.starttls()
server.ehlo()
server.login(app.config['SMTP_USERNAME'], app.config['SMTP_PASSWORD'])
server.sendmail(app.config['SMTP_USERNAME'], to_address, msg.as_string())
server.quit()
email.send_email(email_msg)

View File

@ -202,15 +202,23 @@ def activate_user(payload):
user = models.User.by_id(user_id)
if not user:
# Only allow activating inactive users
if not user or user.status != models.UserStatusType.INACTIVE:
flask.abort(404)
# Set user active
user.status = models.UserStatusType.ACTIVE
db.session.add(user)
db.session.commit()
return flask.redirect(flask.url_for('account.login'))
# Log user in
flask.g.user = user
flask.session['user_id'] = user.id
flask.session.permanent = True
flask.session.modified = True
flask.flash(flask.Markup("You've successfully verified your account!"), 'success')
return flask.redirect(flask.url_for('main.home'))
def _create_user_class_choices(user):

View File

@ -41,6 +41,7 @@ pytest==3.1.1
python-dateutil==2.6.0
python-editor==1.0.3
python-utils==2.1.0
requests==2.18.4
six==1.10.0
SQLAlchemy==1.1.10
SQLAlchemy-FullText-Search==0.2.3