Skip to content

Commit

Permalink
fix: user registration request data validation
Browse files Browse the repository at this point in the history
  • Loading branch information
isabelcosta committed Jul 17, 2018
1 parent 144826b commit 1ffd88b
Show file tree
Hide file tree
Showing 8 changed files with 306 additions and 46 deletions.
2 changes: 1 addition & 1 deletion app/api/dao/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from app.api.email_utils import confirm_token
from app.database.models.user import UserModel
from app.utils.email_utils import is_email_valid
from app.utils.validation_utils import is_email_valid


class UserDAO:
Expand Down
46 changes: 3 additions & 43 deletions app/api/resources/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from flask_jwt_extended import jwt_required, create_access_token, get_jwt_identity
from flask_restplus import Resource, marshal, Namespace

from app.utils.email_utils import is_email_valid
from app.api.validations.user import validate_user_registration_request_data, validate_resend_email_request_data
from app.api.email_utils import send_email_verification_message
from app.api.models.user import *
from app.api.dao.user import UserDAO
Expand Down Expand Up @@ -152,7 +152,7 @@ def post(cls):

data = request.json

is_valid = cls.is_valid_data(data)
is_valid = validate_user_registration_request_data(data)

if is_valid != {}:
return is_valid, 400
Expand All @@ -164,33 +164,6 @@ def post(cls):

return result

@staticmethod
def is_valid_data(data):

# Verify if request body has required fields
if 'name' not in data:
return {"message": "Name field is missing."}
if 'username' not in data:
return {"message": "Username field is missing."}
if 'password' not in data:
return {"message": "Password field is missing."}
if 'email' not in data:
return {"message": "Email field is missing."}
if 'terms_and_conditions_checked' not in data:
return {"message": "Terms and conditions field is missing."}

terms_and_conditions_checked = data['terms_and_conditions_checked']
email = data['email']

# Verify business logic of request body
if terms_and_conditions_checked is False:
return {"message": "Terms and conditions are not checked."}

if not is_email_valid(email):
return {"message": "Your email is invalid."}

return {}


@users_ns.route('user/confirm_email/<string:token>')
@users_ns.param('token', 'Token sent to the user\'s email')
Expand All @@ -210,7 +183,7 @@ def post(cls):

data = request.json

is_valid = cls.is_valid_data(data)
is_valid = validate_resend_email_request_data(data)

if is_valid != {}:
return is_valid, 400
Expand All @@ -226,19 +199,6 @@ def post(cls):

return {"message": "Check your email, a new verification email was sent."}, 200

@staticmethod
def is_valid_data(data):

# Verify if request body has required fields
if 'email' not in data:
return {"message": "Email field is missing."}

email = data['email']
if not is_email_valid(email):
return {"message": "Your email is invalid."}

return {}


@users_ns.route('login')
class LoginUser(Resource):
Expand Down
Empty file added app/api/validations/__init__.py
Empty file.
73 changes: 73 additions & 0 deletions app/api/validations/user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from app.utils.validation_utils import is_email_valid, is_username_valid

# Field character limit

NAME_MAX_LENGTH = 30
NAME_MIN_LENGTH = 2
USERNAME_MAX_LENGTH = 25
USERNAME_MIN_LENGTH = 5
PASSWORD_MAX_LENGTH = 25
PASSWORD_MIN_LENGTH = 8


def validate_user_registration_request_data(data):
# Verify if request body has required fields
if 'name' not in data:
return {"message": "Name field is missing."}
if 'username' not in data:
return {"message": "Username field is missing."}
if 'password' not in data:
return {"message": "Password field is missing."}
if 'email' not in data:
return {"message": "Email field is missing."}
if 'terms_and_conditions_checked' not in data:
return {"message": "Terms and conditions field is missing."}

name = data['name']
username = data['username']
password = data['password']
email = data['email']
terms_and_conditions_checked = data['terms_and_conditions_checked']

if not (isinstance(name, str) and isinstance(username, str) and isinstance(password, str)):
return {"message": "Name, username and password must be in string format."}

if not (NAME_MIN_LENGTH <= len(name) <= NAME_MAX_LENGTH):
return {"message": "The name field has to longer than {min_limit} "
"characters and shorter than {max_limit} characters.".format(min_limit=NAME_MIN_LENGTH-1,
max_limit=NAME_MAX_LENGTH+1)}

