## 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
211 lines
6.3 KiB
YAML
211 lines
6.3 KiB
YAML
openapi: 3.1.0
|
|
info:
|
|
title: Tenant Context Enforcement Contract
|
|
version: 0.1.0
|
|
description: >-
|
|
Internal route and state contract for Spec 147. This feature does not introduce
|
|
new public APIs; the contract documents the expected behavior of the existing
|
|
workspace and tenant context routes plus the shared context-resolution object.
|
|
paths:
|
|
/admin/choose-tenant:
|
|
get:
|
|
summary: Render the standard active-lane tenant selector
|
|
operationId: showChooseTenant
|
|
responses:
|
|
'200':
|
|
description: Choose-tenant page rendered for the current workspace
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ChooseTenantSurfaceContract'
|
|
/admin/select-tenant:
|
|
post:
|
|
summary: Persist active-lane tenant context for the current workspace
|
|
operationId: selectTenantContext
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- tenant_id
|
|
properties:
|
|
tenant_id:
|
|
type: integer
|
|
responses:
|
|
'302':
|
|
description: Tenant context accepted and redirect issued to the tenant-lane destination
|
|
'404':
|
|
description: Tenant missing, outside workspace, not entitled, or not eligible for standard active-lane selection
|
|
/admin/clear-tenant-context:
|
|
post:
|
|
summary: Clear remembered tenant context for the current workspace
|
|
operationId: clearTenantContext
|
|
responses:
|
|
'302':
|
|
description: Tenant context cleared and redirect issued to a workspace-safe destination
|
|
/admin/switch-workspace:
|
|
post:
|
|
summary: Switch the current workspace and re-evaluate tenant context
|
|
operationId: switchWorkspace
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- workspace_id
|
|
properties:
|
|
workspace_id:
|
|
type: integer
|
|
responses:
|
|
'302':
|
|
description: Workspace switched and tenant context cleared or re-evaluated for the new workspace
|
|
'404':
|
|
description: Workspace missing, archived, or actor is not a member
|
|
/admin/tenants/{tenant}:
|
|
get:
|
|
summary: Resolve tenant-bound route subject independently from selected tenant context
|
|
operationId: viewTenantRouteSubject
|
|
parameters:
|
|
- in: path
|
|
name: tenant
|
|
required: true
|
|
schema:
|
|
type: string
|
|
responses:
|
|
'200':
|
|
description: Authorized tenant-bound page renders from route subject authority
|
|
'404':
|
|
description: Tenant missing or actor not entitled to tenant scope
|
|
'403':
|
|
description: Actor is a tenant member but lacks a required capability for the page or action
|
|
/admin/operations/{run}:
|
|
get:
|
|
summary: Resolve canonical workspace record viewer independently from selected tenant context
|
|
operationId: viewCanonicalOperationRun
|
|
parameters:
|
|
- in: path
|
|
name: run
|
|
required: true
|
|
schema:
|
|
type: string
|
|
responses:
|
|
'200':
|
|
description: Authorized canonical run viewer renders even when selected tenant differs or is absent
|
|
'404':
|
|
description: Run missing, workspace membership missing, or tenant entitlement missing for a referenced tenant
|
|
'403':
|
|
description: Actor is otherwise in scope but lacks the required capability for the run type
|
|
components:
|
|
schemas:
|
|
ActiveSelectorOption:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- tenantId
|
|
- workspaceId
|
|
- lifecycle
|
|
- isEligible
|
|
properties:
|
|
tenantId:
|
|
type: integer
|
|
workspaceId:
|
|
type: integer
|
|
lifecycle:
|
|
type: string
|
|
enum:
|
|
- draft
|
|
- onboarding
|
|
- active
|
|
- archived
|
|
isEligible:
|
|
type: boolean
|
|
description: Must be true only for tenants eligible for standard active-lane selection.
|
|
selectorLabel:
|
|
type:
|
|
- string
|
|
- 'null'
|
|
RememberedTenantContextState:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- workspaceId
|
|
- status
|
|
properties:
|
|
workspaceId:
|
|
type: integer
|
|
tenantId:
|
|
type:
|
|
- integer
|
|
- 'null'
|
|
status:
|
|
type: string
|
|
enum:
|
|
- no_selected_tenant
|
|
- remembered_active
|
|
- route_authoritative_tenant
|
|
- stale_context_cleared
|
|
invalidationReason:
|
|
type:
|
|
- string
|
|
- 'null'
|
|
enum:
|
|
- workspace_mismatch
|
|
- tenant_missing
|
|
- tenant_not_entitled
|
|
- tenant_not_selector_eligible
|
|
- explicit_clear
|
|
- null
|
|
ChooseTenantSurfaceContract:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- workspaceId
|
|
- selectorMeaning
|
|
- options
|
|
properties:
|
|
workspaceId:
|
|
type: integer
|
|
selectorMeaning:
|
|
type: string
|
|
const: normal_active_operating_context
|
|
options:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/ActiveSelectorOption'
|
|
emptyStateBehavior:
|
|
type: string
|
|
enum:
|
|
- no_active_tenants_available
|
|
- workspace_selection_required
|
|
RouteLegitimacyContract:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- routeType
|
|
- authority
|
|
- selectedTenantInfluence
|
|
properties:
|
|
routeType:
|
|
type: string
|
|
enum:
|
|
- workspace_level
|
|
- tenant_bound
|
|
- canonical_workspace_record
|
|
authority:
|
|
type: string
|
|
enum:
|
|
- workspace
|
|
- route_tenant
|
|
- route_record
|
|
selectedTenantInfluence:
|
|
type: string
|
|
enum:
|
|
- none
|
|
- informational_only
|
|
- filter_only |