TenantAtlas/specs/150-tenant-owned-query-canon-and-wrong-tenant-guards/tasks.md
2026-03-18 09:30:13 +01:00

14 KiB

Tasks: Tenant-Owned Query Canon and Wrong-Tenant Guards

Input: Design documents from /specs/150-tenant-owned-query-canon-and-wrong-tenant-guards/ Prerequisites: plan.md, spec.md, research.md, data-model.md, contracts/, quickstart.md

Tests: Tests are REQUIRED for this feature because it changes runtime authorization, query scoping, relation-manager behavior, and global-search safety in a Laravel/Pest codebase.

Phase 1: Setup (Shared Infrastructure)

Purpose: Establish the rollout inventory and shared scaffolding used by all stories.

  • T001 Create the first-slice tenant-owned family inventory in app/Support/WorkspaceIsolation/TenantOwnedModelFamilies.php
  • T002 [P] Create shared tenant-owned scope scaffolding in app/Support/WorkspaceIsolation/TenantOwnedQueryScope.php and app/Support/WorkspaceIsolation/TenantOwnedRecordResolver.php
  • T003 [P] Create the feature guard baseline in tests/Feature/Guards/TenantOwnedQueryGuardTest.php

Phase 2: Foundational (Blocking Prerequisites)

Purpose: Put the canonical query and exception model in place before resource-specific work starts.

⚠️ CRITICAL: No user story work can begin until this phase is complete.

  • T004 Implement the canonical tenant-owned query contract in app/Support/WorkspaceIsolation/TenantOwnedQueryScope.php, app/Support/WorkspaceIsolation/TenantOwnedRecordResolver.php, and app/Filament/Concerns/InteractsWithTenantOwnedRecords.php
  • T005 [P] Wire panel-aware tenant resolution into the shared helper in app/Filament/Concerns/ResolvesPanelTenantContext.php and app/Support/OperateHub/OperateHubShell.php
  • T006 [P] Encode first-slice search posture and explicit scope exceptions in app/Support/WorkspaceIsolation/TenantOwnedModelFamilies.php, app/Support/WorkspaceIsolation/TenantOwnedTables.php, and app/Filament/Concerns/ScopesGlobalSearchToTenant.php
  • T007 Update the architectural guard allowlists for the shared tenant-owned helper entry points in tests/Feature/Guards/AdminTenantResolverGuardTest.php and tests/Feature/Guards/NoAdHocFilamentAuthPatternsTest.php

Checkpoint: The shared tenant-owned query canon exists, first-slice search posture and exceptions are explicit, and user story work can proceed in parallel.


Phase 3: User Story 1 - Trust tenant-owned lists and detail links (Priority: P1) 🎯 MVP

Goal: Ensure tenant-owned lists, detail pages, direct links, and safe-search paths only resolve records from the entitled tenant scope.

Independent Test: An operator entitled to two tenants can list and open only the correct tenant's records on covered surfaces, while wrong-tenant direct links resolve as 404 without leaking data.

Tests for User Story 1

  • T008 [P] [US1] Extend Entra group index, detail, and safe-search wrong-tenant coverage in tests/Feature/Filament/EntraGroupAdminScopeTest.php
  • T009 [P] [US1] Add covered tenant-owned list, detail, and persisted tenant-state parity tests in tests/Feature/Filament/TenantOwnedResourceScopeParityTest.php, tests/Feature/Filament/CanonicalAdminTenantFilterStateTest.php, and tests/Feature/Rbac/InventoryItemResourceAuthorizationTest.php
  • T010 [P] [US1] Extend policy and policy-version search posture coverage in tests/Feature/Filament/PolicyResourceAdminSearchParityTest.php and tests/Feature/Filament/PolicyVersionAdminSearchParityTest.php

Implementation for User Story 1

  • T011 [US1] Migrate app/Filament/Resources/EntraGroupResource.php to the shared tenant-owned query and explicit record-resolution helper, and keep tenant-sensitive persisted table state aligned through app/Support/Filament/CanonicalAdminTenantFilterState.php
  • T012 [P] [US1] Migrate app/Filament/Resources/BackupScheduleResource.php, app/Filament/Resources/BackupScheduleResource/Pages/ListBackupSchedules.php, and app/Filament/Resources/BackupSetResource.php to the shared tenant-owned query helper and canonical tenant-state sync
  • T013 [P] [US1] Migrate app/Filament/Resources/RestoreRunResource.php and app/Filament/Resources/InventoryItemResource.php to the shared tenant-owned query helper while preserving tenant-safe persisted filter/search state through app/Support/Filament/CanonicalAdminTenantFilterState.php
  • T014 [P] [US1] Migrate app/Filament/Resources/FindingResource.php, app/Filament/Resources/FindingResource/Pages/ListFindings.php, app/Filament/Resources/PolicyResource.php, app/Filament/Resources/PolicyResource/Pages/ListPolicies.php, and app/Filament/Resources/PolicyVersionResource.php to the shared tenant-owned query, search-posture helper, and tenant-state-safe list behavior
  • T015 [US1] Harden covered direct-view and canonical-viewer lookup paths in routes/web.php, app/Policies/EntraGroupPolicy.php, app/Policies/BackupSchedulePolicy.php, and app/Policies/FindingPolicy.php

