TenantAtlas/docs/research/admin-canonical-tenant-rollout.md
ahmido 1f3619bd16 feat: tenant-owned query canon and wrong-tenant guards (#180)
## 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
2026-03-18 08:33:13 +00:00

6.4 KiB

Admin Canonical Tenant Rollout

Purpose

Spec 136 completes the workspace-admin canonical tenant rule across admin-visible and admin-reachable shared surfaces. Workspace-admin requests under /admin/... resolve tenant context through App\Support\OperateHub\OperateHubShell::activeEntitledTenant(request()). Tenant-panel requests under /admin/t/{tenant}/... keep panel-native tenant semantics.

Rollout Manifest

Type A: Hard tenant-sensitive

  • app/Filament/Resources/PolicyResource.php
  • app/Filament/Resources/BackupScheduleResource.php
  • app/Filament/Resources/BackupSetResource.php
  • app/Filament/Resources/FindingResource.php
  • app/Filament/Pages/BaselineCompareLanding.php
  • app/Filament/Resources/RestoreRunResource.php
  • app/Filament/Resources/InventoryItemResource.php
  • app/Filament/Resources/PolicyVersionResource.php
  • app/Filament/Pages/TenantDiagnostics.php
  • app/Filament/Pages/InventoryCoverage.php
  • app/Filament/Widgets/Inventory/InventoryKpiHeader.php

Type B: Workspace-wide with tenant-default behavior

  • app/Filament/Resources/ProviderConnectionResource.php
  • app/Filament/Pages/Monitoring/AuditLog.php
  • app/Filament/Resources/EntraGroupResource.php
  • app/Filament/Resources/AlertDeliveryResource.php

Type C: Workspace-only non-regression references

  • app/Filament/Resources/AlertRuleResource.php
  • app/Filament/Resources/BaselineProfileResource.php
  • app/Filament/Resources/BaselineSnapshotResource.php
  • app/Filament/Resources/TenantResource.php

Persisted Filter Sync

Apply App\Support\Filament\CanonicalAdminTenantFilterState::sync() on admin list surfaces that persist filters in session.

  • app/Filament/Resources/AlertDeliveryResource/Pages/ListAlertDeliveries.php
  • app/Filament/Pages/Monitoring/AuditLog.php
  • app/Filament/Resources/BackupScheduleResource/Pages/ListBackupSchedules.php
  • app/Filament/Resources/BackupSetResource/Pages/ListBackupSets.php
  • app/Filament/Resources/FindingResource/Pages/ListFindings.php
  • app/Filament/Resources/InventoryItemResource/Pages/ListInventoryItems.php
  • app/Filament/Resources/PolicyVersionResource/Pages/ListPolicyVersions.php
  • app/Filament/Resources/ProviderConnectionResource/Pages/ListProviderConnections.php
  • app/Filament/Resources/EntraGroupResource/Pages/ListEntraGroups.php

Guarded Files

These files must not introduce raw admin-path Tenant::current() or Filament::getTenant() reads:

  • app/Filament/Pages/BaselineCompareLanding.php
  • app/Filament/Pages/TenantDiagnostics.php
  • app/Filament/Pages/InventoryCoverage.php
  • app/Filament/Widgets/Inventory/InventoryKpiHeader.php
  • app/Filament/Resources/PolicyResource.php
  • app/Filament/Resources/BackupScheduleResource.php
  • app/Filament/Resources/InventoryItemResource.php
  • app/Filament/Resources/PolicyVersionResource.php
  • app/Filament/Resources/ProviderConnectionResource.php
  • app/Filament/Resources/AlertDeliveryResource.php
  • app/Filament/Resources/AlertDeliveryResource/Pages/ListAlertDeliveries.php
  • app/Filament/Pages/Monitoring/AuditLog.php
  • app/Filament/Resources/ProviderConnectionResource/Pages/ListProviderConnections.php

Exception Inventory

Approved tenant-panel-native or bootstrapping exceptions:

  • app/Filament/Pages/ChooseTenant.php
  • app/Http/Controllers/SelectTenantController.php
  • app/Support/Middleware/EnsureFilamentTenantSelected.php
  • app/Filament/Concerns/ResolvesPanelTenantContext.php

app/Filament/Concerns/ResolvesPanelTenantContext.php is the only shared internal delegation wrapper allowed for this rollout. It is not a new public resolver. Admin semantics still come from OperateHubShell.

  • PolicyResource: global search disabled explicitly.
  • PolicyVersionResource: global search disabled explicitly.
  • EntraGroupResource: global search remains enabled and uses admin-aware scoping with a View page.

Future-Surface Rule

Any new admin-visible or admin-reachable tenant-sensitive Filament surface must:

  • resolve workspace-admin tenant context through OperateHubShell or the internal ResolvesPanelTenantContext helper
  • route tenant-owned list/detail resolution through the shared InteractsWithTenantOwnedRecords helper where the surface is tenant-owned
  • keep tenant-panel requests panel-native
  • synchronize persisted tenant-derived filters before render when persistFiltersInSession() is used
  • disable global search unless list, detail, and search parity are explicitly tenant-safe
  • keep destructive actions on ->action(...) with ->requiresConfirmation() and server-side authorization

Verification Log

Wave 1

  • 2026-03-11: automated parity coverage added for representative tenant-sensitive resources and admin list scoping.
  • 2026-03-12: manual tenant-switch verification completed on /admin/findings with Phoenicon (DEV) and YPTW2 (DEV).
  • 2026-03-12: the admin shell changed visible finding rows and row-action tenant IDs together after switching tenants, confirming header context, list queries, and deep links stayed aligned.

Wave 2

  • 2026-03-11: automated stale-filter and tenant-default coverage added for persisted filter surfaces.
  • 2026-03-12: manual verification completed on /admin/inventory/inventory-coverage after fixing remembered-canonical-tenant handling in EnsureFilamentTenantSelected.
  • 2026-03-12: switching from YPTW2 (DEV) to Phoenicon (DEV) changed tenant-driven KPI values while the shared support-matrix table remained available, confirming page-widget parity and the expected safe no-data state.
  • 2026-03-12: tenant-panel navigation under /admin/t/{tenant}/... remained route-bound and required explicit tenant switching through the chooser flow instead of reusing remembered admin context.

Wave 3

  • 2026-03-11: guard inventory and shared-surface panel-split coverage updated for the canonical rollout.
  • 2026-03-12: manual verification completed on /admin/provider-connections and confirmed the base dataset stays workspace-wide while the default tenant filter follows the active canonical admin tenant.
  • 2026-03-12: clearing the tenant filter exposed workspace-wide provider connections, while switching back to another tenant reseeded the tenant-default pill and tenant_id deep links for the newly selected tenant.
  • 2026-03-12: admin search safety remained unchanged from the rollout contract: PolicyResource and PolicyVersionResource stay disabled for global search, and EntraGroupResource keeps admin-aware global search with a View page.