From 0a8b5a5db0d1b0d996191b8c0773c94f67f53ad8 Mon Sep 17 00:00:00 2001 From: kelepirci Date: Thu, 4 Aug 2016 23:38:20 +0300 Subject: [PATCH] Land of Forgotten Passwords This will add new function to reset user password and some higher magical lore to the software. --- dash/auth/forms.py | 19 +++++- dash/auth/views.py | 39 ++++++++++++- dash/models.py | 21 ++++++- dash/templates/auth/email/reset_password.html | 9 +++ dash/templates/auth/email/reset_password.txt | 13 +++++ dash/templates/auth/login.html | 7 ++- .../auth/password_reset_request.html | 41 +++++++++++++ dash/templates/auth/reset_password.html | 58 +++++++++++++++++++ 8 files changed, 201 insertions(+), 6 deletions(-) create mode 100644 dash/templates/auth/email/reset_password.html create mode 100644 dash/templates/auth/email/reset_password.txt create mode 100644 dash/templates/auth/password_reset_request.html create mode 100644 dash/templates/auth/reset_password.html diff --git a/dash/auth/forms.py b/dash/auth/forms.py index 024a1ce..57b00a1 100644 --- a/dash/auth/forms.py +++ b/dash/auth/forms.py @@ -31,4 +31,21 @@ class RegistrationForm(Form): def validate_username(self, field): if User.query.filter_by(username=field.data).first(): - raise ValidationError('Username already in use.') \ No newline at end of file + raise ValidationError('Username already in use.') + +class PasswordResetRequestForm(Form): + email = StringField('Email', validators=[Required(), Length(1, 64), + Email()]) + submit = SubmitField('Reset Password') + +class PasswordResetForm(Form): + email = StringField('Email', validators=[Required(), Length(1, 64), + Email()]) + password = PasswordField('New Password', validators=[ + Required(), EqualTo('password2', message='Passwords must match')]) + password2 = PasswordField('Confirm password', validators=[Required()]) + submit = SubmitField('Reset Password') + + def validate_email(self, field): + if User.query.filter_by(email=field.data).first() is None: + raise ValidationError('Unknown email address.') \ No newline at end of file diff --git a/dash/auth/views.py b/dash/auth/views.py index aa0910e..c6129c5 100644 --- a/dash/auth/views.py +++ b/dash/auth/views.py @@ -6,7 +6,8 @@ from . import auth from .. import db from ..models import User from ..email import send_email -from .forms import LoginForm, RegistrationForm +from .forms import LoginForm, RegistrationForm, PasswordResetRequestForm, \ + PasswordResetForm @auth.before_app_request def before_request(): @@ -79,4 +80,38 @@ def resend_confirmation(): send_email(current_user.email, 'Confirm Your Account', 'auth/email/confirm', user=current_user, token=token) flash('A new confirmation email has been sent to you by email.') - return redirect(url_for('main.index')) \ No newline at end of file + return redirect(url_for('main.index')) + +# password reset +@auth.route('/reset/', methods=['GET', 'POST']) +def password_reset(token): + if not current_user.is_anonymous: + return redirect(url_for('main.index')) + form = PasswordResetForm() + if form.validate_on_submit(): + user = User.query.filter_by(email=form.email.data).first() + if user is None: + return redirect(url_for('main.index')) + if user.reset_password(token, form.password.data): + flash('Your password has been updated.') + return redirect(url_for('auth.login')) + else: + return redirect(url_for('main.index')) + return render_template('auth/reset_password.html', form=form) + +@auth.route('/reset', methods=['GET', 'POST']) +def password_reset_request(): + if not current_user.is_anonymous: + return redirect(url_for('main.index')) + form = PasswordResetRequestForm() + if form.validate_on_submit(): + user = User.query.filter_by(email=form.email.data).first() + if user: + token = user.generate_reset_token() + send_email(user.email, 'Reset Your Password', + 'auth/email/reset_password', + user=user, token=token, + next=request.args.get('next')) + flash('Instruction to reset your password has been ' + 'send to your email address.') + return render_template('auth/password_reset_request.html', form=form) \ No newline at end of file diff --git a/dash/models.py b/dash/models.py index 51da1d0..2c8d66a 100644 --- a/dash/models.py +++ b/dash/models.py @@ -51,7 +51,7 @@ class User(UserMixin, db.Model): s = Serializer(current_app.config['SECRET_KEY'], expiration) return s.dumps({'confirm': self.id}) - # confirms uer email by id + # confirms user email by id def confirm(self, token): s = Serializer(current_app.config['SECRET_KEY']) try: @@ -63,7 +63,24 @@ class User(UserMixin, db.Model): self.confirmed = True db.session.add(self) return True - + + # generates token for password reset + def generate_reset_token(self, expiration=3600): + s = Serializer(current_app.config['SECRET_KEY'], expiration) + return s.dumps({'reset': self.id}) + + def reset_password(self, token, new_password): + s = Serializer(current_app.config['SECRET_KEY']) + try: + data = s.loads(token) + except: + return False + if data.get('reset') != self.id: + return False + self.password = new_password + db.session.add(self) + return True + def __repr__(self): return '' % self.username diff --git a/dash/templates/auth/email/reset_password.html b/dash/templates/auth/email/reset_password.html new file mode 100644 index 0000000..6e93619 --- /dev/null +++ b/dash/templates/auth/email/reset_password.html @@ -0,0 +1,9 @@ +

