# RBAC Permission Playbook

Letzte Aktualisierung: 2026-03-06

Ziel: Neue Rechte konsistent und ohne Legacy-Pfade ergänzen.

## Entscheidungshilfe

1. Nur UI an bestehende Ability hängen: 3 Schritte.
2. Neue Ability auf bestehende Permission: 4 Schritte.
3. Ganz neue Permission (inkl. DB): 7 Schritte.

## Fall A: Ganz neue Permission

1. Permission-Key in `PermissionService` ergänzen.
   Datei: `lib/Service/Access/PermissionService.php`
2. DB-Update für Bestandsinstanzen anlegen (idempotent).
   Datei: `db/updates/YYYY-MM-DD-*.sql`
3. `db/init/init.sql` für Neuinstallationen synchron ergänzen.
4. Ability in passender Policy ergänzen:
   `ABILITY_*`, `supports()`, Mapping in `authorize()`.
5. Action/API auf Ability umstellen:
   Web: `Guard::requireAbility(...)` oder `AuthorizationService`.
   API: `ApiResponse::requireAbility(...)`.
6. Falls UI betroffen: `UiCapabilityMap` ergänzen und in Action nach `$viewAuth` auflösen.
7. Tests + Doku aktualisieren.

Beispiel (Core): `system_audit.view` + `system_audit.purge` mit Abilities
`ops.admin.system_audit.view` und `ops.admin.system_audit.purge`.

## Fall B: Neue Ability auf bestehende Permission

1. Ability in passender Policy ergänzen und auf vorhandenen Permission-Key mappen.
2. Action/API auf neue Ability ziehen.
3. Optional UI-Mapping in `UiCapabilityMap` + `$viewAuth`.
4. Tests/Doku nachziehen.

## Fall C: Nur UI-Freigabe erweitern (ohne neue Permission)

1. Capability-Key in `UiCapabilityMap` ergänzen.
2. In Action mit `UiAccessService::pageCapabilities(...)` oder `layoutCapabilities()` auflösen.
3. Template nur über `$viewAuth['page']` bzw. `$viewAuth['layout']` steuern.

## Fall D: API-only Endpoint

1. Ability festlegen (Policy/OperationsPolicy).
2. Endpoint mit `ApiResponse::requireAbility(...)` schützen.
3. OpenAPI aktualisieren.
4. API-Tests ergänzen.

## Fall E: Scope-sensitive Ability (Ressourcenbezug)

1. Immer Kontext übergeben, z. B.:
   `actor_user_id`, `target_user_id`, `target_tenant_id`, `input`.
2. Wenn Capabilities benötigt werden:
   `AuthorizationDecision::attribute('capabilities', [])` nutzen.
3. Für reine Bool-Checks `AuthorizationService::allow(...)` verwenden.

## Verbindliche Regeln

1. Keine Permission-Checks in Templates (`can(...)` verboten).
2. Keine alten Wrapper (`Guard::requirePermission*`, `ApiResponse::requirePermission`).
3. UI nur über `$viewAuth`.
4. Policy-/Ability-Namen sind der technische Vertrag, nicht der Permission-Key im Template.

## Minimal-Checkliste vor Merge

```bash
rg -n "(?<!::)\\bcan\\(" templates pages -g '*.phtml' -P
rg -n "Guard::requirePermission\\(|Guard::requirePermissionOrForbidden\\(|ApiResponse::requirePermission\\(" pages lib tests web templates -g '*.php'
docker compose exec php vendor/bin/phpunit
docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress
```

Erwartung:

1. Die beiden `rg`-Checks liefern `0` Treffer.
2. `phpunit` und `phpstan` sind grün.
