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.
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 | placeholder (à définir en prod) |
JWT_ALGORITHM / JWT_EXPIRE_MINUTES |
algo / durée du token | HS256 / 60 |
Authentification & rôles
POST /auth/login(formusername/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/healthest public. - Rôles :
Admin,Superviseur,Consultant. Les routes/admin/*exigentAdmin(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 (
Adminen écriture) :POST/PUT/DELETEsur/services,/categories,/contactset/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é, carTABLE_FINALré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).
Sécurité
- 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é dansRENDU/02_Dump_SQL/). - Journal d'audit alimenté à chaque login + action admin.
Tests
pip install pytest httpx && pytest -q
Curseur SQL simulé (aucune vraie BDD) : santé, auth (succès/échec), protection 401, RBAC 403, 404, validation des rôles par enum (422), refus de suppression d'un référentiel rattaché (409), désactivation logique d'un monitoring.
Docker & CI/CD
Dockerfile: image Python 3.12 +msodbcsql18.- CI Gitea Actions (
.gitea/workflows/build.yml) : build → pytest (dans l'image) → push versgit.nfteam.ovh/neckfire/datasentinel-api+ notification ntfy. Déclenché sur pushmain. - Déploiement, exploitation, sauvegarde : voir
RUNBOOK.mdet le stack d'hébergement (homelab/dev/datasentinel/). La base tourne sur un SQL Server partagé (dev-mssql), pas dédiée à l'app.