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,27 @@
|
||||
# Pourquoi : même séparation que `login_attempt` par rapport à `audit_log` : ce compteur est
|
||||
# piloté par l'attaquant (une campagne de demandes) et se purge, l'audit log est en ajout seul.
|
||||
# Piège : la tentative est enregistrée même quand l'email est inconnu, sinon le 429 apprendrait
|
||||
# qu'un compte existe.
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import BigInteger, DateTime, Identity, Index, String, func
|
||||
from sqlalchemy.dialects.postgresql import INET
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.db.base import Base
|
||||
|
||||
|
||||
class PasswordResetAttempt(Base):
|
||||
__tablename__ = "password_reset_attempt"
|
||||
__table_args__ = (
|
||||
Index("ix_password_reset_attempt_email_date", "email_tried", "occurred_at"),
|
||||
Index("ix_password_reset_attempt_ip_date", "client_ip", "occurred_at"),
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(BigInteger, Identity(always=True), primary_key=True)
|
||||
occurred_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), nullable=False, server_default=func.now()
|
||||
)
|
||||
email_tried: Mapped[str] = mapped_column(String(320), nullable=False)
|
||||
client_ip: Mapped[str | None] = mapped_column(INET, nullable=True)
|
||||
Reference in New Issue
Block a user