diff options
-rw-r--r-- | gn_auth/auth/authorisation/users/views.py | 54 | ||||
-rw-r--r-- | gn_auth/auth/errors.py | 3 | ||||
-rw-r--r-- | gn_auth/templates/emails/verify-email.html | 27 | ||||
-rw-r--r-- | gn_auth/templates/emails/verify-email.txt | 2 |
4 files changed, 60 insertions, 26 deletions
diff --git a/gn_auth/auth/authorisation/users/views.py b/gn_auth/auth/authorisation/users/views.py index 8919a6d..638f0df 100644 --- a/gn_auth/auth/authorisation/users/views.py +++ b/gn_auth/auth/authorisation/users/views.py @@ -25,11 +25,16 @@ from gn_auth.auth.authorisation.resources.models import ( user_resources as _user_resources) from gn_auth.auth.authorisation.roles.models import ( assign_default_roles, user_roles as _user_roles) -from gn_auth.auth.errors import ( - NotFoundError, UsernameError, PasswordError, UserRegistrationError) from gn_auth.auth.authorisation.resources.groups.models import ( user_group as _user_group) +from gn_auth.auth.errors import ( + NotFoundError, + UsernameError, + PasswordError, + UserVerificationError, + UserRegistrationError) + from gn_auth.auth.authentication.oauth2.resource_server import require_oauth from gn_auth.auth.authentication.users import User, save_user, set_user_password from gn_auth.auth.authentication.oauth2.models.oauth2token import ( @@ -169,6 +174,51 @@ def register_user() -> Response: raise Exception( "unknown_error", "The system experienced an unexpected error.") +def delete_verification_code(cursor, code: str): + """Delete verification code from db.""" + cursor.execute("DELETE FROM user_verification_codes " + "WHERE code=:code", + {"code": code}) + +@users.route("/verify", methods=["GET", "POST"]) +def verify_user(): + """Verify users are not bots.""" + code = request.args.get("verificationcode", + request.form.get("verificationcode", + "nosuchcode")) + with (db.connection(current_app.config["AUTH_DB"]) as conn, + db.cursor(conn) as cursor): + cursor.execute("SELECT * FROM user_verification_codes " + "WHERE code=:code", + {"code": code}) + results = tuple(dict(row) for row in cursor.fetchall()) + + if not bool(results): + raise UserVerificationError( + "Invalid verification code: code not found.") + + if len(results) > 1: + delete_verification_code(cursor, code) + raise UserVerificationError( + "Invalid verification code: code is duplicated.") + + results = results[0] + if (datetime.datetime.fromtimestamp( + int(results["expires"])) < datetime.datetime.now()): + delete_verification_code(cursor, code) + raise UserVerificationError( + "Invalid verification code: code has expired.") + + # Code is good! + delete_verification_code(cursor, code) + cursor.execute("UPDATE users SET verified=1 WHERE user_id=:user_id", + {"user_id": results["user_id"]}) + return jsonify({ + "status": "success", + "message": "User verification successful!" + }) + + @users.route("/group", methods=["GET"]) @require_oauth("profile group") def user_group() -> Response: diff --git a/gn_auth/auth/errors.py b/gn_auth/auth/errors.py index 60d6a22..77b73aa 100644 --- a/gn_auth/auth/errors.py +++ b/gn_auth/auth/errors.py @@ -15,6 +15,9 @@ class ForbiddenAccess(AuthorisationError): class UserRegistrationError(AuthorisationError): """Raised whenever a user registration fails""" +class UserVerificationError(UserRegistrationError): + """Raised when verification of a user fails.""" + class NotFoundError(AuthorisationError): """Raised whenever we try fetching (a/an) object(s) that do(es) not exist.""" error_code: int = 404 diff --git a/gn_auth/templates/emails/verify-email.html b/gn_auth/templates/emails/verify-email.html index 08f1dfe..bc475c9 100644 --- a/gn_auth/templates/emails/verify-email.html +++ b/gn_auth/templates/emails/verify-email.html @@ -2,25 +2,6 @@ <head> <meta charset="UTF-8" /> <title>{{subject}}</title> - <style type="text/css"> - .btn { - display: inline-block; - text-align: center; - vertical-align: center; - cursor: pointer; - border-radius: 4px; - background-color: #336699; - border-color: #357ebd; - } - .verification-code{ - color: #3A3AFF; - font-weight: bolder; - } - .note { - font-weight: bold; - color: #ee55ee; - } - </style> </head> <body> <p>Thank you for registering an account with GeneNetwork.</p> @@ -28,8 +9,8 @@ <p> To avoid bots, we need to verify that you are an actual human. To do so, please - <a href="{{verification_uri}}/{{verification_code}}" - class="btn"> + <a href="{{verification_uri}}?verificationcode={{verification_code}}" + style="display: inline-block;text-align: center;vertical-align: center;cursor: pointer;border-radius: 4px;background-color: #336699;border-color: #357ebd;"> Click here </a>. </p> @@ -38,10 +19,10 @@ If that does not work, please log in to GeneNetwork and copy the verification code below when requested:<br /><br /> - <span class="verification-code">{{verification_code}}</span> + <span style="color: #3A3AFF;font-weight: bolder;">{{verification_code}}</span> </p> - <p class="note"> + <p style="font-weight: bold;color: #ee55ee;"> Please note that the verification code will expire in <strong>{{expiration_minutes}}</strong> minutes after it was generated. </p> diff --git a/gn_auth/templates/emails/verify-email.txt b/gn_auth/templates/emails/verify-email.txt index 92a6435..7a39fea 100644 --- a/gn_auth/templates/emails/verify-email.txt +++ b/gn_auth/templates/emails/verify-email.txt @@ -3,7 +3,7 @@ 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}}". +To avoid bots, we need to verify that you are an actual human. To do so, please go to "{{verification_uri}}?verificationcode={{verification_code}}". If that does not work, please log in to GeneNetwork and copy the verification code below when requested: |