add confirmation via unique link
This commit is contained in:
47
auth.py
47
auth.py
@@ -1,4 +1,6 @@
|
|||||||
|
from datetime import datetime
|
||||||
import functools
|
import functools
|
||||||
|
import logging
|
||||||
from django import db
|
from django import db
|
||||||
|
|
||||||
from flask import (
|
from flask import (
|
||||||
@@ -8,8 +10,8 @@ from werkzeug.security import check_password_hash, generate_password_hash
|
|||||||
from itsdangerous import URLSafeTimedSerializer
|
from itsdangerous import URLSafeTimedSerializer
|
||||||
|
|
||||||
from db import get_db
|
from db import get_db
|
||||||
from app import app
|
import app
|
||||||
|
import calendar_interface
|
||||||
|
|
||||||
bp = Blueprint('auth', __name__, url_prefix='/auth')
|
bp = Blueprint('auth', __name__, url_prefix='/auth')
|
||||||
|
|
||||||
@@ -29,6 +31,7 @@ def register():
|
|||||||
error = 'Password is required.'
|
error = 'Password is required.'
|
||||||
elif not email:
|
elif not email:
|
||||||
error = 'Email is required.'
|
error = 'Email is required.'
|
||||||
|
# TODO: check passwrd complexity
|
||||||
|
|
||||||
if error is None:
|
if error is None:
|
||||||
try:
|
try:
|
||||||
@@ -37,10 +40,19 @@ def register():
|
|||||||
(username, email, generate_password_hash(password)),
|
(username, email, generate_password_hash(password)),
|
||||||
)
|
)
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
except db.IntegrityError:
|
except db.IntegrityError:
|
||||||
error = f"User mit email {email} existiert bereits."
|
error = f"User mit email {email} existiert bereits."
|
||||||
else:
|
else:
|
||||||
return redirect(url_for("auth.login"))
|
token = generate_confirmation_token(email)
|
||||||
|
confirm_url = url_for('auth.confirm_email', token=token, _external=True)
|
||||||
|
logging.info("Generated confirmation url: %s", confirm_url)
|
||||||
|
content = render_template('auth/confirm_mail.html', confirm_url=confirm_url, username=username, email=email)
|
||||||
|
subject = "Registrierung für Planner von %s" % email
|
||||||
|
calendar_interface.send_mail("struebin.patrick@gmail.com", subject, content)
|
||||||
|
|
||||||
|
flash("Registrierung erfolgreich.")
|
||||||
|
return redirect(url_for("index"))
|
||||||
|
|
||||||
flash(error)
|
flash(error)
|
||||||
|
|
||||||
@@ -96,6 +108,7 @@ def login_required(view): # use this as decorator
|
|||||||
@functools.wraps(view)
|
@functools.wraps(view)
|
||||||
def wrapped_view(**kwargs):
|
def wrapped_view(**kwargs):
|
||||||
if g.user is None:
|
if g.user is None:
|
||||||
|
flash("Benutzer bitte anmelden.")
|
||||||
return redirect(url_for('auth.login'))
|
return redirect(url_for('auth.login'))
|
||||||
elif not g.user["confirmed"]:
|
elif not g.user["confirmed"]:
|
||||||
flash("Benutzer noch nicht freigeschaltet.")
|
flash("Benutzer noch nicht freigeschaltet.")
|
||||||
@@ -107,17 +120,17 @@ def login_required(view): # use this as decorator
|
|||||||
|
|
||||||
|
|
||||||
def generate_confirmation_token(email):
|
def generate_confirmation_token(email):
|
||||||
serializer = URLSafeTimedSerializer(app.config['SECRET_KEY'])
|
serializer = URLSafeTimedSerializer(app.app.config['SECRET_KEY'])
|
||||||
return serializer.dumps(email, salt=app.config['SECURITY_PASSWORD_SALT'])
|
return serializer.dumps(email, salt=app.app.config['SECURITY_PASSWORD_SALT'])
|
||||||
|
|
||||||
|
|
||||||
def confirm_token(token, expiration=3600):
|
def confirm_token(token, expiration=3600*14*28): # 2 Wochen expiration
|
||||||
serializer = URLSafeTimedSerializer(app.config['SECRET_KEY'])
|
serializer = URLSafeTimedSerializer(app.app.config['SECRET_KEY'])
|
||||||
try:
|
try:
|
||||||
email = serializer.loads(
|
email = serializer.loads(
|
||||||
token,
|
token,
|
||||||
salt=app.config['SECURITY_PASSWORD_SALT'],
|
salt=app.app.config['SECURITY_PASSWORD_SALT'],
|
||||||
# max_age=expiration
|
max_age=expiration
|
||||||
)
|
)
|
||||||
except:
|
except:
|
||||||
return False
|
return False
|
||||||
@@ -125,22 +138,22 @@ def confirm_token(token, expiration=3600):
|
|||||||
|
|
||||||
|
|
||||||
@bp.route('/confirm/<token>')
|
@bp.route('/confirm/<token>')
|
||||||
@login_required
|
# @login_required # TODO: notwendig? security ?
|
||||||
def confirm_email(token):
|
def confirm_email(token):
|
||||||
try:
|
try:
|
||||||
email = confirm_token(token)
|
email = confirm_token(token)
|
||||||
except:
|
except:
|
||||||
flash('The confirmation link is invalid or has expired.', 'danger')
|
outcome = 'The confirmation link is invalid or has expired.'
|
||||||
|
|
||||||
if g.user["confirmed"]:
|
if g.user["confirmed"]:
|
||||||
flash('Account already confirmed. Please login.', 'success')
|
outcome = 'Account already confirmed.'
|
||||||
|
|
||||||
else:
|
else:
|
||||||
db = get_db()
|
db = get_db()
|
||||||
db.execute(
|
db.execute(
|
||||||
"UPDATE user SET confirmed = '1' where email = ?",
|
"UPDATE user SET confirmed = '1', confirmation_date = ? where email = ?",
|
||||||
(email,)
|
(str(datetime.now()), email)
|
||||||
)
|
)
|
||||||
db.commit()
|
db.commit()
|
||||||
|
outcome= "User wurde erfolgreich freigeschaltet."
|
||||||
|
|
||||||
flash('You have confirmed your account. Thanks!', 'success')
|
return render_template('auth/confirmation_successful.html', email=email, outcome=outcome)
|
||||||
return redirect(url_for('main.home'))
|
|
||||||
@@ -85,10 +85,7 @@ def execute_patch_request(token, endpoint, data):
|
|||||||
def send_mail(to, subject, content, user_id=USER_ID):
|
def send_mail(to, subject, content, user_id=USER_ID):
|
||||||
|
|
||||||
token=get_access_token()
|
token=get_access_token()
|
||||||
|
|
||||||
with open("templates/auth/activate.html") as f:
|
|
||||||
content=f.read()
|
|
||||||
|
|
||||||
mail = {
|
mail = {
|
||||||
"message": {
|
"message": {
|
||||||
"subject": subject,
|
"subject": subject,
|
||||||
|
|||||||
@@ -5,5 +5,6 @@ CREATE TABLE user (
|
|||||||
username TEXT NOT NULL,
|
username TEXT NOT NULL,
|
||||||
email TEXT UNIQUE NOT NULL,
|
email TEXT UNIQUE NOT NULL,
|
||||||
password TEXT NOT NULL,
|
password TEXT NOT NULL,
|
||||||
confirmed BOOLEAN DEFAULT FALSE
|
confirmed BOOLEAN DEFAULT FALSE,
|
||||||
|
confirmation_date TEXT
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
<p>Welcome! Thanks for signing up. Please follow this link to activate your account:</p>
|
|
||||||
<p><a href="{{ confirm_url }}">{{ confirm_url }}</a></p>
|
|
||||||
<br>
|
|
||||||
<p>Cheers!</p>
|
|
||||||
6
templates/auth/confirm_mail.html
Normal file
6
templates/auth/confirm_mail.html
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<p>{{username}} with email {{email}} wants to be confirmed.</p>
|
||||||
|
<p>follow this link only if the user can be trusted to activate the account:</p>
|
||||||
|
<p><a href="{{ confirm_url }}"> {{ confirm_url }}
|
||||||
|
</a></p>
|
||||||
|
<br>
|
||||||
|
<p>Cheers!</p>
|
||||||
7
templates/auth/confirmation.html
Normal file
7
templates/auth/confirmation.html
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>{% block title %}Benutzer mit email: {{email}}.{% endblock %}</h1>
|
||||||
|
<h1>{% block title %}{{outcome}}.{% endblock %}</h1>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@@ -6,10 +6,10 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<form method="post">
|
<form method="post">
|
||||||
|
<label for="email">Email (Account login)</label>
|
||||||
|
<input type="email" name="email" id="email" required>
|
||||||
<label for="username">Name</label>
|
<label for="username">Name</label>
|
||||||
<input name="username" id="username" required>
|
<input name="username" id="username" required>
|
||||||
<label for="email">Email</label>
|
|
||||||
<input type="email" name="email" id="email" required>
|
|
||||||
<label for="password">Password</label>
|
<label for="password">Password</label>
|
||||||
<input type="password" name="password" id="password" required>
|
<input type="password" name="password" id="password" required>
|
||||||
<input type="submit" value="Register">
|
<input type="submit" value="Register">
|
||||||
|
|||||||
Reference in New Issue
Block a user