diff --git a/library/application/user/forgotpassword.py b/library/application/user/forgotpassword.py
new file mode 100644
index 0000000..ad4250e
--- /dev/null
+++ b/library/application/user/forgotpassword.py
@@ -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.
+Click here to
+reset your password."""
+ mail.send(msg)
diff --git a/library/application/user/loginuser.py b/library/application/user/loginuser.py
new file mode 100644
index 0000000..2d5e349
--- /dev/null
+++ b/library/application/user/loginuser.py
@@ -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)
diff --git a/library/application/user/registeruser.py b/library/application/user/registeruser.py
new file mode 100644
index 0000000..722f3c8
--- /dev/null
+++ b/library/application/user/registeruser.py
@@ -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)
diff --git a/library/application/user/resetpassword.py b/library/application/user/resetpassword.py
new file mode 100644
index 0000000..b802dab
--- /dev/null
+++ b/library/application/user/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/library/forms/forgotpasswordform.py b/library/forms/forgotpasswordform.py
new file mode 100644
index 0000000..0ddc0d6
--- /dev/null
+++ b/library/forms/forgotpasswordform.py
@@ -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")
diff --git a/library/forms/loginform.py b/library/forms/loginform.py
new file mode 100644
index 0000000..262bb5d
--- /dev/null
+++ b/library/forms/loginform.py
@@ -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")
diff --git a/library/forms/registerform.py b/library/forms/registerform.py
new file mode 100644
index 0000000..d1d6795
--- /dev/null
+++ b/library/forms/registerform.py
@@ -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")
diff --git a/library/forms/resetpasswordform.py b/library/forms/resetpasswordform.py
new file mode 100644
index 0000000..d24da84
--- /dev/null
+++ b/library/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 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")
diff --git a/library/templates/user/forgotpassword.html b/library/templates/user/forgotpassword.html
new file mode 100644
index 0000000..0656f61
--- /dev/null
+++ b/library/templates/user/forgotpassword.html
@@ -0,0 +1,29 @@
+{% extends "base.html" %}
+{% block main %}
+
+
+
Forgot your password?
+
+ Enter the email address that was used to register with the library.
+
+
+
+
+
+
+{% endblock main %}
diff --git a/library/templates/user/register.html b/library/templates/user/register.html
new file mode 100644
index 0000000..729ba7d
--- /dev/null
+++ b/library/templates/user/register.html
@@ -0,0 +1,39 @@
+{% extends "base.html" %}
+{% block main %}
+
+{% endblock main %}
diff --git a/library/templates/user/resetpassword.html b/library/templates/user/resetpassword.html
new file mode 100644
index 0000000..cb7cf54
--- /dev/null
+++ b/library/templates/user/resetpassword.html
@@ -0,0 +1,29 @@
+{% extends "base.html" %}
+{% block main %}
+
+ {% if linkvalid%}
+
+ {% else %}
+
Password reset link no longer valid.
+ {% endif %}
+
+{% endblock main %}