# 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. - [X] T001 Create the first-slice tenant-owned family inventory in `app/Support/WorkspaceIsolation/TenantOwnedModelFamilies.php` - [X] T002 [P] Create shared tenant-owned scope scaffolding in `app/Support/WorkspaceIsolation/TenantOwnedQueryScope.php` and `app/Support/WorkspaceIsolation/TenantOwnedRecordResolver.php` - [X] 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. - [X] 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` - [X] T005 [P] Wire panel-aware tenant resolution into the shared helper in `app/Filament/Concerns/ResolvesPanelTenantContext.php` and `app/Support/OperateHub/OperateHubShell.php` - [X] 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` - [X] 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 - [X] T008 [P] [US1] Extend Entra group index, detail, and safe-search wrong-tenant coverage in `tests/Feature/Filament/EntraGroupAdminScopeTest.php` - [X] 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` - [X] 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 - [X] 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` - [X] 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 - [X] 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` - [X] 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 - [X] 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 - [X] 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` - [X] T017 [P] [US2] Extend relation-manager and protected-action wrong-tenant coverage in `tests/Feature/Rbac/PolicyVersionsRestoreToIntuneUiEnforcementTest.php` and `tests/Feature/BackupScheduling/BackupScheduleLifecycleAuthorizationTest.php` - [X] 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 - [X] T019 [US2] Anchor `app/Filament/Resources/BackupSetResource/RelationManagers/BackupItemsRelationManager.php` actions to owner-record scope and reject forged foreign-tenant record IDs - [X] 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 - [X] T021 [US2] Normalize covered 404 versus 403 semantics in `app/Policies/BackupSchedulePolicy.php`, `app/Policies/FindingPolicy.php`, and `app/Policies/EntraGroupPolicy.php` - [X] 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 - [X] T023 [P] [US3] Implement the tenant-owned query architectural guard in `tests/Feature/Guards/TenantOwnedQueryGuardTest.php` - [X] 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 - [X] 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` - [X] 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` - [X] 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. - [X] T028 [P] Run the focused validation suite from `specs/150-tenant-owned-query-canon-and-wrong-tenant-guards/quickstart.md` - [X] 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 ```bash # 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.