From c462653f53858359a81cfed561f1622d7e63102b Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Thu, 30 May 2024 15:14:13 -0500 Subject: Send verification email on registration. --- gn_auth/auth/authorisation/users/views.py | 56 ++++++++++++++++++++++++++++-- gn_auth/templates/emails/verify-email.html | 49 ++++++++++++++++++++++++++ gn_auth/templates/emails/verify-email.txt | 12 +++++++ 3 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 gn_auth/templates/emails/verify-email.html create mode 100644 gn_auth/templates/emails/verify-email.txt diff --git a/gn_auth/auth/authorisation/users/views.py b/gn_auth/auth/authorisation/users/views.py index feacae3..8919a6d 100644 --- a/gn_auth/auth/authorisation/users/views.py +++ b/gn_auth/auth/authorisation/users/views.py @@ -1,11 +1,22 @@ """User authorisation endpoints.""" +import sqlite3 +import secrets +import datetime import traceback from typing import Any from functools import partial from dataclasses import asdict -import sqlite3 +from email.headerregistry import Address from email_validator import validate_email, EmailNotValidError -from flask import request, jsonify, Response, Blueprint, current_app +from flask import ( + request, + jsonify, + Response, + Blueprint, + current_app, + render_template) + +from gn_auth.smtp import send_message, build_email_message from gn_auth.auth.db import sqlite3 as db from gn_auth.auth.db.sqlite3 import with_db_connection @@ -86,6 +97,46 @@ def __assert_not_logged_in__(conn: db.DbConnection): raise UserRegistrationError( "Cannot register user while authenticated") +def user_address(user: User) -> Address: + """Compute the `email.headerregistry.Address` from a `User`""" + return Address(display_name=user.name, addr_spec=user.email) + +def send_verification_email(conn, user: User) -> None: + """Send an email verification message.""" + subject="GeneNetwork: Please Verify Your Email" + verification_code = secrets.token_urlsafe(64) + generated = datetime.datetime.now() + expiration_minutes = 15 + def __render__(template): + return render_template(template, + subject=subject, + verification_code=verification_code, + verification_uri="https://please/change/this/", + expiration_minutes=expiration_minutes) + with db.cursor(conn) as cursor: + cursor.execute( + ("INSERT INTO " + "user_verification_codes(user_id, code, generated, expires) " + "VALUES (:user_id, :code, :generated, :expires)"), + { + "user_id": str(user.user_id), + "code": verification_code, + "generated": int(generated.timestamp()), + "expires": int( + (generated + + datetime.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( + to_addresses=(user_address(user),), + subject=subject, + txtmessage=__render__("emails/verify-email.txt"), + htmlmessage=__render__("emails/verify-email.html")), + host=current_app.config["SMTP_HOST"], + port=current_app.config["SMTP_PORT"]) + @users.route("/register", methods=["POST"]) def register_user() -> Response: """Register a user.""" @@ -105,6 +156,7 @@ def register_user() -> Response: cursor, save_user( cursor, email["email"], user_name), password) assign_default_roles(cursor, user) + send_verification_email(conn, user) return jsonify(asdict(user)) except sqlite3.IntegrityError as sq3ie: current_app.logger.debug(traceback.format_exc()) diff --git a/gn_auth/templates/emails/verify-email.html b/gn_auth/templates/emails/verify-email.html new file mode 100644 index 0000000..08f1dfe --- /dev/null +++ b/gn_auth/templates/emails/verify-email.html @@ -0,0 +1,49 @@ + + + + {{subject}} + + + +

Thank you for registering an account with GeneNetwork.

+ +

+ To avoid bots, we need to verify that you are an actual human. To do so, + please + + Click here + . +

+ +

+ If that does not work, please log in to GeneNetwork and copy the + verification code below when requested:

+ + {{verification_code}} +

+ +

+ Please note that the verification code will expire in + {{expiration_minutes}} minutes after it was generated. +

+ + diff --git a/gn_auth/templates/emails/verify-email.txt b/gn_auth/templates/emails/verify-email.txt new file mode 100644 index 0000000..92a6435 --- /dev/null +++ b/gn_auth/templates/emails/verify-email.txt @@ -0,0 +1,12 @@ +{{subject}} +=============== + +Thank you for registering an account with GeneNetwork. + +To avoid bots, we need to verify that you are an actual human. To do so, please go to "{{verification_uri}}/{{verification_code}}". + +If that does not work, please log in to GeneNetwork and copy the verification code below when requested: + +{{verification_code}} + +Please note that the verification code will expire {{expiration_minutes}} minutes after it was generated. -- cgit v1.2.3