From: Chris Fulljames Date: Sat, 2 May 2026 15:28:16 +0000 (-0400) Subject: Add password reset page X-Git-Url: https://littlesong.place/gitweb/gitweb.cgi?a=commitdiff_plain;h=76779acab9de78204409e6cff63914d2446138d5;p=littlesongplace.git Add password reset page --- diff --git a/src/littlesongplace/auth.py b/src/littlesongplace/auth.py index 30acdc8..21b15af 100644 --- a/src/littlesongplace/auth.py +++ b/src/littlesongplace/auth.py @@ -13,6 +13,19 @@ bp = Blueprint("auth", __name__) def signup_get(): return render_template("signup.html") +def _check_password(password, password_confirm): + error = False + if password != password_confirm: + flash_and_log("Passwords do not match", "error") + error = True + elif len(password) < 8: + flash_and_log("Password must be at least 8 characters", "error") + error = True + return error + +def _hash_password(password): + return bcrypt.hashpw(password.encode(), bcrypt.gensalt()) + @bp.post("/signup") def signup_post(): username = request.form["username"] @@ -30,12 +43,7 @@ def signup_post(): flash_and_log("Username cannot be more than 30 characters", "error") error = True - elif password != password_confirm: - flash_and_log("Passwords do not match", "error") - error = True - elif len(password) < 8: - flash_and_log("Password must be at least 8 characters", "error") - error = True + error = error or _check_password(password, password_confirm) if db.query("select * from users where username = ?", [username], one=True): flash_and_log(f"Username '{username}' is already taken", "error") @@ -45,7 +53,7 @@ def signup_post(): current_app.logger.info("Failed signup attempt") return redirect(request.referrer) - password = bcrypt.hashpw(password.encode(), bcrypt.gensalt()) + password = _hash_password(password) timestamp = datetime.now(timezone.utc).isoformat() user_data = db.query( @@ -92,6 +100,56 @@ def login_post(): return render_template("login.html") +@bp.get("/change-password") +def password_reset(): + return render_template("password-reset.html") + +@bp.post("/change-password") +def password_reset_post(): + error = False + + username = request.form["username"] + old_password = request.form["old_password"] + password = request.form["password"] + password_confirm = request.form["password_confirm"] + + # Get user from DB + user_data = db.query("select * from users where username = ?", [username], one=True) + + # Check username + if not user_data: + flash("Invalid username/password", "error") + error = True + + # Check old password + elif not bcrypt.checkpw(old_password.encode(), user_data["password"]): + flash("Invalid username/password", "error") + error = True + + # Check new password + error = error or _check_password(password, password_confirm) + + # Reload page on error + if error: + current_app.logger.info(f"Failed password reset attempt for {username}") + return redirect(request.referrer) + + # OK - Update password + password = _hash_password(password) + db.query("update users set password = ? where userid = ?", [password, user_data["userid"]]) + db.commit() + + flash("Password updated. Please sign in to continue.", "success") + current_app.logger.info(f"Changed password for {username}") + + # Force logout + if "username" in session: + session.pop("username") + if "userid" in session: + session.pop("userid") + + return redirect("/login") + @bp.get("/logout") def logout(): diff --git a/src/littlesongplace/mail.py b/src/littlesongplace/mail.py index 1061dd3..c0474c5 100644 --- a/src/littlesongplace/mail.py +++ b/src/littlesongplace/mail.py @@ -7,7 +7,11 @@ from email.utils import make_msgid from_addr = "mail@littlesong.place" def send(to_addrs, subject, body): - with open(datadir.get_email_pass_path(), "r") as epfile: + email_pass_path = datadir.get_email_pass_path() + if not email_pass_path.exists(): + return + + with open(email_pass_path, "r") as epfile: pw = epfile.read().strip() msg = EmailMessage() diff --git a/src/littlesongplace/templates/password-reset.html b/src/littlesongplace/templates/password-reset.html new file mode 100644 index 0000000..21d34ae --- /dev/null +++ b/src/littlesongplace/templates/password-reset.html @@ -0,0 +1,42 @@ +{% extends "base.html" %} + +{% block title %}Change Password{% endblock %} + +{% block body %} + +

change password

+ +

+ Forgot your password? Please email cfull at + mail@littlesong.place to reset + it. +

+ +
+ + + + + + + + + +
+ +{% endblock %} +