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 %}