Aller au contenu

Conception — Workflow de validation des données + RBAC par commune

Contexte et besoin exprimé

Chaque projet ESM est exécuté dans plusieurs communes. Les agents de terrain (PSYCHOLOGUE, ASSISTANT_SOCIAL) exécutent les activités et saisissent les données opérationnelles. Modèle de gouvernance des données demandé :

  • Agent de terrain : responsable des données qu'il saisit, peut les modifier à tout moment tant qu'elles ne sont pas validées. Voit toutes les données de ses collègues de la même commune (et leur cumul/agrégation), mais ne peut modifier que les siennes.
  • MEAL communal : voit tout ce qui a été saisi dans sa commune (pas les autres communes), peut demander à un agent de modifier une donnée précise, effectue le contrôle qualité et valide.
  • MEAL national : accès en lecture à toutes les communes, contrôle qualité sur les données déjà validées par les MEAL communaux.
  • Lecture seule : Chargé de Programme, Chargé de Projet, partenaire technique et financier.
  • Administration : ADMIN, SUPER_ADMIN.

Audit du code existant (2026-07-08)

Recherche exhaustive menée avant conception — trois constats importants :

  1. Le scope commune est incohérent selon les modules. Trois patterns coexistent : scope forcé avec faille de contournement par paramètre de requête (Enfants), filtre optionnel non sécurisé (majorité des modules), aucun filtrage (Accompagnements, Pilotage, Auth, Audit). Enfants/infrastructure/repositories/prisma/EnfantRepositoryPrisma.ts::lister() laisse le filtre utilisateur écraser le scope de sécurité — un agent peut lire une autre commune via ?communeCode=AUTRE.
  2. Aucun contrôle d'ownership générique. Enfant : n'importe qui avec ENFANT_MODIFIER peut modifier n'importe quel enfant. Accompagnements/MaitreCoranique : au contraire, restriction stricte au créateur exact — même le MEAL ne peut pas voir/modifier les données des autres agents. Tuteur/Parent : aucune restriction du tout.
  3. Aucun workflow de validation n'existe. La permission ENFANT_VALIDER est seedée mais jamais utilisée dans le code (aucun use case, aucune route). StatutEtude/StatutPlanAffaires existent mais sont positionnables librement par n'importe quel détenteur de la permission générique — pas un vrai workflow d'approbation.
  4. 11 des ~32 modèles de données opérationnelles n'ont même pas de champ idUtilisateur (Tuteur, Parent, PersonneRessource, ParticipationFormation, DistributionSupport, EducateurPair, ComiteVillageois, CadreConcertation, PresenceReunionComite, PresenceSessionConcertation, ParticipationSession).

Décision : ces incohérences/bugs sont corrigés dans le même chantier plutôt que dans un chantier séparé, car ils sont directement dans le périmètre "qui voit/modifie quoi".

Décisions de conception validées

1. Statut de validation — sur les entités racines uniquement

Le statut de validation (EN_ATTENTE / VALIDE / A_CORRIGER) est porté par les entités racines (~18 modèles) : Enfant, Accompagnement, AppuiAlimentaire, SoutienPsychologique, PriseEnChargeSanitaire, SeanceAlphabetisation, FormationSensibilisation, ParcoursInsertion (+ ses 4 événements), ComiteVillageois, ReunionComite, CadreConcertation, SessionConcertation, SessionParoleJeunes, EtudeDiagnostique, SupportCommunication.

Pas de statut de validation propre sur les tables de participation/présence (ParticipationFormation, PresenceReunionComite, PresenceSessionConcertation, ParticipationSession) — over-engineering pour des lignes qui n'ont de sens qu'attachées à leur parent.

2. Verrouillage en cascade — le point clé

Ces tables de participation/présence alimentent directement le calcul des indicateurs (ex. 1.2.1.2 taux de participation aux réunions, 3.1.2.3 participants aux sessions de parole, 2.1.3.2 taux de compréhension via scorePostFormation). Sans précaution, on pourrait modifier une présence après validation de la réunion parente, faussant silencieusement un indicateur déjà validé.

Règle : créer/modifier/supprimer une ligne de participation/présence exige de vérifier le statut de validation du parent, pas un statut propre. Tant que le parent est EN_ATTENTE ou A_CORRIGER, les lignes filles restent modifiables par leur créateur. Dès que le parent passe à VALIDE, plus aucune ligne fille ne peut être ajoutée/modifiée/supprimée.

3. Traçabilité — ajout d'idUtilisateur

Les 4 tables de participation/présence sans idUtilisateur (ParticipationFormation, PresenceReunionComite, PresenceSessionConcertation, ParticipationSession) reçoivent ce champ pour tracer qui a saisi quoi — utile au contrôle qualité MEAL fin, même si le verrou de modification reste porté par le parent (cf. point 2).

4. Autorisation générique — remplace les 3 patterns incohérents actuels

Un mécanisme d'autorisation unique, partagé entre modules (pas de réimplémentation ad hoc par repository comme aujourd'hui) :

  • Modifier un enregistrement : autorisé UNIQUEMENT si (créateur ET statut ≠ VALIDE). Ni le MEAL (communal ou national) ni l'ADMIN/SUPER_ADMIN n'ont de droit d'écriture sur les données de mise en œuvre — l'intégrité de la source de l'observation terrain est préservée : seul l'auteur peut corriger son enregistrement, le MEAL ne peut que lire/valider/demander une correction. (Correction apportée le 2026-07-09 après clôture initiale — la conception d'origine donnait à tort un droit d'écriture au MEAL/ADMIN.)
  • Lister/voir : agent terrain et MEAL_COMMUNAL → scope forcé sur leur commune (non contournable par paramètre de requête, contrairement au bug actuel sur Enfant) ; MEAL_NATIONAL/ CHARGE_PROJET/BAILLEUR/ADMIN/SUPER_ADMIN → toutes communes.
  • Valider : MEAL_COMMUNAL (sa commune) ou MEAL_NATIONAL.
  • Demander une correction : MEAL_COMMUNAL ou MEAL_NATIONAL, avec commentaire obligatoire → statut A_CORRIGER ; la modification par l'agent auteur repasse automatiquement le statut à EN_ATTENTE.

Liens connexes