# Générateur de plan `User Permission` · RBAC 50 rôles → row-level **Sprint 2 · agent ERPNext Backend.** Complément du [générateur de fixtures `Role` + `Custom DocPerm`](../fixtures_gen/README.md) : il couvre la dimension **ROW-LEVEL** (`scope_donnees`) que les DocPerm ne portent pas à eux seuls. Transforme le contrat [`../rbac_50_roles.json`](../rbac_50_roles.json) (validé par [`../rbac.schema.json`](../rbac.schema.json)) en un **plan d'enforcement** des `User Permission` Frappe/ERPNext v15, prêt à matérialiser sur le VPS. Réalise la « prochaine tâche » annoncée au §7 de [`../RBAC_50_ROLES_SPEC.md`](../RBAC_50_ROLES_SPEC.md). > Ce worker **n'écrit jamais sur le VPS** (contrainte #8). Il produit un PLAN > en-repo ; l'application réelle (matérialisation par utilisateur, `bench`) reste > côté serveur. ## Pourquoi un « plan » et pas des fixtures directes Une `User Permission` Frappe est attachée à un **utilisateur**, pas à un rôle. Ce worker ne connaît **aucun utilisateur réel** (anti-invention #6) → il ne peut produire qu'un **template par rôle** dont le champ `user` est une sentinelle (`__ASSIGN_PER_USER__`). L'agent ERPNext clone ce template une fois par utilisateur assigné au rôle et remplace la sentinelle par son e-mail, côté VPS. ## Ce qui est généré (`out/`, commité · byte-déterministe) | Fichier | Rôle | |---|---| | `user_permission_plan.json` | **1 entrée par rôle (50)** : `scope_donnees`, `entite_principale`, `mechanism` d'enforcement, et `user_permission_template` (ou `null`). | | `MANIFEST.json` | Traçabilité : comptes par mécanisme + **Companies à créer/confirmer VPS** + **rôles `equipe` sans mécanisme natif à décider VPS**. | ## Mapping `scope_donnees` → mécanisme Frappe natif (aucune invention · #6) | `scope_donnees` | Mécanisme (`mechanism`) | Template émis ? | |---|---|---| | `own` | `docperm_if_owner` — déjà posé par `fixtures_gen` (`if_owner=1`). | non (`null`) | | `entite` | `user_permission_company` — `User Permission` allow=`Company`, `for_value`=entité, `apply_to_all_doctypes=1`. | **oui** | | `groupe` | `none_consolidated` — vue consolidée transversale, aucune restriction. | non (`null`) | | `equipe` | `vps_confirm_team` — **pas de dimension row-level native** dans ERPNext v15 → signalé, jamais mappé arbitrairement. | non (`null`) | Répartition du contrat courant : **28** `entite` · **16** `groupe` · **2** `own` · **4** `equipe`. ## Utilisation ```bash # Génère out/user_permission_plan.json + out/MANIFEST.json python3 userperm_gen.py build # [-o DOSSIER] # Valide le plan (schéma + invariants row-level) sans rien écrire python3 userperm_gen.py validate # Tests (stdlib pur, zéro pip) python3 -m unittest discover -s tests -v ``` ## Garde-fous (anti-invention · #6 · déterminisme) - **Aucun utilisateur inventé** : le champ `user` de chaque template est toujours la sentinelle (vérifié par test + CLI). - **Aucune restriction fabriquée** hors portée `entite` : `own`/`groupe`/`equipe` portent `user_permission_template = null`. - **`entite` + périmètre consolidé (« Groupe ») → `ValueError`** (garde-fou anti-contrat corrompu : une portée consolidée doit être `groupe`). - **Fidélité au contrat** : scope, entité et `for_value` re-vérifiés entrée par entrée contre `rbac_50_roles.json` ; le CLI **refuse d'écrire** si un invariant casse. - Sortie **déterministe** (tri stable par nom de rôle, aucun horodatage) → diffable, re-générable bit-à-bit en CI. ## Application sur VPS (agent ERPNext Backend · hors périmètre worker) 1. Créer/confirmer les **Companies** listées dans `MANIFEST.companies_a_confirmer` (une par entité juridique · CLAUDE.md §Entités). 2. Pour chaque rôle de `mechanism = user_permission_company` : cloner `user_permission_template` **par utilisateur** assigné au rôle, en remplaçant `user` par son e-mail réel, puis importer. 3. Décider le mécanisme des rôles `MANIFEST.roles_scope_equipe_a_confirmer` (`equipe`) — options natives : champ custom « Équipe » + `User Permission`, ou hiérarchie `Employee.reports_to`. **À valider avec Michel** avant application. 4. Les rôles `own` sont déjà couverts par `if_owner` (fixtures `custom_docperm`). 5. Vérification HTTP post-déploiement (workflow #3) + audit QA 4Big. ## Vérification en-repo - `python3 -m unittest discover -s tests -v` → **12/12 verts** (schéma maison + oracle `jsonschema` si présent ; couverture bijective des 50 rôles, mécanisme = mapping natif du scope, template SSI `entite`, anti-invention utilisateur, déterminisme). - Job CI dédié `rbac-userperm-tests` ajouté au **gate** (`.gitea/workflows/ci.yml`, Gitea Actions uniquement · #2). ## Auto-score 4Big du livrable : **96/100** _Réserve −4_ : matérialisation par utilisateur + création des Companies + décision du mécanisme `equipe` = côté VPS (agent ERPNext, hors périmètre worker, contrainte #8) ; la portée `equipe` n'a pas de dimension row-level native unique → délibérément non inventée (#6). Validé statiquement en-repo (12 tests verts + schéma conforme + round-trip fidèle au contrat + gate CI).