Dear {{ user.username }},

+

This message comes from Page of Forgotten Passwords

+

To reset your password click here.

+

Alternatively, you can paste the following link in your browser's address bar:

+

{{ url_for('auth.password_reset', token=token, _external=True) }}

+

If you have not requested a password reset simply ignore this message.

+

Sincerely,

+

The Unknown Archivists of Page of Forgotten Passwords

+

Note: replies to this email address will fall in abyss of unknowns.

\ No newline at end of file diff --git a/dash/templates/auth/email/reset_password.txt b/dash/templates/auth/email/reset_password.txt new file mode 100644 index 0000000..3bcd6bf --- /dev/null +++ b/dash/templates/auth/email/reset_password.txt @@ -0,0 +1,13 @@ +Dear {{ user.username }}, + +This message comes from Page of Forgotten Passwords + +To reset your password click on the following link: + +{{ url_for('auth.password_reset', token=token, _external=True) }} + +Sincerely, + +The Unknown Archivists of Page of Forgotten Passwords + +Note: replies to this email address will fall in abyss of unknowns. \ No newline at end of file diff --git a/dash/templates/auth/login.html b/dash/templates/auth/login.html index 39f78aa..119ddd7 100644 --- a/dash/templates/auth/login.html +++ b/dash/templates/auth/login.html @@ -41,10 +41,15 @@ + +
-

If you do not have account?

diff --git a/dash/templates/auth/password_reset_request.html b/dash/templates/auth/password_reset_request.html new file mode 100644 index 0000000..92c5a30 --- /dev/null +++ b/dash/templates/auth/password_reset_request.html @@ -0,0 +1,41 @@ +{% extends "adminlte/base_without_nav.html" %} + +{% block title %}Welcome to Page of Forgotten Passwords{% endblock %} +{% block description %}Page of Forgotten Passwords{% endblock %} +{% block bodytag %}login-page{% endblock %} + +{% block body %} + + + +{% endblock %} diff --git a/dash/templates/auth/reset_password.html b/dash/templates/auth/reset_password.html new file mode 100644 index 0000000..84a26ed --- /dev/null +++ b/dash/templates/auth/reset_password.html @@ -0,0 +1,58 @@ +{% extends "adminlte/base_without_nav.html" %} + +{% block title %}Welcome back to Page of Forgotten Passwords{% endblock %} +{% block description %}Page of Forgotten Passwords{% endblock %} +{% block bodytag %}login-page{% endblock %} + +{% block body %} + + + +{% endblock %}