TenantAtlas/specs/073-unified-managed-tenant-onboarding-wizard/data-model.md
ahmido 8e34b6084f 073-unified-managed-tenant-onboarding-wizard (#90)
Kontext / Ziel
Diese PR liefert den einzigen kanonischen Onboarding-Entry unter /admin/onboarding (workspace-first, tenantless bis zur Aktivierung) und ergänzt einen tenantless OperationRun-Viewer unter /admin/operations/{run} mit membership→404 Semantik.

Was ist enthalten?
Single entry point: /admin/onboarding ist der einzige Einstieg; Legacy Entry Points liefern echte 404 (keine Redirects).
Wizard v1 (Enterprise): idempotentes Identifizieren eines Managed Tenants (per Entra Tenant ID), resumable Session-Flow.
Provider Connection Step: Auswahl oder Erstellung, Secrets werden nie erneut gerendert / nicht in Session-State persistiert.
Verification als OperationRun: async/queued, DB-only Rendering im Wizard (keine Graph-Calls beim Rendern).
Tenantless Run Viewing: /admin/operations/{run} funktioniert ohne ausgewählten Workspace/Tenant, aber bleibt über Workspace-Mitgliedschaft autorisiert (non-member → 404).
RBAC-UX Semantik: non-member → 404, member ohne Capability → UI disabled + tooltip, server-side Action → 403.
Auditability: Aktivierung/Overrides sind auditierbar, stable action IDs, keine Secrets.
Tech / Version-Safety
Filament v5 / Livewire v4.0+ kompatibel.
Laravel 11+: Panel Provider Registrierung in providers.php (unverändert).
Tests / Format
vendor/bin/sail bin pint --dirty
Full suite: vendor/bin/sail artisan test --no-ansi → 984 passed, 5 skipped (exit 0)
Ops / Deployment Notes
Keine zusätzlichen Services vorausgesetzt.
Falls Assets registriert wurden: Deployment weiterhin mit php artisan filament:assets (wie üblich im Projekt).

Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.fritz.box>
Co-authored-by: Ahmed Darrazi <ahmeddarrazi@MacBookPro.fritz.box>
Reviewed-on: #90
2026-02-04 23:30:55 +00:00

4.2 KiB
Raw Blame History

Data Model — Managed Tenant Onboarding Wizard V1 (Enterprise) (073)

Entities

Workspace

Existing entity: App\Models\Workspace

  • Onboarding is always initiated within a selected workspace context.
  • Workspace membership is the primary isolation boundary for wizard + tenantless operations viewing.

Tenant (Managed Tenant)

Existing model: App\Models\Tenant

Key fields (existing or to extend):

  • id (PK)
  • workspace_id (FK → workspaces)
  • tenant_id (string; Entra Tenant ID) — specs entra_tenant_id (globally unique)
  • external_id (string; Filament tenant route key; currently used in /admin/t/{tenant})
  • name (string)
  • primary_domain (string|null)
  • notes (text|null)
  • environment (string)
  • status (string) — v1 lifecycle:
    • draft
    • onboarding
    • active
    • archived

Indexes / constraints (design intent):

  • Unique: tenant_id (global uniqueness; binds the tenant to exactly one workspace)
  • external_id must remain globally unique for Filament tenancy routing

State transitions:

  • draftonboarding after identification is recorded
  • onboardingactive on owner activation
  • activearchived via archive/deactivate workflow

Provider Connection

Existing model today: App\Models\ProviderConnection (currently tenant-owned)

Spec-aligned ownership model (design intent):

  • Provider connections are workspace-owned.
  • Default binding: provider connection bound to exactly one managed tenant.
  • Reuse across managed tenants is disabled by default and policy-gated.

Proposed key fields (target):

  • id (PK)
  • workspace_id (FK → workspaces)
  • managed_tenant_id (FK → tenants.id; required in v1 default binding)
  • provider (string)
  • entra_tenant_id (string)
  • is_default (bool)
  • metadata (json)

Tenant Onboarding Session (new)

New model/table to persist resumable onboarding state for a workspace + Entra Tenant ID. Must never persist secrets and must render DB-only.

Proposed fields:

  • id (PK)
  • workspace_id (FK)
  • managed_tenant_id (FK → tenants.id; nullable until tenant is created)
  • entra_tenant_id (string; denormalized identity key; globally unique across the system but still stored for idempotency)
  • current_step (string; identify, connection, verify, bootstrap, complete)
  • state (jsonb) — safe fields only (no secrets)
    • tenant_name
    • environment
    • primary_domain
    • notes
    • selected_provider_connection_id
    • verification_run_id (OperationRun id)
    • bootstrap_run_ids (array)
  • started_by_user_id (FK users)
  • updated_by_user_id (FK users)
  • completed_at (timestamp|null)
  • timestamps

Constraints:

  • Unique: entra_tenant_id (global uniqueness) OR (if sessions are separate from tenants) unique (workspace_id, entra_tenant_id) with an additional global “tenant exists elsewhere” guard to enforce deny-as-not-found.

Operation Run

Existing model: App\Models\OperationRun

Spec-aligned visibility model (design intent):

  • Runs are viewable tenantlessly at /admin/operations/{run}.
  • Access is granted only to members of the runs workspace; non-member → deny-as-not-found (404).

Proposed schema changes:

  • Add workspace_id (FK → workspaces), required.
  • Allow tenant_id to be nullable for pre-activation runs.
  • Maintain DB-level active-run idempotency:
    • UNIQUE (tenant_id, run_identity_hash) WHERE tenant_id IS NOT NULL AND status IN ('queued', 'running')
    • UNIQUE (workspace_id, run_identity_hash) WHERE tenant_id IS NULL AND status IN ('queued', 'running')

Validation rules (high level)

  • entra_tenant_id: required, non-empty, validate GUID format.
  • Tenant identification requires: name, environment, entra_tenant_id.
  • Provider connection selected/created must be in the same workspace.
  • Onboarding session state must be strictly whitelisted fields (no secrets).

Authorization boundaries

  • Workspace membership boundary: non-member → 404 (deny-as-not-found) for onboarding and tenantless operations run viewing.
  • Capability boundary (within membership): action attempts without capability → 403.
  • Owner-only boundary: activation and blocked override require workspace owner; override requires reason + audit.