Antho e7b3944436 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.
2026-08-15 14:28:58 +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 placeholder (à définir en prod)
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).

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é dans RENDU/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 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%