aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gn_auth/auth/authorisation/users/views.py72
-rw-r--r--gn_auth/templates/emails/forgot-password.html30
-rw-r--r--gn_auth/templates/emails/forgot-password.txt10
-rw-r--r--gn_auth/templates/users/forgot-password-token-send-success.html21
-rw-r--r--gn_auth/templates/users/forgot-password.html35
5 files changed, 168 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..."
diff --git a/gn_auth/templates/emails/forgot-password.html b/gn_auth/templates/emails/forgot-password.html
new file mode 100644
index 0000000..18321d5
--- /dev/null
+++ b/gn_auth/templates/emails/forgot-password.html
@@ -0,0 +1,30 @@
+<html>
+ <head>
+ <meta charset="UTF-8" />
+ <title>{{subject}}</title>
+ </head>
+ <body>
+ <p>
+ You (or someone pretending to be you) made a request to change your
+ password. Please follow the link below to change it.
+ </p>
+
+ <p>
+ Click the button below to change your password
+ <a href="{{forgot_password_uri}}"
+ style="display: block;text-align: center;vertical-align: center;cursor: pointer;border-radius: 4px;background-color: #336699;border-color: #357ebd;color: white;text-decoration: none;font-size: large;width: 9em;text-transform: capitalize;margin: 1em 0 0 3em;box-shadow: 2px 2px rgba(0, 0, 0, 0.3);">Change my Password</a>.</p>
+
+ <p>
+ Or copy the link below onto your browser's address bar:<br /><br />
+ <span style="font-weight: bolder;">{{forgot_password_uri}}</span>
+ </p>
+
+ <p>
+ If you did not request to change your password, simply ignore this email.
+ </p>
+
+ <p style="font-weight: bold;color: #ee55ee;">
+ The link will expire in <strong>{{expiration_minutes}}</strong> minutes.
+ </p>
+ </body>
+</html>
diff --git a/gn_auth/templates/emails/forgot-password.txt b/gn_auth/templates/emails/forgot-password.txt
new file mode 100644
index 0000000..7eda908
--- /dev/null
+++ b/gn_auth/templates/emails/forgot-password.txt
@@ -0,0 +1,10 @@
+{{subject}}
+===============
+
+You (or someone pretending to be you) made a request to change your password. Please copy the link below onto your browser to change your password:
+
+{{forgot_password_uri}}
+
+If you did not request to change your password, simply ignore this email.
+
+The link will expire {{expiration_minutes}} minutes.
diff --git a/gn_auth/templates/users/forgot-password-token-send-success.html b/gn_auth/templates/users/forgot-password-token-send-success.html
new file mode 100644
index 0000000..ab8a741
--- /dev/null
+++ b/gn_auth/templates/users/forgot-password-token-send-success.html
@@ -0,0 +1,21 @@
+{%extends "base.html"%}
+
+{%block title%}gn-auth: Forgot Password{%endblock%}
+
+{%block pagetitle%}Forgot Password{%endblock%}
+
+{%block content%}
+{{flash_messages()}}
+
+<div class="container-fluid">
+ <div class="row"><h1>Forgot Password</h1></div>
+
+ <div class="row">
+ <p>
+ We have sent an email to '{{email}}'. Please log in to your email and
+ click the URL to change your password.
+ </p>
+ </div>
+
+</div>
+{%endblock%}
diff --git a/gn_auth/templates/users/forgot-password.html b/gn_auth/templates/users/forgot-password.html
new file mode 100644
index 0000000..94fcc68
--- /dev/null
+++ b/gn_auth/templates/users/forgot-password.html
@@ -0,0 +1,35 @@
+{%extends "base.html"%}
+
+{%block title%}gn-auth: Forgot Password{%endblock%}
+
+{%block pagetitle%}Forgot Password{%endblock%}
+
+{%block content%}
+{{flash_messages()}}
+
+<div class="container-fluid">
+ <div class="row"><h1>Forgot Password</h1></div>
+
+ <div class="row">
+ <form method="POST"
+ action="{{url_for('oauth2.users.forgot_password')}}">
+ <div class="form-group">
+ <span>
+ Provide you email below, and we will send you a link you can use to
+ change your password.
+ </span>
+ </div>
+
+ <div class="form-group">
+ <label for="txt-email" class="form-label">Email</label>
+ <input type="email" name="email" id="txt-email" class="form-control" />
+ </div>
+
+ <div class="form-group">
+ <input type="submit" class="btn btn-primary" value="Send Link" />
+ </div>
+ </form>
+ </div>
+
+</div>
+{%endblock%}