docs: acte les décisions d'authentification et met à jour les vues
Trois ADR : le jeton d'accès et le rafraîchissement opaque, le RBAC avec relecture du compte à chaque requête, et le journal d'audit en ajout seul. Chacun porte ses alternatives écartées et son critère de bascule, notamment celui vers OIDC. `31-contrat-authentification.md` est destiné au frontend : endpoints, codes d'erreur à traiter, et les quatre règles qui comptent. La troisième, un seul rafraîchissement en vol, est une exigence et non une optimisation : cinq rotations concurrentes seraient lues comme un rejeu et révoqueraient la session à chaque chargement de page. `owasp-traceabilite.md` remplace la revendication « couverture OWASP Top 10 et API Top 10 » de la NFR4, qui n'a pas de réponse honnête sur vingt items en deux semaines. Un contrôle par ligne, l'item adressé, et une section qui dit ce qui reste ouvert : portée par site, bornage des lectures de séries, transport, et la consommation de l'API Mock. Les vues 00, 20 et 40 suivent, comme l'impose leur propre règle de maintenance. La question ouverte « quel mécanisme d'authentification » est fermée ; trois autres la remplacent, dont la portée par site.
This commit is contained in:
@@ -141,3 +141,35 @@ make check # lint + typage + suite unitaire
|
||||
uv run pytest tests/api/test_health.py # un seul fichier
|
||||
uv run pytest -k readiness # par motif de nom
|
||||
```
|
||||
|
||||
## Trois fichiers à connaître avant de toucher à l'authentification
|
||||
|
||||
`tests/api/test_route_protection.py` interroge réellement chaque route sans identifiant et
|
||||
échoue si l'une d'elles répond autre chose qu'un 401 ou un 403. Il n'inspecte pas l'arbre de
|
||||
dépendances : celui-ci n'est accessible que par l'API privée de FastAPI, et surtout une route
|
||||
peut porter la bonne dépendance tout en répondant quand même. **Rendre une route publique impose
|
||||
donc de modifier la liste `ROUTES_PUBLIQUES` de ce fichier**, ce qui apparaît en clair dans la
|
||||
diff d'une pull request.
|
||||
|
||||
`tests/services/test_auth.py` donne au faux hacheur un **compteur d'appels**. C'est ce qui rend
|
||||
possibles les deux assertions qui prouvent la conception, et qu'aucune autre forme de test
|
||||
n'atteint :
|
||||
|
||||
- adresse inconnue → le compteur vaut 1, donc le haché leurre a bien été vérifié et il n'y a pas
|
||||
d'oracle temporel ;
|
||||
- limite de débit atteinte → le compteur vaut 0, donc la limite est évaluée avant Argon2.
|
||||
|
||||
`tests/api/test_parcours_authentification.py` joue six parcours complets contre la vraie base,
|
||||
sous le marqueur `integration`, sans serveur ni port ouvert. C'est là que se démontrent
|
||||
l'atomicité de la rotation, la mort de la famille au rejeu d'un cookie déjà tourné, et la
|
||||
révocation immédiate d'un compte désactivé.
|
||||
|
||||
## Deux pièges d'écriture de test
|
||||
|
||||
**Lire les attributs avant le `rollback`.** Un `session.rollback()` périme les attributs chargés,
|
||||
et les relire déclenche une entrée-sortie hors du contexte greenlet, donc un `MissingGreenlet`.
|
||||
On capture la valeur dans une variable locale avant d'annuler.
|
||||
|
||||
**`audit_log` ne se nettoie pas.** La table est en ajout seul, garanti par déclencheur : un test
|
||||
ne peut pas effacer ce qu'il y écrit, et les lignes d'une exécution précédente sont encore là.
|
||||
Chaque test filtre donc sur son propre `target_id` plutôt que de supposer une table vide.
|
||||
|
||||
Reference in New Issue
Block a user