## 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
208 lines
23 KiB
Markdown
208 lines
23 KiB
Markdown
# Feature Specification: Canonical Tenant Context Resolution
|
|
|
|
**Feature Branch**: `135-canonical-tenant-context-resolution`
|
|
**Created**: 2026-03-11
|
|
**Status**: Draft
|
|
**Input**: User description: "Spec 135 — Canonical Tenant Context Resolution for Admin and Tenant Panel Flows"
|
|
|
|
## Spec Scope Fields *(mandatory)*
|
|
|
|
- **Scope**: canonical-view
|
|
- **Primary Routes**:
|
|
- Workspace-admin monitoring and operations flows under `/admin/...`
|
|
- Workspace-admin resource detail and list flows that surface tenant-sensitive data under `/admin/...`
|
|
- Tenant-context operational flows under `/admin/t/{tenant}/...` that already rely on panel-native tenant context
|
|
- Direct record URLs, deep links, and global-search entry points that can open tenant-sensitive resources without first visiting a list page
|
|
- **Data Ownership**:
|
|
- Existing workspace-owned monitoring, operation, alert, and governance records remain the system of record
|
|
- Existing tenant-owned or tenant-associated data remains unchanged; this feature standardizes how active tenant context is resolved before data is shown, filtered, or linked
|
|
- No new business domain is introduced; the feature defines and enforces a single resolution rule for the known in-scope Filament screens, searches, and detail routes named below, and leaves a documented residual inventory for remaining admin call sites that are already compliant or intentionally out of scope for this iteration
|
|
- **RBAC**:
|
|
- Workspace membership remains the prerequisite for workspace-admin monitoring flows
|
|
- Tenant entitlement remains required before tenant-scoped or tenant-sensitive data may be shown inside admin or tenant-panel journeys
|
|
- Capability checks continue to gate protected actions, links, and resource access
|
|
- Non-members and out-of-scope tenant access remain deny-as-not-found, while in-scope users without the needed capability continue to receive explicit forbidden behavior on protected targets
|
|
|
|
For canonical-view specs, the spec MUST define:
|
|
|
|
- **Default filter behavior when tenant-context is active**: Workspace-admin flows that support an active operational tenant must default filters, filter options, widgets, badges, counts, and linked destinations from the same canonical tenant context. Tenant-panel flows remain panel-native and must not inherit fallback admin semantics.
|
|
- **Explicit entitlement checks preventing cross-tenant leakage**: Every in-scope admin query, filter option set, record lookup, and global-search result must enforce workspace and tenant entitlement checks before rendering data, labels, or navigation. No deep link, global-search path, or direct record URL may broaden scope beyond the canonical tenant context and the user's entitled access.
|
|
|
|
## User Scenarios & Testing *(mandatory)*
|
|
|
|
### User Story 1 - Trust the visible tenant context (Priority: P1)
|
|
|
|
As an enterprise operator, I want every admin monitoring surface to use one visible and actual tenant context so that headers, counts, tables, filters, actions, and links never contradict each other.
|
|
|
|
**Why this priority**: The feature exists to remove the core architectural inconsistency that can otherwise mislead operators and weaken safety boundaries.
|
|
|
|
**Independent Test**: Can be fully tested by loading the same monitoring flow under remembered-only, Filament-only, conflicting, and no-context request states and verifying that every visible surface resolves the same tenant or the same safe no-context outcome.
|
|
|
|
**Acceptance Scenarios**:
|
|
|
|
1. **Given** an admin request with only a remembered current tenant, **When** the operator opens a tenant-sensitive monitoring flow, **Then** the header, KPIs, table data, filter defaults, and filter options all resolve from that same tenant.
|
|
2. **Given** an admin request with both a Filament tenant and a different remembered tenant, **When** the flow renders, **Then** the Filament tenant wins consistently for every visible and query-driven surface.
|
|
3. **Given** an admin request with no valid tenant context, **When** the operator opens a tenant-sensitive flow, **Then** the page returns a defined safe outcome instead of silently broadening scope.
|
|
|
|
---
|
|
|
|
### User Story 2 - Use filters and detail links safely (Priority: P1)
|
|
|
|
As an enterprise operator, I want tenant-sensitive filters, detail pages, and direct links to respect the same scope as the list I came from so that I never see cross-tenant options or open records that should not be reachable.
|
|
|
|
**Why this priority**: Filter options, direct record resolution, and deep links are the most likely places for scope drift to become a real safety or data-leak problem.
|
|
|
|
**Independent Test**: Can be fully tested by loading tenant-sensitive resources from list pages, direct URLs, and persisted filter state while switching tenant context between requests.
|
|
|
|
**Acceptance Scenarios**:
|
|
|
|
1. **Given** a tenant-sensitive resource list in the admin panel, **When** the operator opens filter dropdowns, **Then** every option is drawn from the same canonical tenant scope as the list results.
|
|
2. **Given** a persisted tenant-sensitive filter from Tenant A, **When** the operator later opens the same flow in Tenant B, **Then** invalid persisted filter state is reset, replaced, or ignored rather than silently reusing the old tenant value.
|
|
3. **Given** a direct record URL or a global-search result for a tenant-sensitive record, **When** the current context is missing or out of scope, **Then** the request returns the same safe bounded behavior as the list flow rather than a broader record lookup.
|
|
|
|
---
|
|
|
|
### User Story 3 - Preserve separate panel semantics (Priority: P2)
|
|
|
|
As a product team member, I want tenant-panel and workspace-admin semantics to stay explicitly separate so that future changes do not reintroduce hidden resolver conflicts or generic context magic.
|
|
|
|
**Why this priority**: The system has two valid but different tenant concepts, and long-term stability depends on documenting and enforcing that separation.
|
|
|
|
**Independent Test**: Can be fully tested by validating that tenant-panel flows continue to use panel-native tenant context while admin-panel flows use the canonical admin resolver, with no mixed behavior in in-scope screens.
|
|
|
|
**Acceptance Scenarios**:
|
|
|
|
1. **Given** a tenant-panel page, **When** it resolves active tenant context, **Then** it continues to use the panel-native tenant model.
|
|
2. **Given** an admin-panel monitoring page, **When** it resolves active tenant context, **Then** it uses only the canonical admin resolver and not tenant-panel-native behavior.
|
|
3. **Given** a tenant-owned Entra group surface, **When** the product renders primary sidebar navigation, **Then** the entry appears only in tenant-panel navigation while any admin-panel access remains a secondary direct-link or global-search path under the canonical admin tenant contract.
|
|
|
|
---
|
|
|
|
### User Story 4 - Prevent regressions cheaply (Priority: P3)
|
|
|
|
As a maintainer, I want a lightweight guardrail and regression matrix around tenant-context resolution so that new admin-panel code cannot quietly reintroduce the same class of inconsistency.
|
|
|
|
**Why this priority**: The architectural risk is recurring drift, not just the currently known defects.
|
|
|
|
**Independent Test**: Can be fully tested by running the focused regression suite plus the agreed lightweight guardrail and verifying that new direct admin-panel tenant reads are flagged before merge.
|
|
|
|
**Acceptance Scenarios**:
|
|
|
|
1. **Given** a new admin-panel tenant-sensitive flow, **When** it introduces an unauthorized direct tenant read pattern, **Then** the guardrail or regression process flags it before release.
|
|
2. **Given** the known in-scope flows, **When** the regression suite runs, **Then** remembered-only, Filament-only, conflict, and no-context cases remain covered.
|
|
|
|
### Edge Cases
|
|
|
|
- A request may carry both a panel tenant and a remembered tenant, and those two values may disagree.
|
|
- A remembered tenant may be the only available tenant context in admin flows and must remain a first-class supported state.
|
|
- Persisted filter state may contain tenant-sensitive values that no longer match the current tenant context after a switch.
|
|
- A list page may be correctly scoped while the corresponding direct record resolution or global-search path is broader.
|
|
- A deep link may open a tenant-sensitive detail page before any explicit tenant choice was made in the current browser session.
|
|
- Some tenant-panel files are allowed to use panel-native tenant semantics and must not be treated as admin-panel violations.
|
|
|
|
### Explicit No-Context Outcomes
|
|
|
|
- **Operations monitoring index `/admin/operations`**: No admin tenant context resolves to an explicit workspace-scoped `All tenants` state that is bounded to entitled tenants in the active workspace, clears tenant-default filters, and hides tenant-only KPI behavior instead of inferring a tenant.
|
|
- **Operation run detail `/admin/operations/{run}`**: No admin tenant context still permits detail rendering only when the run belongs to the active workspace and the operator is entitled to the run's tenant when one exists; otherwise the response is deny-as-not-found.
|
|
- **Admin Entra group list/detail paths**: No admin tenant context results in deny-as-not-found for admin list and detail access rather than a broader workspace-wide group view.
|
|
- **Admin Entra group global search**: No admin tenant context returns no results for tenant-owned Entra group records.
|
|
|
|
## Requirements *(mandatory)*
|
|
|
|
**Constitution alignment (required):** This feature introduces no new Microsoft Graph calls, no new write workflow, and no new scheduled or queued job. It is an architecture-consistency, safety, and regression-hardening feature over existing admin and tenant-panel read and navigation flows. Existing operational data remains authoritative.
|
|
|
|
**Constitution alignment (OPS-UX):** This feature does not create or mutate `OperationRun` records. It may affect how existing monitoring and operation-run data is filtered or displayed, but it does not change run ownership, lifecycle transitions, or notification contracts.
|
|
|
|
**Constitution alignment (RBAC-UX):** This feature affects workspace-admin `/admin/...` flows and tenant-context `/admin/t/{tenant}/...` flows. Cross-plane access remains deny-as-not-found. Users outside workspace or tenant entitlement remain 404-style not-found. In-scope users lacking a protected capability continue to receive forbidden behavior on protected targets. Authorization remains server-side for list queries, filter option population, direct record resolution, global search, and any protected navigation. No raw capability strings or role-name checks may be introduced. Global-search behavior must remain tenant-safe and non-member-safe.
|
|
|
|
**Constitution alignment (OPS-EX-AUTH-001):** Not applicable. No authentication handshake or outbound auth exception behavior is introduced.
|
|
|
|
**Constitution alignment (BADGE-001):** If in-scope pages show tenant-related badges, counts, or status surfaces, those semantics must continue to come from centralized badge rules rather than page-local mappings. This feature standardizes the tenant context behind those surfaces, not their domain meanings.
|
|
|
|
**Constitution alignment (UI-NAMING-001):** Operator-facing wording must consistently describe the active tenant context using the same domain language across headers, badges, filters, links, and safe-state messaging. Implementation-first wording must not become the primary operator language.
|
|
|
|
**Constitution alignment (Filament Action Surfaces):** This feature modifies existing Filament pages, widgets, and resources. The Action Surface Contract remains satisfied because the feature is primarily read, filter, and navigation hardening. No new destructive action is introduced. Existing destructive actions remain subject to confirmation and authorization rules.
|
|
|
|
**Constitution alignment (UX-001 — Layout & Information Architecture):** In-scope Filament screens must keep their established list, widget, and detail layouts while making visible tenant context and safe no-context outcomes clearer and more consistent. Empty or no-context states must be explicit and operator-safe, not silent broad-scope fallbacks.
|
|
|
|
### Functional Requirements
|
|
|
|
- **FR-135-01 Canonical architecture rule**: The product must define one explicit, durable architecture rule that states which tenant-context source applies in the tenant panel and which tenant-context source applies in the workspace-admin panel.
|
|
- **FR-135-02 Separate panel semantics**: Tenant-panel context and workspace-admin context must remain intentionally separate concepts and must not be merged behind an unspecified universal resolver.
|
|
- **FR-135-03 Tenant-panel source of truth**: Tenant-panel flows must continue to treat `Filament::getTenant()` as the canonical tenant-context source.
|
|
- **FR-135-04 Admin-panel source of truth**: Workspace-admin and monitoring flows must treat `OperateHubShell::activeEntitledTenant(Request $request): ?Tenant` as the only canonical API for active operational tenant context.
|
|
- **FR-135-05 Admin fallback behavior**: In workspace-admin flows, remembered or current tenant state may act only as fallback behind an available Filament tenant and must never override it.
|
|
- **FR-135-06 Conflict priority rule**: When both admin-context tenant sources exist and disagree, the product must apply one hard priority rule so every in-scope surface resolves the same tenant.
|
|
- **FR-135-07 Scope follows visible context**: Headers, badges, KPIs, table queries, filter defaults, filter options, actions, and links within the same request flow must use the same tenant-context resolution.
|
|
- **FR-135-08 Remembered-only support**: Workspace-admin flows must support remembered-only tenant context as a first-class valid state and must not collapse into empty or misleading output when that is the only available context.
|
|
- **FR-135-09 No-context safety**: Missing tenant context in tenant-sensitive workspace-admin flows must produce a deterministic safe outcome rather than a broader query scope.
|
|
- **FR-135-10 Filter option parity**: Tenant-sensitive filter option lists must never be broader than the list or view scope they control.
|
|
- **FR-135-11 Filter persistence safety**: Persisted tenant-sensitive filter state must be revalidated against the current canonical tenant context on each relevant request.
|
|
- **FR-135-12 Consistent default filters**: Tenant-sensitive filter defaults must resolve from the same canonical tenant context as the rest of the page.
|
|
- **FR-135-13 Widget consistency**: Monitoring widgets and page-level summaries must use the same tenant-context resolver as the surrounding page header and table.
|
|
- **FR-135-14 Resource query hardening**: Tenant- or workspace-dependent resources must not rely on table-only scoping when those resources can also be reached by direct record URLs, deep links, or global-search results.
|
|
- **FR-135-15 Record-resolution parity**: Direct record resolution must never be broader than the visible list or detail scope for the same resource.
|
|
- **FR-135-16 Global-search safety**: Global-search behavior for tenant-sensitive resources must either follow the same scope rules as list and detail flows or be disabled where that cannot be guaranteed.
|
|
- **FR-135-17 In-scope remediation set**: The known inconsistency class must be corrected at minimum for the operations monitoring KPI flow, the tenant-sensitive operation-run filter flow, and the tenant-sensitive Entra group record-resolution and search flow.
|
|
- **FR-135-18 Reference pattern retention**: The already-correct alert-delivery pattern must remain a valid reference example for the same bug class and must not regress.
|
|
- **FR-135-19 Lightweight guardrail**: The product must add one lightweight and maintainable guardrail against new direct admin-panel tenant reads through tenant-panel-native patterns.
|
|
- **FR-135-20 Documented exceptions**: Allowed exceptions to the admin guardrail must be explicitly documented for tenant-panel-native files and equivalent panel-native tenant surfaces.
|
|
- **FR-135-21 Regression matrix**: The regression suite for this feature must cover remembered-only, Filament-only, conflict, and no-context request states across the in-scope flows.
|
|
- **FR-135-22 Deep-link determinism**: Direct links into tenant-sensitive admin pages and detail pages must behave deterministically and safely even when no prior list page or tenant-selection step was visited.
|
|
- **FR-135-23 Authorization semantics parity**: In-scope admin list, detail, deep-link, and search paths must preserve deny-as-not-found for non-members or out-of-scope tenant access and preserve forbidden responses only after scope membership is established.
|
|
- **FR-135-24 Residual inventory clarity**: This iteration must leave a documented inventory of remaining admin tenant-context call sites that are already compliant or intentionally out of scope so completion does not imply undocumented repo-wide remediation.
|
|
|
|
### Non-Goals
|
|
|
|
- Reworking the full workspace-scoping model
|
|
- Redesigning navigation or information architecture beyond what is needed for consistent tenant context
|
|
- Introducing new tenancy features or a new universal context service for unrelated domains
|
|
- Performing a full security audit of every resource in the codebase
|
|
- Refactoring correctly functioning tenant-panel resources only for stylistic consistency
|
|
- Broadly rewriting all current resources that are already outside the inconsistency class addressed here
|
|
|
|
### Assumptions
|
|
|
|
- The product already has a remembered or current tenant concept in workspace-admin flows that operators rely on today.
|
|
- Tenant-panel flows already use valid panel-native tenant context and should remain unchanged unless they participate directly in an inconsistency addressed by this feature.
|
|
- Alert delivery behavior already demonstrates the intended scope-alignment pattern for this bug class.
|
|
- Deep-link, record-resolution, and global-search paths are part of the operator experience and therefore must follow the same safety rule as list pages.
|
|
|
|
### Dependencies
|
|
|
|
- Existing workspace membership and tenant entitlement enforcement
|
|
- Existing admin monitoring and operation resources that already surface tenant-sensitive data
|
|
- Existing remembered current-tenant behavior in workspace-admin flows
|
|
- Existing global-search and direct-record routing behavior for in-scope resources
|
|
- Existing focused tests around alerts, operations, monitoring, and resource authorization that can be extended for regression coverage
|
|
|
|
## UI Action Matrix *(mandatory when Filament is changed)*
|
|
|
|
| Surface | Location | Header Actions | Inspect Affordance (List/Table) | Row Actions (max 2 visible) | Bulk Actions (grouped) | Empty-State CTA(s) | View Header Actions | Create/Edit Save+Cancel | Audit log? | Notes / Exemptions |
|
|
|---|---|---|---|---|---|---|---|---|---|---|
|
|
| Operations monitoring page | Workspace-admin monitoring flows under `/admin/...` | Existing non-destructive monitoring actions only | Header tenant context, KPI widget, and table remain the primary inspect affordances | Existing inspect actions only | None new | Safe no-context state may expose one operator-safe next step when appropriate | Existing view actions only | N/A | No new audit event | This feature aligns tenant context across header, KPIs, filters, and table data; it introduces no new destructive action. |
|
|
| Operations KPI widget | Workspace-admin monitoring widget surface | None | KPI cards remain the inspect affordance | Linked drill-downs only if already present and authorized | None | N/A | N/A | N/A | No | Widget behavior must remain aligned with the surrounding monitoring page and its polling or refresh behavior. |
|
|
| Operation runs resource | Workspace-admin list and detail flows | Existing non-destructive header actions only | Table and filter UI remain the primary inspect affordance | Existing inspect actions only | Existing grouped bulk actions, unchanged | Existing empty state remains | Existing view actions only | Existing create or edit semantics, unchanged if present | No new audit event | Tenant-sensitive filter defaults and options must remain no broader than the underlying list and detail scope. |
|
|
| Entra groups resource | Any in-scope admin resource list, detail, or search entry point | Existing non-destructive header actions only | List, direct record URL, and global-search entry remain in scope | Existing inspect actions only | Existing grouped bulk actions, unchanged | Existing empty state remains | Existing view actions only | Existing create or edit semantics, unchanged if present | No new audit event | The critical change is scope hardening so direct record resolution and search paths cannot exceed list scope. |
|
|
|
|
### Key Entities *(include if feature involves data)*
|
|
|
|
- **Tenant Panel Context**: The panel-native tenant context used inside tenant-panel flows where tenant is part of the panel model itself.
|
|
- **Admin Operational Tenant Context**: The active tenant context used in workspace-admin monitoring and tenant-sensitive admin flows to align visible context with query scope.
|
|
- **Tenant Context Conflict**: A request state where more than one tenant source is present and the product must apply a documented priority rule.
|
|
- **Tenant-Sensitive Filter State**: Persisted or defaulted filter input whose valid values depend on the canonical tenant context.
|
|
- **Scoped Record Access Path**: Any list, detail, deep link, or global-search route that must obey the same tenant and entitlement boundaries.
|
|
|
|
## Success Criteria *(mandatory)*
|
|
|
|
### Measurable Outcomes
|
|
|
|
- **SC-135-01 Context consistency**: In focused acceptance and regression coverage, 100% of in-scope remembered-only, Filament-only, conflict, and no-context scenarios produce one consistent tenant-context outcome per request flow.
|
|
- **SC-135-02 Filter safety**: In regression tests for tenant-sensitive filters, 100% of covered filter option sets stay within the same tenant scope as the underlying list results.
|
|
- **SC-135-03 Deep-link safety**: In direct record, deep-link, and global-search regression coverage for in-scope resources, 100% of covered out-of-scope or no-context requests avoid broader record access than the corresponding list flow.
|
|
- **SC-135-04 Remediation coverage**: The known inconsistency class is eliminated for the operations KPI flow, operation-run tenant-sensitive filter flow, and Entra group record-resolution or search flow in all covered test states.
|
|
- **SC-135-05 Guardrail effectiveness**: The agreed lightweight guardrail clearly distinguishes allowed tenant-panel-native usage from disallowed new admin-panel usage of tenant-panel-native tenant reads.
|
|
- **SC-135-06 Architecture clarity**: A maintainer can determine the correct tenant-context source, conflict rule, and safe behavior for missing context directly from the feature specification and referenced architecture rule without reading implementation code first.
|
|
- **SC-135-07 Surface-specific no-context clarity**: The operations list, operation-run detail flow, admin Entra group list/detail flow, and admin Entra group global search each have one explicitly documented and testable no-context outcome.
|
|
- **SC-135-08 Residual scope inventory**: The architecture note for this feature lists remaining admin tenant-resolution call sites outside the remediation set and identifies them as compliant, exception-based, or deferred.
|