From 3a1245a2e040c1a6beddb75e4f026ff679193c78 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 3 Nov 2021 10:23:28 +0100 Subject: [PATCH] API - handle user username and email case on login/register --- fittrackee/tests/fixtures/fixtures_users.py | 8 +++ fittrackee/tests/users/test_auth_api.py | 67 +++++++++++++++++++-- fittrackee/users/auth.py | 13 ++-- 3 files changed, 80 insertions(+), 8 deletions(-) diff --git a/fittrackee/tests/fixtures/fixtures_users.py b/fittrackee/tests/fixtures/fixtures_users.py index e5523ad1..d80ff8fa 100644 --- a/fittrackee/tests/fixtures/fixtures_users.py +++ b/fittrackee/tests/fixtures/fixtures_users.py @@ -14,6 +14,14 @@ def user_1() -> User: return user +@pytest.fixture() +def user_1_upper() -> User: + user = User(username='TEST', email='TEST@TEST.COM', password='12345678') + db.session.add(user) + db.session.commit() + return user + + @pytest.fixture() def user_1_admin() -> User: admin = User( diff --git a/fittrackee/tests/users/test_auth_api.py b/fittrackee/tests/users/test_auth_api.py index bbf3d451..f4462463 100644 --- a/fittrackee/tests/users/test_auth_api.py +++ b/fittrackee/tests/users/test_auth_api.py @@ -3,6 +3,7 @@ from datetime import datetime, timedelta from io import BytesIO from unittest.mock import Mock, patch +import pytest from flask import Flask from freezegun import freeze_time @@ -37,8 +38,38 @@ class TestUserRegistration: assert response.content_type == 'application/json' assert response.status_code == 201 - def test_it_returns_error_if_user_already_exists( - self, app: Flask, user_1: User + @pytest.mark.parametrize( + 'input_username', + ['test', 'TEST'], + ) + def test_it_returns_error_if_user_already_exists_with_same_username( + self, app: Flask, user_1: User, input_username: str + ) -> None: + client = app.test_client() + response = client.post( + '/api/auth/register', + data=json.dumps( + dict( + username=input_username, + email='another_email@test.com', + password='12345678', + password_conf='12345678', + ) + ), + content_type='application/json', + ) + data = json.loads(response.data.decode()) + assert data['status'] == 'error' + assert data['message'] == 'sorry, that user already exists' + assert response.content_type == 'application/json' + assert response.status_code == 400 + + @pytest.mark.parametrize( + 'input_email', + ['test@test.com', 'TEST@TEST.COM'], + ) + def test_it_returns_error_if_user_already_exists_with_same_email( + self, app: Flask, user_1: User, input_email: str ) -> None: client = app.test_client() response = client.post( @@ -292,12 +323,40 @@ class TestUserRegistration: class TestUserLogin: - def test_user_can_register(self, app: Flask, user_1: User) -> None: + @pytest.mark.parametrize( + 'input_email', + ['test@test.com', 'TEST@TEST.COM'], + ) + def test_user_can_login( + self, app: Flask, user_1: User, input_email: str + ) -> None: client = app.test_client() response = client.post( '/api/auth/login', - data=json.dumps(dict(email='test@test.com', password='12345678')), + data=json.dumps(dict(email=input_email, password='12345678')), + content_type='application/json', + ) + + assert response.content_type == 'application/json' + assert response.status_code == 200 + data = json.loads(response.data.decode()) + assert data['status'] == 'success' + assert data['message'] == 'successfully logged in' + assert data['auth_token'] + + @pytest.mark.parametrize( + 'input_email', + ['test@test.com', 'TEST@TEST.COM'], + ) + def test_user_can_login_when_user_email_is_uppercase( + self, app: Flask, user_1_upper: User, input_email: str + ) -> None: + client = app.test_client() + + response = client.post( + '/api/auth/login', + data=json.dumps(dict(email=input_email, password='12345678')), content_type='application/json', ) diff --git a/fittrackee/users/auth.py b/fittrackee/users/auth.py index 5a08ef91..33f8c3e5 100644 --- a/fittrackee/users/auth.py +++ b/fittrackee/users/auth.py @@ -4,7 +4,7 @@ from typing import Dict, Tuple, Union import jwt from flask import Blueprint, current_app, request -from sqlalchemy import exc, or_ +from sqlalchemy import exc, func, or_ from werkzeug.exceptions import RequestEntityTooLarge from werkzeug.utils import secure_filename @@ -117,7 +117,10 @@ def register_user() -> Union[Tuple[Dict, int], HttpResponse]: try: # check for existing user user = User.query.filter( - or_(User.username == username, User.email == email) + or_( + func.lower(User.username) == func.lower(username), + func.lower(User.email) == func.lower(email), + ) ).first() if user: return InvalidPayloadErrorResponse( @@ -193,11 +196,13 @@ def login_user() -> Union[Dict, HttpResponse]: post_data = request.get_json() if not post_data: return InvalidPayloadErrorResponse() - email = post_data.get('email') + email = post_data.get('email', '') password = post_data.get('password') try: # check for existing user - user = User.query.filter(User.email == email).first() + user = User.query.filter( + func.lower(User.email) == func.lower(email) + ).first() if user and bcrypt.check_password_hash(user.password, password): # generate auth token auth_token = user.encode_auth_token(user.id)