3.3 KiB
3.3 KiB
Data Model — Managed Tenant Onboarding Wizard v1
This design is aligned to current repo reality where the “managed tenant” is the existing Tenant model.
Entity: Tenant (App\\Models\\Tenant)
Relevant existing fields
id(PK)name(display name)tenant_id(Entra tenant GUID; used as canonical external id)external_id(route key; kept in sync withtenant_idwhen present)domain(optional)environment(prod|dev|staging|other)app_client_id(optional)app_client_secret(encrypted cast; must never be displayed back to the user)- RBAC health / verification storage:
rbac_last_checked_at(datetime)rbac_last_setup_at(datetime)rbac_canary_results(array)rbac_last_warnings(array)
New fields (proposed)
If onboarding needs to be explicitly tracked on the tenant record:
onboarding_statusenum-like string:not_started|in_progress|completed(default:not_started)onboarding_completed_atnullable datetime
Rationale: makes it cheap to render “Resume wizard” / completion status without loading session records.
Entity: TenantOnboardingSession (new)
Table name (proposed)
tenant_onboarding_sessions
Columns
id(PK)tenant_idnullable FK →tenants.id- nullable at the very beginning if the user hasn’t provided a valid tenant GUID yet
created_by_user_idFK →users.idstatusstring:active|completed|abandonedcurrent_stepstring:welcome|tenant_details|credentials|permissions|verificationpayloadjsonb- contains non-secret form state only (e.g., name, tenant_id, domain, environment)
- MUST NOT contain secrets
last_error_codenullable stringlast_error_messagenullable string (sanitized; no tokens/secrets)completed_atnullable datetimeabandoned_atnullable datetimecreated_at,updated_at
Indexes and constraints
- Ensure at most one active session per tenant:
- PostgreSQL partial unique index:
(tenant_id)wherestatus = 'active'
- PostgreSQL partial unique index:
- Dedupe/resume lookup:
- index
(created_by_user_id, status) - index
(tenant_id, status)
- index
State transitions
active→completedwhen:- tenant record exists
- credentials requirement (if enabled) is satisfied
- last verification run indicates success
active→abandonedwhen user explicitly cancels
Entity: OperationRun (App\\Models\\OperationRun)
Wizard-triggered checks must be observable via OperationRun.
Relevant fields
tenant_idFKtypestring (examples already in repo:provider.connection.check,inventory.sync,compliance.snapshot)status/outcomerun_identity_hash(dedupe identity)context(json)
Idempotency
Use OperationRunService::ensureRun() / ensureRunWithIdentity() to get DB-level active-run dedupe.
Capability / Authorization model
- Capabilities are strings from the canonical registry
App\\Support\\Auth\\Capabilities. - Capability checks:
- Membership:
CapabilityResolver::isMember() - Capability:
CapabilityResolver::can()
- Membership:
- Tenant-scoped non-member access is denied-as-not-found (404) by
DenyNonMemberTenantAccessmiddleware. - Filament actions use
App\\Support\\Rbac\\UiEnforcementto apply:- hidden UI for non-members
- disabled UI + tooltip for members lacking the capability
- server-side 404/403 guardrails