reset password functionality

This commit is contained in:
crunk 2022-03-27 16:07:44 +02:00
parent fd32d87509
commit 296838951e
9 changed files with 184 additions and 17 deletions

View File

@ -42,7 +42,7 @@ def create_app():
csrf.init_app(APP) csrf.init_app(APP)
login_manager.init_app(APP) login_manager.init_app(APP)
db.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) bcrypt.init_app(APP)
return APP return APP

View File

@ -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")

View File

@ -34,6 +34,7 @@ from statuspengguna.helper import ResetUserState
from statuspengguna.loginuser import LoginUser from statuspengguna.loginuser import LoginUser
from statuspengguna.registeruser import RegisterUser from statuspengguna.registeruser import RegisterUser
from statuspengguna.forgotpassword import ForgotPassword from statuspengguna.forgotpassword import ForgotPassword
from statuspengguna.resetpassword import ResetPassword
from distribusisinfo import DistribusisInfo from distribusisinfo import DistribusisInfo
APP = create_app() APP = create_app()
@ -153,6 +154,12 @@ def forgotpassword():
return ForgotPassword(mail) return ForgotPassword(mail)
@APP.route("/resetpassword/<path>", methods=["GET", "POST"])
def resetpassword(path):
print(path)
return ResetPassword(path)
@APP.errorhandler(CSRFError) @APP.errorhandler(CSRFError)
def handle_csrf_error(e): def handle_csrf_error(e):
return render_template("csrf_error.html", reason=e.description), 400 return render_template("csrf_error.html", reason=e.description), 400

View File

@ -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 import render_template
from flask_mail import Message
from usermodel import User from usermodel import User
from forms.forgotpasswordform import ForgotPasswordForm from forms.forgotpasswordform import ForgotPasswordForm
from flask_mail import Message from app import db
def ForgotPassword(mail): def ForgotPassword(mail):
@ -11,7 +21,8 @@ def ForgotPassword(mail):
email=forgotpasswordform.email.data email=forgotpasswordform.email.data
).first() ).first()
if user is not None: if user is not None:
ResetPassWordMessage(user, mail) resethash = AddResetPasswordHash(user, forgotpasswordform)
ResetPassWordMessage(user, resethash, mail)
forgotpasswordform.email.errors.append( forgotpasswordform.email.errors.append(
f"""If {forgotpasswordform.email.data} exists, an email is send with f"""If {forgotpasswordform.email.data} exists, an email is send with
a password reset link. (If your inbox doesn't 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( msg = Message(
"Distribusiverse Forgotten Password ", "Distribusiverse Forgotten Password ",
sender=("Distribusiverse mailer", "test@this.com"), sender=("Distribusiverse mailer", "test@this.com"),
recipients=[user.email], recipients=[user.email],
) )
msg.html = f"""You have requested a password reset for Distribusiverse.<br> msg.html = f"""{user.username} has requested a password reset for
<a href='http://localhost:5000/resetpassword/{user.email}'>Click here to Distribusiverse.<br><hr>
reset your password and make a new one.</a>""" <a href='http://localhost:5000/resetpassword/{resethash}'>Click here to
reset your password.</a>"""
mail.send(msg) mail.send(msg)

View File

@ -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")

View File

@ -113,5 +113,4 @@ simple online editor or by uploading their own Css file.</p>
</p> </p>
<hr> <hr>
</div> </div>
<!-- a div with all the distribusis listed in the distribusiverse -->
{% endblock %} {% endblock %}

View File

@ -19,13 +19,13 @@
<input type="button" name="button" value="Distribusi"></input> <input type="button" name="button" value="Distribusi"></input>
</a> </a>
</div> </div>
{% if adminuser %} {% if adminuser %}
<div class="admin"> <div class="admin">
<a href="/admin"> <a href="/admin">
<input type="button" name="button" value="Admin"></input> <input type="button" name="button" value="Admin"></input>
</a> </a>
</div> </div>
{% endif %} {% endif %}
<div class="logout"> <div class="logout">
<a href="/logout"> <a href="/logout">
<input type="button" name="button" value="Logout"></input> <input type="button" name="button" value="Logout"></input>

View File

@ -0,0 +1,29 @@
{% extends "base.html" %}
{% block main %}
<div id="login">
{% if linkvalid%}
<form class="form" action="{{ url_for('resetpassword', path=path) }}" method="post">
{{ resetpasswordform.csrf_token }}
<fieldset class="required">
{{ resetpasswordform.password.label }}
{{ resetpasswordform.password }}
{% for message in resetpasswordform.password.errors %}
<div class="error">{{ message }}</div>
{% endfor %}
</fieldset>
<fieldset class="required">
{{ resetpasswordform.confirmpassword.label }}
{{ resetpasswordform.confirmpassword }}
{% for message in resetpasswordform.confirmpassword.errors %}
<div class="error">{{ message }}</div>
{% endfor %}
</fieldset>
<fieldset class="button required">
{{ resetpasswordform.submit }}
</fieldset>
</form>
{% else %}
<h3>Password reset link no longer valid.</h3>
{% endif %}
</div>
{% endblock main %}

View File

@ -12,8 +12,8 @@ class User(UserMixin, db.Model):
email = db.Column(db.String(150), unique=True, nullable=False) email = db.Column(db.String(150), unique=True, nullable=False)
password = db.Column(db.String(300), nullable=False, unique=False) password = db.Column(db.String(300), nullable=False, unique=False)
currentdistribusi = db.Column(db.String(300), nullable=True, unique=False) currentdistribusi = db.Column(db.String(300), nullable=True, unique=False)
# resethash = db.Column(db.String(300), nullable=True, unique=True) resethash = db.Column(db.String(300), nullable=True, unique=True)
# resettime = db.Column(db.DateTime) resettime = db.Column(db.DateTime)
tutor = db.Column(db.Boolean, default=False) tutor = db.Column(db.Boolean, default=False)
admin = db.Column(db.Boolean, default=False) admin = db.Column(db.Boolean, default=False)