feat(auth): politique de complexite du mot de passe et flux de reinitialisation
Remplace la regle de longueur seule (12 caracteres) par une exigence de composition (8 caracteres minimum, majuscule, minuscule, chiffre, caractere special), non documentee dans les exigences officielles du projet, par une regle explicite partagee entre le backend (validateur Pydantic) et le frontend. Ajoute un flux "mot de passe oublie" en libre-service, absent jusqu'ici : jeton a usage unique hache en base (meme principe que les refresh tokens), expirant a 15 minutes, envoye par email via un service SMTP (aiosmtplib, Mailpit en dev), avec limitation de debit dediee et reponse generique pour eviter l'enumeration des comptes. Closes #87
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from app.schemas.auth import PasswordChangeRequest, valide_complexite
|
||||
|
||||
MOT_DE_PASSE_VALIDE = "Un-mot-de-passe1!"
|
||||
|
||||
|
||||
def test_password_change_request_accepts_a_password_covering_the_four_classes() -> None:
|
||||
requete = PasswordChangeRequest(
|
||||
current_password="peu-importe", new_password=MOT_DE_PASSE_VALIDE
|
||||
)
|
||||
|
||||
assert requete.new_password == MOT_DE_PASSE_VALIDE
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"new_password",
|
||||
[
|
||||
"un-mot-de-passe1!",
|
||||
"UN-MOT-DE-PASSE1!",
|
||||
"Un-mot-de-passe!",
|
||||
"Un mot de passe 1",
|
||||
],
|
||||
ids=["sans_majuscule", "sans_minuscule", "sans_chiffre", "sans_caractere_special"],
|
||||
)
|
||||
def test_password_change_request_rejects_a_password_missing_a_character_class(
|
||||
new_password: str,
|
||||
) -> None:
|
||||
with pytest.raises(ValidationError):
|
||||
PasswordChangeRequest(current_password="peu-importe", new_password=new_password)
|
||||
|
||||
|
||||
def test_password_change_request_rejects_a_password_below_the_minimum_length() -> None:
|
||||
with pytest.raises(ValidationError):
|
||||
PasswordChangeRequest(current_password="peu-importe", new_password="Ab1!")
|
||||
|
||||
|
||||
def test_valide_complexite_names_every_missing_class_in_the_error() -> None:
|
||||
with pytest.raises(ValueError, match=r"majuscule.*chiffre|chiffre.*majuscule"):
|
||||
valide_complexite("minuscules-seulement")
|
||||
Reference in New Issue
Block a user