Trois domaines n'étaient pas couverts : l'historique et les courbes
d'évolution, les contrôles de sécurité, et le CRUD des référentiels.
- test_historique.py : bornes de dates passées en paramètres, rejet d'un
format de date invalide, agrégation des séries par monitoring et par
service à partir de lignes à plat.
- test_securite.py : protection 401 vérifiée route par route, RBAC 403 pour
Superviseur et Consultant sur chaque écriture, rejet d'un jeton signé avec
une autre clé, en-têtes de sécurité, rate-limit 429 à la 6e tentative,
refus d'un compte désactivé, absence du hachage dans la réponse de login,
anonymisation RGPD sans suppression de ligne.
- test_referentiels.py : CRUD catégories et contacts, refus 409 sur
rattachement, 404 sur enregistrement inexistant, modification partielle
limitée aux champs fournis, réactivation d'un monitoring, plafonnement de
limit, terme de recherche transmis en paramètre et non concaténé.
Corrige au passage un défaut d'isolation révélé par la suite complète : le
limiteur de débit est un état global, les tests de connexion se
comptabilisaient entre eux et un test échouait selon l'ordre d'exécution
tout en passant fichier par fichier. La fixture limiteur_vierge le remet à
zéro, et un test dédié vérifie désormais explicitement le seuil.
Suite vérifiée stable sur 3 exécutions consécutives et fichier par fichier.
JWT_SECRET retombait sur "data-sentinel-secret-change-in-prod", une valeur
lisible par quiconque a accès au code : un oubli de configuration suffisait
à permettre de forger un jeton d'administrateur.
Sans JWT_SECRET, une clé aléatoire est désormais tirée au démarrage, avec un
avertissement explicite. Le démarrage n'est volontairement pas bloqué : un
oubli de variable d'environnement ne doit pas transformer une configuration
incomplète en indisponibilité totale du service. Contrepartie assumée et
documentée : les sessions ne survivent pas à un redémarrage tant que la
variable n'est pas définie.
Vérifié : la production signe déjà avec un secret propre, ce correctif ne
change donc rien à son fonctionnement.
Ajoute par ailleurs APP_BUILD, injecté par la CI depuis le SHA du commit et
exposé par GET /health (et /version.json côté front). Jusqu'ici, rien ne
permettait de savoir quelle version tournait réellement : un déploiement
non appliqué était indiscernable d'un déploiement réussi.
- fix(config): rend le serveur SQL configurable en développement
- refactor(api): découpe main.py en routeurs par domaine
- feat(api): CRUD des référentiels réservé aux administrateurs
L'écran d'administration du front proposait des boutons Modifier et
Désactiver qui n'appelaient rien (console.log côté client), faute
d'endpoints correspondants. Ajoute POST/PUT/DELETE sur /services,
/categories, /contacts et /monitorings, tous protégés par require_admin.
Deux garde-fous métier :
- la suppression d'un service ou d'une catégorie est refusée (409) tant
que des monitorings ou contacts y sont rattachés, avec le décompte
dans le message, plutôt que de laisser remonter une violation de clé
étrangère ;
- un monitoring est désactivé (actif = 0) et jamais supprimé, car
TABLE_FINAL référence son identifiant et l'historique doit rester
consultable.
Le rôle utilisateur est désormais typé par l'enum UserRole : Pydantic
le valide seul (422), ce qui supprime les deux contrôles manuels
dupliqués dans create_user et update_user.
Tests : 8 -> 16. Couvre la validation par enum, le refus 409 sur
rattachement, le 403 pour un non-administrateur et la désactivation
logique du monitoring.
main.py comptait 829 lignes et regroupait 9 domaines fonctionnels.
Il ne fait plus que l'assemblage de l'application (configuration,
middlewares, montage des routeurs), soit 115 lignes.
- routers/ : un module par domaine, chacun déclarant sa propre
dépendance d'authentification
- domain.py : enums UserRole et AuditAction, mapping MONITO_TABLES ;
les rôles étaient jusqu'ici répétés en dur à deux endroits
- helpers.py : conversion des lignes pyodbc, écriture du journal
- rate_limit.py : limiteur partagé, isolé pour éviter un import
circulaire entre main.py et le routeur d'authentification
Les codes HTTP littéraux (404, 401, 400, 201) passent aux constantes
fastapi.status, comme le faisait déjà auth.py. Les actions du journal
d'audit passent en paramètre SQL au lieu d'être concaténées.
Aucune route modifiée : la comparaison des specs OpenAPI avant/après
confirme que les 26 URL existantes sont identiques.
conftest patchait main.get_cursor ; chaque routeur important désormais
get_cursor dans son propre espace de noms, la fixture remplace le nom
dans tous les modules concernés.
Le repli de développement pointait en dur sur LaptopCA\SQLEXPRESS,
ce qui rendait l'API inutilisable sur toute autre machine sans éditer
le code. Le serveur vient désormais de DB_SERVER (défaut localhost),
et DB_TRUSTED_CONNECTION permet de forcer l'authentification Windows.
DB_PORT peut rester vide : une instance nommée (MonPoste\SQLEXPRESS)
se joint sans port explicite.
Ajoute sql/create_admin_jure.sql, script idempotent créant/réinitialisant
le compte de démonstration destiné aux personnes qui consultent le site
(rôle Admin). Aucune instruction USE : la base cible vient du paramètre
-d de la connexion, avec un garde-fou qui arrête le script si les tables
attendues sont absentes.
Copie aussi sql/data_sentinel_auth.sql (déjà présent dans le dump livré
sous RENDU/02_Dump_SQL/) : le README de ce dépôt le référençait déjà,
mais le dossier sql/ n'existait pas encore ici.
Documente le compte dans le tableau des comptes de démo du README.
VUE_CONSO ne rattache pas tous les services/catégories des référentiels
SERVICE et CATEGORIE à un monitoring actif (ex. "Business Intelligence").
Les proposer tels quels dans les filtres du dashboard menait à un écran
vide. Le nouvel endpoint renvoie un SELECT DISTINCT service, categorie
sur VUE_CONSO, avec les combinaisons pour permettre un filtrage en
cascade côté frontend.
Ajoute un test dédié et régénère docs/openapi.json.