From 730a12accd2c80e5a6adcd8c6b74fcffb7ddde61 Mon Sep 17 00:00:00 2001 From: crunk Date: Fri, 25 Mar 2022 15:37:00 +0100 Subject: [PATCH] halfway implementation of forgotten passwords --- requirements.txt | 9 +++++-- verse/app.py | 7 ++++++ verse/forms/forgotpasswordform.py | 19 ++++++++++++++ verse/start.py | 14 ++++++++--- verse/statuspengguna/forgotpassword.py | 34 ++++++++++++++++++++++++++ verse/templates/forgotpassword.html | 29 ++++++++++++++++++++++ verse/usermodel.py | 2 ++ 7 files changed, 108 insertions(+), 6 deletions(-) create mode 100644 verse/forms/forgotpasswordform.py create mode 100644 verse/statuspengguna/forgotpassword.py create mode 100644 verse/templates/forgotpassword.html diff --git a/requirements.txt b/requirements.txt index 3f744c9..915a7c8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,9 +2,11 @@ alembic==1.7.5 Babel==2.9.1 bcrypt==3.2.0 black==21.11b1 +bleach==4.1.0 blinker==1.4 cffi==1.15.0 click==8.0.3 +distribusi @ git+file:///home/shambler/Development/distribusiverse/distribusi@e291e7497e40211c2ebd54ca32a1f4bdaed71230 dnspython==2.1.0 email-validator==1.1.3 Flask==2.0.2 @@ -15,6 +17,7 @@ Flask-Mail==0.9.1 Flask-Migrate==3.1.0 Flask-Principal==0.4.0 Flask-Security==3.0.0 +Flask-Security-Too==4.1.3 Flask-SQLAlchemy==2.5.1 Flask-WTF==1.0.0 greenlet==1.1.2 @@ -24,11 +27,13 @@ Jinja2==3.0.3 Mako==1.1.6 MarkupSafe==2.0.1 mypy-extensions==0.4.3 +packaging==21.3 passlib==1.7.4 pathspec==0.9.0 Pillow==8.3.2 platformdirs==2.4.0 pycparser==2.21 +pyparsing==3.0.7 python-magic==0.4.24 pytz==2021.3 regex==2021.11.10 @@ -36,7 +41,7 @@ six==1.16.0 speaklater==1.3 SQLAlchemy==1.4.27 tomli==1.2.2 -typing-extensions==4.0.1 +typing_extensions==4.0.1 +webencodings==0.5.1 Werkzeug==2.0.2 WTForms==3.0.0 --e git+https://git.vvvvvvaria.org/crunk/distribusi-verse.git@1a50898d216ae95c3eb9c144bb7ec678e638daa6#egg=distribusi diff --git a/verse/app.py b/verse/app.py index 8291340..7dd6f6a 100644 --- a/verse/app.py +++ b/verse/app.py @@ -22,6 +22,13 @@ def create_app(): APP.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True APP.config["MAX_CONTENT_LENGTH"] = 150 * 1024 * 1024 + APP.config["MAIL_SERVER"] = "0.0.0.0" + APP.config["MAIL_PORT"] = 1025 + APP.config["MAIL_USE_SSL"] = False + APP.config["MAIL_USE_TLS"] = True + # APP.config['MAIL_USERNAME'] = 'username' + # APP.config['MAIL_PASSWORD'] = 'password' + login_manager.session_protection = "strong" login_manager.login_view = "index" login_manager.login_message_category = "info" diff --git a/verse/forms/forgotpasswordform.py b/verse/forms/forgotpasswordform.py new file mode 100644 index 0000000..3737181 --- /dev/null +++ b/verse/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 distribusiverse form class.""" + + email = StringField( + "Email address:", + validators=[validators.InputRequired(), Email(), Length(6, 64)], + ) + submit = SubmitField("Send email") diff --git a/verse/start.py b/verse/start.py index b77283d..de1be21 100644 --- a/verse/start.py +++ b/verse/start.py @@ -12,6 +12,7 @@ from flask_login import ( login_required, current_user, ) +from flask_mail import Mail from flask_wtf.csrf import CSRFError from app import create_app, login_manager from usermodel import User @@ -31,11 +32,13 @@ from uploadpage import UploadPage from statuspengguna.helper import ResetUserState from statuspengguna.loginuser import LoginUser from statuspengguna.registeruser import RegisterUser +from statuspengguna.forgotpassword import ForgotPassword from distribusisinfo import DistribusisInfo APP = create_app() stash_page = Blueprint("stash_page", __name__, static_folder="stash") APP.register_blueprint(stash_page) +mail = Mail(APP) @APP.before_request @@ -129,14 +132,17 @@ def logout(): @APP.route("/login", methods=["GET", "POST"]) def login(): - result = LoginUser() - return result + return LoginUser() @APP.route("/register", methods=["GET", "POST"]) def register(): - result = RegisterUser() - return result + return RegisterUser() + + +@APP.route("/forgotpassword", methods=["GET", "POST"]) +def forgotpassword(): + return ForgotPassword(mail) @APP.errorhandler(CSRFError) diff --git a/verse/statuspengguna/forgotpassword.py b/verse/statuspengguna/forgotpassword.py new file mode 100644 index 0000000..69a1289 --- /dev/null +++ b/verse/statuspengguna/forgotpassword.py @@ -0,0 +1,34 @@ +from flask import render_template +from usermodel import User +from forms.forgotpasswordform import ForgotPasswordForm +from flask_mail import Message + + +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: + ResetPassWordMessage(user, 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 ResetPassWordMessage(user, 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.""" + mail.send(msg) diff --git a/verse/templates/forgotpassword.html b/verse/templates/forgotpassword.html new file mode 100644 index 0000000..3932ca0 --- /dev/null +++ b/verse/templates/forgotpassword.html @@ -0,0 +1,29 @@ +{% extends "base.html" %} +{% block main %} +
+
+

Forgot your password?

+

+ Enter the email address that was used to register with Distribusiverse. +

+
+ {{ forgotpasswordform.csrf_token }} +
+ {{ forgotpasswordform.email.label }} + {{ forgotpasswordform.email }} + {% for message in forgotpasswordform.email.errors %} +
{{ message }}
+ {% endfor %} +
+
+ {{ forgotpasswordform.submit }} +
+ + + +
+
+
+
+