Checkpoint: Covered tenant-owned list, detail, and direct-link paths are consistent and independently testable without story 2 or story 3 work.


Phase 4: User Story 2 - Block wrong-tenant mutations and bulk actions (Priority: P1)

Goal: Ensure row actions, bulk actions, and relation-manager actions cannot target foreign-tenant records, even with forged identifiers.

Independent Test: Covered relation managers and protected actions reject foreign-tenant record IDs with no side effects, while in-scope capability denials still return 403 semantics.

Tests for User Story 2

  • T016 [P] [US2] Extend backup item row-action and bulk-action wrong-tenant coverage in tests/Feature/Rbac/BackupItemsRelationManagerSemanticsTest.php and tests/Feature/Rbac/BackupItemsRelationManagerUiEnforcementTest.php
  • T017 [P] [US2] Extend relation-manager and protected-action wrong-tenant coverage in tests/Feature/Rbac/PolicyVersionsRestoreToIntuneUiEnforcementTest.php and tests/Feature/BackupScheduling/BackupScheduleLifecycleAuthorizationTest.php
  • T018 [P] [US2] Add representative protected-action 404 and 403 parity tests in tests/Feature/Findings/FindingRbacTest.php and tests/Feature/RunAuthorizationTenantIsolationTest.php

Implementation for User Story 2

  • T019 [US2] Anchor app/Filament/Resources/BackupSetResource/RelationManagers/BackupItemsRelationManager.php actions to owner-record scope and reject forged foreign-tenant record IDs
  • T020 [P] [US2] Anchor app/Filament/Resources/PolicyResource/RelationManagers/VersionsRelationManager.php and app/Filament/Resources/BackupScheduleResource/RelationManagers/BackupScheduleOperationRunsRelationManager.php to owner-record scope and action-target congruence
  • T021 [US2] Normalize covered 404 versus 403 semantics in app/Policies/BackupSchedulePolicy.php, app/Policies/FindingPolicy.php, and app/Policies/EntraGroupPolicy.php
  • T022 [US2] Route covered protected mutation entry points through the shared tenant-owned record resolver in app/Filament/Resources/BackupScheduleResource.php, app/Filament/Resources/FindingResource.php, and app/Filament/Resources/RestoreRunResource.php

Checkpoint: Covered mutation and relation-manager flows are independently safe against wrong-tenant execution and can be validated without story 3 guardrail work.


Phase 5: User Story 3 - Maintain one canonical query pattern (Priority: P2)

Goal: Make the tenant-owned query canon reusable and enforceable for future resource work.

Independent Test: The shared guardrail fails on new forbidden tenant-owned query patterns, and the covered resource families share the same regression matrix for index, detail, action, relation-manager, and search posture behavior.

Tests for User Story 3

  • T023 [P] [US3] Implement the tenant-owned query architectural guard in tests/Feature/Guards/TenantOwnedQueryGuardTest.php
  • T024 [P] [US3] Extend cross-resource wrong-tenant matrix and Filament action-surface contract coverage in tests/Feature/Filament/TenantOwnedResourceScopeParityTest.php, tests/Feature/Rbac/AdminGlobalSearchContextSafetyTest.php, and tests/Feature/Guards/ActionSurfaceContractTest.php

Implementation for User Story 3

  • T025 [US3] Finalize the reusable tenant-owned family registry, scope-exception map, and residual rollout inventory in app/Support/WorkspaceIsolation/TenantOwnedModelFamilies.php, app/Support/WorkspaceIsolation/TenantOwnedTables.php, and specs/150-tenant-owned-query-canon-and-wrong-tenant-guards/data-model.md
  • T026 [US3] Integrate safe-search posture, Action Surface Contract verification, and explicit action-surface exemptions with app/Filament/Concerns/ScopesGlobalSearchToTenant.php, app/Support/Ui/ActionSurface/ActionSurfaceExemptions.php, and tests/Feature/Guards/ActionSurfaceContractTest.php
  • T027 [US3] Remove remaining first-slice ad hoc tenant-owned query paths and align tenant-sensitive list-page state sync in app/Filament/Resources/PolicyResource/Pages/ListPolicies.php, app/Filament/Resources/FindingResource/Pages/ListFindings.php, and app/Filament/Resources/BackupScheduleResource/Pages/ListBackupSchedules.php

