From 296838951e16113bbf99db9b3e0d5ff2931660a6 Mon Sep 17 00:00:00 2001
From: crunk
Date: Sun, 27 Mar 2022 16:07:44 +0200
Subject: [PATCH] reset password functionality
---
verse/app.py | 2 +-
verse/forms/resetpasswordform.py | 27 +++++++++
verse/start.py | 7 +++
verse/statuspengguna/forgotpassword.py | 36 ++++++++++--
verse/statuspengguna/resetpassword.py | 81 ++++++++++++++++++++++++++
verse/templates/help.html | 1 -
verse/templates/index.html | 14 ++---
verse/templates/resetpassword.html | 29 +++++++++
verse/usermodel.py | 4 +-
9 files changed, 184 insertions(+), 17 deletions(-)
create mode 100644 verse/forms/resetpasswordform.py
create mode 100644 verse/statuspengguna/resetpassword.py
create mode 100644 verse/templates/resetpassword.html
diff --git a/verse/app.py b/verse/app.py
index 8bb6721..9665d34 100644
--- a/verse/app.py
+++ b/verse/app.py
@@ -42,7 +42,7 @@ def create_app():
csrf.init_app(APP)
login_manager.init_app(APP)
db.init_app(APP)
- migrate.init_app(APP, db)
+ migrate.init_app(APP, db, render_as_batch=True)
bcrypt.init_app(APP)
return APP
diff --git a/verse/forms/resetpasswordform.py b/verse/forms/resetpasswordform.py
new file mode 100644
index 0000000..ed32281
--- /dev/null
+++ b/verse/forms/resetpasswordform.py
@@ -0,0 +1,27 @@
+"""Reset Password Form form to reset a users PasswordField."""
+from wtforms import (
+ SubmitField,
+ PasswordField,
+)
+
+from wtforms import validators
+from wtforms.validators import Length, EqualTo
+from flask_wtf import FlaskForm
+
+
+class ResetPasswordForm(FlaskForm):
+ """ResetPassword for distribusi-verse form class"""
+
+ password = PasswordField(
+ "New password:",
+ validators=[validators.InputRequired(), Length(12, 72)],
+ )
+ confirmpassword = PasswordField(
+ "Confirm your password:",
+ validators=[
+ validators.InputRequired(),
+ Length(12, 72),
+ EqualTo("password", message="Passwords must match !"),
+ ],
+ )
+ submit = SubmitField("Reset your password")
diff --git a/verse/start.py b/verse/start.py
index 6442868..82d6346 100644
--- a/verse/start.py
+++ b/verse/start.py
@@ -34,6 +34,7 @@ from statuspengguna.helper import ResetUserState
from statuspengguna.loginuser import LoginUser
from statuspengguna.registeruser import RegisterUser
from statuspengguna.forgotpassword import ForgotPassword
+from statuspengguna.resetpassword import ResetPassword
from distribusisinfo import DistribusisInfo
APP = create_app()
@@ -153,6 +154,12 @@ def forgotpassword():
return ForgotPassword(mail)
+@APP.route("/resetpassword/", methods=["GET", "POST"])
+def resetpassword(path):
+ print(path)
+ return ResetPassword(path)
+
+
@APP.errorhandler(CSRFError)
def handle_csrf_error(e):
return render_template("csrf_error.html", reason=e.description), 400
diff --git a/verse/statuspengguna/forgotpassword.py b/verse/statuspengguna/forgotpassword.py
index 69a1289..2d09c38 100644
--- a/verse/statuspengguna/forgotpassword.py
+++ b/verse/statuspengguna/forgotpassword.py
@@ -1,7 +1,17 @@
+from uuid import uuid1
+from datetime import datetime
+from sqlalchemy.exc import (
+ DataError,
+ DatabaseError,
+ InterfaceError,
+ InvalidRequestError,
+)
from flask import render_template
+from flask_mail import Message
+
from usermodel import User
from forms.forgotpasswordform import ForgotPasswordForm
-from flask_mail import Message
+from app import db
def ForgotPassword(mail):
@@ -11,7 +21,8 @@ def ForgotPassword(mail):
email=forgotpasswordform.email.data
).first()
if user is not None:
- ResetPassWordMessage(user, mail)
+ resethash = AddResetPasswordHash(user, forgotpasswordform)
+ ResetPassWordMessage(user, resethash, mail)
forgotpasswordform.email.errors.append(
f"""If {forgotpasswordform.email.data} exists, an email is send with
a password reset link. (If your inbox doesn't
@@ -22,13 +33,26 @@ def ForgotPassword(mail):
)
-def ResetPassWordMessage(user, mail):
+def AddResetPasswordHash(user, forgotpasswordform):
+ resethash = uuid1().hex
+ try:
+ user.resettime = datetime.now()
+ user.resethash = resethash
+ db.session.commit()
+ except (InvalidRequestError, DataError, InterfaceError, DatabaseError):
+ forgotpasswordform.email.errors.append("Something went wrong!")
+ db.session.rollback()
+ return resethash
+
+
+def ResetPassWordMessage(user, resethash, mail):
msg = Message(
"Distribusiverse Forgotten Password ",
sender=("Distribusiverse mailer", "test@this.com"),
recipients=[user.email],
)
- msg.html = f"""You have requested a password reset for Distribusiverse.
-Click here to
-reset your password and make a new one."""
+ msg.html = f"""{user.username} has requested a password reset for
+Distribusiverse.
+Click here to
+reset your password."""
mail.send(msg)
diff --git a/verse/statuspengguna/resetpassword.py b/verse/statuspengguna/resetpassword.py
new file mode 100644
index 0000000..b802dab
--- /dev/null
+++ b/verse/statuspengguna/resetpassword.py
@@ -0,0 +1,81 @@
+from datetime import datetime
+from flask import (
+ render_template,
+ redirect,
+ flash,
+ url_for,
+)
+from sqlalchemy.exc import (
+ IntegrityError,
+ DataError,
+ DatabaseError,
+ InterfaceError,
+ InvalidRequestError,
+)
+from werkzeug.routing import BuildError
+from usermodel import User
+from forms.resetpasswordform import ResetPasswordForm
+from flask_login import login_user
+from flask_bcrypt import generate_password_hash
+from app import db
+
+
+def ResetPassword(path):
+ linkvalid = False
+ user = User.query.filter_by(resethash=path).first()
+ if user is None:
+ return redirect(url_for("index"))
+ timepassed = datetime.now() - user.resettime
+ if timepassed.days < 1:
+ linkvalid = True
+
+ resetpasswordform = ResetPasswordForm()
+ if resetpasswordform.validate_on_submit():
+ return ResetUserPasswordInDB(user, resetpasswordform)
+ return render_template(
+ "resetpassword.html",
+ resetpasswordform=resetpasswordform,
+ path=path,
+ linkvalid=linkvalid,
+ )
+
+
+def ResetUserPasswordInDB(user, resetpasswordform):
+ try:
+ newpassword = resetpasswordform.confirmpassword.data
+ user.password = generate_password_hash(newpassword)
+ user.resethash = None
+ user.resettime = None
+ db.session.commit()
+ flash("Password Succesfully updated", "success")
+ login_user(user)
+ return redirect(url_for("index"))
+
+ except InvalidRequestError:
+ db.session.rollback()
+ resetpasswordform.email.errors.append("Something went wrong!")
+ flash("Something went wrong!", "danger")
+ except IntegrityError:
+ db.session.rollback()
+ resetpasswordform.email.errors.append("User already exists!")
+ flash("User already exists!", "warning")
+ except DataError:
+ db.session.rollback()
+ resetpasswordform.email.errors.append("Invalid Entry")
+ flash("Invalid Entry", "warning")
+ except InterfaceError:
+ db.session.rollback()
+ resetpasswordform.email.errors.append(
+ "Error connecting to the database"
+ )
+ flash("Error connecting to the database", "danger")
+ except DatabaseError:
+ db.session.rollback()
+ resetpasswordform.email.errors.append(
+ "Error connecting to the database"
+ )
+ flash("Error connecting to the database", "danger")
+ except BuildError:
+ db.session.rollback()
+ resetpasswordform.email.errors.append("Unknown error occured!")
+ flash("An error occured !", "danger")
diff --git a/verse/templates/help.html b/verse/templates/help.html
index ea636f2..d0c0450 100644
--- a/verse/templates/help.html
+++ b/verse/templates/help.html
@@ -113,5 +113,4 @@ simple online editor or by uploading their own Css file.
-
{% endblock %}
diff --git a/verse/templates/index.html b/verse/templates/index.html
index fa3befe..afcbcc0 100644
--- a/verse/templates/index.html
+++ b/verse/templates/index.html
@@ -19,13 +19,13 @@
- {% if adminuser %}
-
- {% endif %}
+ {% if adminuser %}
+
+ {% endif %}
diff --git a/verse/templates/resetpassword.html b/verse/templates/resetpassword.html
new file mode 100644
index 0000000..cb7cf54
--- /dev/null
+++ b/verse/templates/resetpassword.html
@@ -0,0 +1,29 @@
+{% extends "base.html" %}
+{% block main %}
+
+ {% if linkvalid%}
+
+ {% else %}
+
Password reset link no longer valid.
+ {% endif %}
+
+{% endblock main %}
diff --git a/verse/usermodel.py b/verse/usermodel.py
index b0b0254..913cb7f 100644
--- a/verse/usermodel.py
+++ b/verse/usermodel.py
@@ -12,8 +12,8 @@ class User(UserMixin, db.Model):
email = db.Column(db.String(150), unique=True, nullable=False)
password = db.Column(db.String(300), nullable=False, unique=False)
currentdistribusi = db.Column(db.String(300), nullable=True, unique=False)
- # resethash = db.Column(db.String(300), nullable=True, unique=True)
- # resettime = db.Column(db.DateTime)
+ resethash = db.Column(db.String(300), nullable=True, unique=True)
+ resettime = db.Column(db.DateTime)
tutor = db.Column(db.Boolean, default=False)
admin = db.Column(db.Boolean, default=False)