Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added validation function to validate Update Profile request body #107

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 7 additions & 2 deletions app/api/resources/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
from flask_jwt_extended import jwt_required, create_access_token, get_jwt_identity
from flask_restplus import Resource, marshal, Namespace

from app.api.validations.user import (validate_user_registration_request_data, validate_resend_email_request_data,
validate_new_password)
from app.api.validations.user import *
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 @@ -91,6 +90,12 @@ def put(cls):
"""

data = request.json

is_valid = validate_update_profile_request_data(data)

if is_valid != {}:
return is_valid, 400

user_id = get_jwt_identity()
return DAO.update_user_profile(user_id, data)

Expand Down
122 changes: 103 additions & 19 deletions app/api/validations/user.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from app.utils.validation_utils import is_email_valid, is_username_valid
from app.utils.validation_utils import is_email_valid, is_username_valid, validate_length, get_stripped_string

# Field character limit

Expand All @@ -9,6 +9,15 @@
PASSWORD_MAX_LENGTH = 25
PASSWORD_MIN_LENGTH = 8

BIO_MAX_LENGTH = 450
LOCATION_MAX_LENGTH = 60
OCCUPATION_MAX_LENGTH = 60
ORGANIZATION_MAX_LENGTH = 60
SLACK_USERNAME_MAX_LENGTH = 60
SKILLS_MAX_LENGTH = 450
INTERESTS_MAX_LENGTH = 150
SOCIALS_MAX_LENGTH = 400


def validate_user_registration_request_data(data):
# Verify if request body has required fields
Expand All @@ -32,20 +41,17 @@ def validate_user_registration_request_data(data):
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)}
is_valid = validate_length(len(get_stripped_string(name)), NAME_MIN_LENGTH, NAME_MAX_LENGTH, 'name')
if not is_valid[0]:
return is_valid[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)}
is_valid = validate_length(len(get_stripped_string(username)), USERNAME_MIN_LENGTH, USERNAME_MAX_LENGTH, 'username')
if not is_valid[0]:
return is_valid[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)}
is_valid = validate_length(len(get_stripped_string(password)), PASSWORD_MIN_LENGTH, PASSWORD_MAX_LENGTH, 'password')
if not is_valid[0]:
return is_valid[1]

# Verify business logic of request body
if terms_and_conditions_checked is False:
Expand All @@ -61,7 +67,6 @@ def validate_user_registration_request_data(data):


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."}
Expand All @@ -73,16 +78,95 @@ def validate_resend_email_request_data(data):
return {}


def validate_update_profile_request_data(data):
# todo this does not check if non expected fields are being sent

if not data:
return {"message": "No data for updating profile was sent."}

username = data.get('username', None)
if username:
is_valid = validate_length(len(get_stripped_string(username)), USERNAME_MIN_LENGTH, USERNAME_MAX_LENGTH,
'username')
if not is_valid[0]:
return is_valid[1]

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

name = data.get('name', None)
if name:
is_valid = validate_length(len(get_stripped_string(name)), NAME_MIN_LENGTH, NAME_MAX_LENGTH, 'name')
if not is_valid[0]:
return is_valid[1]

bio = data.get('bio', None)
if bio:
is_valid = validate_length(len(get_stripped_string(bio)), 0, BIO_MAX_LENGTH, 'bio')
if not is_valid[0]:
return is_valid[1]

location = data.get('location', None)
if location:
is_valid = validate_length(len(get_stripped_string(location)), 0, LOCATION_MAX_LENGTH, 'location')
if not is_valid[0]:
return is_valid[1]

occupation = data.get('occupation', None)
if occupation:
is_valid = validate_length(len(get_stripped_string(occupation)), 0, OCCUPATION_MAX_LENGTH, 'occupation')
if not is_valid[0]:
return is_valid[1]

organization = data.get('organization', None)
if organization:
is_valid = validate_length(len(get_stripped_string(organization)), 0, ORGANIZATION_MAX_LENGTH, 'organization')
if not is_valid[0]:
return is_valid[1]

slack_username = data.get('slack_username', None)
if slack_username:
is_valid = validate_length(len(get_stripped_string(slack_username)), 0, SLACK_USERNAME_MAX_LENGTH,
'slack_username')
if not is_valid[0]:
return is_valid[1]

social_media_links = data.get('social_media_links', None)
if social_media_links:
is_valid = validate_length(len(get_stripped_string(social_media_links)), 0, SOCIALS_MAX_LENGTH,
'social_media_links')
if not is_valid[0]:
return is_valid[1]

skills = data.get('skills', None)
if skills:
is_valid = validate_length(len(get_stripped_string(skills)), 0, SKILLS_MAX_LENGTH, 'skills')
if not is_valid[0]:
return is_valid[1]

interests = data.get('interests', None)
if interests:
is_valid = validate_length(len(get_stripped_string(interests)), 0, INTERESTS_MAX_LENGTH, 'interests')
if not is_valid[0]:
return is_valid[1]

return {}


def validate_new_password(data):
if 'current_password' not in data:
return {"message": "Current password field is missing."}
if 'new_password' not in data:
return {"message": "New password field is missing."}

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

if " " in new_password:
return {"message": "Password shouldn't contain spaces"}
return {"message": "Password shouldn't contain spaces."}

is_valid = validate_length(len(get_stripped_string(new_password)), PASSWORD_MIN_LENGTH, PASSWORD_MAX_LENGTH,
'new_password')
if not is_valid[0]:
return is_valid[1]

return {}
28 changes: 27 additions & 1 deletion app/utils/validation_utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import re

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


def is_email_valid(email):
Expand All @@ -10,3 +10,29 @@ def is_email_valid(email):

def is_username_valid(username):
return re.match(username_regex, username)


def validate_length(field_length, min_length, max_length, field_name):
if not (min_length <= field_length <= max_length):
if min_length <= 0:
error_msg = {"message": get_length_validation_error_message(field_name, None, max_length)}
else:
error_msg = {"message": get_length_validation_error_message(field_name, min_length, max_length)}
return False, error_msg
else:
return True, {}


def get_length_validation_error_message(field_name, min_length, max_length):
if min_length is None:
return "The {field_name} field has to be shorter than {max_limit} characters.".format(field_name=field_name,
max_limit=max_length + 1)
else:
return "The {field_name} field has to be longer than {min_limit} " \
"characters and shorter than {max_limit} characters.".format(field_name=field_name,
min_limit=min_length - 1,
max_limit=max_length + 1)


def get_stripped_string(string_with_whitespaces):
return ''.join(string_with_whitespaces.split())
37 changes: 37 additions & 0 deletions tests/users/test_api_update_user.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import unittest
from random import SystemRandom
from string import ascii_lowercase

from flask import json

from app.api.validations.user import USERNAME_MAX_LENGTH, USERNAME_MIN_LENGTH
from app.database.models.user import UserModel
from app.database.sqlalchemy_extension import db
from app.utils.validation_utils import get_length_validation_error_message
from tests.base_test_case import BaseTestCase
from tests.test_data import user1
from tests.test_utils import get_test_request_header
Expand Down Expand Up @@ -66,6 +72,37 @@ def test_update_username_not_taken(self):
self.assertEqual(expected_response, json.loads(actual_response.data))
self.assertEqual(user1_new_username, self.first_user.username)

def test_update_username_invalid_length(self):

self.first_user = UserModel(
name=user1['name'],
email=user1['email'],
username=user1['username'],
password=user1['password'],
terms_and_conditions_checked=user1['terms_and_conditions_checked']
)
self.first_user.is_email_verified = True

db.session.add(self.first_user)
db.session.commit()

field_name = 'username'
secure_random = SystemRandom()
random_generated_username = "".join(
secure_random.choice(ascii_lowercase) for x in range(USERNAME_MAX_LENGTH + 1))

auth_header = get_test_request_header(self.first_user.id)
expected_response = {"message": get_length_validation_error_message(field_name, USERNAME_MIN_LENGTH, USERNAME_MAX_LENGTH)}
actual_response = self.client.put('/user', follow_redirects=True,
headers=auth_header,
data=json.dumps(dict(username=random_generated_username)),
content_type='application/json')

self.assertEqual(400, actual_response.status_code)
self.assertEqual(expected_response, json.loads(actual_response.data))
self.assertNotEqual(random_generated_username, self.first_user.username)
self.assertEqual(user1['username'], self.first_user.username)


if __name__ == "__main__":
unittest.main()
2 changes: 1 addition & 1 deletion tests/users/test_dao_update_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def test_dao_update_user(self):

data = dict(
occupation='good_developer',
organization='good_org',
organization='good_org'
)
UserDAO.update_user_profile(self.admin_user.id, data)

Expand Down