TenantAtlas/specs/066-rbac-ui-enforcement-helper/tasks.md
2026-01-30 17:39:03 +01:00

14 KiB
Raw Blame History

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.php with tooltip constants in app/Support/Rbac/UiTooltips.php
  • 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

  • T004 Implement UiEnforcement::forAction() static method in app/Support/Rbac/UiEnforcement.php
  • T005 Implement ->requireMembership() method (default: true) in app/Support/Rbac/UiEnforcement.php
  • T006 Implement ->requireCapability(string $capability) method in app/Support/Rbac/UiEnforcement.php
  • T007 Implement ->destructive() method (confirmation modal) in app/Support/Rbac/UiEnforcement.php
  • T008 Implement ->tooltip(string $message) override method in app/Support/Rbac/UiEnforcement.php
  • T009 Implement ->apply() method (sets hidden/disabled/guards) in app/Support/Rbac/UiEnforcement.php
  • T010 Implement UiEnforcement::forTableAction() static method in app/Support/Rbac/UiEnforcement.php
  • 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

  • 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) in app/Support/Rbac/UiEnforcement.php
  • 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
  • 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

  • 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) in app/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) in app/Support/Rbac/UiEnforcement.php
  • T024 [US2] Migrate BackupSetResource actions (row + bulk) to UiEnforcement (mixed visibility via preserveVisibility()) in app/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) in tests/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_ACKNOWLEDGE in app/Support/Auth/Capabilities.php
  • T037 Grant TENANT_FINDINGS_ACKNOWLEDGE to Owner/Manager/Operator (not Readonly) + update role-matrix tests
  • T038 Update Finding list acknowledge action to require TENANT_FINDINGS_ACKNOWLEDGE in app/Filament/Resources/FindingResource/Pages/ListFindings.php
  • 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.

  • T040 Remove BackupScheduleResource.php from the legacy allowlist after migration
  • T041 Migrate ListEntraGroupSyncRuns.php to UiEnforcement + add a focused Livewire test
  • T042 Remove ListEntraGroupSyncRuns.php from the legacy allowlist after migration
  • T043 Migrate ListProviderConnections.php create action to UiEnforcement + add a focused Livewire test
  • T044 Remove ListProviderConnections.php from the legacy allowlist after migration
  • T045 Migrate DriftLanding.php generation permission check to CapabilityResolver (remove Gate facade) + add a focused Livewire test
  • T046 Remove DriftLanding.php from the legacy allowlist after migration
  • T047 Migrate RegisterTenant.php page-level checks to CapabilityResolver + replace abort_unless() with abort()
  • T048 Remove RegisterTenant.php from the legacy allowlist after migration
  • T049 Migrate EditProviderConnection.php actions + save guards to UiEnforcement/CapabilityResolver (remove Gate facade + abort_unless) + add a focused Livewire test
  • T050 Remove EditProviderConnection.php from the legacy allowlist after migration
  • T051 Migrate CreateRestoreRun.php page authorization to CapabilityResolver (remove Gate facade + abort_unless) + add a focused Livewire test
  • T052 Remove CreateRestoreRun.php from the legacy allowlist after migration
  • T053 Migrate InventoryItemResource.php resource authorization to CapabilityResolver (remove Gate facade) + add a focused Pest test
  • T054 Remove InventoryItemResource.php from the legacy allowlist after migration
  • T055 Migrate VersionsRelationManager.php restore action to UiEnforcement/CapabilityResolver (remove Gate facade + abort_unless) + add a focused Livewire test
  • T056 Remove VersionsRelationManager.php from the legacy allowlist after migration
  • T057 Migrate BackupItemsRelationManager.php actions to UiEnforcement (remove Gate facade) + add a focused Livewire test
  • T058 Remove BackupItemsRelationManager.php from the legacy allowlist after migration
  • 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
  • T060 Remove PolicyVersionResource.php from the legacy allowlist after migration
  • T061 Migrate RestoreRunResource.php actions/bulk actions off ad-hoc patterns to UiEnforcement/CapabilityResolver (remove Gate facade + abort_unless)
  • T062 Remove RestoreRunResource.php from the legacy allowlist after migration
  • T063 Fix UiEnforcement server-side guard to use Filament lifecycle hooks (->before()) to preserve Filament action parameter injection
  • T064 Migrate PolicyResource.php actions/bulk actions off ad-hoc patterns to UiEnforcement (remove Gate facade + abort_unless)
  • T065 Remove PolicyResource.php from the legacy allowlist after migration
  • T066 Migrate EditTenant.php archive action off ad-hoc patterns to UiEnforcement (remove Gate facade + abort_unless)
  • T067 Remove EditTenant.php from the legacy allowlist after migration
  • T068 Migrate TenantMembershipsRelationManager.php actions off ad-hoc patterns to UiEnforcement (remove Gate facade)
  • T069 Remove TenantMembershipsRelationManager.php from the legacy allowlist after migration
  • T070 Migrate TenantResource.php off ad-hoc patterns to CapabilityResolver (remove Gate facade + abort_unless)
  • 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

# 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)