## Summary - complete Spec 136 canonical admin tenant rollout across admin-visible and shared Filament surfaces - add the shared panel-aware tenant resolver helper, persisted filter-state synchronization, and admin navigation segregation for tenant-sensitive resources - expand regression, guard, and parity coverage for admin-path tenant resolution, stale filters, workspace-wide tenant-default surfaces, and panel split behavior ## Validation - `vendor/bin/sail artisan test --compact tests/Feature/Guards/AdminTenantResolverGuardTest.php` - `vendor/bin/sail artisan test --compact tests/Feature/Filament/TableStatePersistenceTest.php` - `vendor/bin/sail artisan test --compact --filter='CanonicalAdminTenantFilterState|PolicyResource|BackupSchedule|BackupSet|FindingResource|BaselineCompareLanding|RestoreRunResource|InventoryItemResource|PolicyVersionResource|ProviderConnectionResource|TenantDiagnostics|InventoryCoverage|InventoryKpiHeader|AuditLog|EntraGroup'` - `vendor/bin/sail bin pint --dirty --format agent` ## Notes - Livewire v4.0+ compliance is preserved with Filament v5. - Provider registration remains unchanged in `bootstrap/providers.php`. - `PolicyResource` and `PolicyVersionResource` have admin global search disabled explicitly; `EntraGroupResource` keeps admin-aware scoped search with a View page. - Destructive and governance-sensitive actions retain existing confirmation and authorization behavior while using canonical tenant parity. - No new assets were introduced, so deployment asset strategy is unchanged and does not add new `filament:assets` work. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #165
186 lines
7.1 KiB
Markdown
186 lines
7.1 KiB
Markdown
# Data Model: Spec 136 Admin Panel Canonical Tenant Resolution Full Rollout
|
|
|
|
## Overview
|
|
|
|
This feature introduces no new database tables or persisted business objects. Its data model is request-time and behavioral: it formalizes how surface classification, panel mode, canonical admin tenant context, session-backed filter state, and sensitive actions combine into one deterministic tenant outcome.
|
|
|
|
## Entity: Surface Inventory Entry
|
|
|
|
**Purpose**: Represents one admin-visible or admin-reachable surface in the rollout manifest.
|
|
|
|
**Fields**:
|
|
- `surface_name`
|
|
- `class_name`
|
|
- `surface_kind` enum: `resource`, `page`, `widget`, `shared_helper`, `guarded_file`
|
|
- `classification` enum: `type_a`, `type_b`, `type_c`
|
|
- `visibility_mode` enum: `admin_visible`, `shared_code_path`, `tenant_panel_only_but_reviewed`
|
|
- `has_persisted_tenant_filters` boolean
|
|
- `has_global_search` boolean
|
|
- `has_sensitive_actions` boolean
|
|
- `guard_required` boolean
|
|
|
|
**Relationships**:
|
|
- may reference one resource, page, or widget class
|
|
- may depend on one or more support-layer resolver or filter-state helpers
|
|
|
|
**Validation rules**:
|
|
- Every rollout surface must have exactly one classification.
|
|
- Type A and Type B surfaces must be guard-covered unless explicitly documented as an exception.
|
|
- Type C surfaces must not acquire hidden tenant enforcement in read or write paths.
|
|
|
|
## Entity: Canonical Admin Tenant Context
|
|
|
|
**Purpose**: Represents the single tenant context used by workspace-admin tenant-sensitive flows.
|
|
|
|
**Source fields**:
|
|
- `workspace_id`
|
|
- `panel_id`
|
|
- `route_tenant_id` nullable
|
|
- `filament_tenant_id` nullable
|
|
- `remembered_tenant_id` nullable
|
|
- `resolved_tenant_id` nullable
|
|
- `resolution_source` enum: `route`, `filament`, `remembered`, `none`
|
|
- `is_entitled` boolean
|
|
|
|
**Relationships**:
|
|
- belongs to one current workspace
|
|
- may resolve to one entitled tenant
|
|
- drives header context, queries, filter defaults, widgets, links, and sensitive actions for Type A and Type B admin surfaces
|
|
|
|
**Validation rules**:
|
|
- `resolved_tenant_id` must be null when no entitled tenant exists.
|
|
- The same resolved tenant must drive every tenant-sensitive element of the same admin surface.
|
|
- When the panel is admin, raw panel-native tenant reads are not valid substitutes for this entity.
|
|
|
|
## Entity: Panel Resolver Contract
|
|
|
|
**Purpose**: Encodes which resolver is allowed in a given panel mode.
|
|
|
|
**Fields**:
|
|
- `panel_id`
|
|
- `allowed_source` enum: `operate_hub_shell`, `filament_tenant`, `none`
|
|
- `fallback_behavior` enum: `remembered_allowed`, `no_fallback`, `safe_none`
|
|
- `applies_to` enum: `admin`, `tenant`, `shared_surface`
|
|
|
|
**Validation rules**:
|
|
- Admin panel uses `operate_hub_shell`.
|
|
- Tenant panel uses `filament_tenant`.
|
|
- Shared surfaces must branch by current panel rather than blending both rules.
|
|
|
|
## Entity: Persisted Tenant Filter Session State
|
|
|
|
**Purpose**: Represents session-backed filter state whose validity depends on the current canonical admin tenant.
|
|
|
|
**Fields**:
|
|
- `filters_session_key`
|
|
- `tenant_sensitive_filters` array
|
|
- `previous_resolved_tenant_id` nullable
|
|
- `current_resolved_tenant_id` nullable
|
|
- `resolution_action` enum: `apply`, `reseed`, `clear`
|
|
- `has_invalid_values` boolean
|
|
|
|
**Relationships**:
|
|
- belongs to one table or page surface
|
|
- depends on one `Canonical Admin Tenant Context`
|
|
|
|
**Validation rules**:
|
|
- Tenant-sensitive filter values must be cleared or reseeded when resolved tenant changes.
|
|
- If `current_resolved_tenant_id` is null, tenant-specific filter state must not remain active.
|
|
- Workspace-wide Type B surfaces may preserve non-tenant filters while clearing or reseeding tenant-specific ones.
|
|
|
|
## Entity: Sensitive Action Scope Contract
|
|
|
|
**Purpose**: Represents the tenant parity requirement between visible surface context and a sensitive action target.
|
|
|
|
**Fields**:
|
|
- `surface_name`
|
|
- `action_name`
|
|
- `visible_tenant_id` nullable
|
|
- `query_tenant_id` nullable
|
|
- `action_tenant_id` nullable
|
|
- `authorization_outcome` enum: `ok`, `not_found`, `forbidden`
|
|
- `confirmation_required` boolean
|
|
|
|
**Relationships**:
|
|
- belongs to one rollout surface
|
|
- depends on one resolved tenant context and one authorization decision
|
|
|
|
**Validation rules**:
|
|
- `action_tenant_id` must equal `visible_tenant_id` for Type A surfaces.
|
|
- Out-of-scope or missing tenant access must remain `not_found` when membership is not established.
|
|
- Existing destructive-like actions must still require confirmation and server-side authorization.
|
|
|
|
## Entity: Search and Record Resolution Contract
|
|
|
|
**Purpose**: Represents list, detail, deep-link, and global-search parity for tenant-sensitive resources.
|
|
|
|
**Fields**:
|
|
- `surface_name`
|
|
- `access_path` enum: `list`, `detail`, `direct_url`, `deep_link`, `global_search`
|
|
- `panel_mode` enum: `admin`, `tenant`
|
|
- `resolved_tenant_id` nullable
|
|
- `record_tenant_id` nullable
|
|
- `parity_outcome` enum: `aligned`, `disabled`, `not_found`, `forbidden`
|
|
|
|
**Validation rules**:
|
|
- Detail and direct access must never be broader than list scope.
|
|
- Global search must either match list/detail parity or be disabled.
|
|
- Admin no-context behavior must be explicit and deterministic per surface class.
|
|
|
|
## Entity: Guard Coverage Entry
|
|
|
|
**Purpose**: Documents whether a file is enforced by the admin tenant resolver guard or allowed as an exception.
|
|
|
|
**Fields**:
|
|
- `relative_path`
|
|
- `surface_name`
|
|
- `guard_status` enum: `guarded`, `exception`
|
|
- `exception_reason` nullable
|
|
- `review_owner`
|
|
|
|
**Validation rules**:
|
|
- Exceptions must be explicit and stable.
|
|
- Admin-only files are not valid exceptions for raw `Filament::getTenant()` or `Tenant::current()` reads.
|
|
- Guard output must clearly distinguish true violations from approved tenant-panel-native usage.
|
|
|
|
## State Transitions
|
|
|
|
### Canonical admin tenant state
|
|
|
|
1. `none`
|
|
- No entitled route, Filament, or remembered tenant is available.
|
|
- Outcome: safe no-tenant-selected behavior by surface type.
|
|
|
|
2. `remembered`
|
|
- Only remembered tenant is valid and entitled.
|
|
- Outcome: remembered tenant becomes the canonical admin tenant for the full request.
|
|
|
|
3. `filament`
|
|
- A Filament tenant is valid and entitled.
|
|
- Outcome: Filament tenant becomes the canonical admin tenant for the full request.
|
|
|
|
4. `route`
|
|
- A route tenant parameter is present and entitled for a tenant-panel or shared route.
|
|
- Outcome: route tenant governs the request where panel-native semantics apply.
|
|
|
|
### Persisted filter synchronization state
|
|
|
|
1. `unchanged`
|
|
- Previous and current resolved tenant IDs match.
|
|
- Outcome: persisted tenant filter values may remain.
|
|
|
|
2. `tenant_switched`
|
|
- Previous and current resolved tenant IDs differ.
|
|
- Outcome: tenant-sensitive filter values are cleared or reseeded.
|
|
|
|
3. `tenant_removed`
|
|
- Current resolved tenant ID becomes null.
|
|
- Outcome: tenant-sensitive filter values are cleared.
|
|
|
|
## Invariants
|
|
|
|
- One workspace-admin surface uses one tenant source for every tenant-sensitive element.
|
|
- Shared resources must branch by panel mode instead of mixing admin and tenant rules inside one execution path.
|
|
- Persisted tenant filter state is never trusted without synchronization.
|
|
- Search, list, detail, and deep-link behavior cannot exceed the same tenant boundary.
|
|
- Existing destructive or governance-sensitive actions keep their confirmations and authorization while gaining tenant-target parity. |