Complète le pipeline RBAC (Role + Custom DocPerm déjà livrés) par la dimension row-level. Mapping natif ERPNext v15 des 4 scope_donnees : - entite → User Permission allow=Company (28 templates, user=sentinelle) - own → if_owner (déjà posé par fixtures_gen) - groupe → aucune restriction (vue consolidée) - equipe → pas de dimension native → signalé VPS (jamais mappé, #6) Module userperm_gen/ : permlib/{frappe,builder}, CLI build/validate (refuse d'écrire si invariant KO), userperm.schema.json (validateur maison, zéro pip), 12 tests unittest, README. Job CI rbac-userperm-tests ajouté au gate. Docs SPEC §7 + fixtures_gen README cousues. Régression 72 tests verts, gate OK. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5.1 KiB
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 :
il couvre la dimension ROW-LEVEL (scope_donnees) que les DocPerm ne portent
pas à eux seuls. Transforme le contrat ../rbac_50_roles.json
(validé par ../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.
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/, non commité)
| 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
# 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
userde chaque template est toujours la sentinelle (vérifié par test + CLI). - Aucune restriction fabriquée hors portée
entite:own/groupe/equipeportentuser_permission_template = null. entite+ périmètre consolidé (« Groupe ») →ValueError(garde-fou anti-contrat corrompu : une portée consolidée doit êtregroupe).- Fidélité au contrat : scope, entité et
for_valuere-vérifiés entrée par entrée contrerbac_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)
- Créer/confirmer les Companies listées dans
MANIFEST.companies_a_confirmer(une par entité juridique · CLAUDE.md §Entités). - Pour chaque rôle de
mechanism = user_permission_company: cloneruser_permission_templatepar utilisateur assigné au rôle, en remplaçantuserpar son e-mail réel, puis importer. - Décider le mécanisme des rôles
MANIFEST.roles_scope_equipe_a_confirmer(equipe) — options natives : champ custom « Équipe » +User Permission, ou hiérarchieEmployee.reports_to. À valider avec Michel avant application. - Les rôles
ownsont déjà couverts parif_owner(fixturescustom_docperm). - 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 + oraclejsonschemasi présent ; couverture bijective des 50 rôles, mécanisme = mapping natif du scope, template SSIentite, anti-invention utilisateur, déterminisme).- Job CI dédié
rbac-userperm-testsajouté 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).