14 KiB
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
- T001 Create directory structure
app/Support/Rbac/ - T002 [P] Create
UiTooltips.phpwith tooltip constants inapp/Support/Rbac/UiTooltips.php - T003 [P] Create
TenantAccessContext.phpDTO inapp/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
- T004 Implement
UiEnforcement::forAction()static method inapp/Support/Rbac/UiEnforcement.php - T005 Implement
->requireMembership()method (default: true) inapp/Support/Rbac/UiEnforcement.php - T006 Implement
->requireCapability(string $capability)method inapp/Support/Rbac/UiEnforcement.php - T007 Implement
->destructive()method (confirmation modal) inapp/Support/Rbac/UiEnforcement.php - T008 Implement
->tooltip(string $message)override method inapp/Support/Rbac/UiEnforcement.php - T009 Implement
->apply()method (sets hidden/disabled/guards) inapp/Support/Rbac/UiEnforcement.php - T010 Implement
UiEnforcement::forTableAction()static method inapp/Support/Rbac/UiEnforcement.php - T011 Implement
UiEnforcement::forBulkAction()static method with all-or-nothing logic inapp/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
- T012 [P] [US1] Test: member without capability sees disabled action + tooltip in
tests/Feature/Rbac/UiEnforcementMemberDisabledTest.php - T013 [P] [US1] Test: member without capability is blocked from execution in
tests/Feature/Rbac/UiEnforcementMemberDisabledTest.php - T014 [P] [US1] Test: member with capability sees enabled action + can execute in
tests/Feature/Rbac/UiEnforcementMemberDisabledTest.php - T014a [P] [US1] Test: destructive action shows confirmation modal before execution in
tests/Feature/Rbac/UiEnforcementDestructiveTest.php
Implementation for User Story 1
- T015 [US1] Validate
->apply()correctly sets->disabled()+->tooltip()for members lacking capability (logic in T009; this task verifies + adjusts if needed) inapp/Support/Rbac/UiEnforcement.php - T016 [US1] Validate
->apply()correctly blocks unauthorized execution (via Filament's isDisabled check + defense-in-depth abort) inapp/Support/Rbac/UiEnforcement.php T017 [US1] Migrate TenantResource table actions to UiEnforcementOUT OF SCOPE v1: TenantResource is record==tenant, not tenant-scoped- T018 [US1] Migrate ProviderConnectionResource actions to UiEnforcement (mixed visibility via
preserveVisibility()) inapp/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
- T019 [P] [US2] Test: non-member sees action hidden in UI in
tests/Feature/Rbac/UiEnforcementNonMemberHiddenTest.php - T020 [P] [US2] Test: non-member action is blocked (via Filament hidden-action semantics) in
tests/Feature/Rbac/UiEnforcementNonMemberHiddenTest.php - T021 [P] [US2] Test: membership revoked mid-session still enforces protection in
tests/Feature/Rbac/UiEnforcementNonMemberHiddenTest.php
Implementation for User Story 2
- T022 [US2] Validate
->apply()correctly sets->hidden()for non-members (logic in T009; this task verifies + adjusts if needed) inapp/Support/Rbac/UiEnforcement.php - T023 [US2] Validate
->apply()blocks non-member execution (via Filament's isHidden → isDisabled chain; 404 server-side guard is defense-in-depth) inapp/Support/Rbac/UiEnforcement.php - T024 [US2] Migrate BackupSetResource actions (row + bulk) to UiEnforcement (mixed visibility via
preserveVisibility()) inapp/Filament/Resources/BackupSetResource.php - 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
- T026 [P] [US3] Guard test: scan
app/Filament/**for forbidden ad-hoc patterns (Gate + abort helpers) intests/Feature/Guards/NoAdHocFilamentAuthPatternsTest.php - T027 [P] [US3] Unit test: UiEnforcement uses only canonical Capabilities constants in
tests/Unit/Support/Rbac/UiEnforcementTest.php
Implementation for User Story 3
- T028 [US3] Replace Pest-Arch guard with stable file-scan guard (CI-failing, allowlist for legacy only) in
tests/Feature/Guards/NoAdHocFilamentAuthPatternsTest.php - T029 [US3] Migrate EntraGroupResource sync actions to UiEnforcement in
app/Filament/Resources/EntraGroupResource/Pages/ListEntraGroups.php - 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
- T031 [P] PHPDoc blocks present on all public methods in
app/Support/Rbac/UiEnforcement.php - T032 [P] Update quickstart.md with migration examples in
specs/066-rbac-ui-enforcement-helper/quickstart.md - T033 Run Pint formatter on new files with
vendor/bin/sail bin pint app/Support/Rbac - T034 Run full test suite with
vendor/bin/sail artisan test --compact— 837 passed, 5 skipped - 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.
- T036 Add
Capabilities::TENANT_FINDINGS_ACKNOWLEDGEinapp/Support/Auth/Capabilities.php - T037 Grant
TENANT_FINDINGS_ACKNOWLEDGEto Owner/Manager/Operator (not Readonly) + update role-matrix tests - T038 Update Finding list acknowledge action to require
TENANT_FINDINGS_ACKNOWLEDGEinapp/Filament/Resources/FindingResource/Pages/ListFindings.php - T039 Refactor
FindingPolicy::update()to useCapabilityResolverwithTENANT_FINDINGS_ACKNOWLEDGE(remove ad-hocGate::forUser(...)->allows(...))
Phase 8: Follow-up — Legacy allowlist shrink (Stepwise)
Purpose: Keep shrinking the Filament guard allowlist with one-file migrations.
- T040 Remove
BackupScheduleResource.phpfrom the legacy allowlist after migration - T041 Migrate
ListEntraGroupSyncRuns.phpto UiEnforcement + add a focused Livewire test - T042 Remove
ListEntraGroupSyncRuns.phpfrom the legacy allowlist after migration - T043 Migrate
ListProviderConnections.phpcreate action to UiEnforcement + add a focused Livewire test - T044 Remove
ListProviderConnections.phpfrom the legacy allowlist after migration - T045 Migrate
DriftLanding.phpgeneration permission check toCapabilityResolver(remove Gate facade) + add a focused Livewire test - T046 Remove
DriftLanding.phpfrom the legacy allowlist after migration - T047 Migrate
RegisterTenant.phppage-level checks toCapabilityResolver+ replaceabort_unless()withabort() - T048 Remove
RegisterTenant.phpfrom the legacy allowlist after migration - T049 Migrate
EditProviderConnection.phpactions + save guards toUiEnforcement/CapabilityResolver(remove Gate facade + abort_unless) + add a focused Livewire test - T050 Remove
EditProviderConnection.phpfrom the legacy allowlist after migration - T051 Migrate
CreateRestoreRun.phppage authorization toCapabilityResolver(remove Gate facade + abort_unless) + add a focused Livewire test - T052 Remove
CreateRestoreRun.phpfrom the legacy allowlist after migration - T053 Migrate
InventoryItemResource.phpresource authorization toCapabilityResolver(remove Gate facade) + add a focused Pest test - T054 Remove
InventoryItemResource.phpfrom the legacy allowlist after migration - T055 Migrate
VersionsRelationManager.phprestore action toUiEnforcement/CapabilityResolver(remove Gate facade + abort_unless) + add a focused Livewire test - T056 Remove
VersionsRelationManager.phpfrom the legacy allowlist after migration - T057 Migrate
BackupItemsRelationManager.phpactions toUiEnforcement(remove Gate facade) + add a focused Livewire test - T058 Remove
BackupItemsRelationManager.phpfrom the legacy allowlist after migration - T059 Migrate
PolicyVersionResource.phpactions/bulk actions off ad-hoc patterns toUiEnforcement/CapabilityResolver(remove Gate facade + abort_unless) while preserving metadata-only restore behavior - T060 Remove
PolicyVersionResource.phpfrom the legacy allowlist after migration - T061 Migrate
RestoreRunResource.phpactions/bulk actions off ad-hoc patterns toUiEnforcement/CapabilityResolver(remove Gate facade + abort_unless) - T062 Remove
RestoreRunResource.phpfrom the legacy allowlist after migration - T063 Fix
UiEnforcementserver-side guard to use Filament lifecycle hooks (->before()) to preserve Filament action parameter injection - T064 Migrate
PolicyResource.phpactions/bulk actions off ad-hoc patterns toUiEnforcement(remove Gate facade + abort_unless) - T065 Remove
PolicyResource.phpfrom the legacy allowlist after migration - T066 Migrate
EditTenant.phparchive action off ad-hoc patterns toUiEnforcement(remove Gate facade + abort_unless) - T067 Remove
EditTenant.phpfrom the legacy allowlist after migration - T068 Migrate
TenantMembershipsRelationManager.phpactions off ad-hoc patterns toUiEnforcement(remove Gate facade) - T069 Remove
TenantMembershipsRelationManager.phpfrom the legacy allowlist after migration - T070 Migrate
TenantResource.phpoff ad-hoc patterns toCapabilityResolver(remove Gate facade + abort_unless) - T071 Remove
TenantResource.phpfrom 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 3–5): 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.phpbefore migrating Filament surfaces - Migrate surfaces one at a time, verify tests pass
Parallel Opportunities
- T002 + T003 (Setup) can run in parallel
- All test tasks (T012–T014, T019–T021, T026–T027) 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
# 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)
- Complete Phase 1: Setup (T001–T003)
- Complete Phase 2: Foundational (T004–T011)
- Complete Phase 3: User Story 1 (T012–T018)
- STOP and VALIDATE: Members see disabled + tooltip + 403
- Deploy/demo if ready
Incremental Delivery
- Setup + Foundational →
UiEnforcementready - US1 → Consistent disabled UX for members (MVP!)
- US2 → Non-member 404 enforcement
- US3 → CI-failing guardrail + all 6 surfaces migrated
- 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 1–3 (T001–T018) |