if not (USERNAME_MIN_LENGTH <= len(username) <= USERNAME_MAX_LENGTH):
return {"message": "The username field has to longer than {min_limit} "
"characters and shorter than {max_limit} characters.".format(min_limit=USERNAME_MIN_LENGTH-1,
max_limit=USERNAME_MAX_LENGTH+1)}

if not (PASSWORD_MIN_LENGTH <= len(password) <= PASSWORD_MAX_LENGTH):
return {"message": "The password field has to longer than {min_limit} "
"characters and shorter than {max_limit} characters.".format(min_limit=PASSWORD_MIN_LENGTH-1,
max_limit=PASSWORD_MAX_LENGTH+1)}

# Verify business logic of request body
if terms_and_conditions_checked is False:
return {"message": "Terms and conditions are not checked."}

if not is_email_valid(email):
return {"message": "Your email is invalid."}

if not is_username_valid(username):
return {"message": "Your username is invalid."}

return {}


def validate_resend_email_request_data(data):

# Verify if request body has required fields
if 'email' not in data:
return {"message": "Email field is missing."}

email = data['email']
if not is_email_valid(email):
return {"message": "Your email is invalid."}

return {}
5 changes: 5 additions & 0 deletions app/utils/email_utils.py → app/utils/validation_utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import re

email_regex = r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)"
username_regex = r"(^[a-zA-Z0-9]+$)"


def is_email_valid(email):
return re.match(email_regex, email)


def is_username_valid(username):
return re.match(username_regex, username)
183 changes: 183 additions & 0 deletions tests/users/test_validation_request_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import unittest
from random import choice
from string import ascii_lowercase

from app.api.validations.user import validate_user_registration_request_data, NAME_MIN_LENGTH, NAME_MAX_LENGTH, \
USERNAME_MIN_LENGTH, USERNAME_MAX_LENGTH, PASSWORD_MAX_LENGTH, PASSWORD_MIN_LENGTH
from tests.test_data import user1


class TestUserApiRequestDataValidation(unittest.TestCase):

def test_user_registration_valid_request_data(self):
expected_result = {}
request_body = dict(
name=user1['name'],
username=user1['username'],
password=user1['password'],
email=user1['email'],
terms_and_conditions_checked=user1['terms_and_conditions_checked'])
actual_result = validate_user_registration_request_data(request_body)

self.assertEqual(expected_result, actual_result)

def test_user_registration_request_data_missing_name_field(self):
expected_result = {"message": "Name field is missing."}
request_body = dict(
username=user1['username'],
password=user1['password'],
email=user1['email'],
terms_and_conditions_checked=user1['terms_and_conditions_checked'])
actual_result = validate_user_registration_request_data(request_body)

self.assertEqual(expected_result, actual_result)

def test_user_registration_request_data_missing_username_field(self):
expected_result = {"message": "Username field is missing."}
request_body = dict(
name=user1['name'],
password=user1['password'],
email=user1['email'],
terms_and_conditions_checked=user1['terms_and_conditions_checked'])
actual_result = validate_user_registration_request_data(request_body)

self.assertEqual(expected_result, actual_result)

def test_user_registration_request_data_missing_password_field(self):
expected_result = {"message": "Password field is missing."}
request_body = dict(
name=user1['name'],
username=user1['username'],
email=user1['email'],
terms_and_conditions_checked=user1['terms_and_conditions_checked'])
actual_result = validate_user_registration_request_data(request_body)

self.assertEqual(expected_result, actual_result)

def test_user_registration_request_data_missing_email_field(self):
expected_result = {"message": "Email field is missing."}
request_body = dict(
name=user1['name'],
username=user1['username'],
password=user1['password'],
terms_and_conditions_checked=user1['terms_and_conditions_checked'])
actual_result = validate_user_registration_request_data(request_body)

self.assertEqual(expected_result, actual_result)

def test_user_registration_request_data_missing_terms_and_conditions_field(self):
expected_result = {"message": "Terms and conditions field is missing."}
request_body = dict(
name=user1['name'],
username=user1['username'],
password=user1['password'],
email=user1['email'])
actual_result = validate_user_registration_request_data(request_body)

self.assertEqual(expected_result, actual_result)

def test_user_registration_request_data_with_terms_unchecked(self):
expected_result = {"message": "Terms and conditions are not checked."}
request_body = dict(
name=user1['name'],
username=user1['username'],
password=user1['password'],
email=user1['email'],
terms_and_conditions_checked=False)
actual_result = validate_user_registration_request_data(request_body)

self.assertEqual(expected_result, actual_result)

