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:
@@ -16,6 +16,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.core.config import Settings, get_settings
|
||||
from app.core.hashing import Argon2Hasher, build_hasher
|
||||
from app.core.mailer import Mailer, SmtpConfig
|
||||
from app.core.principal import Principal
|
||||
from app.core.roles import AccountKind, Role, has_at_least
|
||||
from app.core.security import TokenExpiredError, TokenInvalidError, TokenPolicy
|
||||
@@ -24,13 +25,15 @@ from app.db.session import get_session
|
||||
from app.repositories.alert import AlertRepository
|
||||
from app.repositories.audit_log import AuditLogRepository
|
||||
from app.repositories.login_attempt import LoginAttemptRepository
|
||||
from app.repositories.password_reset_attempt import PasswordResetAttemptRepository
|
||||
from app.repositories.password_reset_token import PasswordResetTokenRepository
|
||||
from app.repositories.reading import ReadingRepository
|
||||
from app.repositories.recommendation import RecommendationRepository
|
||||
from app.repositories.refresh_token import RefreshTokenRepository
|
||||
from app.repositories.site import SiteRepository
|
||||
from app.repositories.user import UserRepository
|
||||
from app.services.alert import AlertService
|
||||
from app.services.auth import AuthService, LoginPolicy
|
||||
from app.services.auth import AuthService, LoginPolicy, PasswordResetPolicy
|
||||
from app.services.recommendation import RecommendationService
|
||||
from app.services.sensor import SensorService
|
||||
from app.services.site import SiteService
|
||||
@@ -97,11 +100,27 @@ def get_client_ip(request: Request, settings: SettingsDep) -> str | None:
|
||||
return request.client.host if request.client else None
|
||||
|
||||
|
||||
def get_mailer(settings: SettingsDep) -> Mailer:
|
||||
return Mailer(
|
||||
SmtpConfig(
|
||||
host=settings.smtp_host,
|
||||
port=settings.smtp_port,
|
||||
username=settings.smtp_username,
|
||||
password=(
|
||||
settings.smtp_password.get_secret_value() if settings.smtp_password else None
|
||||
),
|
||||
use_tls=settings.smtp_use_tls,
|
||||
from_address=settings.smtp_from_address,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def get_auth_service(
|
||||
session: SessionDep,
|
||||
settings: SettingsDep,
|
||||
hasher: Annotated[Argon2Hasher, Depends(get_hasher)],
|
||||
token_policy: Annotated[TokenPolicy, Depends(get_token_policy)],
|
||||
mailer: Annotated[Mailer, Depends(get_mailer)],
|
||||
) -> AuthService:
|
||||
return AuthService(
|
||||
users=UserRepository(session),
|
||||
@@ -118,6 +137,16 @@ def get_auth_service(
|
||||
max_failures_per_identifier=settings.login_max_failures_per_identifier,
|
||||
),
|
||||
refresh_ttl=timedelta(seconds=settings.refresh_token_ttl_seconds),
|
||||
reset_tokens=PasswordResetTokenRepository(session),
|
||||
reset_attempts=PasswordResetAttemptRepository(session),
|
||||
reset_policy=PasswordResetPolicy(
|
||||
window_seconds=settings.password_reset_window_seconds,
|
||||
max_requests_per_identifier=settings.password_reset_max_requests_per_identifier,
|
||||
max_requests_per_ip=settings.password_reset_max_requests_per_ip,
|
||||
token_ttl=timedelta(seconds=settings.password_reset_ttl_seconds),
|
||||
frontend_reset_url=settings.frontend_reset_password_url,
|
||||
),
|
||||
mailer=mailer,
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user