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:
Johan LEROY
2026-09-15 15:05:28 +02:00
parent c60081a5ac
commit 3b7383697e
13 changed files with 883 additions and 62 deletions
+46 -10
View File
@@ -57,13 +57,21 @@ independants de l'environnement.
```
app/
├── api/
│ ├── deps.py Dependances FastAPI partagees (session, settings)
│ ├── deps.py Dépendances partagées : session, settings, principal, gardes de rôle
│ ├── errors.py Gestionnaires 422 et 500
│ ├── middleware.py En-têtes de sécurité
│ ├── security.py Garde du point /metrics
│ └── v1/
│ ├── router.py Agregation des routes de la version 1
│ └── endpoints/ Un module par ressource exposee
│ ├── router.py Agrégation des routes de la version 1
│ └── endpoints/ Un module par ressource exposée
├── core/
│ ├── config.py Settings Pydantic, source unique de configuration
│ └── logging.py Journalisation console en local, JSON en production
│ ├── cookies.py Attributs du cookie de rafraîchissement
│ ├── hashing.py Argon2id, poussé dans un fil sous limiteur
│ ├── logging.py Journalisation console en local, JSON en production
│ ├── principal.py L'identité que voit le code métier
│ ├── roles.py Rôles ordonnés
│ └── security.py Encodage et décodage des jetons d'accès
├── db/
│ ├── base.py Base declarative SQLAlchemy
│ └── session.py Engine et sessions asynchrones
@@ -71,6 +79,7 @@ app/
├── schemas/ Modeles Pydantic d'entree et de sortie
├── repositories/ Acces aux donnees, une classe par agregat
├── services/ Regles metier, orchestrent les repositories
├── cli.py Commandes hors HTTP, dont l'amorcage du premier admin
└── main.py Factory applicative
tests/ Miroir de app/
alembic/ Migrations du schema applicatif
@@ -81,12 +90,39 @@ Le sens de dependance est unique : `endpoints` vers `services` vers `repositorie
## Routes
| Route | Role |
|------------------------|-------------------------------------------------|
| `/api/v1/health/live` | Sonde de vivacite, aucune dependance externe |
| `/api/v1/health/ready` | Sonde de disponibilite, verifie la base et TimescaleDB |
| `/metrics` | Metriques au format Prometheus |
| `/docs`, `/openapi.json` | Documentation, desactivee quand `APP_ENV=prod` |
| Route | Rôle | Accès |
|---|---|---|
| `/api/v1/health/live` | Sonde de vivacité, aucune dépendance externe | public |
| `/api/v1/health/ready` | Sonde de disponibilité, vérifie la base et TimescaleDB | public |
| `/api/v1/auth/login` | Ouvre une session | public |
| `/api/v1/auth/refresh` | Fait tourner la session | cookie |
| `/api/v1/auth/logout` | Ferme la session courante | cookie, idempotente |
| `/api/v1/auth/logout-all` | Ferme toutes les sessions du compte | jeton |
| `/api/v1/auth/password` | Change son propre mot de passe | jeton |
| `/api/v1/auth/me` | Décrit le compte connecté | jeton |
| `/api/v1/users` | Liste et crée des comptes | `admin` |
| `/api/v1/users/{id}` | Change le rôle ou l'activation | `admin` |
| `/api/v1/users/{id}/password-reset` | Réinitialise et ferme les sessions | `admin` |
| `/metrics` | Métriques au format Prometheus | jeton si `APP_METRICS_TOKEN` |
| `/docs`, `/openapi.json` | Documentation, fermée en `staging` et `prod` | public sinon |
Le contrat détaillé pour le frontend est dans
[`docs/architecture/31-contrat-authentification.md`](../../docs/architecture/31-contrat-authentification.md).
## Premier administrateur
Aucun compte n'existe après les migrations. Il s'en crée un en ligne de commande :
```bash
make bootstrap-admin EMAIL=prenom.nom@enervision.fr # mot de passe saisi au clavier
# ou, depuis apps/backend :
uv run python -m app.cli create-admin --email prenom.nom@enervision.fr --generate
```
Le compte est créé avec `must_change_password`, donc la première connexion ne donne accès qu'à
`/auth/me` et `/auth/password` jusqu'au changement. Le mot de passe ne transite jamais par
`argv`, visible de tout `ps`, et aucune révision Alembic n'insère de compte : son empreinte
resterait dans Git pour toujours.
## Migrations