def test_user_registration_request_data_name_inferior_to_limit(self):
expected_result = {"message": "The name field has to longer than {min_limit} "
"characters and shorter than {max_limit} characters.".format(min_limit=NAME_MIN_LENGTH-1,
max_limit=NAME_MAX_LENGTH+1)}
random_generated_name = "".join(choice(ascii_lowercase) for x in range(NAME_MIN_LENGTH-1))
request_body = dict(
name=random_generated_name,
username=user1['username'],
password=user1['password'],
email=user1['email'],
terms_and_conditions_checked=user1['terms_and_conditions_checked'])
actual_result = validate_user_registration_request_data(request_body)

self.assertEqual(expected_result, actual_result)

def test_user_registration_request_data_name_superior_to_limit(self):
expected_result = {"message": "The name field has to longer than {min_limit} "
"characters and shorter than {max_limit} characters.".format(min_limit=NAME_MIN_LENGTH-1,
max_limit=NAME_MAX_LENGTH+1)}
random_generated_name = "".join(choice(ascii_lowercase) for x in range(NAME_MAX_LENGTH+1))
request_body = dict(
name=random_generated_name,
username=user1['username'],
password=user1['password'],
email=user1['email'],
terms_and_conditions_checked=user1['terms_and_conditions_checked'])
actual_result = validate_user_registration_request_data(request_body)

self.assertEqual(expected_result, actual_result)

def test_user_registration_request_data_username_inferior_to_limit(self):
expected_result = {"message": "The username field has to longer than {min_limit} "
"characters and shorter than {max_limit} characters.".format(min_limit=USERNAME_MIN_LENGTH-1,
max_limit=USERNAME_MAX_LENGTH+1)}
random_generated_username = "".join(choice(ascii_lowercase) for x in range(USERNAME_MIN_LENGTH-1))
request_body = dict(
name=user1['name'],
username=random_generated_username,
password=user1['password'],
email=user1['email'],
terms_and_conditions_checked=user1['terms_and_conditions_checked'])
actual_result = validate_user_registration_request_data(request_body)

self.assertEqual(expected_result, actual_result)

def test_user_registration_request_data_username_superior_to_limit(self):
expected_result = {"message": "The username field has to longer than {min_limit} "
"characters and shorter than {max_limit} characters.".format(min_limit=USERNAME_MIN_LENGTH-1,
max_limit=USERNAME_MAX_LENGTH+1)}
random_generated_username = "".join(choice(ascii_lowercase) for x in range(USERNAME_MAX_LENGTH+1))
request_body = dict(
name=user1['name'],
username=random_generated_username,
password=user1['password'],
email=user1['email'],
terms_and_conditions_checked=user1['terms_and_conditions_checked'])
actual_result = validate_user_registration_request_data(request_body)

self.assertEqual(expected_result, actual_result)

def test_user_registration_request_data_password_inferior_to_limit(self):
expected_result = {"message": "The password field has to longer than {min_limit} "
"characters and shorter than {max_limit} characters.".format(min_limit=PASSWORD_MIN_LENGTH-1,
max_limit=PASSWORD_MAX_LENGTH+1)}
random_generated_password = "".join(choice(ascii_lowercase) for x in range(PASSWORD_MIN_LENGTH-1))
request_body = dict(
name=user1['name'],
username=user1['username'],
password=random_generated_password,
email=user1['email'],
terms_and_conditions_checked=user1['terms_and_conditions_checked'])
actual_result = validate_user_registration_request_data(request_body)

self.assertEqual(expected_result, actual_result)

def test_user_registration_request_data_password_superior_to_limit(self):
expected_result = {"message": "The password field has to longer than {min_limit} "
"characters and shorter than {max_limit} characters.".format(min_limit=PASSWORD_MIN_LENGTH-1,
max_limit=PASSWORD_MAX_LENGTH+1)}
random_generated_password = "".join(choice(ascii_lowercase) for x in range(PASSWORD_MAX_LENGTH+1))
request_body = dict(
name=user1['name'],
username=user1['username'],
password=random_generated_password,
email=user1['email'],
terms_and_conditions_checked=user1['terms_and_conditions_checked'])
actual_result = validate_user_registration_request_data(request_body)

self.assertEqual(expected_result, actual_result)


if __name__ == '__main__':
unittest.main()
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import unittest
from app.utils.email_utils import is_email_valid
from app.utils.validation_utils import is_email_valid


class TestEmailUtils(unittest.TestCase):
class TestEmailValidation(unittest.TestCase):

def test_empty_email(self):
email = ""
Expand Down

0 comments on commit 1ffd88b

Please sign in to comment.