Files
ENI-projet-piscine/apps/backend/app/core/mailer.py
Johan LEROY 9161b74874 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
2026-09-17 10:53:58 +02:00

49 lines
1.6 KiB
Python

# Piège : l'URL de réinitialisation porte le jeton en clair. Ne jamais la journaliser :
# `send_password_reset_email()` ne logue que le destinataire, jamais `reset_url`.
from dataclasses import dataclass
from email.message import EmailMessage
import aiosmtplib
from app.core.logging import get_logger
logger = get_logger(__name__)
@dataclass(frozen=True, slots=True)
class SmtpConfig:
host: str
port: int
username: str | None
password: str | None
use_tls: bool
from_address: str
class Mailer:
def __init__(self, config: SmtpConfig) -> None:
self._config = config
async def send_password_reset_email(self, *, to: str, reset_url: str) -> None:
message = EmailMessage()
message["From"] = self._config.from_address
message["To"] = to
message["Subject"] = "Réinitialisation de votre mot de passe EnerVision"
message.set_content(
"Une réinitialisation de mot de passe a été demandée pour ce compte.\n\n"
f"Ouvrez ce lien dans les 15 minutes pour choisir un nouveau mot de passe : "
f"{reset_url}\n\n"
"Si vous n'êtes pas à l'origine de cette demande, ignorez cet email."
)
_, message_recu = await aiosmtplib.send(
message,
hostname=self._config.host,
port=self._config.port,
username=self._config.username,
password=self._config.password,
use_tls=self._config.use_tls,
)
logger.info("mailer.password_reset_sent to=%s smtp_response=%s", to, message_recu)