From 49222eeeef69d780f1155505a991f3785c38c6ee Mon Sep 17 00:00:00 2001
From: Patrick Jentsch <p.jentsch@uni-bielefeld.de>
Date: Mon, 8 Jul 2019 15:13:32 +0200
Subject: [PATCH] Add password reset functionality.

---
 app/auth/forms.py                         |   7 +++++++
 app/auth/views.py                         |  17 ++++++++++++++---
 app/models.py                             |  14 ++++++++++++++
 app/templates/auth/reset_password.html.j2 |  17 +++++++++++++++++
 data_dev.sqlite                           | Bin 32768 -> 32768 bytes
 5 files changed, 52 insertions(+), 3 deletions(-)

diff --git a/app/auth/forms.py b/app/auth/forms.py
index 3a71b095..d23e4e2d 100644
--- a/app/auth/forms.py
+++ b/app/auth/forms.py
@@ -35,6 +35,13 @@ class RegistrationForm(FlaskForm):
             raise ValidationError('Username already in use.')
 
 
+class PasswordResetForm(FlaskForm):
+    password = PasswordField('New Password', validators=[
+        DataRequired(), EqualTo('password2', message='Passwords must match')])
+    password2 = PasswordField('Confirm password', validators=[DataRequired()])
+    submit = SubmitField('Reset Password')
+
+
 class PasswordResetRequestForm(FlaskForm):
     email = StringField('Email', validators=[DataRequired(), Length(1, 64),
                                              Email()])
diff --git a/app/auth/views.py b/app/auth/views.py
index 04afc1e6..5ecb5713 100644
--- a/app/auth/views.py
+++ b/app/auth/views.py
@@ -2,7 +2,7 @@ from flask import flash, redirect, render_template, request, url_for
 from flask_login import current_user, login_required, login_user, logout_user
 from . import auth
 from .. import db
-from .forms import LoginForm, PasswordResetRequestForm, RegistrationForm
+from .forms import LoginForm, PasswordResetForm, PasswordResetRequestForm, RegistrationForm
 from ..email import send_email
 from ..models import User
 
@@ -64,6 +64,17 @@ def password_reset_request():
                            title='Password Reset')
 
 
-@auth.route('/reset/<token>')
+@auth.route('/reset/<token>', methods=['GET', 'POST'])
 def password_reset(token):
-    return 'test'
+    if not current_user.is_anonymous:
+        return redirect(url_for('main.index'))
+    form = PasswordResetForm()
+    if form.validate_on_submit():
+        if User.reset_password(token, form.password.data):
+            db.session.commit()
+            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.j2', form=form,
+                           title='Password Reset')
diff --git a/app/models.py b/app/models.py
index d7d33b85..1a34dd64 100644
--- a/app/models.py
+++ b/app/models.py
@@ -32,6 +32,20 @@ class User(UserMixin, db.Model):
         s = Serializer(current_app.config['SECRET_KEY'], expiration)
         return s.dumps({'reset': self.id}).decode('utf-8')
 
+    @staticmethod
+    def reset_password(token, new_password):
+        s = Serializer(current_app.config['SECRET_KEY'])
+        try:
+            data = s.loads(token.encode('utf-8'))
+        except:
+            return False
+        user = User.query.get(data.get('reset'))
+        if user is None:
+            return False
+        user.password = new_password
+        db.session.add(user)
+        return True
+
     @property
     def password(self):
         raise AttributeError('password is not a readable attribute')
diff --git a/app/templates/auth/reset_password.html.j2 b/app/templates/auth/reset_password.html.j2
index bb9cfe74..e545e464 100644
--- a/app/templates/auth/reset_password.html.j2
+++ b/app/templates/auth/reset_password.html.j2
@@ -7,10 +7,27 @@
       <span class="card-title">Reset Your Password</span>
       <form method="POST">
         {{ form.hidden_tag() }}
+        {% if form.email is defined %}
         <div class="input-field">
           {{ form.email(class='validate', type='email') }}
           {{ form.email.label }}
         </div>
+        {% endif %}
+        {% if form.password is defined %}
+        <div class="input-field">
+          {{ form.password(class='validate', type='password') }}
+          {{ form.password.label }}
+          {% for error in form.password.errors %}
+            <span class="helper-text" style="color:red;">{{ error }}</span>
+          {% endfor %}
+        </div>
+        {% endif %}
+        {% if form.password2 is defined %}
+        <div class="input-field">
+          {{ form.password2(class='validate', type='password') }}
+          {{ form.password2.label }}
+        </div>
+        {% endif %}
         <div class="card-action">
           {{ form.submit(class='btn right') }}
         </div>
diff --git a/data_dev.sqlite b/data_dev.sqlite
index 75d6c1549dd45cb57958eab66cc0bea1a30ac0cc..2f7bde00ac842344510abc2641c436f33afb700a 100644
GIT binary patch
delta 100
zcmZo@U}|V!njp={J5k1&m6t)!z-nVkaJ;9VX-;LHd%llKiiL@}S&ETaYO0y3iGi_^
ziHU(>s*!;~vSDgcVq$7)l4VkwQBsPzp}9#)YO;}qp^;&-p`oR*ftitInuVqLq67s1
DcS0MS

delta 100
zcmZo@U}|V!njp={Gf~Ewm4`u3X2Zsm;CN5}d^59<EaMy%17ibAL-W)m6N8i#1Jl$L
zW6PvO1B;{-3uE&XAj8DcAk{q0AlcB=Jk8WF+0ruA!oa}9$kHe|#lqae(kN+Bf&u_~
Cpc{_>

-- 
GitLab