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(), [InputRequired(), Email(),
Length(min=5, max=EMAIL_LEN)], Length(min=5, max=EMAIL_LEN)],
description="we never share your e-mail address") description="we never share your e-mail address")
password = PasswordField('password', password = StringField('password',
[InputRequired(), Length(min=4, max=PASSWORD_LEN)]) [InputRequired(), Length(min=4, max=PASSWORD_LEN)])
def validate_username(form, field): def validate_username(form, field):
@ -51,8 +51,12 @@ class LoginForm(Form):
return False return False
class ForgotPasswordForm(Form): 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)]) [InputRequired(), Length(max=EMAIL_LEN)])
class PasswordForm(Form):
password = PasswordField('new password',
[InputRequired(), Length(min=4, max=PASSWORD_LEN)])
class AccountSettingsForm(Form): class AccountSettingsForm(Form):
full_name = StringField('full name', [Length(max=64)]) 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 %} {% set fields %}
{{ render_field(form.user_or_email) }} {{ render_field(form.user_or_email) }}
{{ render_field(form.password) }} {{ 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 %} {% endset %}
{% include "form/simple.html" %} {% include "form/simple.html" %}

View file

@ -1,9 +1,10 @@
from flask import (Blueprint, render_template, request, redirect, flash, 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, from flask_login import (login_user, current_user, logout_user,
login_required, LoginManager) login_required, LoginManager)
from .forms import (LoginForm, SignupForm, AccountSettingsForm, from .forms import (LoginForm, SignupForm, AccountSettingsForm,
UploadSourceDocForm, SourceDocForm, ItemForm) UploadSourceDocForm, SourceDocForm, ItemForm,
ForgotPasswordForm, PasswordForm)
from .model import User, SourceDoc, Item, XanaDoc, XanaLink from .model import User, SourceDoc, Item, XanaDoc, XanaLink
from .url import get_url from .url import get_url
from .edl import fulfil_edl_with_sources 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 jinja2 import evalcontextfilter, Markup
from functools import wraps from functools import wraps
from .utils import nbsp_at_start from .utils import nbsp_at_start
from itsdangerous import URLSafeTimedSerializer
import re import re
@ -60,6 +62,45 @@ def home():
docs = Item.query.order_by(Item.created) docs = Item.query.order_by(Item.created)
return render_template('home.html', docs=docs) 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"]) @bp.route('/source_doc_upload', methods=["POST"])
@show_errors @show_errors
def source_doc_upload(): def source_doc_upload():