user login scaffolding
This commit is contained in:
parent
4a2e6f76ca
commit
c77661e985
58
library/application/user/forgotpassword.py
Normal file
58
library/application/user/forgotpassword.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
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 app import db
|
||||||
|
|
||||||
|
|
||||||
|
def ForgotPassword(mail):
|
||||||
|
forgotpasswordform = ForgotPasswordForm()
|
||||||
|
if forgotpasswordform.validate_on_submit():
|
||||||
|
user = User.query.filter_by(
|
||||||
|
email=forgotpasswordform.email.data
|
||||||
|
).first()
|
||||||
|
if user is not None:
|
||||||
|
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
|
||||||
|
contain any new mail, please check your spam folder.)"""
|
||||||
|
)
|
||||||
|
return render_template(
|
||||||
|
"forgotpassword.html", forgotpasswordform=forgotpasswordform
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
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(
|
||||||
|
"Forgotten Password ",
|
||||||
|
sender=("mailer", "test@this.com"),
|
||||||
|
recipients=[user.email],
|
||||||
|
)
|
||||||
|
msg.html = f"""{user.username} has requested a password reset for
|
||||||
|
libary website.<br><hr>
|
||||||
|
<a href='http://localhost:5000/resetpassword/{resethash}'>Click here to
|
||||||
|
reset your password.</a>"""
|
||||||
|
mail.send(msg)
|
36
library/application/user/loginuser.py
Normal file
36
library/application/user/loginuser.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
from flask import (
|
||||||
|
render_template,
|
||||||
|
redirect,
|
||||||
|
request,
|
||||||
|
flash,
|
||||||
|
url_for,
|
||||||
|
abort,
|
||||||
|
)
|
||||||
|
from usermodel import User
|
||||||
|
from forms.loginform import LoginForm
|
||||||
|
from flask_login import login_user
|
||||||
|
from flask_bcrypt import check_password_hash
|
||||||
|
|
||||||
|
|
||||||
|
def LoginUser():
|
||||||
|
loginform = LoginForm()
|
||||||
|
if loginform.validate_on_submit():
|
||||||
|
try:
|
||||||
|
user = User.query.filter_by(email=loginform.email.data).first()
|
||||||
|
if user is None:
|
||||||
|
loginform.password.errors.append("Invalid email or password!")
|
||||||
|
return render_template("login.html", loginform=loginform)
|
||||||
|
if check_password_hash(user.password, loginform.password.data):
|
||||||
|
login_user(user)
|
||||||
|
flash("Logged in successfully.", "success")
|
||||||
|
next = request.args.get("next")
|
||||||
|
if next is not None and not is_safe_url(next): # noqa: F821
|
||||||
|
return abort(400)
|
||||||
|
return redirect(next or url_for("index"))
|
||||||
|
else:
|
||||||
|
flash("Invalid email or password!", "danger")
|
||||||
|
loginform.password.errors.append("Invalid email or password!")
|
||||||
|
return render_template("login.html", loginform=loginform)
|
||||||
|
except Exception as e:
|
||||||
|
flash(e, "danger")
|
||||||
|
return render_template("login.html", loginform=loginform)
|
70
library/application/user/registeruser.py
Normal file
70
library/application/user/registeruser.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
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.registerform import RegisterForm
|
||||||
|
from flask_login import login_user
|
||||||
|
from flask_bcrypt import generate_password_hash
|
||||||
|
from app import db
|
||||||
|
|
||||||
|
|
||||||
|
def RegisterUser():
|
||||||
|
registerform = RegisterForm()
|
||||||
|
if registerform.validate_on_submit():
|
||||||
|
try:
|
||||||
|
username = registerform.username.data
|
||||||
|
email = registerform.email.data
|
||||||
|
password = registerform.confirmpassword.data
|
||||||
|
|
||||||
|
newuser = User(
|
||||||
|
username=username,
|
||||||
|
email=email,
|
||||||
|
password=generate_password_hash(password),
|
||||||
|
)
|
||||||
|
|
||||||
|
db.session.add(newuser)
|
||||||
|
db.session.commit()
|
||||||
|
flash("Account Succesfully created", "success")
|
||||||
|
login_user(newuser)
|
||||||
|
return redirect(url_for("index"))
|
||||||
|
|
||||||
|
except InvalidRequestError:
|
||||||
|
db.session.rollback()
|
||||||
|
registerform.email.errors.append("Something went wrong!")
|
||||||
|
flash("Something went wrong!", "danger")
|
||||||
|
except IntegrityError:
|
||||||
|
db.session.rollback()
|
||||||
|
registerform.email.errors.append("User already exists!")
|
||||||
|
flash("User already exists!", "warning")
|
||||||
|
except DataError:
|
||||||
|
db.session.rollback()
|
||||||
|
registerform.email.errors.append("Invalid Entry")
|
||||||
|
flash("Invalid Entry", "warning")
|
||||||
|
except InterfaceError:
|
||||||
|
db.session.rollback()
|
||||||
|
registerform.email.errors.append(
|
||||||
|
"Error connecting to the database"
|
||||||
|
)
|
||||||
|
flash("Error connecting to the database", "danger")
|
||||||
|
except DatabaseError:
|
||||||
|
db.session.rollback()
|
||||||
|
registerform.email.errors.append(
|
||||||
|
"Error connecting to the database"
|
||||||
|
)
|
||||||
|
flash("Error connecting to the database", "danger")
|
||||||
|
except BuildError:
|
||||||
|
db.session.rollback()
|
||||||
|
registerform.email.errors.append("Unknown error occured!")
|
||||||
|
flash("An error occured !", "danger")
|
||||||
|
return render_template("register.html", registerform=registerform)
|
81
library/application/user/resetpassword.py
Normal file
81
library/application/user/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")
|
19
library/forms/forgotpasswordform.py
Normal file
19
library/forms/forgotpasswordform.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
"""Forgotten password form to help user."""
|
||||||
|
from wtforms import (
|
||||||
|
StringField,
|
||||||
|
SubmitField,
|
||||||
|
)
|
||||||
|
|
||||||
|
from wtforms import validators
|
||||||
|
from wtforms.validators import Length, Email
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
|
||||||
|
|
||||||
|
class ForgotPasswordForm(FlaskForm):
|
||||||
|
"""Forgotten password csv-library form"""
|
||||||
|
|
||||||
|
email = StringField(
|
||||||
|
"Email address:",
|
||||||
|
validators=[validators.InputRequired(), Email(), Length(6, 64)],
|
||||||
|
)
|
||||||
|
submit = SubmitField("Send email")
|
23
library/forms/loginform.py
Normal file
23
library/forms/loginform.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
"""Login form to validate user."""
|
||||||
|
from wtforms import (
|
||||||
|
StringField,
|
||||||
|
SubmitField,
|
||||||
|
PasswordField,
|
||||||
|
)
|
||||||
|
|
||||||
|
from wtforms import validators
|
||||||
|
from wtforms.validators import Length, Email
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
|
||||||
|
|
||||||
|
class LoginForm(FlaskForm):
|
||||||
|
"""Login csv-library form"""
|
||||||
|
|
||||||
|
email = StringField(
|
||||||
|
"Email address:",
|
||||||
|
validators=[validators.InputRequired(), Email(), Length(6, 64)],
|
||||||
|
)
|
||||||
|
password = PasswordField(
|
||||||
|
"Password:", validators=[validators.InputRequired()]
|
||||||
|
)
|
||||||
|
submit = SubmitField("Sign In")
|
43
library/forms/registerform.py
Normal file
43
library/forms/registerform.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
"""Register form to make a new user."""
|
||||||
|
from wtforms import (
|
||||||
|
StringField,
|
||||||
|
SubmitField,
|
||||||
|
PasswordField,
|
||||||
|
)
|
||||||
|
|
||||||
|
from wtforms import validators
|
||||||
|
from wtforms.validators import Length, Email, EqualTo, ValidationError
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
|
||||||
|
|
||||||
|
class RegisterForm(FlaskForm):
|
||||||
|
"""Register for csv-library form"""
|
||||||
|
|
||||||
|
username = StringField(
|
||||||
|
"Username:",
|
||||||
|
validators=[validators.InputRequired(), Length(3, 150)],
|
||||||
|
)
|
||||||
|
|
||||||
|
email = StringField(
|
||||||
|
"Email address:",
|
||||||
|
validators=[
|
||||||
|
validators.InputRequired(),
|
||||||
|
Email(),
|
||||||
|
Length(6, 64),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
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("Register to the library")
|
27
library/forms/resetpasswordform.py
Normal file
27
library/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 csv-library form"""
|
||||||
|
|
||||||
|
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")
|
29
library/templates/user/forgotpassword.html
Normal file
29
library/templates/user/forgotpassword.html
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% block main %}
|
||||||
|
<div id="mainworkflow">
|
||||||
|
<div class="workflow">
|
||||||
|
<h2>Forgot your password?</h2>
|
||||||
|
<p>
|
||||||
|
Enter the email address that was used to register with the library.
|
||||||
|
</p>
|
||||||
|
<form class="form" action="{{ url_for('forgotpassword') }}" method="post">
|
||||||
|
{{ forgotpasswordform.csrf_token }}
|
||||||
|
<fieldset class="required">
|
||||||
|
{{ forgotpasswordform.email.label }}
|
||||||
|
{{ forgotpasswordform.email }}
|
||||||
|
{% for message in forgotpasswordform.email.errors %}
|
||||||
|
<div class="error">{{ message }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="button required error">
|
||||||
|
{{ forgotpasswordform.submit }}
|
||||||
|
<div class="overview">
|
||||||
|
<a href="/">
|
||||||
|
<input type="button" name="button" value="Back to main page"></input>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div
|
||||||
|
{% endblock main %}
|
26
library/templates/user/login.html
Normal file
26
library/templates/user/login.html
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% block main %}
|
||||||
|
<div id="login">
|
||||||
|
<form class="form" action="{{ url_for('login') }}" method="post">
|
||||||
|
{{ loginform.csrf_token }}
|
||||||
|
<fieldset class="required">
|
||||||
|
{{ loginform.email.label }}
|
||||||
|
{{ loginform.email }}
|
||||||
|
{% for message in loginform.email.errors %}
|
||||||
|
<div class="error">{{ message }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="required">
|
||||||
|
{{ loginform.password.label }}
|
||||||
|
{{ loginform.password }}
|
||||||
|
{% for message in loginform.password.errors %}
|
||||||
|
<div class="error">{{ message }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="button required">
|
||||||
|
{{ loginform.submit }}
|
||||||
|
<a href="/forgotpassword">Forgot Password?</a>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endblock main %}
|
39
library/templates/user/register.html
Normal file
39
library/templates/user/register.html
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% block main %}
|
||||||
|
<div id="login">
|
||||||
|
<form class="form" action="{{ url_for('register') }}" method="post">
|
||||||
|
{{ registerform.csrf_token }}
|
||||||
|
<fieldset class="required">
|
||||||
|
{{ registerform.username.label }}
|
||||||
|
{{ registerform.username }}
|
||||||
|
{% for message in registerform.username.errors %}
|
||||||
|
<div class="error">{{ message }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="required">
|
||||||
|
{{ registerform.email.label }}
|
||||||
|
{{ registerform.email }}
|
||||||
|
{% for message in registerform.email.errors %}
|
||||||
|
<div class="error">{{ message }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="required">
|
||||||
|
{{ registerform.password.label }}
|
||||||
|
{{ registerform.password }}
|
||||||
|
{% for message in registerform.password.errors %}
|
||||||
|
<div class="error">{{ message }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="required">
|
||||||
|
{{ registerform.confirmpassword.label }}
|
||||||
|
{{ registerform.confirmpassword }}
|
||||||
|
{% for message in registerform.confirmpassword.errors %}
|
||||||
|
<div class="error">{{ message }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="button required">
|
||||||
|
{{ registerform.submit }}
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endblock main %}
|
29
library/templates/user/resetpassword.html
Normal file
29
library/templates/user/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 %}
|
Loading…
Reference in New Issue
Block a user