Files
GGThedandClaude Opus 5 8d7de75e99 chore(ops): nommer le stockage du rate limiting au lieu de le subir
SEC-WEB-004. Limiter() ne nommait aucun stockage, donc Flask-Limiter
retombait sur memory://. Le choix n'avait jamais ete fait : c'etait
simplement ce qui arrivait.

Pour un seul processus Waitress, memory:// est la bonne reponse -- ce qui
est precisement pourquoi il fallait l'ecrire. Un deuxieme worker laisserait
passer deux fois chaque limite, en silence, avec une configuration qui a
l'air inchangee. RATELIMIT_STORAGE_URI rend la valeur lisible dans .env,
modifiable en une ligne le jour ou le deploiement gagne un processus, et le
demarrage journalise laquelle est active.

La part qui reste bloquee est nommee dans le code : un stockage partage ne
rend pas les limites solides tant qu'elles sont indexees sur une adresse IP
falsifiable, c'est-a-dire tant qu'OPS-002 / SEC-WEB-002 n'est pas tranche
avec le developpeur. C'est pour cela que celui-la est le prerequis et pas
celui-ci.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-11 18:35:27 -04:00

53 lines
1.6 KiB
Python

from flask_babel import Babel
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
from flask_login import LoginManager
from flask_sqlalchemy import SQLAlchemy
from flask_wtf.csrf import CSRFProtect
from werkzeug.security import check_password_hash, generate_password_hash
# Database and extension initialization
db = SQLAlchemy()
login_manager = LoginManager()
login_manager.login_view = 'auth.login'
login_manager.login_message_category = 'info'
csrf = CSRFProtect()
# Internationalisation. French is the primary language of the site; English
# stays available. See app/i18n.py for how a locale is chosen.
babel = Babel()
# Rate limiter for brute-force protection.
#
# No storage is named here on purpose: it comes from RATELIMIT_STORAGE_URI in
# app.config, which create_app fills from the environment and defaults to
# `memory://` (SEC-WEB-004). Naming it in both places is how the two drift.
limiter = Limiter(key_func=get_remote_address, default_limits=["200 per day", "50 per hour"])
def hash_password(password):
"""
Hash a plain text password using werkzeug's security functions.
Args:
password (str): The plain text password to hash.
Returns:
str: The hashed password string.
"""
return generate_password_hash(password)
def check_password(password_hash, password):
"""
Verify a password against its hash.
Args:
password_hash (str): The stored password hash.
password (str): The plain text password to verify.
Returns:
bool: True if the password matches the hash, False otherwise.
"""
return check_password_hash(password_hash, password)