Implements workspace-scoped managed tenant onboarding wizard (Filament v5 / Livewire v4) with strict RBAC (404/403 semantics), resumable sessions, provider connection selection/creation, verification OperationRun, and optional bootstrap. Removes legacy onboarding entrypoints and adds Pest coverage + spec artifacts (073). ## Summary <!-- Kurz: Was ändert sich und warum? --> ## Spec-Driven Development (SDD) - [ ] Es gibt eine Spec unter `specs/<NNN>-<feature>/` - [ ] Enthaltene Dateien: `plan.md`, `tasks.md`, `spec.md` - [ ] Spec beschreibt Verhalten/Acceptance Criteria (nicht nur Implementation) - [ ] Wenn sich Anforderungen während der Umsetzung geändert haben: Spec/Plan/Tasks wurden aktualisiert ## Implementation - [ ] Implementierung entspricht der Spec - [ ] Edge cases / Fehlerfälle berücksichtigt - [ ] Keine unbeabsichtigten Änderungen außerhalb des Scopes ## Tests - [ ] Tests ergänzt/aktualisiert (Pest/PHPUnit) - [ ] Relevante Tests lokal ausgeführt (`./vendor/bin/sail artisan test` oder `php artisan test`) ## Migration / Config / Ops (falls relevant) - [ ] Migration(en) enthalten und getestet - [ ] Rollback bedacht (rückwärts kompatibel, sichere Migration) - [ ] Neue Env Vars dokumentiert (`.env.example` / Doku) - [ ] Queue/cron/storage Auswirkungen geprüft ## UI (Filament/Livewire) (falls relevant) - [ ] UI-Flows geprüft - [ ] Screenshots/Notizen hinzugefügt ## Notes <!-- Links, Screenshots, Follow-ups, offene Punkte --> Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.fritz.box> Reviewed-on: #88
85 lines
2.6 KiB
Markdown
85 lines
2.6 KiB
Markdown
# Data Model — Unified Managed Tenant Onboarding Wizard (073)
|
||
|
||
## Entities
|
||
|
||
### Workspace
|
||
|
||
Existing entity. Onboarding is always initiated within a selected workspace.
|
||
|
||
### Tenant (Managed Tenant)
|
||
|
||
Existing model: `App\Models\Tenant`
|
||
|
||
**Key fields (existing or to be confirmed/extended):**
|
||
|
||
- `id` (PK)
|
||
- `workspace_id` (FK to workspaces)
|
||
- `tenant_id` (string; Entra tenant ID) — spec’s `entra_tenant_id`
|
||
- `external_id` (string; globally unique route key used by Filament tenancy)
|
||
- `name` (string)
|
||
- `domain` (string|null)
|
||
- `status` (string) — v1 lifecycle:
|
||
- `pending` (created / onboarding)
|
||
- `active` (ready)
|
||
- `archived` (no longer managed)
|
||
|
||
**Indexes / constraints (design intent):**
|
||
|
||
- Unique: `(workspace_id, tenant_id)`
|
||
- Keep `external_id` globally unique (for `/admin/t/{tenant}` routing) and do **not** force it to equal `tenant_id`.
|
||
|
||
**State transitions:**
|
||
|
||
- `pending` → `active` after successful verification
|
||
- `active` → `archived` on soft-delete (existing behavior)
|
||
- `archived` → `active` on restore (existing behavior)
|
||
|
||
### ProviderConnection
|
||
|
||
Existing model: `App\Models\ProviderConnection`
|
||
|
||
- Belongs to `Tenant`
|
||
- Contains `entra_tenant_id` (string) and default/active flags.
|
||
|
||
### TenantOnboardingSession (new)
|
||
|
||
New model/table to persist resumable onboarding state. Must never persist or return secrets.
|
||
|
||
**Proposed fields:**
|
||
|
||
- `id` (PK)
|
||
- `workspace_id` (FK)
|
||
- `tenant_id` (FK to tenants.id) — nullable until tenant is created, depending on wizard flow
|
||
- `entra_tenant_id` (string) — denormalized for upsert/idempotency before tenant exists
|
||
- `current_step` (string; e.g., `identify`, `connection`, `verify`, `bootstrap`, `complete`)
|
||
- `state` (jsonb/json) — safe fields only (no secrets)
|
||
- `tenant_name`
|
||
- `tenant_domain`
|
||
- `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: `(workspace_id, entra_tenant_id)`
|
||
|
||
**State transitions:**
|
||
|
||
- `in_progress` (implied by `completed_at = null`) → `completed` (`completed_at != null`)
|
||
|
||
## Validation rules (high level)
|
||
|
||
- `entra_tenant_id` (`tenant_id`) must be a non-empty string; validate as GUID format if enforced elsewhere.
|
||
- Tenant name required to create tenant.
|
||
- ProviderConnection selection must belong to the same tenant/workspace.
|
||
|
||
## Authorization boundaries
|
||
|
||
- Workspace scope: non-members denied as 404.
|
||
- Workspace member but missing onboarding capability: 403.
|
||
- Tenant scope: once tenant exists/selected, tenant membership rules apply as currently implemented.
|