diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..4cee413 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,20 @@ +{ + "_meta": { + "hash": { + "sha256": "e2a8a78582d100dc86a0694f5ad982ca341a6b861fd871c6306733562f9e16cc" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.8" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.python.org/simple", + "verify_ssl": true + } + ] + }, + "default": {}, + "develop": {} +} diff --git a/backend/Pipfile.lock b/backend/Pipfile.lock index eafb780..cdd11d6 100644 --- a/backend/Pipfile.lock +++ b/backend/Pipfile.lock @@ -793,4 +793,4 @@ "version": "==1.14.1" } } -} +} \ No newline at end of file diff --git a/backend/app.py b/backend/app.py index 9710251..599aa4f 100644 --- a/backend/app.py +++ b/backend/app.py @@ -76,4 +76,4 @@ def create_app(config={}): if __name__ == "__main__": app = create_app() - app.run(host="0.0.0.0", port=5001) + app.run(host="0.0.0.0", port=5001) \ No newline at end of file diff --git a/backend/auth/user.py b/backend/auth/user.py index fa79ccb..6819cab 100644 --- a/backend/auth/user.py +++ b/backend/auth/user.py @@ -141,4 +141,4 @@ def get(id): cursor.close() conn.close() - return User(email, password, id) + return User(email, password, id) \ No newline at end of file diff --git a/backend/common/database.py b/backend/common/database.py index 361c996..d2de08b 100644 --- a/backend/common/database.py +++ b/backend/common/database.py @@ -221,7 +221,25 @@ def getAllCompetitions(): def updateUsername(username, uid): query = f""" update Users - set username = {username} + set username = '{username}' + where uid = {uid}; + """ + cur.execute(query) + conn.commit() + +def updateEmail(email, uid): + query = f""" + update Users + set email = '{email}' + where uid = {uid}; + """ + cur.execute(query) + conn.commit() + +def updatePassword(password, uid): + query = f""" + update Users + set password = '{password}' where uid = {uid}; """ cur.execute(query) @@ -396,4 +414,3 @@ def add_user_with_uid(uid, email, username, password): """ cur.execute(query) conn.commit() - diff --git a/backend/common/exceptions.py b/backend/common/exceptions.py index 825bde1..2218b1a 100644 --- a/backend/common/exceptions.py +++ b/backend/common/exceptions.py @@ -16,4 +16,4 @@ class AccessError(Forbidden): pass class InvalidError(InternalServerError): - pass + pass \ No newline at end of file diff --git a/backend/common/redis.py b/backend/common/redis.py index ce87583..8b29b6d 100644 --- a/backend/common/redis.py +++ b/backend/common/redis.py @@ -71,4 +71,4 @@ def is_blocked(id): ## GENERAL FUNCTIONS def clear_redis(): - cache.flushdb() + cache.flushdb() \ No newline at end of file diff --git a/backend/models/user.py b/backend/models/user.py index 4ec70b2..55cd544 100644 --- a/backend/models/user.py +++ b/backend/models/user.py @@ -107,4 +107,4 @@ def get(id): id, email, github_username, username, password = result - return User(id, email, username, password, github_username) + return User(id, email, username, password, github_username) \ No newline at end of file diff --git a/backend/routes/auth.py b/backend/routes/auth.py index 8907577..11593fc 100644 --- a/backend/routes/auth.py +++ b/backend/routes/auth.py @@ -102,4 +102,4 @@ def logout(): @auth.route("/protected", methods=["POST"]) def protected(): verify_jwt_in_request() - return jsonify({}), 200 + return jsonify({}), 200 \ No newline at end of file diff --git a/backend/routes/user.py b/backend/routes/user.py index 6622666..cac3267 100644 --- a/backend/routes/user.py +++ b/backend/routes/user.py @@ -1,32 +1,39 @@ -from flask import Blueprint, jsonify, request +import email +from flask import Blueprint, jsonify, request, render_template from flask_jwt_extended import jwt_required, create_access_token, set_access_cookies, unset_jwt_cookies, verify_jwt_in_request, get_jwt_identity - +from flask_mail import Message +from email_validator import validate_email, EmailNotValidError import re +import os +from datetime import timedelta from common.exceptions import AuthError, RequestError -from common.database import getCompetitionQuestions, getUserStatsPerComp, updateUsername +from common.database import getCompetitionQuestions, getUserStatsPerComp, updateUsername, updateEmail, updatePassword from database.user import username_exists from models.user import User +from itsdangerous import URLSafeTimedSerializer +from common.redis import cache +from common.plugins import mail user = Blueprint("user", __name__) +verify_serialiser = URLSafeTimedSerializer(os.environ["FLASK_SECRET"], salt="verify") @user.route("/profile", methods=["GET"]) def get_profile(): try: verify_jwt_in_request() id = get_jwt_identity() - - user_data = User.get(id) - - return jsonify({ - "email": user_data.email, - "username": user_data.username - }) except: raise AuthError("Invalid Token") + user_data = User.get(id) + return jsonify({ + "email": user_data.email, + "username": user_data.username + }) +@jwt_required() @user.route("/stats", methods=['GET']) def get_stats(): @@ -34,59 +41,48 @@ def get_stats(): try: verify_jwt_in_request() - id = get_jwt_identity() - competition = request.get_json()['competition'] - - if getCompetitionQuestions(competition) == []: - raise RequestError("The competition doesn't exist") - - # TODO: fix this function. - - stats = getUserStatsPerComp(competition, id) - - ## find a way to get the stat infos, they are spread across multiple tables. - - return jsonify({ - "stats": stats - }) except: raise AuthError("Invalid token") + id = get_jwt_identity() + competition = request.args.get('competition') + + if getCompetitionQuestions(competition) == {}: + raise RequestError("The competition doesn't exist") + stats = getUserStatsPerComp(competition, id) + return jsonify({ + "stats": stats + }) + +@jwt_required() @user.route("/set_name", methods=['POST']) def set_name(): # { # token: token (in cookies) # username: string # } - try: - print('hello') - print('hello0') verify_jwt_in_request() - print('hello1') id = get_jwt_identity() - print('hello2') - user_data = User.get(id) - print('hello3') - json = request.get_json() - print('hello4') - username = json["username"] - print('hello5') - - # if username already in database, raise RequestError. - if username_exists(username): - raise RequestError(description="Username already used") - else: - updateUsername(username, id) - - return jsonify({}) - except: + except Exception as e: + print(e) raise AuthError("Invalid token") -""" -@user.route("/user/reset_email/request", methods=["POST"]) + # user_data = User.get(id) + json = request.get_json() + username = json["username"] + + # if username already in database, raise RequestError. + if username_exists(username): + raise RequestError(description="Username already used") + else: + updateUsername(username, id) + return jsonify({}) + +@jwt_required() +@user.route("/reset_email/request", methods=["POST"]) def reset_email_request(): - data = request.get_json() + json = request.get_json() ''' { token: token (in cookies) @@ -97,57 +93,126 @@ def reset_email_request(): verify_jwt_in_request() except: raise AuthError("Invalid token") + + try: + normalised = validate_email(json['email']).email + except EmailNotValidError as e: + raise RequestError(description="Invalid email") from e + code = verify_serialiser.dumps(normalised) + data = { + "email": normalised, + } + # We use a pipeline here to ensure these instructions are atomic + pipeline = cache.pipeline() + pipeline.hset(f"email_reset_code:{code}", mapping=data) + pipeline.expire(f"email_reset_code:{code}", timedelta(hours=1)) + pipeline.execute() + + url = f"{os.environ['TESTING_ADDRESS']}/verify/{code}" + html = render_template("email_request.html", reset_code=url) + + # Send it over to email + message = Message( + "Email Request for Week in Wonderland", + sender="weekinwonderland@csesoc.org.au", + recipients=[normalised], + html=html + ) + message.body = f"Your email reset code is: {code}" + + mail.send(message) + response = jsonify({}) + + return response, 200 + +@jwt_required() +@user.route("/reset_email/reset", methods=['POST']) +def reset_email(): + json = request.get_json() + try: + verify_jwt_in_request() + id = get_jwt_identity() + except: + raise AuthError("Invalid token") + cache_key = f"email_reset_code:{json['reset_code']}" + if not cache.exists(cache_key): + raise AuthError("Reset code expired or does not correspond to registering user") + result = cache.hgetall(cache_key) + stringified = {} + + for key, value in result.items(): + stringified[key.decode()] = value.decode() + updateEmail(stringified["email"], id) + response = jsonify({}) + return response, 200 + +@jwt_required() +@user.route("/reset_password/request", methods=["POST"]) +def reset_password_request(): + ''' + { + token: token (in cookies) + } + ''' + try: + verify_jwt_in_request() + id = get_jwt_identity() + except: + raise AuthError("Invalid token") -# @user.route("/reset_email/request", methods=["POST"]) -# def reset_email_request(): -# data = request.get_json() -# ''' -# { -# token: token (in cookies) -# email: string -# } -# ''' -# try: -# verify_jwt_in_request() -# except: -# raise AuthError("Invalid token") - + email = User.get(id).email -# # Check if email refers to an actual email. -# if not re.match('^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$', data['email']): -# raise RequestError("email not valid") -# return {} + code = verify_serialiser.dumps(email) + data = { + "email": email, + } + # We use a pipeline here to ensure these instructions are atomic + pipeline = cache.pipeline() + pipeline.hset(f"password_reset_code:{code}", mapping=data) + pipeline.expire(f"password_reset_code:{code}", timedelta(hours=1)) + pipeline.execute() + + url = f"{os.environ['TESTING_ADDRESS']}/verify/{code}" + html = render_template("password_request.html", reset_code=url) + + # Send it over to email + message = Message( + "Email Request for Week in Wonderland", + sender="weekinwonderland@csesoc.org.au", + recipients=[email], + html=html + ) + + mail.send(message) + response = jsonify({}) + return response, 200 + +@jwt_required() +@user.route("/reset_password/reset", methods=["POST"]) +def reset_password(): + ''' + { + token: token (in cookies) + reset_code: string + password: string + } + ''' + json = request.get_json() + try: + verify_jwt_in_request() + id = get_jwt_identity() + except: + raise AuthError("Invalid token") + cache_key = f"password_reset_code:{json['reset_code']}" + if not cache.exists(cache_key): + raise AuthError("Reset code expired or does not correspond to registering user") + result = cache.hgetall(cache_key) + hashed = User.hash_password(json["password"]) + updatePassword(hashed, id) - -# @user.route("/reset_email/reset", methods=['POST']) -# def reset_email(): -# json = request.get_json() -# ''' -# { -# token: token (in cookie) -# reset_code: string -# } -# ''' -# try: -# verify_jwt_in_request() -# except: -# raise AuthError("Invalid token") - -# ''' -# if (json['reset_code'] not match the code in database for user): -# raise AuthError("The reset code is wrong.") -# else: -# reset email. -# ''' - - - -# @user.route("/reset_password/request", methods=["POST"]) -# def reset_password_request(): -# json = request.get_json() -# return jsonify({}) -""" \ No newline at end of file + response = jsonify({}) + return response, 200 \ No newline at end of file diff --git a/backend/templates/email_request.html b/backend/templates/email_request.html new file mode 100644 index 0000000..061c070 --- /dev/null +++ b/backend/templates/email_request.html @@ -0,0 +1,4 @@ +
Here's your email reset code:
+ +Cheers!
\ No newline at end of file diff --git a/backend/templates/password_request.html b/backend/templates/password_request.html new file mode 100644 index 0000000..5528923 --- /dev/null +++ b/backend/templates/password_request.html @@ -0,0 +1,4 @@ +Here's your password reset code:
+ +Cheers!
\ No newline at end of file diff --git a/backend/test/helpers.py b/backend/test/helpers.py index 1b83689..0c5b333 100644 --- a/backend/test/helpers.py +++ b/backend/test/helpers.py @@ -32,7 +32,6 @@ def get_cookie_from_header(response, cookie_name): for header in cookie_headers: attributes = header.split(";") - if cookie_name in attributes[0]: cookie = {} diff --git a/backend/test/profile/profile_test.py b/backend/test/profile/profile_test.py index 9caf6e4..4d99ff1 100644 --- a/backend/test/profile/profile_test.py +++ b/backend/test/profile/profile_test.py @@ -4,7 +4,7 @@ import re # Imports for pytest -from test.helpers import clear_all +from test.helpers import clear_all, db_add_user, generate_csrf_header from test.fixtures import app, client ## HELPER FUNCTIONS @@ -20,40 +20,25 @@ def find_token(contents): def test_profile(client): clear_all() - register_response = client.post("/auth/register", json={ + db_add_user("asdfghjkl@gmail.com", "asdf", "foobar") + response = client.post("/auth/login", json={ "email": "asdfghjkl@gmail.com", - "username": "asdf", "password": "foobar" }) - - assert register_response.status_code == 200 - - # Check inbox - mailbox = poplib.POP3("pop3.mailtrap.io", 1100) - mailbox.user(os.environ["MAILTRAP_USERNAME"]) - mailbox.pass_(os.environ["MAILTRAP_PASSWORD"]) - - # Check the contents of the email, and harvest the token from there - raw_email = b"\n".join(mailbox.retr(1)[1]) - parsed_email = email.message_from_bytes(raw_email) - - # Assuming there's a HTML part - for part in parsed_email.walk(): - if part.get_content_type() == "text/html": - content = part.get_payload() - - # Extract the token from the HTML - token = find_token(content) - - response = client.post("/auth/register/verify", json={ - "token": token - }) - assert response.status_code == 200 - profile = client.get("/user/profile") - assert profile.status_code == 200 - assert profile.json == { + + profile2 = client.get("/user/profile", headers=generate_csrf_header(response)) + assert profile2.status_code == 200 + assert profile2.json == { "email": "asdfghjkl@gmail.com", "username": "asdf" } +def test_profile_fail(client): + clear_all() + + db_add_user("asdfghjkl@gmail.com", "asdf", "foobar") + + profile2 = client.get("/user/profile") + assert profile2.status_code == 401 + diff --git a/backend/test/profile/reset_email_request_test.py b/backend/test/profile/reset_email_request_test.py new file mode 100644 index 0000000..3bea654 --- /dev/null +++ b/backend/test/profile/reset_email_request_test.py @@ -0,0 +1,93 @@ +import email +import os +import poplib +import re + +# Imports for pytest +from test.helpers import clear_all, db_add_user, generate_csrf_header +from test.fixtures import app, client +from test.mock.mock_mail import mailbox +from pytest_mock import mocker + +## HELPER FUNCTIONS + +def find_token(contents): + verify_link = "http://localhost:5001/verify/" + results = re.findall(rf"", contents) + + return results[0] + +### test starts here + +def test_email_request(client, mocker): + clear_all() + mocker.patch("routes.user.mail", mailbox) + + db_add_user("asdfghjkl@gmail.com", "asdf", "foobar") + + response = client.post("/auth/login", json={ + "email": "asdfghjkl@gmail.com", + "password": "foobar" + }) + assert response.status_code == 200 + + profile = client.get("/user/profile") + assert profile.status_code == 200 + assert profile.json == { + "email": "asdfghjkl@gmail.com", + "username": "asdf" + } + + reset = client.post("/user/reset_email/request", json={ + "email": "numail@gmail.com" + },headers=generate_csrf_header(response)) + + assert reset.status_code == 200 + +def test_email_request_invalid_email(client): + clear_all() + + db_add_user("asdfghjkl@gmail.com", "asdf", "foobar") + + response = client.post("/auth/login", json={ + "email": "asdfghjkl@gmail.com", + "password": "foobar" + }) + assert response.status_code == 200 + + profile = client.get("/user/profile") + assert profile.status_code == 200 + assert profile.json == { + "email": "asdfghjkl@gmail.com", + "username": "asdf" + } + + reset = client.post("/user/reset_email/request", json={ + "email": "chungas" + },headers=generate_csrf_header(response)) + + assert reset.status_code == 400 + +def test_email_request_invalid_token(client): + clear_all() + + db_add_user("asdfghjkl@gmail.com", "asdf", "foobar") + + response = client.post("/auth/login", json={ + "email": "asdfghjkl@gmail.com", + "password": "foobar" + }) + assert response.status_code == 200 + + profile = client.get("/user/profile") + assert profile.status_code == 200 + assert profile.json == { + "email": "asdfghjkl@gmail.com", + "username": "asdf" + } + + reset = client.post("/user/reset_email/request", json={ + "email": "chungas" + }) + + assert reset.status_code == 401 diff --git a/backend/test/profile/reset_email_test.py b/backend/test/profile/reset_email_test.py new file mode 100644 index 0000000..195577c --- /dev/null +++ b/backend/test/profile/reset_email_test.py @@ -0,0 +1,77 @@ +import email +import os +import poplib +import re + +# Imports for pytest +from test.helpers import clear_all, db_add_user, generate_csrf_header +from test.fixtures import app, client +from test.mock.mock_mail import mailbox +from pytest_mock import mocker + +## HELPER FUNCTIONS + +def find_token(contents): + verify_link = "http://localhost:5001/verify/" + results = re.findall(rf"", contents) + + return results[0] + +### test starts here + +def test_email_reset(client, mocker): + clear_all() + mocker.patch("routes.user.mail", mailbox) + + db_add_user("asdfghjkl@gmail.com", "asdf", "foobar") + + response = client.post("/auth/login", json={ + "email": "asdfghjkl@gmail.com", + "password": "foobar" + }) + assert response.status_code == 200 + + before = len(mailbox.messages) + + profile = client.get("/user/profile") + assert profile.status_code == 200 + assert profile.json == { + "email": "asdfghjkl@gmail.com", + "username": "asdf" + } + + reset = client.post("/user/reset_email/request", json={ + "email": "numail@gmail.com" + },headers=generate_csrf_header(response)) + + assert reset.status_code == 200 + + after = len(mailbox.messages) + + assert after == before + 1 + + # Verify recipient + parsed_email = mailbox.get_message(-1) + + assert parsed_email["To"] == "numail@gmail.com" + + # Assuming there's a HTML part + for part in parsed_email.walk(): + if part.get_content_type() == "text/html": + content = part.get_payload() + # Extract the token from the HTML + token = find_token(content) + + response = client.post("/user/reset_email/reset", json={ + "reset_code": token, + },headers=generate_csrf_header(response)) + + assert response.status_code == 200 + profile = client.get("/user/profile") + assert profile.status_code == 200 + assert profile.json == { + "email": "numail@gmail.com", + "username": "asdf" + } + + diff --git a/backend/test/profile/reset_password_request_test.py b/backend/test/profile/reset_password_request_test.py new file mode 100644 index 0000000..442d71b --- /dev/null +++ b/backend/test/profile/reset_password_request_test.py @@ -0,0 +1,68 @@ +import email +import os +import poplib +import re + +# Imports for pytest +from test.helpers import clear_all, db_add_user, generate_csrf_header +from test.fixtures import app, client +from test.mock.mock_mail import mailbox +from pytest_mock import mocker + +## HELPER FUNCTIONS + +def find_token(contents): + verify_link = "http://localhost:5001/verify/" + results = re.findall(rf"", contents) + + return results[0] + +### test starts here + +def test_password_test(client, mocker): + clear_all() + mocker.patch("routes.user.mail", mailbox) + + db_add_user("asdfghjkl@gmail.com", "asdf", "foobar") + + response = client.post("/auth/login", json={ + "email": "asdfghjkl@gmail.com", + "password": "foobar" + }) + assert response.status_code == 200 + + profile = client.get("/user/profile") + assert profile.status_code == 200 + assert profile.json == { + "email": "asdfghjkl@gmail.com", + "username": "asdf" + } + + before = len(mailbox.messages) + + reset = client.post("/user/reset_password/request", json={ + },headers=generate_csrf_header(response)) + + assert reset.status_code == 200 + + after = len(mailbox.messages) + + assert after == before + 1 + +def test_password_request_invalid_token(client, mocker): + clear_all() + mocker.patch("routes.user.mail", mailbox) + + db_add_user("asdfghjkl@gmail.com", "asdf", "foobar") + + response = client.post("/auth/login", json={ + "email": "asdfghjkl@gmail.com", + "password": "foobar" + }) + assert response.status_code == 200 + + reset = client.post("/user/reset_password/request", json={ + }) + + assert reset.status_code == 401 + \ No newline at end of file diff --git a/backend/test/profile/reset_password_reset_test.py b/backend/test/profile/reset_password_reset_test.py new file mode 100644 index 0000000..21f1b5d --- /dev/null +++ b/backend/test/profile/reset_password_reset_test.py @@ -0,0 +1,173 @@ +import email +import os +import poplib +import re + +# Imports for pytest +from test.helpers import clear_all, db_add_user, generate_csrf_header +from test.fixtures import app, client +from test.mock.mock_mail import mailbox +from pytest_mock import mocker + + +## HELPER FUNCTIONS + +def find_token(contents): + verify_link = "http://localhost:5001/verify/" + results = re.findall(rf"", contents) + + return results[0] + +### test starts here + +def test_password_reset(client, mocker): + clear_all() + mocker.patch("routes.user.mail", mailbox) + + db_add_user("asdfghjkl@gmail.com", "asdf", "foobar") + + response = client.post("/auth/login", json={ + "email": "asdfghjkl@gmail.com", + "password": "foobar" + }) + assert response.status_code == 200 + + profile = client.get("/user/profile") + assert profile.status_code == 200 + assert profile.json == { + "email": "asdfghjkl@gmail.com", + "username": "asdf" + } + + before = len(mailbox.messages) + reset = client.post("/user/reset_password/request", json={ + "email": "asdfghjkl@gmail.com" + },headers=generate_csrf_header(response)) + + assert reset.status_code == 200 + after = len(mailbox.messages) + assert after == before + 1 + + # Check inbox + parsed_email = mailbox.get_message(-1) + assert parsed_email["To"] == "asdfghjkl@gmail.com" + + # Assuming there's a HTML part + for part in parsed_email.walk(): + if part.get_content_type() == "text/html": + content = part.get_payload() + # Extract the token from the HTML + token = find_token(content) + + response2 = client.post("/user/reset_password/reset", json={ + "reset_code": token, + "password": "ghjkl" + }, headers=generate_csrf_header(response)) + + assert response2.status_code == 200 + + response = client.post("/auth/logout") + + assert response.status_code == 200 + + # Check there's no more cookies + assert len(client.cookie_jar) == 0 + + response = client.post("/auth/login", json={ + "email": "asdfghjkl@gmail.com", + "password": "ghjkl" + }) + + assert response.status_code == 200 + +def test_password_reset_invalid_token(client, mocker): + clear_all() + mocker.patch("routes.user.mail", mailbox) + + db_add_user("asdfghjkl@gmail.com", "asdf", "foobar") + + response = client.post("/auth/login", json={ + "email": "asdfghjkl@gmail.com", + "password": "foobar" + }) + assert response.status_code == 200 + + profile = client.get("/user/profile") + assert profile.status_code == 200 + assert profile.json == { + "email": "asdfghjkl@gmail.com", + "username": "asdf" + } + + before = len(mailbox.messages) + reset = client.post("/user/reset_password/request", json={ + "email": "asdfghjkl@gmail.com" + },headers=generate_csrf_header(response)) + + assert reset.status_code == 200 + after = len(mailbox.messages) + assert after == before + 1 + + # Check inbox + parsed_email = mailbox.get_message(-1) + assert parsed_email["To"] == "asdfghjkl@gmail.com" + + # Assuming there's a HTML part + for part in parsed_email.walk(): + if part.get_content_type() == "text/html": + content = part.get_payload() + # Extract the token from the HTML + token = find_token(content) + + response2 = client.post("/user/reset_password/reset", json={ + "reset_code": token, + "password": "ghjkl" + }) + + response2.status_code == 401 + +def test_password_reset_wrong_code(client, mocker): + clear_all() + mocker.patch("routes.user.mail", mailbox) + + db_add_user("asdfghjkl@gmail.com", "asdf", "foobar") + + response = client.post("/auth/login", json={ + "email": "asdfghjkl@gmail.com", + "password": "foobar" + }) + assert response.status_code == 200 + + profile = client.get("/user/profile") + assert profile.status_code == 200 + assert profile.json == { + "email": "asdfghjkl@gmail.com", + "username": "asdf" + } + + before = len(mailbox.messages) + reset = client.post("/user/reset_password/request", json={ + "email": "asdfghjkl@gmail.com" + },headers=generate_csrf_header(response)) + + assert reset.status_code == 200 + after = len(mailbox.messages) + assert after == before + 1 + + # Check inbox + parsed_email = mailbox.get_message(-1) + assert parsed_email["To"] == "asdfghjkl@gmail.com" + + # Assuming there's a HTML part + for part in parsed_email.walk(): + if part.get_content_type() == "text/html": + content = part.get_payload() + # Extract the token from the HTML + token = find_token(content) + + response2 = client.post("/user/reset_password/reset", json={ + "reset_code": "wrong code wrong code wrong code", + "password": "ghjkl" + },headers=generate_csrf_header(response)) + + response2.status_code == 401 \ No newline at end of file diff --git a/backend/test/profile/set_name_test.py b/backend/test/profile/set_name_test.py index f8fbb29..2c3b1d1 100644 --- a/backend/test/profile/set_name_test.py +++ b/backend/test/profile/set_name_test.py @@ -4,7 +4,7 @@ import re # Imports for pytest -from test.helpers import clear_all, db_add_user +from test.helpers import clear_all, db_add_user, generate_csrf_header from test.fixtures import app, client ## HELPER FUNCTIONS @@ -20,36 +20,14 @@ def find_token(contents): def test_set_name(client): clear_all() - register_response = client.post("/auth/register", json={ + db_add_user("asdfghjkl@gmail.com", "asdf", "foobar") + + response = client.post("/auth/login", json={ "email": "asdfghjkl@gmail.com", - "username": "asdf", "password": "foobar" }) - - assert register_response.status_code == 200 - - # Check inbox - mailbox = poplib.POP3("pop3.mailtrap.io", 1100) - mailbox.user(os.environ["MAILTRAP_USERNAME"]) - mailbox.pass_(os.environ["MAILTRAP_PASSWORD"]) - - # Check the contents of the email, and harvest the token from there - raw_email = b"\n".join(mailbox.retr(1)[1]) - parsed_email = email.message_from_bytes(raw_email) - - # Assuming there's a HTML part - for part in parsed_email.walk(): - if part.get_content_type() == "text/html": - content = part.get_payload() - - # Extract the token from the HTML - token = find_token(content) - - response = client.post("/auth/register/verify", json={ - "token": token - }) - assert response.status_code == 200 + profile = client.get("/user/profile") assert profile.status_code == 200 assert profile.json == { @@ -58,9 +36,8 @@ def test_set_name(client): } change = client.post("/user/set_name", json={ - "token": token, "username": "nunu" - }) + }, headers=generate_csrf_header(response)) assert change.status_code == 200 @@ -74,41 +51,15 @@ def test_set_name(client): def test_set_name_repeated(client): clear_all() - reused_username = "foo" - # Register the user in the database directly db_add_user("a@gmail.com", reused_username, "bar") + db_add_user("asdfghjkl@gmail.com", "asdf", "foobar") - register_response = client.post("/auth/register", json={ + response = client.post("/auth/login", json={ "email": "asdfghjkl@gmail.com", - "username": "asdf", "password": "foobar" }) - - assert register_response.status_code == 200 - - # Check inbox - mailbox = poplib.POP3("pop3.mailtrap.io", 1100) - mailbox.user(os.environ["MAILTRAP_USERNAME"]) - mailbox.pass_(os.environ["MAILTRAP_PASSWORD"]) - - # Check the contents of the email, and harvest the token from there - raw_email = b"\n".join(mailbox.retr(1)[1]) - parsed_email = email.message_from_bytes(raw_email) - - # Assuming there's a HTML part - for part in parsed_email.walk(): - if part.get_content_type() == "text/html": - content = part.get_payload() - - # Extract the token from the HTML - token = find_token(content) - - response = client.post("/auth/register/verify", json={ - "token": token - }) - assert response.status_code == 200 profile = client.get("/user/profile") assert profile.status_code == 200 @@ -118,8 +69,6 @@ def test_set_name_repeated(client): } change = client.post("/user/set_name", json={ - "token": token, "username": reused_username - }) - + }, headers=generate_csrf_header(response)) change.status_code == 400 diff --git a/backend/test/profile/stat_test.py b/backend/test/profile/stat_test.py index ea435a1..9c56d63 100644 --- a/backend/test/profile/stat_test.py +++ b/backend/test/profile/stat_test.py @@ -2,10 +2,9 @@ import os import poplib import re -from sqlite3 import paramstyle # Imports for pytest -from test.helpers import clear_all +from test.helpers import clear_all, db_add_user from test.fixtures import app, client ## HELPER FUNCTIONS @@ -21,35 +20,12 @@ def find_token(contents): def test_stats(client): clear_all() - register_response = client.post("/auth/register", json={ + db_add_user("asdfghjkl@gmail.com", "asdf", "foobar") + + response = client.post("/auth/login", json={ "email": "asdfghjkl@gmail.com", - "username": "asdf", "password": "foobar" }) - - assert register_response.status_code == 200 - - # Check inbox - mailbox = poplib.POP3("pop3.mailtrap.io", 1100) - mailbox.user(os.environ["MAILTRAP_USERNAME"]) - mailbox.pass_(os.environ["MAILTRAP_PASSWORD"]) - - # Check the contents of the email, and harvest the token from there - raw_email = b"\n".join(mailbox.retr(1)[1]) - parsed_email = email.message_from_bytes(raw_email) - - # Assuming there's a HTML part - for part in parsed_email.walk(): - if part.get_content_type() == "text/html": - content = part.get_payload() - - # Extract the token from the HTML - token = find_token(content) - - response = client.post("/auth/register/verify", json={ - "token": token - }) - assert response.status_code == 200 profile = client.get("/user/profile") assert profile.status_code == 200