Antho 138bc3c87c test(api): étend la couverture de 16 à 82 tests
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.
2026-08-15 15:01:42 +02:00
2026-05-20 10:30:04 +02:00
2026-05-20 10:35:05 +02:00

Api-DataSentinel

API FastAPI de monitoring de la qualité des données — Data Sentinel (XEFI). Backend en lecture seule sur SQL Server, sécurisé par authentification JWT.

Stack

  • Python 3.12 · FastAPI 0.136 · uvicorn
  • SQL Server 2022 via pyodbc (ODBC Driver 18)
  • Auth : JWT (python-jose) + bcrypt (passlib), rate-limit (slowapi)

Lancement en local (dev)

python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
python -m uvicorn main:app --reload --port 8000
  • Swagger : http://127.0.0.1:8000/docs · Santé : http://127.0.0.1:8000/health

Sans variables d'environnement, l'API se connecte en authentification Windows sur localhost. Pour une instance nommée, définir DB_SERVER (ex. DB_SERVER=MonPoste\SQLEXPRESS, sans DB_PORT).

Configuration (variables d'environnement)

Variable Rôle Défaut
DB_SERVER hôte SQL Server (ou hôte\instance) localhost
DB_PORT / DB_NAME port / base — laisser DB_PORT vide pour une instance nommée — / DataSentinel
DB_USER / DB_PASSWORD compte applicatif ; sa présence active l'authentification SQL — (sinon auth Windows)
DB_TRUSTED_CONNECTION force l'authentification Windows même si DB_USER est défini
DB_DRIVER pilote ODBC ODBC Driver 18 for SQL Server
CORS_ORIGINS origines autorisées (séparées par ,) localhost:5173,localhost:3000
JWT_SECRET clé de signature JWT — obligatoire en déploiement clé aléatoire régénérée à chaque démarrage
APP_BUILD SHA du commit construit, injecté par la CI et exposé par /health local
JWT_ALGORITHM / JWT_EXPIRE_MINUTES algo / durée du token HS256 / 60

Authentification & rôles

  • POST /auth/login (form username/password) → {access_token, token_type, user}. Rate-limité 5/min.
  • GET /auth/me → profil courant.
  • Tous les endpoints de données exigent Authorization: Bearer <token> (401 sinon). Seul /health est public.
  • Rôles : Admin, Superviseur, Consultant. Les routes /admin/* exigent Admin (403 sinon).

Comptes de démo

Identifiant Mot de passe Rôle Usage
Juré (JURE@NEXA.com) 123456 Admin Compte de visite / évaluation — à communiquer aux personnes qui consultent le site
admin Admin2026! Admin Compte d'administration technique
superviseur Super2026! Superviseur Illustration du RBAC (pas d'accès /admin/*)
consultant Conseil2026! Consultant Illustration du RBAC (lecture seule)

Ces comptes sont créés par sql/data_sentinel_auth.sql. Pour (re)créer le seul compte Juré sur une base déjà déployée, sans rejouer tout le script d'auth :

sqlcmd -S <serveur> -d DataSentinel -U <user> -P <mdp> -i sql/create_admin_jure.sql

Les mots de passe ci-dessus sont des identifiants de démonstration : ils sont stockés hachés (bcrypt, coût 12) et doivent être régénérés avant toute mise en production réelle (POST /admin/users/{id}/reset-password).

Organisation du code

main.py ne fait que l'assemblage (configuration, middlewares, montage des routeurs). Chaque domaine fonctionnel vit dans routers/ :

Module Responsabilité
routers/authentification.py /auth/login (rate-limité), /auth/me
routers/referentiels.py services, catégories, contacts — lecture + CRUD Admin
routers/monitorings.py nomenclature + données détaillées par table dédiée
routers/dashboard.py VUE_CONSO : vue consolidée, filtres, KPI
routers/historique.py snapshots journaliers (TABLE_FINAL)
routers/evolution.py VUE_TABLE_FINAL_CONSO : courbes global / monitoring / service
routers/admin.py comptes utilisateurs et journal d'audit
routers/rgpd.py portabilité et droit à l'oubli
domain.py enums (UserRole, AuditAction) et mapping MONITO_TABLES
helpers.py conversion des lignes pyodbc, écriture du journal d'audit

Endpoints (résumé)

  • Données (protégés) : /categories, /services, /contacts, /monitorings[...], /dashboard[...], /historique[...], /evolution/*.
  • Référentiels (Admin en écriture) : POST/PUT/DELETE sur /services, /categories, /contacts et /monitorings. La suppression est refusée (409) tant que des enregistrements y sont rattachés ; un monitoring est désactivé (actif = 0) et jamais supprimé, car TABLE_FINAL référence son identifiant.
  • Admin (Admin) : GET/POST /admin/users, PUT/DELETE /admin/users/{id}, POST /admin/users/{id}/reset-password, GET /admin/journal.
  • RGPD : GET /me/data-export (portabilité), DELETE /me (droit à l'oubli + anonymisation).

Spécification complète : GET /openapi.json (export dans docs/openapi.json).

Vérifier qu'un déploiement a pris

GET /health expose le SHA du commit dont l'image a été construite :

curl -s https://datasentinel-api.nfteam.ovh/health
# {"api":"ok","database":"ok","version":"1.0.0","build":"cb84e20e1f2a",...}

Comparer build au dernier commit poussé sur main. S'ils diffèrent, le conteneur tourne encore une ancienne image : docker compose pull puis docker compose up -d (un up -d seul ne retélécharge rien). Le front expose la même information sur /version.json.

Sécurité

  • JWT_SECRET doit être défini en déploiement. À défaut, l'API démarre quand même mais tire une clé aléatoire à chaque lancement (sessions perdues au redémarrage) : aucun secret de repli n'est écrit dans le dépôt, un secret public permettrait de forger un jeton d'administrateur.
  • En-têtes : X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Strict-Transport-Security.
  • CORS restreint aux origines CORS_ORIGINS, tous verbes + credentials.
  • Requêtes SQL paramétrées (noms de tables/colonnes whitelistés) ; le compte applicatif n'est pas sa. Tables d'auth : [USER] + JOURNAL_AUDIT (sql/data_sentinel_auth.sql, identique au dump livré dans RENDU/02_Dump_SQL/).
  • Journal d'audit alimenté à chaque login + action admin.

Tests

pip install pytest httpx && pytest -q

82 tests, curseur SQL simulé (aucune vraie base requise) :

Fichier Couverture
test_endpoints.py santé, référentiels, authentification, filtres du dashboard, rôles par enum
test_historique.py bornes de dates, validation du format, agrégation des courbes par monitoring et par service
test_securite.py protection 401 de chaque route, RBAC 403, jeton forgé rejeté, en-têtes de sécurité, rate-limit 429, compte désactivé, droits RGPD
test_referentiels.py CRUD complet, refus 409 sur rattachement, modification partielle, paramétrage des requêtes de recherche

Le compteur anti brute-force est remis à zéro entre les tests (fixture limiteur_vierge) : c'est un état global, et sans cela les tests de connexion se comptabilisent entre eux et échouent selon l'ordre d'exécution.

Docker & CI/CD

  • Dockerfile : image Python 3.12 + msodbcsql18.
  • CI Gitea Actions (.gitea/workflows/build.yml) : build → pytest (dans l'image) → push vers git.nfteam.ovh/neckfire/datasentinel-api + notification ntfy. Déclenché sur push main.
  • Déploiement, exploitation, sauvegarde : voir RUNBOOK.md et le stack d'hébergement (homelab/dev/datasentinel/). La base tourne sur un SQL Server partagé (dev-mssql), pas dédiée à l'app.
S
Description
No description provided
Readme
171 KiB
Languages
Python 89.2%
TSQL 9.5%
Dockerfile 1.3%