add password reset

This commit is contained in:
Edward Betts 2017-02-23 21:02:58 +00:00
parent edbe2e2395
commit ff4617196f
7 changed files with 117 additions and 6 deletions

View file

@ -17,7 +17,7 @@ class SignupForm(Form):
[InputRequired(), Email(),
Length(min=5, max=EMAIL_LEN)],
description="we never share your e-mail address")
password = PasswordField('password',
password = StringField('password',
[InputRequired(), Length(min=4, max=PASSWORD_LEN)])
def validate_username(form, field):
@ -51,9 +51,13 @@ class LoginForm(Form):
return False
class ForgotPasswordForm(Form):
username_or_email = StringField('username or e-mail address',
user_or_email = StringField('username or e-mail address',
[InputRequired(), Length(max=EMAIL_LEN)])
class PasswordForm(Form):
password = PasswordField('new password',
[InputRequired(), Length(min=4, max=PASSWORD_LEN)])
class AccountSettingsForm(Form):
full_name = StringField('full name', [Length(max=64)])

View file

@ -0,0 +1,10 @@
{% from "form/controls.html" import render_field, checkbox, submit %}
{% set title="Reset password" %}
{% set label="send e-mail" %}
{% set fields %}
{{ render_field(form.user_or_email) }}
{% endset %}
{% include "form/simple.html" %}

View file

@ -0,0 +1,19 @@
{% from "form/controls.html" import render_field, checkbox, submit %}
{% set title="Reset password" %}
{% set label="send e-mail" %}
{% include "head.html" %}
<div class="row">
<div class="col-md-12">
<h1>{{ title }}</h1>
<p>Your password has been set. You may go ahead and log in now.</p>
<p><a href="{{ url_for('.login') }}">Log in</a></p>
</div>
</div>
{% include "foot.html" %}

View file

@ -0,0 +1,10 @@
{% from "form/controls.html" import render_field %}
{% set title="Reset password" %}
{% set label="reset" %}
{% set fields %}
{{ render_field(form.password) }}
{% endset %}
{% include "form/simple.html" %}

View file

@ -0,0 +1,17 @@
{% from "form/controls.html" import render_field, checkbox, submit %}
{% set title="Reset password" %}
{% set label="send e-mail" %}
{% include "head.html" %}
<div class="row">
<div class="col-md-12">
<h1>{{ title }}</h1>
<p>We've emailed you instructions for setting your password, if an account exists with the email you entered. You should receive them shortly.</p>
<p>If you don't receive an email, please make sure you've entered the address you registered with, and check your spam folder.</p>
</div>
</div>
{% include "foot.html" %}

View file

@ -7,7 +7,17 @@
{% set fields %}
{{ render_field(form.user_or_email) }}
{{ render_field(form.password) }}
{{ checkbox(form.remember) }}
<div class="form-group">
<label class="checkbox-inline">
{{ form.remember() }}
{{ form.remember.label.text }}
</label>
·
<a href="{{ url_for('.password_reset') }}">forgot password?</a>
</div>
{% endset %}
{% include "form/simple.html" %}

View file

@ -1,9 +1,10 @@
from flask import (Blueprint, render_template, request, redirect, flash,
url_for, abort, jsonify, Response)
url_for, abort, jsonify, Response, current_app)
from flask_login import (login_user, current_user, logout_user,
login_required, LoginManager)
from .forms import (LoginForm, SignupForm, AccountSettingsForm,
UploadSourceDocForm, SourceDocForm, ItemForm)
UploadSourceDocForm, SourceDocForm, ItemForm,
ForgotPasswordForm, PasswordForm)
from .model import User, SourceDoc, Item, XanaDoc, XanaLink
from .url import get_url
from .edl import fulfil_edl_with_sources
@ -13,6 +14,7 @@ from werkzeug.debug.tbtools import get_current_traceback
from jinja2 import evalcontextfilter, Markup
from functools import wraps
from .utils import nbsp_at_start
from itsdangerous import URLSafeTimedSerializer
import re
@ -60,6 +62,45 @@ def home():
docs = Item.query.order_by(Item.created)
return render_template('home.html', docs=docs)
@bp.route('/password_reset', methods=['GET', 'POST'])
def password_reset():
form = ForgotPasswordForm()
if not form.validate_on_submit():
return render_template('auth/password_reset.html', form=form)
ts = URLSafeTimedSerializer(current_app.config["SECRET_KEY"])
user = User.lookup_user_or_email(form.user_or_email.data)
if user:
token = ts.dumps(user.id, salt='password-reset')
print(token)
return redirect(url_for('.password_reset_sent'))
@bp.route('/password_reset/sent', methods=['GET', 'POST'])
def password_reset_sent():
return render_template('auth/password_reset_sent.html')
@bp.route('/reset/<token>', methods=['GET', 'POST'])
def reset_with_token(token):
ts = URLSafeTimedSerializer(current_app.config["SECRET_KEY"])
try:
user_id = ts.loads(token, salt='password-reset', max_age=86400)
except:
abort(404)
form = PasswordForm()
if not form.validate_on_submit():
return render_template('auth/password_reset_confirm.html', form=form)
user = User.query.get(user_id)
user.set_password(form.password.data)
session.add(user)
session.commit()
return redirect(url_for('.password_reset_complete'))
@bp.route('/reset/done')
def password_reset_complete():
return render_template('auth/password_reset_complete.html')
@bp.route('/source_doc_upload', methods=["POST"])
@show_errors
def source_doc_upload():