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
63 lines
3.9 KiB
Markdown
63 lines
3.9 KiB
Markdown
# Research — Unified Managed Tenant Onboarding Wizard (073)
|
||
|
||
This document resolves planning unknowns and records key implementation decisions.
|
||
|
||
## Decisions
|
||
|
||
### 1) Managed Tenant model = existing `Tenant`
|
||
|
||
- **Decision:** Treat the existing `App\Models\Tenant` as the “Managed Tenant” concept.
|
||
- **Rationale:** The admin panel tenancy, membership model, and most operational flows already key off `Tenant`.
|
||
- **Alternatives considered:**
|
||
- Introduce a new `ManagedTenant` model/table.
|
||
- Keep `Tenant` as-is and build onboarding as “just another page”.
|
||
- **Why rejected:** A second tenant-like model would duplicate authorization, routing, and operational conventions.
|
||
|
||
### 2) Workspace-scoped uniqueness + stable route key
|
||
|
||
- **Decision:** Enforce uniqueness by `(workspace_id, tenant_id)` (where `tenant_id` is the Entra tenant ID), and ensure Filament’s route tenant key stays globally unique.
|
||
- **Rationale:** The feature spec explicitly defines the uniqueness key, and cross-workspace safety requires first-class scoping.
|
||
- **Implementation note:** Today `tenants.external_id` is unique and is force-set to `tenant_id` in `Tenant::saving()`. If we allow the same `tenant_id` across workspaces, `external_id` must NOT be set to `tenant_id` anymore. Prefer a generated opaque stable `external_id` (UUID) and keep `tenant_id` strictly as the business identifier.
|
||
- **Alternatives considered:**
|
||
- Keep global uniqueness on `tenant_id` and keep using `external_id = tenant_id`.
|
||
- **Why rejected:** Conflicts with the clarified uniqueness key and complicates “deny-as-not-found” behavior via DB constraint errors.
|
||
|
||
### 3) Wizard route location = workspace-scoped (`/admin/w/{workspace}/...`)
|
||
|
||
- **Decision:** Mount onboarding at a workspace-scoped route: `/admin/w/{workspace}/managed-tenants/onboarding`.
|
||
- **Rationale:** This path is explicitly exempted from forced tenant selection in `EnsureFilamentTenantSelected`, allowing onboarding before a tenant exists.
|
||
- **Alternatives considered:**
|
||
- Tenant-scoped Filament routes (`/admin/t/{tenant}/...`).
|
||
- Reusing Filament’s built-in tenant registration page (`tenantRegistration`).
|
||
- **Why rejected:** Tenant-scoped routes require a tenant to exist/selected; built-in registration is a legacy entry point we must remove.
|
||
|
||
### 4) Verification implementation = existing provider operation (`provider.connection.check`)
|
||
|
||
- **Decision:** Use `provider.connection.check` (module `health_check`) executed via `ProviderConnectionHealthCheckJob` as the onboarding verification run.
|
||
- **Rationale:** It already uses `OperationRun`, writes sanitized outcomes, and performs Graph calls off-request.
|
||
- **Alternatives considered:**
|
||
- New onboarding-specific operation type.
|
||
- **Why rejected:** Adds duplication without a clear benefit for v1.
|
||
|
||
### 5) Authorization surface = workspace capability (Owner+Manager)
|
||
|
||
- **Decision:** Add a dedicated workspace capability for onboarding (e.g., `workspace_managed_tenant.onboard`) and grant it to workspace Owner and Manager in `WorkspaceRoleCapabilityMap`.
|
||
- **Rationale:** The spec requires Owner+Manager; existing workspace capabilities don’t exactly match this (e.g., `WORKSPACE_MANAGE` is Owner-only).
|
||
- **Alternatives considered:**
|
||
- Check workspace role strings (`owner/manager`) directly.
|
||
- Reuse an unrelated capability like `WORKSPACE_MEMBERSHIP_MANAGE`.
|
||
- **Why rejected:** Constitution forbids role-string checks in feature code; reusing unrelated capability broadens authorization implicitly.
|
||
|
||
### 6) Legacy entry points = removed/404 (no redirects)
|
||
|
||
- **Decision:** Remove/disable these entry points and ensure 404 behavior:
|
||
- `/admin/register-tenant` (Filament registration page)
|
||
- `/admin/managed-tenants*` legacy redirects
|
||
- `/admin/new` redirect
|
||
- `/admin/w/{workspace}/managed-tenants/onboarding` redirect stub
|
||
- **Rationale:** FR-001 requires wizard-only entry and “not found” behavior.
|
||
|
||
## Open Questions
|
||
|
||
- None. All technical unknowns required for planning are resolved.
|