diff options
-rw-r--r-- | gn_auth/auth/authorisation/users/views.py | 72 | ||||
-rw-r--r-- | gn_auth/templates/emails/forgot-password.html | 30 | ||||
-rw-r--r-- | gn_auth/templates/emails/forgot-password.txt | 10 | ||||
-rw-r--r-- | gn_auth/templates/users/forgot-password-token-send-success.html | 21 | ||||
-rw-r--r-- | gn_auth/templates/users/forgot-password.html | 35 |
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%} |