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.phpandapp/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, andapp/Filament/Concerns/InteractsWithTenantOwnedRecords.php - T005 [P] Wire panel-aware tenant resolution into the shared helper in
app/Filament/Concerns/ResolvesPanelTenantContext.phpandapp/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, andapp/Filament/Concerns/ScopesGlobalSearchToTenant.php - T007 Update the architectural guard allowlists for the shared tenant-owned helper entry points in
tests/Feature/Guards/AdminTenantResolverGuardTest.phpandtests/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, andtests/Feature/Rbac/InventoryItemResourceAuthorizationTest.php - T010 [P] [US1] Extend policy and policy-version search posture coverage in
tests/Feature/Filament/PolicyResourceAdminSearchParityTest.phpandtests/Feature/Filament/PolicyVersionAdminSearchParityTest.php
Implementation for User Story 1
- T011 [US1] Migrate
app/Filament/Resources/EntraGroupResource.phpto the shared tenant-owned query and explicit record-resolution helper, and keep tenant-sensitive persisted table state aligned throughapp/Support/Filament/CanonicalAdminTenantFilterState.php - T012 [P] [US1] Migrate
app/Filament/Resources/BackupScheduleResource.php,app/Filament/Resources/BackupScheduleResource/Pages/ListBackupSchedules.php, andapp/Filament/Resources/BackupSetResource.phpto the shared tenant-owned query helper and canonical tenant-state sync - T013 [P] [US1] Migrate
app/Filament/Resources/RestoreRunResource.phpandapp/Filament/Resources/InventoryItemResource.phpto the shared tenant-owned query helper while preserving tenant-safe persisted filter/search state throughapp/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, andapp/Filament/Resources/PolicyVersionResource.phpto 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, andapp/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.phpandtests/Feature/Rbac/BackupItemsRelationManagerUiEnforcementTest.php - T017 [P] [US2] Extend relation-manager and protected-action wrong-tenant coverage in
tests/Feature/Rbac/PolicyVersionsRestoreToIntuneUiEnforcementTest.phpandtests/Feature/BackupScheduling/BackupScheduleLifecycleAuthorizationTest.php - T018 [P] [US2] Add representative protected-action 404 and 403 parity tests in
tests/Feature/Findings/FindingRbacTest.phpandtests/Feature/RunAuthorizationTenantIsolationTest.php
Implementation for User Story 2
- T019 [US2] Anchor
app/Filament/Resources/BackupSetResource/RelationManagers/BackupItemsRelationManager.phpactions to owner-record scope and reject forged foreign-tenant record IDs - T020 [P] [US2] Anchor
app/Filament/Resources/PolicyResource/RelationManagers/VersionsRelationManager.phpandapp/Filament/Resources/BackupScheduleResource/RelationManagers/BackupScheduleOperationRunsRelationManager.phpto owner-record scope and action-target congruence - T021 [US2] Normalize covered 404 versus 403 semantics in
app/Policies/BackupSchedulePolicy.php,app/Policies/FindingPolicy.php, andapp/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, andapp/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, andtests/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, andspecs/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, andtests/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, andapp/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)
- Complete Phase 1: Setup.
- Complete Phase 2: Foundational.
- Complete Phase 3: User Story 1.
- Validate the covered list, detail, direct-link, and safe-search paths.
Incremental Delivery
- Ship the shared helper and first-slice list/detail adoption.
- Add protected-action and relation-manager hardening.
- Add the architectural guard, action-surface verification, and residual inventory so future tenant-owned surfaces inherit the pattern.
Team Strategy
- One developer lands the foundational helper layer.
- A second developer can migrate covered resources for US1 while another hardens relation managers for US2.
- 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.