a30ff937f0
Défaut réel (même classe que le bug regression_run.json corrigé plus tôt) : les 4 générateurs RBAC (fixtures_gen/userperm_gen/roleprofile_gen/apply_plan) .gitignore-aient leur out/, alors qu'ils sont audités en archétype `generator` (critère HANDOFF). En checkout PROPRE (git archive HEAD = ce que voit le runner Gitea) leur out/ est absent → qa/audit_4big (dont le build relit le out/ de CHAQUE module) les note 80<95 ⇒ INV7 ⇒ build refusé ⇒ check-artifacts ET check-regression ROUGES. Ça ne passait qu'en local via les out/ non suivis laissés par des build manuels. Fix : committer le out/ des 4 générateurs (build byte-déterministe prouvé ; apply_plan lit ses frères en process, pas via out/) → alignement sur les 15 autres générateurs ; audit 100/100 en checkout propre. Durcissement : check_artifacts.sh exige désormais `git ls-files --error-unmatch` sur chaque fichier produit → un out/ ignoré/non commité devient une erreur LOCALE honnête au lieu d'une surprise en CI. Bite-proof : git rm --cached d'un artefact (laissé sur disque) ⇒ exit 1 ; re-add ⇒ exit 0. Régénéré : quality_report.json (dérive DOC = taille des 4 README édités). Vérifs : 6 gates verts sur git archive propre ; 534/21 inchangé ; suites OK. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
99 lines
5.1 KiB
Markdown
99 lines
5.1 KiB
Markdown
# 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).
|