TenantAtlas/specs/066-rbac-ui-enforcement-helper/tasks.md
ahmido 6a86c5901a 066-rbac-ui-enforcement-helper (#81)
Kontext / Ziel
Diese PR standardisiert Tenant‑RBAC Enforcement in der Filament‑UI: statt ad-hoc Gate::*, abort_if/abort_unless und kopierten ->visible()/->disabled()‑Closures gibt es jetzt eine zentrale, wiederverwendbare Implementierung für Actions (Header/Table/Bulk).

Links zur Spec:

spec.md
plan.md
quickstart.md
Was ist drin
Neue zentrale Helper-API: UiEnforcement (Tenant-plane RBAC‑UX “source of truth” für Filament Actions)
Standardisierte Tooltip-Texte und Context-DTO (UiTooltips, TenantAccessContext)
Migration vieler tenant‑scoped Filament Action-Surfaces auf das Standardpattern (ohne ad-hoc Auth-Patterns)
CI‑Guard (Test) gegen neue ad-hoc Patterns in app/Filament/**:
verbietet Gate::allows/denies/check/authorize, use Illuminate\Support\Facades\Gate, abort_if/abort_unless
Legacy-Allowlist ist aktuell leer (neue Verstöße failen sofort)
RBAC-UX Semantik (konsequent & testbar)
Non-member: UI Actions hidden (kein Tenant‑Leak); Execution wird blockiert (Filament hidden→disabled chain), Defense‑in‑depth enthält zusätzlich serverseitige Guards.
Member ohne Capability: Action visible aber disabled + Standard-Tooltip; Execution wird blockiert (keine Side Effects).
Member mit Capability: Action enabled und ausführbar.
Destructive actions: über ->destructive() immer mit ->requiresConfirmation() + klare Warntexte (Execution bleibt über ->action(...)).
Wichtig: In Filament v5 sind hidden/disabled Actions typischerweise “silently blocked” (200, keine Ausführung). Die Tests prüfen daher UI‑State + “no side effects”, nicht nur HTTP‑Statuscodes.

Sicherheit / Scope
Keine neuen DB-Tabellen, keine Migrations, keine Microsoft Graph Calls (DB‑only bei Render; kein outbound HTTP).
Tenant Isolation bleibt Isolation‑Boundary (deny-as-not-found auf Tenant‑Ebene, Capability erst nach Membership).
Kein Asset-Setup erforderlich; keine neuen Filament Assets.
Compliance Notes (Repo-Regeln)
Filament v5 / Livewire v4.0+ kompatibel.
Keine Änderungen an Provider‑Registrierung (Laravel 11+/12: providers.php bleibt der Ort; hier unverändert).
Global Search: keine gezielte Änderung am Global‑Search-Verhalten in dieser PR.
Tests / Qualität
Pest Feature/Unit Tests für Member/Non-member/Tooltip/Destructive/Regression‑Guard.
Guard-Test: “No ad-hoc Filament auth patterns”.
Full suite laut Tasks: vendor/bin/sail artisan test --compact → 837 passed, 5 skipped.
Checklist: requirements.md vollständig (16/16).
Review-Fokus
API‑Usage in neuen/angepassten Filament Actions: UiEnforcement::forAction/forTableAction/forBulkAction(...)->requireCapability(...)->apply()
Guard-Test soll “red” werden, sobald jemand neue ad-hoc Auth‑Patterns einführt (by design).

Co-authored-by: Ahmed Darrazi <ahmeddarrazi@MacBookPro.fritz.box>
Reviewed-on: #81
2026-01-30 16:58:02 +00:00

255 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Tasks: RBAC UI Enforcement Helper v1
**Input**: Design documents from `/specs/066-rbac-ui-enforcement-helper/`
**Prerequisites**: plan.md ✓, spec.md ✓, quickstart.md ✓
**Tests**: REQUIRED (Pest) — this feature changes runtime authorization behavior.
**RBAC**: This feature IS the RBAC enforcement helper — all tasks enforce constitution RBAC-UX rules.
**Organization**: Tasks grouped by user story for independent implementation.
## Format: `[ID] [P?] [Story?] Description`
- **[P]**: Can run in parallel (different files, no dependencies)
- **[Story]**: US1/US2/US3 for user story phases; omitted for Setup/Foundational/Polish
---
## Phase 1: Setup
**Purpose**: Create helper infrastructure with no external dependencies
- [X] T001 Create directory structure `app/Support/Rbac/`
- [X] T002 [P] Create `UiTooltips.php` with tooltip constants in `app/Support/Rbac/UiTooltips.php`
- [X] T003 [P] Create `TenantAccessContext.php` DTO in `app/Support/Rbac/TenantAccessContext.php`
---
## Phase 2: Foundational (Blocking Prerequisites)
**Purpose**: Core `UiEnforcement` helper — MUST complete before any user story tests
**⚠️ CRITICAL**: No user story work can begin until this phase is complete
- [X] T004 Implement `UiEnforcement::forAction()` static method in `app/Support/Rbac/UiEnforcement.php`
- [X] T005 Implement `->requireMembership()` method (default: true) in `app/Support/Rbac/UiEnforcement.php`
- [X] T006 Implement `->requireCapability(string $capability)` method in `app/Support/Rbac/UiEnforcement.php`
- [X] T007 Implement `->destructive()` method (confirmation modal) in `app/Support/Rbac/UiEnforcement.php`
- [X] T008 Implement `->tooltip(string $message)` override method in `app/Support/Rbac/UiEnforcement.php`
- [X] T009 Implement `->apply()` method (sets hidden/disabled/guards) in `app/Support/Rbac/UiEnforcement.php`
- [X] T010 Implement `UiEnforcement::forTableAction()` static method in `app/Support/Rbac/UiEnforcement.php`
- [X] T011 Implement `UiEnforcement::forBulkAction()` static method with all-or-nothing logic in `app/Support/Rbac/UiEnforcement.php`
**Checkpoint**: `UiEnforcement` class ready — user story tests can now be written
---
## Phase 3: User Story 1 — Tenant member sees consistent disabled UX (Priority: P1) 🎯 MVP
**Goal**: Members lacking capability see actions visible-but-disabled with standard tooltip; 403 on execution
**Independent Test**: Visit tenant page as member with insufficient permission → action disabled with tooltip, cannot execute
### Tests for User Story 1
- [X] T012 [P] [US1] Test: member without capability sees disabled action + tooltip in `tests/Feature/Rbac/UiEnforcementMemberDisabledTest.php`
- [X] T013 [P] [US1] Test: member without capability is blocked from execution in `tests/Feature/Rbac/UiEnforcementMemberDisabledTest.php`
- [X] T014 [P] [US1] Test: member with capability sees enabled action + can execute in `tests/Feature/Rbac/UiEnforcementMemberDisabledTest.php`
- [X] T014a [P] [US1] Test: destructive action shows confirmation modal before execution in `tests/Feature/Rbac/UiEnforcementDestructiveTest.php`
### Implementation for User Story 1
- [X] T015 [US1] Validate `->apply()` correctly sets `->disabled()` + `->tooltip()` for members lacking capability (logic in T009; this task verifies + adjusts if needed) in `app/Support/Rbac/UiEnforcement.php`
- [X] T016 [US1] Validate `->apply()` correctly blocks unauthorized execution (via Filament's isDisabled check + defense-in-depth abort) in `app/Support/Rbac/UiEnforcement.php`
- ~~T017 [US1] Migrate TenantResource table actions to UiEnforcement~~ **OUT OF SCOPE v1**: TenantResource is record==tenant, not tenant-scoped
- [X] T018 [US1] Migrate ProviderConnectionResource actions to UiEnforcement (mixed visibility via `preserveVisibility()`) in `app/Filament/Resources/ProviderConnectionResource.php`
**Checkpoint**: US1 complete — members see consistent disabled UX with tooltip (exemplar: ListPolicies)
---
## Phase 4: User Story 2 — Non-members cannot infer tenant resources (Priority: P2)
**Goal**: Non-members receive 404 (deny-as-not-found) for all tenant-scoped actions; actions hidden in UI
**Independent Test**: Access tenant page as non-member → actions hidden, execution returns 404
### Tests for User Story 2
- [X] T019 [P] [US2] Test: non-member sees action hidden in UI in `tests/Feature/Rbac/UiEnforcementNonMemberHiddenTest.php`
- [X] T020 [P] [US2] Test: non-member action is blocked (via Filament hidden-action semantics) in `tests/Feature/Rbac/UiEnforcementNonMemberHiddenTest.php`
- [X] T021 [P] [US2] Test: membership revoked mid-session still enforces protection in `tests/Feature/Rbac/UiEnforcementNonMemberHiddenTest.php`
### Implementation for User Story 2
- [X] T022 [US2] Validate `->apply()` correctly sets `->hidden()` for non-members (logic in T009; this task verifies + adjusts if needed) in `app/Support/Rbac/UiEnforcement.php`
- [X] T023 [US2] Validate `->apply()` blocks non-member execution (via Filament's isHidden → isDisabled chain; 404 server-side guard is defense-in-depth) in `app/Support/Rbac/UiEnforcement.php`
- [X] T024 [US2] Migrate BackupSetResource actions (row + bulk) to UiEnforcement (mixed visibility via `preserveVisibility()`) in `app/Filament/Resources/BackupSetResource.php`
- [X] T025 [US2] Migrate PolicyResource sync actions to UiEnforcement in `app/Filament/Resources/PolicyResource/Pages/ListPolicies.php`
**Checkpoint**: US2 complete — non-members receive 404 semantics, no information leakage
---
## Phase 5: User Story 3 — Maintainers add actions safely by default (Priority: P3)
**Goal**: CI-failing guard flags new ad-hoc authorization patterns; standard pattern documented
**Independent Test**: Introduce ad-hoc `Gate::allows` or `abort_unless()` in Filament → guard test fails
### Tests for User Story 3
- [X] T026 [P] [US3] Guard test: scan `app/Filament/**` for forbidden ad-hoc patterns (Gate + abort helpers) in `tests/Feature/Guards/NoAdHocFilamentAuthPatternsTest.php`
- [X] T027 [P] [US3] Unit test: UiEnforcement uses only canonical Capabilities constants in `tests/Unit/Support/Rbac/UiEnforcementTest.php`
### Implementation for User Story 3
- [X] T028 [US3] Replace Pest-Arch guard with stable file-scan guard (CI-failing, allowlist for legacy only) in `tests/Feature/Guards/NoAdHocFilamentAuthPatternsTest.php`
- [X] T029 [US3] Migrate EntraGroupResource sync actions to UiEnforcement in `app/Filament/Resources/EntraGroupResource/Pages/ListEntraGroups.php`
- [X] T030 [US3] Remove Gate facade usage from FindingResource (migrate auth to canonical checks) in `app/Filament/Resources/FindingResource.php`
**Checkpoint**: US3 complete — guardrail prevents regression (file-scan), exemplar surfaces migrated
---
## Phase 6: Polish & Cross-Cutting Concerns
**Purpose**: Cleanup, additional tests, documentation
- [X] T031 [P] PHPDoc blocks present on all public methods in `app/Support/Rbac/UiEnforcement.php`
- [X] T032 [P] Update quickstart.md with migration examples in `specs/066-rbac-ui-enforcement-helper/quickstart.md`
- [X] T033 Run Pint formatter on new files with `vendor/bin/sail bin pint app/Support/Rbac`
- [X] T034 Run full test suite with `vendor/bin/sail artisan test --compact` — 837 passed, 5 skipped
- [X] T035 Validate quickstart.md examples work in codebase (ListPolicies migration verified)
---
## Phase 7: Follow-up — Findings capability cleanup (Mini-feature)
**Purpose**: Avoid overloading broad capabilities (e.g. `TENANT_SYNC`) for findings acknowledgement.
- [X] T036 Add `Capabilities::TENANT_FINDINGS_ACKNOWLEDGE` in `app/Support/Auth/Capabilities.php`
- [X] T037 Grant `TENANT_FINDINGS_ACKNOWLEDGE` to Owner/Manager/Operator (not Readonly) + update role-matrix tests
- [X] T038 Update Finding list acknowledge action to require `TENANT_FINDINGS_ACKNOWLEDGE` in `app/Filament/Resources/FindingResource/Pages/ListFindings.php`
- [X] T039 Refactor `FindingPolicy::update()` to use `CapabilityResolver` with `TENANT_FINDINGS_ACKNOWLEDGE` (remove ad-hoc `Gate::forUser(...)->allows(...)`)
---
## Phase 8: Follow-up — Legacy allowlist shrink (Stepwise)
**Purpose**: Keep shrinking the Filament guard allowlist with one-file migrations.
- [X] T040 Remove `BackupScheduleResource.php` from the legacy allowlist after migration
- [X] T041 Migrate `ListEntraGroupSyncRuns.php` to UiEnforcement + add a focused Livewire test
- [X] T042 Remove `ListEntraGroupSyncRuns.php` from the legacy allowlist after migration
- [X] T043 Migrate `ListProviderConnections.php` create action to UiEnforcement + add a focused Livewire test
- [X] T044 Remove `ListProviderConnections.php` from the legacy allowlist after migration
- [X] T045 Migrate `DriftLanding.php` generation permission check to `CapabilityResolver` (remove Gate facade) + add a focused Livewire test
- [X] T046 Remove `DriftLanding.php` from the legacy allowlist after migration
- [X] T047 Migrate `RegisterTenant.php` page-level checks to `CapabilityResolver` + replace `abort_unless()` with `abort()`
- [X] T048 Remove `RegisterTenant.php` from the legacy allowlist after migration
- [X] T049 Migrate `EditProviderConnection.php` actions + save guards to `UiEnforcement`/`CapabilityResolver` (remove Gate facade + abort_unless) + add a focused Livewire test
- [X] T050 Remove `EditProviderConnection.php` from the legacy allowlist after migration
- [X] T051 Migrate `CreateRestoreRun.php` page authorization to `CapabilityResolver` (remove Gate facade + abort_unless) + add a focused Livewire test
- [X] T052 Remove `CreateRestoreRun.php` from the legacy allowlist after migration
- [X] T053 Migrate `InventoryItemResource.php` resource authorization to `CapabilityResolver` (remove Gate facade) + add a focused Pest test
- [X] T054 Remove `InventoryItemResource.php` from the legacy allowlist after migration
- [X] T055 Migrate `VersionsRelationManager.php` restore action to `UiEnforcement`/`CapabilityResolver` (remove Gate facade + abort_unless) + add a focused Livewire test
- [X] T056 Remove `VersionsRelationManager.php` from the legacy allowlist after migration
- [X] T057 Migrate `BackupItemsRelationManager.php` actions to `UiEnforcement` (remove Gate facade) + add a focused Livewire test
- [X] T058 Remove `BackupItemsRelationManager.php` from the legacy allowlist after migration
- [X] T059 Migrate `PolicyVersionResource.php` actions/bulk actions off ad-hoc patterns to `UiEnforcement`/`CapabilityResolver` (remove Gate facade + abort_unless) while preserving metadata-only restore behavior
- [X] T060 Remove `PolicyVersionResource.php` from the legacy allowlist after migration
- [X] T061 Migrate `RestoreRunResource.php` actions/bulk actions off ad-hoc patterns to `UiEnforcement`/`CapabilityResolver` (remove Gate facade + abort_unless)
- [X] T062 Remove `RestoreRunResource.php` from the legacy allowlist after migration
- [X] T063 Fix `UiEnforcement` server-side guard to use Filament lifecycle hooks (`->before()`) to preserve Filament action parameter injection
- [X] T064 Migrate `PolicyResource.php` actions/bulk actions off ad-hoc patterns to `UiEnforcement` (remove Gate facade + abort_unless)
- [X] T065 Remove `PolicyResource.php` from the legacy allowlist after migration
- [X] T066 Migrate `EditTenant.php` archive action off ad-hoc patterns to `UiEnforcement` (remove Gate facade + abort_unless)
- [X] T067 Remove `EditTenant.php` from the legacy allowlist after migration
- [X] T068 Migrate `TenantMembershipsRelationManager.php` actions off ad-hoc patterns to `UiEnforcement` (remove Gate facade)
- [X] T069 Remove `TenantMembershipsRelationManager.php` from the legacy allowlist after migration
- [X] T070 Migrate `TenantResource.php` off ad-hoc patterns to `CapabilityResolver` (remove Gate facade + abort_unless)
- [X] T071 Remove `TenantResource.php` from the legacy allowlist after migration
---
## Dependencies & Execution Order
### Phase Dependencies
- **Setup (Phase 1)**: No dependencies — can start immediately
- **Foundational (Phase 2)**: Depends on Setup — BLOCKS all user stories
- **User Stories (Phase 35)**: All depend on Foundational; can proceed in parallel or by priority
- **Polish (Phase 6)**: Depends on all user stories
### User Story Dependencies
- **US1 (P1)**: Foundational only — no cross-story dependencies
- **US2 (P2)**: Foundational only — no cross-story dependencies
- **US3 (P3)**: Foundational only — no cross-story dependencies
### Within Each User Story
- Tests MUST be written FIRST and FAIL before implementation
- Wire logic in `UiEnforcement.php` before migrating Filament surfaces
- Migrate surfaces one at a time, verify tests pass
### Parallel Opportunities
- T002 + T003 (Setup) can run in parallel
- All test tasks (T012T014, T019T021, T026T027) can run in parallel
- US1, US2, US3 can run in parallel after Foundational
- T031 + T032 (Polish) can run in parallel
---
## Parallel Example: User Story 1
```bash
# Launch all tests for US1 together:
T012: "Test: member without capability sees disabled action + tooltip"
T013: "Test: member without capability receives 403 on execution"
T014: "Test: member with capability sees enabled action + can execute"
# Then implement sequentially:
T015 → T016 → T017 → T018
```
---
## Implementation Strategy
### MVP First (User Story 1 Only)
1. Complete Phase 1: Setup (T001T003)
2. Complete Phase 2: Foundational (T004T011)
3. Complete Phase 3: User Story 1 (T012T018)
4. **STOP and VALIDATE**: Members see disabled + tooltip + 403
5. Deploy/demo if ready
### Incremental Delivery
1. Setup + Foundational → `UiEnforcement` ready
2. US1 → Consistent disabled UX for members (MVP!)
3. US2 → Non-member 404 enforcement
4. US3 → CI-failing guardrail + all 6 surfaces migrated
5. Polish → Docs, cleanup, full test suite
---
## Summary
| Metric | Count |
|--------|-------|
| Total tasks | 40 |
| Setup tasks | 3 |
| Foundational tasks | 8 |
| US1 tasks | 8 |
| US2 tasks | 7 |
| US3 tasks | 5 |
| Polish tasks | 5 |
| Follow-up tasks | 4 |
| Parallel opportunities | 13 |
| MVP scope | Phases 13 (T001T018) |