## Summary - introduce a shared tenant-owned query and record-resolution canon for first-slice Filament resources - harden direct views, row actions, bulk actions, relation managers, and workspace-admin canonical viewers against wrong-tenant access - add registry-backed rollout metadata, search posture handling, architectural guards, and focused Pest coverage for scope parity and 404/403 semantics ## Included - Spec 150 package under `specs/150-tenant-owned-query-canon-and-wrong-tenant-guards/` - shared support classes: `TenantOwnedModelFamilies`, `TenantOwnedQueryScope`, `TenantOwnedRecordResolver` - shared Filament concern: `InteractsWithTenantOwnedRecords` - resource/page/policy hardening across findings, policies, policy versions, backup schedules, backup sets, restore runs, inventory items, and Entra groups - additional regression coverage for canonical tenant state, wrong-tenant record resolution, relation-manager congruence, and action-surface guardrails ## Validation - `vendor/bin/sail artisan test --compact` passed - full suite result: `2733 passed, 8 skipped` - formatting applied with `vendor/bin/sail bin pint --dirty --format agent` ## Notes - Livewire v4.0+ compliant via existing Filament v5 stack - provider registration remains in `bootstrap/providers.php` - globally searchable first-slice posture: Entra groups scoped; policies and policy versions explicitly disabled - destructive actions continue to use confirmation and policy authorization - no new Filament assets added; existing deployment flow remains unchanged, including `php artisan filament:assets` when registered assets are used Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #180
187 lines
14 KiB
Markdown
187 lines
14 KiB
Markdown
# 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. |