## Summary
- implement Spec 147 for workspace-first tenant selector and remembered tenant context enforcement
- harden canonical and tenant-bound route behavior so selected tenant mismatch stays informational
- fix drift finding subject fallback for workspace-safe RBAC identifiers and centralize finding subject resolution
## Testing
- vendor/bin/sail artisan test --compact tests/Feature/Filament/FindingViewRbacEvidenceTest.php tests/Feature/Findings/FindingsListDefaultsTest.php
- vendor/bin/sail bin pint --dirty --format agent
## Notes
- branch pushed at de0679cd8b
- includes the spec artifacts under specs/147-tenant-selector-remembered-context-enforcement/
Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #176
161 lines
5.4 KiB
Markdown
161 lines
5.4 KiB
Markdown
# Data Model: Tenant Selector and Remembered Context Enforcement
|
|
|
|
## Overview
|
|
|
|
This feature does not introduce a new persistence model. It formalizes how existing workspace, tenant, route, and remembered-context concepts interact at the shell and routing layers.
|
|
|
|
## Entities
|
|
|
|
### 1. Workspace Context
|
|
|
|
**Represents**: The primary operating boundary for the current admin session.
|
|
|
|
**Key fields**:
|
|
- `workspace_id`
|
|
- `workspace.slug`
|
|
- `workspace.archived_at`
|
|
|
|
**Relationships**:
|
|
- Owns many tenants
|
|
- Owns many canonical workspace records such as operation runs
|
|
- Owns one session-scoped remembered tenant preference map entry per user session
|
|
|
|
**Validation rules**:
|
|
- Must exist
|
|
- Must not be archived for active selection
|
|
- Actor must be a workspace member or the request resolves as deny-as-not-found
|
|
|
|
### 2. Tenant
|
|
|
|
**Represents**: A workspace-owned tenant record that may be active, onboarding, draft, or archived.
|
|
|
|
**Key fields**:
|
|
- `id`
|
|
- `workspace_id`
|
|
- `external_id`
|
|
- `status`
|
|
- `deleted_at`
|
|
- operator-facing identity fields such as `name`, `domain`, `environment`
|
|
|
|
**Relationships**:
|
|
- Belongs to one workspace
|
|
- May be a route subject on tenant-bound pages
|
|
- May be referenced by canonical workspace records such as operation runs
|
|
- May be persisted as remembered active-lane context only when selector-eligible
|
|
|
|
**Validation rules**:
|
|
- For standard active selector use, tenant must belong to current workspace, remain entitled to the actor, exist, and satisfy active-lane eligibility
|
|
- For tenant-bound route validity, tenant must satisfy workspace and entitlement checks; selector eligibility is not required
|
|
|
|
### 3. Remembered Tenant Context
|
|
|
|
**Represents**: A workspace-scoped user/session preference for the last active-lane tenant.
|
|
|
|
**Key fields**:
|
|
- `workspace_last_tenant_ids[workspace_id]` session entry
|
|
- optional persisted recency signals through `users.last_tenant_id` or `user_tenant_preferences.last_used_at`
|
|
|
|
**Relationships**:
|
|
- Belongs logically to one workspace context
|
|
- References one tenant candidate for active-lane convenience
|
|
|
|
**Validation rules**:
|
|
- Must only be read inside an established workspace context
|
|
- Must resolve to an existing tenant in the current workspace
|
|
- Must satisfy entitlement-sensitive access checks
|
|
- Must satisfy active-lane eligibility checks
|
|
- Invalid values must be cleared or ignored deterministically
|
|
|
|
### 4. Active Selector Option
|
|
|
|
**Represents**: A tenant that is eligible to appear in the standard active tenant selector.
|
|
|
|
**Derived from**:
|
|
- `TenantOperabilityDecision.canSelectAsContext`
|
|
|
|
**Required attributes**:
|
|
- tenant identity
|
|
- lifecycle presentation
|
|
- selector-safe label and helper copy
|
|
|
|
**Validation rules**:
|
|
- Must not include `draft`, `onboarding`, or `archived` tenants under the current lifecycle model
|
|
- Must not include tenants outside the current workspace
|
|
- Must not include tenants the actor cannot access
|
|
|
|
### 5. Route Subject
|
|
|
|
**Represents**: The record made authoritative by the current route.
|
|
|
|
**Variants**:
|
|
- tenant-bound route subject: `Tenant`
|
|
- canonical workspace record subject: `OperationRun` or other workspace-owned records
|
|
|
|
**Validation rules**:
|
|
- Route subject legitimacy is resolved from route record identity plus policy checks
|
|
- Remembered tenant context may influence convenience UI only and must not replace route authority
|
|
|
|
## Derived Domain Objects
|
|
|
|
### Tenant Operability Decision
|
|
|
|
**Represents**: The existing lifecycle-aware rule set produced by `TenantOperabilityService`.
|
|
|
|
**Relevant flags for this feature**:
|
|
- `canSelectAsContext`
|
|
- `canViewTenantSurface`
|
|
- `canReferenceInWorkspaceMonitoring`
|
|
|
|
### Shell Context Resolution Result
|
|
|
|
**Represents**: The runtime decision for what tenant, if any, the shell should treat as active convenience context.
|
|
|
|
**Possible states**:
|
|
- `route_authoritative_tenant`
|
|
- `validated_selected_tenant`
|
|
- `no_selected_tenant`
|
|
- `stale_context_cleared`
|
|
|
|
## State Transitions
|
|
|
|
### Remembered Tenant Context Lifecycle
|
|
|
|
1. `unset`
|
|
- No remembered tenant exists for the current workspace.
|
|
2. `remembered_active`
|
|
- An active-lane-eligible tenant is selected and stored for the current workspace.
|
|
3. `revalidated_active`
|
|
- A later request reads the remembered tenant and confirms workspace match, entitlement, existence, and selector eligibility.
|
|
4. `invalidated_cleared`
|
|
- A later request detects stale or ineligible remembered tenant context and clears or ignores it.
|
|
5. `no_selected_tenant`
|
|
- The shell falls back to a legitimate workspace-level no-tenant state.
|
|
|
|
**Invalidation triggers**:
|
|
- workspace switch
|
|
- tenant no longer exists
|
|
- tenant no longer belongs to current workspace
|
|
- tenant no longer satisfies active-lane eligibility
|
|
- actor no longer has tenant entitlement where required
|
|
|
|
### Page Semantics By Category
|
|
|
|
#### Workspace-level page
|
|
- Accepts `remembered_active`, `invalidated_cleared`, or `no_selected_tenant`
|
|
- Selected tenant may become a filter only
|
|
|
|
#### Tenant-bound page
|
|
- Route tenant is authoritative
|
|
- Selected tenant may match, differ, or be absent
|
|
|
|
#### Canonical workspace record viewer
|
|
- Route record is authoritative
|
|
- Referenced tenant may differ from selected tenant without invalidating the page
|
|
|
|
## Invariants
|
|
|
|
- Workspace is always the primary context boundary.
|
|
- Remembered tenant context never broadens authorization.
|
|
- Standard selector membership never implies universal tenant discoverability.
|
|
- Route legitimacy always outranks selected tenant context.
|
|
- No-selected-tenant is a valid workspace shell state. |