Checkpoint: The canonical query pattern is reusable, guard-protected, and independently enforceable for future resource rollouts.


Phase 6: Polish & Cross-Cutting Concerns

Purpose: Validate the completed rollout and keep the branch releasable.

  • T028 [P] Run the focused validation suite from specs/150-tenant-owned-query-canon-and-wrong-tenant-guards/quickstart.md
  • T029 Run formatting with vendor/bin/sail bin pint --dirty --format agent

Dependencies & Execution Order

Phase Dependencies

  • Setup (Phase 1): No dependencies, can start immediately.
  • Foundational (Phase 2): Depends on Setup completion and blocks all user stories.
  • User Story 1 (Phase 3): Starts after Foundational completion.
  • User Story 2 (Phase 4): Starts after Foundational completion and may reuse the shared helper from US1, but remains independently testable.
  • User Story 3 (Phase 5): Starts after Foundational completion and should land after at least one representative family uses the shared canon.
  • Polish (Phase 6): Runs after the desired story phases are complete.

User Story Dependencies

  • US1: No dependency on other stories. This is the MVP slice.
  • US2: Depends only on the foundational helper layer, not on US1 completion for conceptual correctness.
  • US3: Depends on the foundational helper layer and benefits from US1 and US2 landing first so the guard inventory reflects real adoption.

Within Each User Story

  • Tests MUST be written and fail before implementation.
  • Shared helper adoption and tenant-sensitive persisted-state handling must happen before resource-specific cleanups.
  • Policy and action-target hardening must happen before relation-manager or bulk-action cleanup is considered complete.

Parallel Opportunities

  • T002 and T003 can run in parallel.
  • T005 and T006 can run in parallel.
  • US1 test tasks T008, T009, and T010 can run in parallel.
  • US1 implementation tasks T012, T013, and T014 can run in parallel after T004 through T006.
  • US2 test tasks T016, T017, and T018 can run in parallel.
  • US2 implementation tasks T020 and T021 can run in parallel after T019 begins anchoring the relation-manager pattern.
  • US3 tasks T023 and T024 can run in parallel.

Parallel Example: User Story 1

# Launch the first-slice list/detail regressions together:
Task: "Extend Entra group index, detail, and safe-search wrong-tenant coverage in tests/Feature/Filament/EntraGroupAdminScopeTest.php"
Task: "Add covered tenant-owned list, detail, and persisted tenant-state parity tests in tests/Feature/Filament/TenantOwnedResourceScopeParityTest.php, tests/Feature/Filament/CanonicalAdminTenantFilterStateTest.php, and tests/Feature/Rbac/InventoryItemResourceAuthorizationTest.php"
Task: "Extend policy and policy-version search posture coverage in tests/Feature/Filament/PolicyResourceAdminSearchParityTest.php and tests/Feature/Filament/PolicyVersionAdminSearchParityTest.php"

# Then migrate independent resource groups in parallel:
Task: "Migrate app/Filament/Resources/BackupScheduleResource.php, app/Filament/Resources/BackupScheduleResource/Pages/ListBackupSchedules.php, and app/Filament/Resources/BackupSetResource.php to the shared tenant-owned query helper and canonical tenant-state sync"
Task: "Migrate app/Filament/Resources/RestoreRunResource.php and app/Filament/Resources/InventoryItemResource.php to the shared tenant-owned query helper while preserving tenant-safe persisted filter/search state"
Task: "Migrate app/Filament/Resources/FindingResource.php, app/Filament/Resources/FindingResource/Pages/ListFindings.php, app/Filament/Resources/PolicyResource.php, app/Filament/Resources/PolicyResource/Pages/ListPolicies.php, and app/Filament/Resources/PolicyVersionResource.php to the shared tenant-owned query, search-posture helper, and tenant-state-safe list behavior"

Implementation Strategy

MVP First (User Story 1 Only)

  1. Complete Phase 1: Setup.
  2. Complete Phase 2: Foundational.
  3. Complete Phase 3: User Story 1.
  4. Validate the covered list, detail, direct-link, and safe-search paths.

Incremental Delivery

  1. Ship the shared helper and first-slice list/detail adoption.
  2. Add protected-action and relation-manager hardening.
  3. Add the architectural guard, action-surface verification, and residual inventory so future tenant-owned surfaces inherit the pattern.

Team Strategy

  1. One developer lands the foundational helper layer.
  2. A second developer can migrate covered resources for US1 while another hardens relation managers for US2.
  3. A final pass lands the guard, action-surface verification, and residual inventory once real first-slice adoption is in place.

Notes

  • [P] tasks are limited to work on different files with no incomplete dependency overlap.
  • US1 is the recommended MVP because it proves the canonical lookup rule on visible read paths first.
  • US2 focuses on side-effect safety after the read-path canon exists.
  • US3 converts the implementation into a reusable and enforceable repository pattern.