TenantAtlas/specs/276-support-access-governance/contracts/workspace-support-access-governance.logical.openapi.yaml
Ahmed Darrazi 37105653a1
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 1m29s
feat(spec-276): implement support access governance — commit all changes
2026-05-05 22:17:14 +02:00

403 lines
13 KiB
YAML

openapi: 3.0.3
info:
title: TenantPilot System/Admin - Support Access Governance (Conceptual)
version: 0.1.0
description: |
Conceptual contract for the bounded workspace-scoped support-access package.
NOTE: These routes are implemented as existing Filament pages, resources,
and Livewire-backed actions. Exact Livewire payload shapes are not part of
this contract. The file captures logical route boundaries, plane separation,
approval flow, and the explicit rule that recovery requires both support
access and break-glass.
paths:
/directory/workspaces/{workspace}:
servers:
- url: /system
get:
summary: View current support-access posture for one workspace in the system plane
parameters:
- $ref: '#/components/parameters/WorkspaceId'
responses:
'200':
description: System workspace detail rendered
content:
text/html:
schema:
type: string
x-logical-view-model:
$ref: '#/components/schemas/WorkspaceSupportAccessSummaryView'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
/directory/workspaces/{workspace}/actions/request-support-access:
servers:
- url: /system
post:
summary: Request bounded workspace-scoped support access from the system detail page
description: |
`audit_view` may activate immediately for an authorized platform actor.
`workspace_recovery` follows one of two logical branches:
- If the workspace still has at least one owner, the request becomes pending and must be approved from the admin workspace settings surface.
- If the workspace has zero owners, the request may only use the ownerless waiver branch when break-glass is already active and the caller supplies a distinct `waiver_reason`.
The ownerless waiver branch is blocked when break-glass is not active and is invalid when no waiver reason is supplied.
parameters:
- $ref: '#/components/parameters/WorkspaceId'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RequestSupportAccessCommand'
responses:
'204':
description: Support-access request created or immediately activated
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'409':
description: Active or pending grant already exists for the same workspace, actor, and scope, or ownerless recovery waiver prerequisites are blocked because break-glass is not active
'422':
$ref: '#/components/responses/ValidationError'
x-ownerless-waiver-preconditions:
applies_when:
scope: workspace_recovery
workspace_has_owners: false
requires:
- active_break_glass
- waiver_reason
failure_modes:
- status: 409
reason: ownerless waiver branch is blocked until break-glass is active
- status: 422
reason: ownerless waiver branch is invalid without a waiver reason
/directory/workspaces/{workspace}/support-access/{grant}/actions/end:
servers:
- url: /system
post:
summary: End an active support-access grant early
parameters:
- $ref: '#/components/parameters/WorkspaceId'
- $ref: '#/components/parameters/GrantId'
responses:
'204':
description: Active support access ended successfully
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
/settings/workspace:
servers:
- url: /admin
get:
summary: View pending recovery requests and the current support-access summary for the active workspace
responses:
'200':
description: Workspace settings support-access context rendered
content:
text/html:
schema:
type: string
x-logical-view-model:
$ref: '#/components/schemas/WorkspaceSupportApprovalView'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
/settings/workspace/support-access/{grant}/actions/approve:
servers:
- url: /admin
post:
summary: Approve a pending recovery-scoped support-access request from workspace settings
parameters:
- $ref: '#/components/parameters/GrantId'
responses:
'204':
description: Pending recovery request approved and activated
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'409':
description: The request is no longer pending or no longer approvable in the current workspace context
/settings/workspace/support-access/{grant}/actions/deny:
servers:
- url: /admin
post:
summary: Deny a pending recovery-scoped support-access request from workspace settings
parameters:
- $ref: '#/components/parameters/GrantId'
responses:
'204':
description: Pending recovery request denied
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'409':
description: The request is no longer pending or no longer denyable in the current workspace context
/audit-log:
servers:
- url: /admin
get:
summary: View support-access history through the existing workspace audit-log page
parameters:
- name: supportAccess
in: query
required: false
schema:
type: boolean
responses:
'200':
description: Audit log rendered
content:
text/html:
schema:
type: string
x-logical-view-model:
$ref: '#/components/schemas/SupportAccessAuditHistoryView'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
/audit-log/actions/export-support-access-history:
servers:
- url: /admin
post:
summary: Export support-access history for the active workspace
responses:
'202':
description: Export accepted or streamed according to the existing audit export pattern
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
/repair-workspace-owners:
servers:
- url: /system
get:
summary: View the current recovery blocker state before attempting owner repair
responses:
'200':
description: Recovery utility page rendered with current prerequisite state
content:
text/html:
schema:
type: string
x-logical-view-model:
$ref: '#/components/schemas/RecoveryBoundaryView'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
/repair-workspace-owners/actions/assign-owner:
servers:
- url: /system
post:
summary: Execute owner repair only when both support access and break-glass are active
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/AssignOwnerCommand'
responses:
'204':
description: Owner repair succeeded
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/BusinessStateBlocked'
/security/access-logs:
servers:
- url: /system
get:
summary: View system access logs including support-access events
responses:
'200':
description: Access-log page rendered
content:
text/html:
schema:
type: string
x-logical-view-model:
$ref: '#/components/schemas/SystemAccessLogView'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
components:
parameters:
WorkspaceId:
name: workspace
in: path
required: true
schema:
type: integer
GrantId:
name: grant
in: path
required: true
schema:
type: integer
responses:
Forbidden:
description: Actor is in-scope but missing the required capability
NotFound:
description: Wrong plane, wrong workspace, or non-member access is hidden as not found
ValidationError:
description: Submitted support-access data is invalid for the requested scope or approval mode, including missing waiver detail for the ownerless recovery branch
BusinessStateBlocked:
description: Actor is otherwise authorized, but support-access state or break-glass state blocks the action
schemas:
RequestSupportAccessCommand:
type: object
required:
- scope
- reason
- ttl_minutes
properties:
scope:
type: string
enum:
- audit_view
- workspace_recovery
description: `workspace_recovery` may require owner approval or the explicit ownerless waiver branch.
reason:
type: string
ttl_minutes:
type: integer
waiver_reason:
type: string
nullable: true
description: Required only when `workspace_recovery` is requested for a workspace with zero owners and break-glass is already active.
AssignOwnerCommand:
type: object
required:
- workspace_id
- target_user_id
- reason
properties:
workspace_id:
type: integer
target_user_id:
type: integer
reason:
type: string
WorkspaceSupportAccessSummaryView:
type: object
properties:
workspace_id:
type: integer
active_grant_id:
type: integer
nullable: true
pending_grant_id:
type: integer
nullable: true
scope:
type: string
nullable: true
scope_label:
type: string
nullable: true
status:
type: string
requester_label:
type: string
nullable: true
reason:
type: string
nullable: true
approval_mode:
type: string
nullable: true
approver_label:
type: string
nullable: true
expires_at:
type: string
format: date-time
nullable: true
needs_break_glass:
type: boolean
WorkspaceSupportApprovalView:
type: object
properties:
workspace_id:
type: integer
current_support_summary:
$ref: '#/components/schemas/WorkspaceSupportAccessSummaryView'
pending_recovery_requests:
type: array
items:
$ref: '#/components/schemas/PendingRecoveryRequestView'
PendingRecoveryRequestView:
type: object
properties:
grant_id:
type: integer
requester_label:
type: string
reason:
type: string
ttl_minutes:
type: integer
requested_at:
type: string
format: date-time
approval_mode:
type: string
waiver_reason:
type: string
nullable: true
SupportAccessAuditHistoryView:
type: object
properties:
workspace_id:
type: integer
support_access_filter_active:
type: boolean
export_available:
type: boolean
SystemAccessLogView:
type: object
properties:
includes_platform_auth:
type: boolean
includes_break_glass:
type: boolean
includes_support_access:
type: boolean
RecoveryBoundaryView:
type: object
properties:
workspace_id:
type: integer
nullable: true
has_active_break_glass:
type: boolean
has_active_recovery_grant:
type: boolean
recovery_grant_id:
type: integer
nullable: true
recovery_grant_expires_at:
type: string
format: date-time
nullable: true
approver_label:
type: string
nullable: true
blocker_state:
type: string
blocker_message:
type: string
nullable: true