API & Client - allow only alphanumeric characters and '_' in username
This commit is contained in:
parent
66cd3c9655
commit
94a6f2fa9c
@ -1,9 +1,17 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from flask import Flask
|
||||
|
||||
from fittrackee.users.exceptions import UserNotFoundException
|
||||
from fittrackee.users.models import User
|
||||
from fittrackee.users.utils import set_admin_rights
|
||||
from fittrackee.users.utils import (
|
||||
check_passwords,
|
||||
check_username,
|
||||
is_valid_email,
|
||||
register_controls,
|
||||
set_admin_rights,
|
||||
)
|
||||
|
||||
from ..utils import random_string
|
||||
|
||||
@ -28,3 +36,179 @@ class TestSetAdminRights:
|
||||
set_admin_rights(user_1_admin.username)
|
||||
|
||||
assert user_1_admin.admin is True
|
||||
|
||||
|
||||
class TestIsValidEmail:
|
||||
@pytest.mark.parametrize(
|
||||
('input_email',),
|
||||
[
|
||||
('',),
|
||||
('foo',),
|
||||
('foo@',),
|
||||
('@foo.fr',),
|
||||
('foo@foo',),
|
||||
('.',),
|
||||
('./',),
|
||||
],
|
||||
)
|
||||
def test_it_returns_false_if_email_is_invalid(
|
||||
self, input_email: str
|
||||
) -> None:
|
||||
assert is_valid_email(input_email) is False
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
('input_email',),
|
||||
[
|
||||
('admin@example.com',),
|
||||
('admin@test.example.com',),
|
||||
('admin.site@test.example.com',),
|
||||
('admin-site@test-example.com',),
|
||||
],
|
||||
)
|
||||
def test_it_returns_true_if_email_is_valid(self, input_email: str) -> None:
|
||||
assert is_valid_email(input_email) is True
|
||||
|
||||
|
||||
class TestCheckPasswords:
|
||||
def test_it_returns_error_message_string_if_passwords_do_not_match(
|
||||
self,
|
||||
) -> None:
|
||||
assert check_passwords('password', 'pasword') == (
|
||||
'password: password and password confirmation do not match\n'
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
('input_password_length',),
|
||||
[
|
||||
(0,),
|
||||
(3,),
|
||||
(7,),
|
||||
],
|
||||
)
|
||||
def test_it_returns_error_message_string_if_password_length_is_below_8_characters( # noqa
|
||||
self, input_password_length: int
|
||||
) -> None:
|
||||
password = random_string(input_password_length)
|
||||
assert check_passwords(password, password) == (
|
||||
'password: 8 characters required\n'
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
('input_password_length',),
|
||||
[
|
||||
(8,),
|
||||
(10,),
|
||||
],
|
||||
)
|
||||
def test_it_returns_empty_string_when_password_length_exceeds_7_characters(
|
||||
self, input_password_length: int
|
||||
) -> None:
|
||||
password = random_string(input_password_length)
|
||||
assert check_passwords(password, password) == ''
|
||||
|
||||
def test_it_returns_multiple_errors(self) -> None:
|
||||
password = random_string(3)
|
||||
password_conf = random_string(8)
|
||||
assert check_passwords(password, password_conf) == (
|
||||
'password: password and password confirmation do not match\n'
|
||||
'password: 8 characters required\n'
|
||||
)
|
||||
|
||||
|
||||
class TestIsUsernameValid:
|
||||
@pytest.mark.parametrize(
|
||||
('input_username_length',),
|
||||
[
|
||||
(2,),
|
||||
(13,),
|
||||
],
|
||||
)
|
||||
def test_it_returns_error_message_when_username_length_is_invalid(
|
||||
self, input_username_length: int
|
||||
) -> None:
|
||||
assert (
|
||||
check_username(
|
||||
username=random_string(input_username_length),
|
||||
)
|
||||
== 'username: 3 to 12 characters required\n'
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
('input_invalid_character',),
|
||||
[
|
||||
('.',),
|
||||
('/',),
|
||||
('$',),
|
||||
],
|
||||
)
|
||||
def test_it_returns_error_message_when_username_has_invalid_character(
|
||||
self, input_invalid_character: str
|
||||
) -> None:
|
||||
username = random_string() + input_invalid_character
|
||||
assert check_username(username=username) == (
|
||||
'username: only alphanumeric characters and the '
|
||||
'underscore character "_" allowed\n'
|
||||
)
|
||||
|
||||
def test_it_returns_empty_string_when_username_is_valid(self) -> None:
|
||||
assert check_username(username=random_string()) == ''
|
||||
|
||||
def test_it_returns_multiple_errors(self) -> None:
|
||||
username = random_string(1) + '.'
|
||||
assert check_username(username=username) == (
|
||||
'username: 3 to 12 characters required\n'
|
||||
'username: only alphanumeric characters and the underscore '
|
||||
'character "_" allowed\n'
|
||||
)
|
||||
|
||||
|
||||
class TestRegisterControls:
|
||||
module_path = 'fittrackee.users.utils.'
|
||||
valid_username = random_string()
|
||||
valid_email = f'{random_string()}@example.com'
|
||||
valid_password = random_string()
|
||||
|
||||
def test_it_calls_all_validators(self) -> None:
|
||||
with patch(
|
||||
self.module_path + 'check_passwords'
|
||||
) as check_passwords_mock, patch(
|
||||
self.module_path + 'check_username'
|
||||
) as check_username_mock, patch(
|
||||
self.module_path + 'is_valid_email'
|
||||
) as is_valid_email_mock:
|
||||
register_controls(
|
||||
self.valid_username,
|
||||
self.valid_email,
|
||||
self.valid_password,
|
||||
self.valid_password,
|
||||
)
|
||||
|
||||
check_passwords_mock.assert_called_once_with(
|
||||
self.valid_password, self.valid_password
|
||||
)
|
||||
check_username_mock.assert_called_once_with(self.valid_username)
|
||||
is_valid_email_mock.assert_called_once_with(self.valid_email)
|
||||
|
||||
def test_it_returns_empty_string_when_inputs_are_valid(self) -> None:
|
||||
assert (
|
||||
register_controls(
|
||||
self.valid_username,
|
||||
self.valid_email,
|
||||
self.valid_password,
|
||||
self.valid_password,
|
||||
)
|
||||
== ''
|
||||
)
|
||||
|
||||
def test_it_returns_multiple_errors_when_inputs_are_invalid(self) -> None:
|
||||
invalid_username = random_string(2)
|
||||
assert register_controls(
|
||||
username=invalid_username,
|
||||
email=invalid_username,
|
||||
password=random_string(8),
|
||||
password_conf=random_string(8),
|
||||
) == (
|
||||
'username: 3 to 12 characters required\n'
|
||||
'email: valid email must be provided\n'
|
||||
'password: password and password confirmation do not match\n'
|
||||
)
|
||||
|
@ -37,6 +37,21 @@ def check_passwords(password: str, password_conf: str) -> str:
|
||||
return ret
|
||||
|
||||
|
||||
def check_username(username: str) -> str:
|
||||
"""
|
||||
Return if username is valid
|
||||
"""
|
||||
ret = ''
|
||||
if not 2 < len(username) < 13:
|
||||
ret += 'username: 3 to 12 characters required\n'
|
||||
if not re.match(r'^[a-zA-Z0-9_]+$', username):
|
||||
ret += (
|
||||
'username: only alphanumeric characters and the '
|
||||
'underscore character "_" allowed\n'
|
||||
)
|
||||
return ret
|
||||
|
||||
|
||||
def register_controls(
|
||||
username: str, email: str, password: str, password_conf: str
|
||||
) -> str:
|
||||
@ -45,9 +60,7 @@ def register_controls(
|
||||
|
||||
If not, it returns not empty string
|
||||
"""
|
||||
ret = ''
|
||||
if not 2 < len(username) < 13:
|
||||
ret += 'username: 3 to 12 characters required\n'
|
||||
ret = check_username(username)
|
||||
if not is_valid_email(email):
|
||||
ret += 'email: valid email must be provided\n'
|
||||
ret += check_passwords(password, password_conf)
|
||||
|
@ -21,6 +21,9 @@
|
||||
id="username"
|
||||
:disabled="registration_disabled"
|
||||
required
|
||||
pattern="[a-zA-Z0-9_]+"
|
||||
minlength="3"
|
||||
maxlength="12"
|
||||
@invalid="invalidateForm"
|
||||
v-model="formData.username"
|
||||
:placeholder="$t('user.USERNAME')"
|
||||
@ -46,6 +49,7 @@
|
||||
required
|
||||
@invalid="invalidateForm"
|
||||
type="password"
|
||||
minlength="8"
|
||||
v-model="formData.password"
|
||||
:placeholder="
|
||||
action === 'reset'
|
||||
@ -58,6 +62,7 @@
|
||||
id="confirm-password"
|
||||
:disabled="registration_disabled"
|
||||
type="password"
|
||||
minlength="8"
|
||||
required
|
||||
@invalid="invalidateForm"
|
||||
v-model="formData.password_conf"
|
||||
|
@ -17,13 +17,11 @@
|
||||
"no selected file": "No selected file.",
|
||||
"password: password and password confirmation do not match": "Password: password and password confirmation don't match.",
|
||||
"provide a valid auth token": "Provide a valid auth token.",
|
||||
"password: 8 characters required": "Password: 8 characters required.",
|
||||
"sorry, that user already exists": "Sorry, that user already exists.",
|
||||
"sport does not exist": "Sport does not exist.",
|
||||
"signature expired, please log in again": "Signature expired. Please log in again.",
|
||||
"successfully registered": "Successfully registered.",
|
||||
"user does not exist": "User does not exist.",
|
||||
"username: 3 to 12 characters required": "Username: 3 to 12 characters required.",
|
||||
"you can not delete your account, no other user has admin rights": "You can not delete your account, no other user has admin rights.",
|
||||
"you do not have permissions": "You do not have permissions."
|
||||
},
|
||||
|
@ -17,13 +17,11 @@
|
||||
"Network Error": "Erreur Réseau.",
|
||||
"password: password and password confirmation do not match": "Mot de passe : les mots de passe saisis sont différents.",
|
||||
"provide a valid auth token": "Merci de fournir un jeton de connexion valide.",
|
||||
"password: 8 characters required": "Mot de passe : 8 caractères minimum.",
|
||||
"sport does not exist": "Ce sport n'existe pas.",
|
||||
"signature expired, please log in again": "Signature expirée. Merci de vous reconnecter.",
|
||||
"sorry, that user already exists": "Désolé, cet utilisateur existe déjà.",
|
||||
"successfully registered": "Inscription validée.",
|
||||
"user does not exist": "L'utilisateur n'existe pas",
|
||||
"username: 3 to 12 characters required": "Nom d'utilisateur : 3 à 12 caractères requis.",
|
||||
"you can not delete your account, no other user has admin rights": "Vous ne pouvez pas supprimer votre compte, aucun autre utilisateur n'a des droits d'administration.",
|
||||
"you do not have permissions": "Vous n'avez pas les permissions nécessaires."
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user