4 Commits
Author SHA1 Message Date
Johan LEROY e8f22bf427 feat(backend): durcit la surface exposée
En-têtes de sécurité, CORS resserré, caviardage des journaux, `/metrics`
derrière un jeton facultatif, documentation fermée en préproduction, et
la sonde de disponibilité cesse de publier la version de TimescaleDB.

HSTS et CSP sont volontairement absents : l'application ignore si TLS
termine devant elle, et une CSP sur une API JSON ne protège presque rien.
Les deux appartiennent au terminateur TLS, celle qui compte protège la
page Angular.

`/metrics` est gardé par un jeton statique et non par un rôle : coupler
la supervision au modèle d'utilisateurs casserait la collecte à chaque
panne d'authentification, c'est-à-dire quand on en a le plus besoin. Le
contrôle principal reste le réseau.

Le caviardage est la troisième ligne de défense, pas la première. On ne
passe aucun secret au logger et aucun jeton dans une URL ; le filtre
rattrape ce que personne n'a relu, à commencer par l'écho SQL qui
publiait les empreintes Argon2 quand `debug` est actif.

Corrige un défaut que le test a révélé : `create_app(settings)` ne
pilotait que la construction, les dépendances continuaient de lire
`get_settings()` depuis l'environnement. Un test « en production » ne
testait donc pas la production, et `TESTING.md` promet le contraire.
2026-09-15 14:56:17 +02:00
Johan LEROY a8f59e6e76 feat(backend): ajoute les comptes applicatifs et l'amorçage du premier admin
Table `app_user`, son dépôt, et la commande `create-admin`. Le nom évite
`user`, mot réservé de PostgreSQL, et rappelle qu'il s'agit d'un compte
applicatif, par opposition au rôle PostgreSQL qui portera le
cantonnement des accès ETL et ML.

`credentials_changed_at` couvre à elle seule le changement de mot de
passe, le changement de rôle et la désactivation : tout jeton émis avant
cet instant sera refusé, sans attendre son expiration.

La configuration refuse désormais de démarrer sur cinq erreurs
silencieuses : secret trop court ou laissé à sa valeur d'exemple, `debug`
en production, joker CORS, origines vides hors local, et cookie
`SameSite=None` sans `Secure`. Les fixtures de test et les deux
`.env.example` suivent, sans quoi rien ne démarrerait.

Le mot de passe de l'admin ne transite jamais par `argv`, visible de tout
`ps` : il est saisi par `getpass` ou tiré au sort. Une révision Alembic
qui insérerait ce compte graverait son empreinte dans Git pour toujours.
2026-09-15 14:30:05 +02:00
Johan LEROY 53af7a76d8 feat(backend): pose les primitives de sécurité
Couche pure, sans FastAPI ni session : rôles ordonnés, `Principal`,
encodage et décodage des jetons d'accès, empreinte des jetons de
rafraîchissement, et hachage Argon2id poussé dans un fil borné.

Aucun de ces modules ne lit `get_settings()`, mis en cache par
`lru_cache` et donc contaminé entre tests : les paramètres arrivent par
`TokenPolicy` et par `build_hasher()`.

Argon2id est calibré à m=19456 KiB, t=2, p=1, soit 17 ms mesurés sur un
poste de développement.
2026-09-15 14:25:41 +02:00
Johan LEROY 3db4419bdf test(backend): calque l'arborescence des tests sur celle de app
Le README annonce deja tests/ comme miroir de app/, mais seul tests/api
existait. Les paquets core, db, services et repositories attendent le metier
a venir, pour que personne n'ait a choisir ou poser son premier test.
2026-09-15 09:58:30 +02:00