Rework auth package

This commit is contained in:
Patrick Jentsch 2020-02-19 16:24:58 +01:00
parent c90cd4d8a3
commit 3b798cb617
10 changed files with 190 additions and 123 deletions

View File

@ -10,7 +10,7 @@ import logging
db = SQLAlchemy()
logger = logging.getLogger(__name__)
login_manager = LoginManager()
login_manager.login_view = 'main.index'
login_manager.login_view = 'auth.login'
mail = Mail()
socketio = SocketIO()

View File

@ -12,25 +12,6 @@ class LoginForm(FlaskForm):
submit = SubmitField('Log In')
class PasswordResetForm(FlaskForm):
password = PasswordField(
'New Password',
validators=[DataRequired(),
EqualTo('password2', message='Passwords must match')]
)
password2 = PasswordField(
'Confirm password',
validators=[DataRequired(),
EqualTo('password', message='Passwords must match.')]
)
submit = SubmitField('Reset Password')
class PasswordResetRequestForm(FlaskForm):
email = StringField('Email', validators=[DataRequired(), Email()])
submit = SubmitField('Reset Password')
class RegistrationForm(FlaskForm):
email = StringField('Email', validators=[DataRequired(), Email()])
username = StringField(
@ -55,3 +36,22 @@ class RegistrationForm(FlaskForm):
def validate_username(self, field):
if User.query.filter_by(username=field.data).first():
raise ValidationError('Username already in use.')
class ResetPasswordForm(FlaskForm):
password = PasswordField(
'New Password',
validators=[DataRequired(),
EqualTo('password2', message='Passwords must match')]
)
password2 = PasswordField(
'Confirm password',
validators=[DataRequired(),
EqualTo('password', message='Passwords must match.')]
)
submit = SubmitField('Reset Password')
class ResetPasswordRequestForm(FlaskForm):
email = StringField('Email', validators=[DataRequired(), Email()])
submit = SubmitField('Reset Password')

View File

@ -1,62 +1,14 @@
from app import db
from app import db, logger
from app.email import send_email
from app.models import User
from flask import (current_app, flash, redirect, render_template, request,
url_for)
from flask_login import current_user, login_required, logout_user
from flask_login import current_user, login_user, login_required, logout_user
from . import auth
from .forms import (PasswordResetForm, PasswordResetRequestForm,
from .forms import (LoginForm, ResetPasswordForm, ResetPasswordRequestForm,
RegistrationForm)
import os
@auth.route('/logout')
@login_required
def logout():
logout_user()
flash('You have been logged out.')
return redirect(url_for('main.index'))
@auth.route('/register', methods=['GET', 'POST'])
def register():
if not current_user.is_anonymous:
return redirect(url_for('main.dashboard'))
registration_form = RegistrationForm(prefix='registration-form')
if registration_form.validate_on_submit():
user = User(email=registration_form.email.data.lower(),
password=registration_form.password.data,
username=registration_form.username.data)
db.session.add(user)
db.session.commit()
dir = os.path.join(current_app.config['NOPAQUE_STORAGE'], str(user.id))
try:
os.makedirs(dir)
except OSError:
flash('[ERROR]!')
user.delete()
else:
token = user.generate_confirmation_token()
send_email(user.email, 'Confirm Your Account',
'auth/email/confirm', token=token, user=user)
flash('A confirmation email has been sent to you by email.')
return redirect(url_for('main.index'))
return render_template('auth/register.html.j2',
registration_form=registration_form,
title='Register')
@auth.route('/confirm/<token>')
@login_required
def confirm(token):
if current_user.confirmed:
return redirect(url_for('main.index'))
if current_user.confirm(token):
db.session.commit()
flash('You have confirmed your account. Thanks!')
else:
flash('The confirmation link is invalid or has expired.')
return redirect(url_for('main.dashboard'))
import shutil
@auth.before_app_request
@ -72,10 +24,76 @@ def before_request():
return redirect(url_for('auth.unconfirmed'))
@auth.route('/login')
def login():
login_form = LoginForm(prefix='login-form')
if login_form.validate_on_submit():
user = User.query.filter_by(username=login_form.user.data).first()
if user is not None and user.verify_password(login_form.password.data):
login_user(user, login_form.remember_me.data)
next = request.args.get('next')
if next is None or not next.startswith('/'):
next = url_for('main.dashboard')
return redirect(next)
flash('Invalid username or password.')
return render_template('auth/login.html.j2', login_form=login_form,
title='nopaque')
@auth.route('/logout')
@login_required
def logout():
logout_user()
flash('You have been logged out.')
return redirect(url_for('main.index'))
@auth.route('/register', methods=['GET', 'POST'])
def register():
if current_user.is_authenticated:
return redirect(url_for('main.dashboard'))
registration_form = RegistrationForm(prefix='registration-form')
if registration_form.validate_on_submit():
user = User(email=registration_form.email.data.lower(),
password=registration_form.password.data,
username=registration_form.username.data)
db.session.add(user)
db.session.commit()
user_dir = os.path.join(current_app.config['NOPAQUE_STORAGE'],
str(user.id))
if os.path.exists(user_dir):
shutil.rmtree(user_dir)
os.mkdir(user_dir)
token = user.generate_confirmation_token()
send_email(user.email, 'Confirm Your Account',
'auth/email/confirm', token=token, user=user)
flash('A confirmation email has been sent to you by email.')
return redirect(url_for('auth.login'))
return render_template('auth/register.html.j2',
registration_form=registration_form,
title='Register')
@auth.route('/confirm/<token>')
@login_required
def confirm(token):
if current_user.confirmed:
return redirect(url_for('main.dashboard'))
if current_user.confirm(token):
db.session.commit()
flash('You have confirmed your account. Thanks!')
return redirect(url_for('main.dashboard'))
else:
flash('The confirmation link is invalid or has expired.')
return redirect(url_for('auth.unconfirmed'))
@auth.route('/unconfirmed')
def unconfirmed():
if current_user.is_anonymous or current_user.confirmed:
if current_user.is_anonymous:
return redirect(url_for('main.index'))
elif current_user.confirmed:
return redirect(url_for('main.dashboard'))
return render_template('auth/unconfirmed.html.j2', title='Unconfirmed')
@ -83,49 +101,46 @@ def unconfirmed():
@login_required
def resend_confirmation():
token = current_user.generate_confirmation_token()
send_email(current_user.email,
'Confirm Your Account',
'auth/email/confirm',
token=token,
user=current_user)
send_email(current_user.email, 'Confirm Your Account',
'auth/email/confirm', token=token, user=current_user)
flash('A new confirmation email has benn sent to you by email.')
return redirect(url_for('main.dashboard'))
return redirect(url_for('auth.unconfirmed'))
@auth.route('/reset', methods=['GET', 'POST'])
def password_reset_request():
if not current_user.is_anonymous:
def reset_password_request():
if current_user.is_authenticated:
return redirect(url_for('main.dashboard'))
form = PasswordResetRequestForm()
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data.lower()).first()
reset_password_request_form = ResetPasswordRequestForm()
if reset_password_request_form.validate_on_submit():
submitted_email = reset_password_request_form.email.data
user = User.query.filter_by(email=submitted_email.lower()).first()
if user:
token = user.generate_reset_token()
send_email(user.email,
'Reset Your Password',
'auth/email/reset_password',
token=token,
user=user)
send_email(user.email, 'Reset Your Password',
'auth/email/reset_password', token=token, user=user)
flash('An email with instructions to reset your password has been '
'sent to you.')
return redirect(url_for('main.index'))
return render_template('auth/reset_password_request.html.j2',
form=form,
title='Password Reset')
return redirect(url_for('auth.login'))
return render_template(
'auth/reset_password_request.html.j2',
reset_password_request_form=reset_password_request_form,
title='Password Reset'
)
@auth.route('/reset/<token>', methods=['GET', 'POST'])
def password_reset(token):
if not current_user.is_anonymous:
def reset_password(token):
if current_user.is_authenticated:
return redirect(url_for('main.dashboard'))
form = PasswordResetForm()
if form.validate_on_submit():
if User.reset_password(token, form.password.data):
reset_password_form = ResetPasswordForm()
if reset_password_form.validate_on_submit():
if User.reset_password(token, reset_password_form.password.data):
db.session.commit()
flash('Your password has been updated.')
return redirect(url_for('main.index'))
return redirect(url_for('auth.login'))
else:
return redirect(url_for('main.index'))
return render_template('auth/reset_password.html.j2',
form=form,
reset_password_form=reset_password_form,
title='Password Reset')

View File

@ -1,7 +1,7 @@
<p>Dear {{ user.username }},</p>
<p>To reset your password <a href="{{ url_for('auth.password_reset', token=token, _external=True) }}">click here</a>.</p>
<p>To reset your password <a href="{{ url_for('auth.reset_password', token=token, _external=True) }}">click here</a>.</p>
<p>Alternatively, you can paste the following link in your browser's address bar:</p>
<p>{{ url_for('auth.password_reset', token=token, _external=True) }}</p>
<p>{{ url_for('auth.reset_password', token=token, _external=True) }}</p>
<p>If you have not requested a password reset simply ignore this message.</p>
<p>Sincerely,</p>
<p>The nopaque Team</p>

View File

@ -2,7 +2,7 @@ Dear {{ user.username }},
To reset your password click on the following link:
{{ url_for('auth.password_reset', token=token, _external=True) }}
{{ url_for('auth.reset_password', token=token, _external=True) }}
If you have not requested a password reset simply ignore this message.

View File

@ -0,0 +1,52 @@
{% extends "nopaque.html.j2" %}
{% block page_content %}
<div class="col s12 m4">
<h3>Cyber cyber</h3>
<p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p>
</div>
<div class="col s12 m8">
<div class="card">
<form method="POST">
<div class="card-content">
<span class="card-title">Log in</span>
{{ login_form.hidden_tag() }}
<div class="input-field">
<i class="material-icons prefix">person</i>
{{ login_form.user(class='validate') }}
{{ login_form.user.label }}
{% for error in login_form.user.errors %}
<span class="helper-text red-text">{{ error }}</span>
{% endfor %}
</div>
<div class="input-field">
<i class="material-icons prefix">vpn_key</i>
{{ login_form.password(class='validate') }}
{{ login_form.password.label }}
{% for error in login_form.password.errors %}
<span class="helper-text red-text">{{ error }}</span>
{% endfor %}
</div>
<div class="row" style="margin-bottom: 0;">
<div class="col s6 left-align">
<a href="{{ url_for('auth.reset_password_request') }}">Forgot your password?</a>
</div>
<div class="col s6 right-align">
<div class="switch">
<label>
Remember me
{{ login_form.remember_me() }}
<span class="lever"></span>
</label>
</div>
</div>
</div>
</div>
<div class="card-action right-align">
{{ login_form.submit(class='btn') }}
</div>
</form>
</div>
</div>
{% endblock %}

View File

@ -7,27 +7,27 @@
</div>
<div class="col s12 m8">
<div class="card small">
<div class="card">
<form method="POST">
<div class="card-content">
{{ form.hidden_tag() }}
{{ reset_password_form.hidden_tag() }}
<div class="input-field">
{{ form.password(class='validate', type='password') }}
{{ form.password.label }}
{% for error in form.password.errors %}
{{ reset_password_form.password(type='password') }}
{{ reset_password_form.password.label }}
{% for error in reset_password_form.password.errors %}
<span class="helper-text red-text">{{ error }}</span>
{% endfor %}
</div>
<div class="input-field">
{{ form.password2(class='validate', type='password') }}
{{ form.password2.label }}
{% for error in form.password2.errors %}
{{ reset_password_form.password2(type='password') }}
{{ reset_password_form.password2.label }}
{% for error in reset_password_form.password2.errors %}
<span class="helper-text red-text">{{ error }}</span>
{% endfor %}
</div>
</div>
<div class="card-action">
{{ form.submit(class='btn right') }}
<div class="card-action right-align">
{{ form.submit(class='btn') }}
</div>
</form>
</div>

View File

@ -7,20 +7,20 @@
</div>
<div class="col s12 m8">
<div class="card small">
<div class="card">
<form method="POST">
<div class="card-content">
{{ form.hidden_tag() }}
{{ reset_password_request_form.hidden_tag() }}
<div class="input-field">
{{ form.email(class='validate', type='email') }}
{{ form.email.label }}
{% for error in form.email.errors %}
{{ reset_password_request_form.email(class='validate', type='email') }}
{{ reset_password_request_form.email.label }}
{% for error in reset_password_request_form.email.errors %}
<span class="helper-text red-text">{{ error }}</span>
{% endfor %}
</div>
</div>
<div class="card-action">
{{ form.submit(class='btn right') }}
<div class="card-action right-align">
{{ reset_password_request_form.submit(class='btn') }}
</div>
</form>
</div>

View File

@ -116,7 +116,7 @@
</div>
<div class="row" style="margin-bottom: 0;">
<div class="col s6 left-align">
<a href="{{ url_for('auth.password_reset_request') }}">Forgot your password?</a>
<a href="{{ url_for('auth.reset_password_request') }}">Forgot your password?</a>
</div>
<div class="col s6 right-align">
<div class="switch">

View File

@ -51,7 +51,7 @@
<li><a href="{{ url_for('profile.settings') }}"><i class="material-icons">settings</i>Settings</a></li>
<li><a href="{{ url_for('auth.logout') }}"><i class="material-icons">power_settings_new</i>Log out</a></li>
{% else %}
<li><a href="{{ url_for('main.index', _anchor='registration-and-log-in') }}"><i class="material-icons">person</i>Log in</a></li>
<li><a href="{{ url_for('auth.login') }}"><i class="material-icons">person</i>Log in</a></li>
<li><a href="{{ url_for('auth.register') }}"><i class="material-icons">person_add</i>Register</a></li>
{% endif %}
</ul>
@ -77,7 +77,7 @@
<li><a href="{{ url_for('profile.settings') }}"><i class="material-icons">settings</i>Settings</a></li>
<li><a href="{{ url_for('auth.logout') }}"><i class="material-icons">power_settings_new</i>Log out</a></li>
{% else %}
<li><a href="{{ url_for('main.index') }}"><i class="material-icons">person</i>Log in</a></li>
<li><a href="{{ url_for('auth.login') }}"><i class="material-icons">person</i>Log in</a></li>
<li><a href="{{ url_for('auth.register') }}"><i class="material-icons">person_add</i>Register</a></li>
{% endif %}
{% if current_user.is_administrator() %}