aboutsummaryrefslogtreecommitdiff
path: root/gn_auth/auth/authorisation/users/views.py
diff options
context:
space:
mode:
Diffstat (limited to 'gn_auth/auth/authorisation/users/views.py')
-rw-r--r--gn_auth/auth/authorisation/users/views.py72
1 files changed, 72 insertions, 0 deletions
diff --git a/gn_auth/auth/authorisation/users/views.py b/gn_auth/auth/authorisation/users/views.py
index 4b56c3d..84d7ef0 100644
--- a/gn_auth/auth/authorisation/users/views.py
+++ b/gn_auth/auth/authorisation/users/views.py
@@ -366,3 +366,75 @@ def send_verification_code():
})
resp.code = 400
return resp
+
+
+def send_forgot_password_email(conn, user: User):
+ """Send the 'forgot-password' email."""
+ subject="GeneNetwork: Change Your Password"
+ token = secrets.token_urlsafe(64)
+ generated = datetime.now()
+ expiration_minutes = 15
+ def __render__(template):
+ return render_template(template,
+ subject=subject,
+ forgot_password_uri=urljoin(
+ request.url,
+ url_for("oauth2.users.change_password",
+ forgot_password_token=token)),
+ expiration_minutes=expiration_minutes)
+
+ with db.cursor(conn) as cursor:
+ cursor.execute(
+ ("INSERT INTO "
+ "forgot_password_tokens(user_id, token, generated, expires) "
+ "VALUES (:user_id, :token, :generated, :expires) "
+ "ON CONFLICT (user_id) REPLACE"),
+ {
+ "user_id": str(user.user_id),
+ "token": token,
+ "generated": int(generated.timestamp()),
+ "expires": int(
+ (generated +
+ timedelta(
+ minutes=expiration_minutes)).timestamp())
+ })
+ send_message(smtp_user=current_app.config["SMTP_USER"],
+ smtp_passwd=current_app.config["SMTP_PASSWORD"],
+ message=build_email_message(
+ from_address=current_app.config["EMAIL_ADDRESS"],
+ to_addresses=(user_address(user),),
+ subject=subject,
+ txtmessage=__render__("emails/forgot-password.txt"),
+ htmlmessage=__render__("emails/forgot-password.html")),
+ host=current_app.config["SMTP_HOST"],
+ port=current_app.config["SMTP_PORT"])
+
+
+@users.route("/forgot-password", methods=["GET", "POST"])
+def forgot_password():
+ """Enable user to request password change."""
+ if request.method == "GET":
+ return render_template("users/forgot-password.html")
+
+ form = request.form
+ email = form.get("email", "").strip()
+ if not bool(email):
+ flash("You MUST provide an email.", "alert-danger")
+ return redirect(url_for("oauth2.users.forgot_password"))
+
+ with (db.connection(current_app.config["AUTH_DB"]) as conn,
+ db.cursor(conn) as cursor):
+ user = user_by_email(conn, form["email"])
+ if not bool(user):
+ flash("We could not find an account with that email.",
+ "alert-danger")
+ return redirect(url_for("oauth2.users.forgot_password"))
+
+ send_forgot_password_email(conn, user)
+ return render_template("users/forgot-password-token-send-success.html")
+
+
+@users.route("/change-password/<forgot_password_token>", methods=["GET", "POST"])
+def change_password(forgot_password_token):
+ """Enable user to perform password change."""
+ return "Would change password..."