## Summary - introduce a canonical admin tenant filter-state helper and route all in-scope workspace-admin tenant resolution through `OperateHubShell::activeEntitledTenant()` - align operations monitoring, operation-run deep links, Entra group admin list/view/search behavior, and shared context-bar rendering with the documented scope contract - add the Spec 135 design artifacts, architecture note, focused guardrail coverage, and non-regression tests for filter persistence, direct-record access, and global search safety ## Validation - `vendor/bin/sail bin pint --dirty --format agent` - `vendor/bin/sail artisan test --compact tests/Feature/Monitoring/OperationsKpiHeaderTenantContextTest.php tests/Feature/Monitoring/OperationsTenantScopeTest.php tests/Feature/Monitoring/OperationsCanonicalUrlsTest.php tests/Feature/Spec085/OperationsIndexHeaderTest.php tests/Feature/Spec085/RunDetailBackAffordanceTest.php tests/Feature/Filament/OperationRunListFiltersTest.php tests/Feature/Filament/EntraGroupAdminScopeTest.php tests/Feature/Filament/EntraGroupGlobalSearchScopeTest.php tests/Feature/DirectoryGroups/BrowseGroupsTest.php tests/Feature/Filament/EntraGroupEnterpriseDetailPageTest.php tests/Feature/Filament/PolicyVersionResolvedReferenceLinksTest.php tests/Feature/Filament/EntraGroupResolvedReferencePresentationTest.php tests/Feature/Guards/AdminTenantResolverGuardTest.php tests/Feature/OpsUx/OperateHubShellTest.php tests/Feature/Filament/Alerts/AlertsKpiHeaderTest.php tests/Feature/Alerts/AlertDeliveryDeepLinkFiltersTest.php` - `vendor/bin/sail artisan test --compact tests/Feature/Filament/TableStatePersistenceTest.php tests/Feature/Filament/TenantScopingTest.php tests/Feature/Filament/Alerts/AlertDeliveryViewerTest.php tests/Unit/Support/References/CapabilityAwareReferenceResolverTest.php` ## Notes - Filament v5 remains on Livewire v4.0+ compliant surfaces only. - No provider registration changes were needed; Laravel 12 provider registration remains in `bootstrap/providers.php`. - Entra group global search remains enabled and is now scoped to the canonical admin tenant contract. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #164
224 lines
15 KiB
Markdown
224 lines
15 KiB
Markdown
# Implementation Plan: Spec 135 Canonical Tenant Context Resolution
|
|
|
|
**Branch**: `135-canonical-tenant-context-resolution` | **Date**: 2026-03-11 | **Spec**: `specs/135-canonical-tenant-context-resolution/spec.md`
|
|
**Spec (absolute)**: `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/135-canonical-tenant-context-resolution/spec.md`
|
|
**Input**: `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/135-canonical-tenant-context-resolution/spec.md`
|
|
|
|
## Summary
|
|
|
|
Establish and enforce one explicit tenant-context rule across tenant-sensitive admin flows: tenant-panel screens continue to use panel-native tenant context, while workspace-admin screens use `OperateHubShell::activeEntitledTenant(Request $request): ?Tenant` as the only public canonical operational tenant resolver, with Filament tenant taking precedence over remembered tenant state when both exist.
|
|
|
|
Implementation focuses on three concrete remediation targets already visible in the codebase:
|
|
1. the workspace-admin Operations monitoring shell and KPI widget,
|
|
2. the OperationRun resource filters and detail navigation behavior used by Monitoring,
|
|
3. the Entra group list, direct-record, and global-search access path.
|
|
|
|
The work also adds one lightweight regression guard so new admin-panel code does not reintroduce raw tenant-panel-native tenant reads, while preserving tenant-panel-native behavior in approved tenant-context files.
|
|
This iteration remediates the known high-risk surfaces named in the spec and records a residual inventory for remaining admin call sites that are already compliant or intentionally deferred.
|
|
|
|
## Technical Context
|
|
|
|
**Language/Version**: PHP 8.4 on Laravel 12
|
|
**Primary Dependencies**: Filament v5, Livewire v4, Pest v4, Laravel Sail
|
|
**Storage**: PostgreSQL application database
|
|
**Testing**: Pest feature tests via `vendor/bin/sail artisan test --compact`
|
|
**Target Platform**: Web application with Filament admin and tenant panels
|
|
**Project Type**: Laravel monolith with Filament resources, pages, widgets, policies, and support-layer helpers
|
|
**Performance Goals**: Maintain DB-only render behavior for monitoring/admin pages, avoid broader-than-visible query scopes, and keep context resolution deterministic per request without extra remote calls
|
|
**Constraints**:
|
|
- No dependency changes.
|
|
- No new Graph calls, queued jobs, or write workflows.
|
|
- No generic “universal tenancy resolver” that erases the admin versus tenant-panel distinction.
|
|
- Direct URLs, deep links, and global search must fail safe when context is missing or out of scope.
|
|
- Existing Filament action surfaces stay structurally unchanged unless a safe-state affordance is required.
|
|
**Scale/Scope**: Targeted architecture hardening across existing support helpers, 3 in-scope admin surfaces, 1 reference pattern, 1 lightweight guardrail, and a focused regression matrix covering remembered-only, Filament-only, conflict, and no-context states
|
|
|
|
## Constitution Check
|
|
|
|
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
|
|
|
|
- **Inventory-first / snapshots**: PASS — this feature changes read-time scope resolution only; inventory, backups, and snapshots remain unchanged.
|
|
- **Read/write separation**: PASS — no new mutating flow is introduced; this is read, filter, navigation, and authorization hardening.
|
|
- **Graph contract path**: PASS — no Graph calls or contract-registry changes are involved.
|
|
- **Deterministic capabilities**: PASS — no capability registry changes; existing policies and capability helpers remain authoritative.
|
|
- **RBAC-UX planes**: PASS — `/admin` admin flows and `/admin/t/{tenant}/...` tenant-context flows stay explicitly separate; non-members remain 404, in-scope capability denials remain 403.
|
|
- **Workspace isolation**: PASS — workspace-admin routes stay tenant-safe even without tenant in the URL; missing workspace context continues to collapse to safe empty/not-found behavior.
|
|
- **Tenant isolation**: PASS — all in-scope queries, filter options, direct-record lookups, and search results are planned to remain bounded by tenant entitlement.
|
|
- **Global search safety**: PASS — tenant-sensitive resources will either adopt admin-safe scoped search behavior or disable global search where parity cannot be guaranteed.
|
|
- **Run observability**: PASS — no new operational run type or lifecycle behavior is introduced; Monitoring remains DB-only at render time.
|
|
- **Ops-UX lifecycle / summary counts / notifications**: PASS — unchanged.
|
|
- **BADGE-001**: PASS — badge semantics stay centralized; only the tenant context feeding those surfaces is normalized.
|
|
- **UI-NAMING-001**: PASS — plan preserves operator-facing language such as “Filtered by tenant” and explicit safe-state wording.
|
|
- **Filament Action Surface Contract**: PASS WITH EXEMPTION — affected resources/pages remain read-only or existing-action surfaces; the feature changes scoping and safe outcomes, not action inventory.
|
|
- **Filament UX-001**: PASS WITH EXEMPTION — no layout redesign is required beyond explicit safe no-context messaging.
|
|
- **Livewire v4.0+ compliance**: PASS — Filament v5 + Livewire v4 remain unchanged and are the required stack for all affected screens.
|
|
- **Provider registration location**: PASS — no panel-provider changes are needed; Laravel 12 provider registration remains in `bootstrap/providers.php`.
|
|
|
|
## Phase 0 — Research Summary
|
|
|
|
Research findings are recorded in `specs/135-canonical-tenant-context-resolution/research.md`.
|
|
|
|
Key decisions:
|
|
- Keep the two-context model explicit instead of abstracting it into a single cross-panel resolver.
|
|
- Treat `OperateHubShell::activeEntitledTenant()` as the admin-panel source of truth because it already implements the desired priority order: entitled Filament tenant first, remembered workspace tenant second, otherwise null.
|
|
- Use the existing alert-delivery resource as the reference pattern for admin-scope query, filter default, and filter-option alignment.
|
|
- Harden Entra group list/view/search behavior because the current table path scopes with `Tenant::current()` while `getEloquentQuery()` remains broader, creating direct-record and potential search drift.
|
|
- Implement a lightweight architectural guard as a focused test that flags `Filament::getTenant()` / `Tenant::current()` reads inside approved admin directories, with an allowlist for tenant-panel-native files.
|
|
|
|
## Phase 1 — Design & Contracts
|
|
|
|
### Data Model
|
|
|
|
Design details are recorded in `specs/135-canonical-tenant-context-resolution/data-model.md`.
|
|
|
|
Key design points:
|
|
- No schema changes are required.
|
|
- The feature introduces a request-time domain model around context state, not persisted business data.
|
|
- Canonical admin context is derived from workspace membership, current Filament tenant, remembered tenant session state, and entitlement checks.
|
|
- Filter state becomes explicitly revalidated view state, not trusted persisted state.
|
|
|
|
### Contracts
|
|
|
|
Internal behavior contracts are recorded in `specs/135-canonical-tenant-context-resolution/contracts/context-resolution-matrix.yaml`.
|
|
|
|
Contract scope:
|
|
- `/admin/operations`
|
|
- tenantless operation-run detail flows under `/admin/operations/{run}`
|
|
- admin-scoped OperationRun filters and related option lists
|
|
- Entra group list/view/global-search access paths
|
|
- alert-delivery admin resource as the non-regression reference pattern
|
|
|
|
### Quickstart
|
|
|
|
Implementation and verification steps are recorded in `specs/135-canonical-tenant-context-resolution/quickstart.md`.
|
|
|
|
### Post-Design Constitution Re-check
|
|
|
|
- **Inventory-first / snapshots**: PASS — unchanged.
|
|
- **Read/write separation**: PASS — only read-path behavior and regression guards are added.
|
|
- **Graph contract path**: PASS — still no Graph activity.
|
|
- **RBAC-UX**: PASS — design explicitly keeps 404 for out-of-scope access, 403 for capability denial after scope establishment, and server-side authorization for detail/search/list paths.
|
|
- **Workspace isolation**: PASS — admin routes without workspace or tenant context stay bounded and deterministic.
|
|
- **Tenant isolation**: PASS — detail URLs and search results are planned to use the same tenant-bound query contract as lists.
|
|
- **Global search safety**: PASS — design requires parity or explicit disablement for tenant-sensitive admin resources.
|
|
- **Run observability / Ops-UX lifecycle**: PASS — no changes.
|
|
- **BADGE-001 / UI-NAMING-001**: PASS — existing centralized badge and operator language rules remain intact.
|
|
- **Filament Action Surface Contract**: PASS WITH EXEMPTION — no new destructive actions, no action-surface expansion.
|
|
- **Livewire v4.0+ compliance**: PASS — all affected Filament screens remain on the supported Filament v5 / Livewire v4 stack.
|
|
- **Provider registration location**: PASS — no provider changes; `bootstrap/providers.php` remains the correct Laravel 12 location.
|
|
|
|
## Project Structure
|
|
|
|
### Documentation (this feature)
|
|
|
|
```text
|
|
specs/135-canonical-tenant-context-resolution/
|
|
├── plan.md
|
|
├── research.md
|
|
├── data-model.md
|
|
├── quickstart.md
|
|
├── contracts/
|
|
│ └── context-resolution-matrix.yaml
|
|
└── tasks.md
|
|
```
|
|
|
|
### Source Code (repository root)
|
|
|
|
```text
|
|
app/
|
|
├── Filament/
|
|
│ ├── Concerns/
|
|
│ ├── Pages/
|
|
│ │ ├── Monitoring/
|
|
│ │ └── Operations/
|
|
│ ├── Resources/
|
|
│ │ ├── EntraGroupResource/
|
|
│ │ └── AlertDeliveryResource/
|
|
│ └── Widgets/
|
|
│ └── Operations/
|
|
├── Models/
|
|
├── Policies/
|
|
├── Support/
|
|
│ ├── OperateHub/
|
|
│ ├── Workspaces/
|
|
│ └── Navigation/
|
|
└── Http/Controllers/
|
|
|
|
resources/views/
|
|
└── filament/
|
|
|
|
tests/
|
|
├── Feature/
|
|
│ ├── Monitoring/
|
|
│ ├── Operations/
|
|
│ ├── Filament/
|
|
│ └── Spec085/
|
|
└── Unit/
|
|
```
|
|
|
|
**Structure Decision**: Keep implementation inside the existing Laravel / Filament monolith. Reuse the support-layer admin resolver in `app/Support/OperateHub/`, harden the affected Filament pages/resources/widgets in place, and add focused Pest regression tests rather than introducing new infrastructure.
|
|
|
|
## Phase 2 — Implementation Planning
|
|
|
|
Implementation should be delivered in the following slices so `/speckit.tasks` can break work cleanly.
|
|
|
|
1. **Document and centralize the canonical rule**
|
|
- Refine and document `OperateHubShell::activeEntitledTenant(Request $request): ?Tenant` as the only public admin-panel tenant resolver.
|
|
- Any helper introduced for implementation convenience must remain internal-only and delegate to `OperateHubShell::activeEntitledTenant(Request)` rather than becoming a second public resolver.
|
|
- Ensure callers can distinguish admin-panel canonical context from tenant-panel-native context without introducing a universal cross-panel API.
|
|
- Preserve remembered-only support and explicit null/no-context outcomes.
|
|
|
|
2. **Remediate Monitoring shell consistency**
|
|
- Update `OperationsKpiHeader` to use the same canonical admin tenant as `Monitoring\Operations`.
|
|
- Verify header label, KPI cards, table query, and “Show all tenants” behavior stay aligned in remembered-only, Filament-only, conflict, and no-context states.
|
|
- Make the operations-index no-context outcome explicit: show the workspace-scoped `All tenants` state only for entitled tenants in the active workspace, clear tenant-default filters, and hide tenant-only KPI behavior instead of inferring a tenant.
|
|
|
|
3. **Harden OperationRun resource filter parity**
|
|
- Ensure tenant-sensitive filter options and defaults use the canonical admin resolver, not raw `Filament::getTenant()`.
|
|
- Revalidate persisted filter state whenever canonical tenant context changes between requests.
|
|
- Keep direct record/detail access bounded by the same workspace + tenant entitlement rules as the list, with explicit `404` for non-members or out-of-scope tenant access and `403` only after scope membership is established where capability gates apply.
|
|
- Make the run-detail no-context outcome explicit: allow rendering only for active-workspace records that still pass tenant entitlement checks when tenant-bound; otherwise respond as not found.
|
|
|
|
4. **Harden Entra group list, detail, and search behavior**
|
|
- Replace raw `Tenant::current()` assumptions for admin-sensitive access paths with explicit panel-aware resolution.
|
|
- Align `table()` scoping, `getEloquentQuery()`, record URL resolution, and view page behavior so direct record URLs cannot exceed list scope.
|
|
- Apply admin-safe global search scoping or disable global search for this resource if parity is not maintainable.
|
|
- Make the admin Entra-group no-context outcome explicit: admin list/detail requests respond as not found, and admin global search returns no tenant-owned Entra-group results when no canonical admin tenant exists.
|
|
- Preserve tenant-panel-native behavior in explicitly approved tenant-context files.
|
|
|
|
5. **Retain alert-delivery as the reference pattern**
|
|
- Confirm the alert-delivery resource continues to model the intended admin query and filter alignment.
|
|
- Refactor only if needed to extract a reusable pattern without regressing current behavior.
|
|
|
|
6. **Add the lightweight guardrail**
|
|
- Implement a focused regression/architecture test that scans approved admin Filament directories for new raw `Filament::getTenant()` / `Tenant::current()` patterns.
|
|
- Maintain an explicit allowlist for tenant-panel-native files and equivalent approved panel-native surfaces.
|
|
- Fail with actionable file references so future regressions are cheap to fix.
|
|
|
|
7. **Record residual scope explicitly**
|
|
- Document remaining admin tenant-resolution call sites outside the remediation set.
|
|
- Mark each residual call site as already compliant, intentionally excepted, or deferred beyond this iteration.
|
|
|
|
8. **Expand the regression matrix**
|
|
- Extend existing Spec 085 monitoring coverage for remembered-only, Filament-only, conflict, and no-context states.
|
|
- Add focused tests for OperationRun filter default/option parity, stale persisted filter handling, and positive/negative authorization semantics.
|
|
- Add Entra group list/view/search regression tests for direct URLs, out-of-scope access, no-context safety, and non-member-safe search behavior.
|
|
- Reuse existing factories, workspace-session helpers, and authorization helpers; avoid bespoke test harnesses.
|
|
|
|
## Testing Strategy
|
|
|
|
- Extend existing monitoring tests in `tests/Feature/Spec085/` rather than replacing them.
|
|
- Add focused feature coverage for:
|
|
- workspace-admin operations page and KPI consistency,
|
|
- tenantless operation-run detail access parity,
|
|
- OperationRun filter default and option scoping,
|
|
- Entra group list/view/global-search tenant safety,
|
|
- explicit `404` vs `403` authorization semantics for in-scope admin access paths,
|
|
- guardrail enforcement for new admin-only raw tenant reads.
|
|
- Run only the affected Pest files with Sail during implementation.
|
|
- Finish with `vendor/bin/sail bin pint --dirty --format agent` and the minimal related `vendor/bin/sail artisan test --compact ...` commands.
|
|
|
|
## Complexity Tracking
|
|
|
|
No constitution violations or complexity exemptions are required for this plan.
|