forked from crunk/distribusi-verse
reset password functionality
This commit is contained in:
parent
fd32d87509
commit
296838951e
@ -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
|
||||
|
27
verse/forms/resetpasswordform.py
Normal file
27
verse/forms/resetpasswordform.py
Normal 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")
|
@ -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/<path>", 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
|
||||
|
@ -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.<br>
|
||||
<a href='http://localhost:5000/resetpassword/{user.email}'>Click here to
|
||||
reset your password and make a new one.</a>"""
|
||||
msg.html = f"""{user.username} has requested a password reset for
|
||||
Distribusiverse.<br><hr>
|
||||
<a href='http://localhost:5000/resetpassword/{resethash}'>Click here to
|
||||
reset your password.</a>"""
|
||||
mail.send(msg)
|
||||
|
81
verse/statuspengguna/resetpassword.py
Normal file
81
verse/statuspengguna/resetpassword.py
Normal 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")
|
@ -113,5 +113,4 @@ simple online editor or by uploading their own Css file.</p>
|
||||
</p>
|
||||
<hr>
|
||||
</div>
|
||||
<!-- a div with all the distribusis listed in the distribusiverse -->
|
||||
{% endblock %}
|
||||
|
@ -19,13 +19,13 @@
|
||||
<input type="button" name="button" value="Distribusi"></input>
|
||||
</a>
|
||||
</div>
|
||||
{% if adminuser %}
|
||||
<div class="admin">
|
||||
<a href="/admin">
|
||||
<input type="button" name="button" value="Admin"></input>
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if adminuser %}
|
||||
<div class="admin">
|
||||
<a href="/admin">
|
||||
<input type="button" name="button" value="Admin"></input>
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="logout">
|
||||
<a href="/logout">
|
||||
<input type="button" name="button" value="Logout"></input>
|
||||
|
29
verse/templates/resetpassword.html
Normal file
29
verse/templates/resetpassword.html
Normal 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 %}
|
@ -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)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user