Files
Claude Code DTP Worker b1b011ab81 [DTP-Worker] Sprint 2 · Générateur plan User Permission (RBAC row-level scope_donnees → Frappe)
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>
2026-07-30 03:33:44 +00:00

119 lines
4.3 KiB
JSON

{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://oto-enterprise-os-dtp/rbac/userperm.schema.json",
"title": "Plan User Permission (row-level) généré depuis rbac_50_roles.json",
"description": "Contrat de sortie de userperm_gen.py. Validé par le validateur maison Publiciste (zéro pip) dans le job CI rbac-userperm-tests. Un template User Permission n'est présent que pour la portée `entite` ; les portées own/groupe/equipe portent user_permission_template=null (mécanisme documenté par `mechanism`).",
"type": "object",
"additionalProperties": false,
"required": ["manifest", "plan"],
"properties": {
"manifest": {
"type": "object",
"additionalProperties": false,
"required": [
"generated_from",
"source_version",
"cible_rbac_roles",
"counts",
"companies_a_confirmer",
"roles_scope_equipe_a_confirmer"
],
"properties": {
"generated_from": { "type": "string", "const": "rbac_50_roles.json" },
"source_version": { "type": "string", "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$" },
"cible_rbac_roles": { "type": "integer", "const": 50 },
"counts": {
"type": "object",
"additionalProperties": false,
"required": ["plan_entries", "user_permission_templates", "by_mechanism"],
"properties": {
"plan_entries": { "type": "integer", "minimum": 50, "maximum": 50 },
"user_permission_templates": { "type": "integer", "minimum": 0 },
"by_mechanism": {
"type": "object",
"additionalProperties": false,
"required": [
"docperm_if_owner",
"user_permission_company",
"none_consolidated",
"vps_confirm_team"
],
"properties": {
"docperm_if_owner": { "type": "integer", "minimum": 0 },
"user_permission_company": { "type": "integer", "minimum": 0 },
"none_consolidated": { "type": "integer", "minimum": 0 },
"vps_confirm_team": { "type": "integer", "minimum": 0 }
}
}
}
},
"companies_a_confirmer": {
"type": "array",
"uniqueItems": true,
"items": { "type": "string", "minLength": 1 }
},
"roles_scope_equipe_a_confirmer": {
"type": "array",
"uniqueItems": true,
"items": { "type": "string", "pattern": "^OTO " }
}
}
},
"plan": {
"type": "array",
"minItems": 50,
"maxItems": 50,
"items": { "$ref": "#/definitions/plan_entry" }
}
},
"definitions": {
"plan_entry": {
"type": "object",
"additionalProperties": false,
"required": [
"erpnext_role_name",
"scope_donnees",
"entite_principale",
"mechanism",
"user_permission_template"
],
"properties": {
"erpnext_role_name": { "type": "string", "pattern": "^OTO " },
"scope_donnees": { "type": "string", "enum": ["own", "equipe", "entite", "groupe"] },
"entite_principale": { "type": "string", "minLength": 1 },
"mechanism": {
"type": "string",
"enum": [
"docperm_if_owner",
"user_permission_company",
"none_consolidated",
"vps_confirm_team"
]
},
"user_permission_template": {
"type": ["object", "null"],
"additionalProperties": false,
"required": [
"doctype",
"user",
"allow",
"for_value",
"apply_to_all_doctypes",
"is_default",
"hide_descendants"
],
"properties": {
"doctype": { "type": "string", "const": "User Permission" },
"user": { "type": "string", "const": "__ASSIGN_PER_USER__" },
"allow": { "type": "string", "const": "Company" },
"for_value": { "type": "string", "minLength": 1 },
"apply_to_all_doctypes": { "type": "integer", "enum": [0, 1] },
"is_default": { "type": "integer", "enum": [0, 1] },
"hide_descendants": { "type": "integer", "enum": [0, 1] }
}
}
}
}
}
}