## Summary
- add the shared trusted-state model and resolver helpers for first-slice Livewire and Filament surfaces
- harden managed tenant onboarding, tenant required permissions, and system runbooks against forged or stale public state
- add focused Pest guard and regression coverage plus the complete spec 152 artifact set
## Validation
- `vendor/bin/sail artisan test --compact`
- manual smoke validated on `/admin/onboarding/{onboardingDraft}`
- manual smoke validated on `/admin/tenants/{tenant}/required-permissions`
- manual smoke validated on `/system/ops/runbooks`
## Notes
- Livewire v4.0+ / Filament v5 stack unchanged
- no new panels, routes, assets, or global-search changes
- provider registration remains in `bootstrap/providers.php`
Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #182
189 lines
6.7 KiB
Markdown
189 lines
6.7 KiB
Markdown
# Data Model: Livewire Context Locking and Trusted-State Reduction
|
|
|
|
This feature does not introduce new database tables in the first slice. The data-model work formalizes trust-boundary entities and field classes that are already implicit in existing Livewire and Filament components.
|
|
|
|
## 1. Trusted State Class
|
|
|
|
### Purpose
|
|
|
|
Defines how a given piece of component state may be stored and used.
|
|
|
|
### Allowed values
|
|
|
|
- `presentation`
|
|
- `locked_identity`
|
|
- `server_derived_authority`
|
|
|
|
### Rules
|
|
|
|
- `presentation` state may remain public and mutable.
|
|
- `locked_identity` state may remain public only when it is client-immutable and still re-resolved before protected actions.
|
|
- `server_derived_authority` state must not rely on client mutability at all and is derived from route, session, resolver, or persisted workflow truth.
|
|
|
|
## 2. Component Trusted-State Policy
|
|
|
|
### Purpose
|
|
|
|
Represents the trust contract for one stateful Livewire or Filament surface.
|
|
|
|
### Fields
|
|
|
|
- `component_name`: human-readable identifier for the surface
|
|
- `plane`: `admin_tenant`, `admin_workspace`, or `system_platform`
|
|
- `route_anchor`: route-bound record or context source, if any
|
|
- `authority_sources`: canonical sources used to re-derive protected targets
|
|
- `locked_identities`: list of public scalar IDs allowed to persist for continuity
|
|
- `mutable_selectors`: list of user-controlled selectors that remain proposals only
|
|
- `forbidden_public_authority_fields`: list of model objects or mutable IDs disallowed as final authority
|
|
|
|
### Relationships
|
|
|
|
- One component policy has many trusted fields.
|
|
- One component policy has many forged-state regression cases.
|
|
|
|
## 3. Trusted Field
|
|
|
|
### Purpose
|
|
|
|
Represents one public property or equivalent state slot on a covered component.
|
|
|
|
### Fields
|
|
|
|
- `field_name`
|
|
- `state_class`
|
|
- `php_type`
|
|
- `source_of_truth`
|
|
- `used_for_protected_action`: boolean
|
|
- `revalidation_required`: boolean
|
|
- `notes`
|
|
|
|
### Validation rules
|
|
|
|
- If `state_class = presentation`, then `used_for_protected_action` must be false.
|
|
- If `used_for_protected_action = true`, then `state_class` must be `locked_identity` or `server_derived_authority`.
|
|
- If `php_type` is an Eloquent model and the field is ownership-relevant, it is a migration target and should be phased toward locked scalar or server-derived access.
|
|
|
|
## 4. Authority Source
|
|
|
|
### Purpose
|
|
|
|
Represents the canonical seam used to re-derive truth on the server.
|
|
|
|
### First-slice source types
|
|
|
|
- `route_binding`
|
|
- `workspace_context`
|
|
- `tenant_panel_context`
|
|
- `persisted_onboarding_draft`
|
|
- `allowed_tenant_universe`
|
|
- `explicit_scoped_query`
|
|
|
|
### Example mappings
|
|
|
|
- Onboarding draft identity: route binding + persisted onboarding draft
|
|
- Current workspace: `WorkspaceContext`
|
|
- Tenant-context page scope: route binding or `ResolvesPanelTenantContext`
|
|
- System runbook tenant selector: `AllowedTenantUniverse`
|
|
|
|
## 5. Selector Proposal
|
|
|
|
### Purpose
|
|
|
|
Represents mutable client-submitted selection state that is allowed to change but must be validated before use.
|
|
|
|
### Fields
|
|
|
|
- `selector_name`
|
|
- `target_model`
|
|
- `scope_rules`
|
|
- `null_allowed`: boolean
|
|
- `validation_outcome`
|
|
|
|
### Validation outcomes
|
|
|
|
- `accepted`
|
|
- `rejected_not_found`
|
|
- `rejected_forbidden`
|
|
- `reset_required`
|
|
|
|
### Rules
|
|
|
|
- A selector proposal never grants authority by itself.
|
|
- A selector proposal must be re-scoped to the current workspace, tenant, or allowed-universe before action execution.
|
|
|
|
## 6. Forged-State Regression Case
|
|
|
|
### Purpose
|
|
|
|
Represents a reproducible test scenario where client state is mutated to challenge the trust boundary.
|
|
|
|
### Fields
|
|
|
|
- `component_name`
|
|
- `mutation_type`: `foreign_id`, `stale_id`, `null_forced`, `cross_workspace`, `cross_plane`
|
|
- `entry_point`: page load, action call, modal submit, or rerun path
|
|
- `expected_outcome`: `404`, `403`, or no-op fail-closed
|
|
- `must_preserve_data_integrity`: boolean
|
|
|
|
### State transitions
|
|
|
|
- `pending_design` → `covered_by_test`
|
|
- `covered_by_test` → `guarded_in_ci`
|
|
|
|
## 7. First-Slice Surface Inventory
|
|
|
|
### Managed Tenant Onboarding Wizard
|
|
|
|
- `route_anchor`: onboarding draft route parameter
|
|
- Locked identities:
|
|
- `managedTenantId` (`?int`) backed by persisted onboarding draft identity and re-resolved through `currentManagedTenantRecord()`
|
|
- `onboardingSessionId` (`?int`) backed by persisted onboarding draft identity and re-resolved through `currentOnboardingSessionRecord()`
|
|
- Mutable selector proposals:
|
|
- `selectedProviderConnectionId` (`?int`) revalidated against canonical draft and scoped provider queries before verify/bootstrap paths
|
|
- `selectedBootstrapOperationTypes` (`array<int, string>`) remains presentation-only wizard state
|
|
- Server-derived authority fields:
|
|
- public `Workspace $workspace` refreshed from `WorkspaceContext`
|
|
- public `?Tenant $managedTenant` treated as display convenience only; canonical tenant truth comes from draft/scoped query
|
|
- public `?TenantOnboardingSession $onboardingSession` treated as display convenience only; canonical draft truth comes from resolver-backed persisted session lookup
|
|
- Canonical authority sources:
|
|
- `WorkspaceContext`
|
|
- onboarding draft resolver
|
|
- persisted draft state
|
|
- scoped provider-connection query
|
|
|
|
### Tenant Required Permissions Page
|
|
|
|
- `route_anchor`: tenant route parameter
|
|
- Locked identities:
|
|
- `scopedTenantId` (`?int`) derived from the route tenant and revalidated through `trustedScopedTenant()`
|
|
- Mutable selector proposals:
|
|
- `status`, `type`, `features`, and `search` remain presentation-only filters
|
|
- Server-derived authority fields:
|
|
- canonical tenant scope is derived through `resolveScopedTenant()`, `WorkspaceContext`, and `trustedScopedTenant()`
|
|
- Canonical authority sources:
|
|
- route-bound tenant resolution
|
|
- workspace context
|
|
|
|
### System Runbooks Page
|
|
|
|
- `route_anchor`: none
|
|
- Locked identities:
|
|
- none in the first slice; platform selector state remains proposal-only
|
|
- Public selector proposals:
|
|
- `findingsTenantId` (`?int`) is revalidated through `AllowedTenantUniverse::resolveAllowedOrFail()`
|
|
- `tenantId` (`?int`) is mirrored display state for the last trusted preflight result
|
|
- `findingsScopeMode` and `scopeMode` remain mutable UI state that must normalize into a trusted scope DTO before action execution
|
|
- Server-derived authority fields:
|
|
- trusted scope derives from `trustedFindingsScopeFromFormData()` / `trustedFindingsScopeFromState()` and the allowed tenant universe
|
|
- Canonical authority sources:
|
|
- authenticated platform user
|
|
- allowed tenant universe
|
|
- runbook scope DTO
|
|
|
|
## 8. Out-of-Scope but Related Fields
|
|
|
|
- Public widget record models such as tenant widgets storing `public ?Tenant $record`
|
|
- Resource page state handled primarily through Filament route model binding
|
|
- Non-stateful controller endpoints and queued job payloads
|
|
|
|
These remain rollout inventory candidates after the first slice proves the trusted-state pattern. |