TenantAtlas/specs/073-unified-managed-tenant-onboarding-wizard/research.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

68 lines
4.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Research — Managed Tenant Onboarding Wizard V1 (Enterprise) (073)
This document resolves planning unknowns and records key implementation decisions aligned with the clarified spec.
## Decisions
### 1) Managed Tenant model = existing `Tenant`
- **Decision:** Treat `App\Models\Tenant` as the “Managed Tenant” record.
- **Rationale:** Filament tenancy, membership model, and tenant-scoped flows already depend on `Tenant`; duplicating a second tenant-like table would multiply authorization and routing complexity.
- **Alternatives considered:** Introduce a new `ManagedTenant` model/table.
- **Why rejected:** Duplicates tenancy and membership boundaries; increases cross-plane leak risk.
### 2) Entra Tenant ID uniqueness = global, bound to one workspace
- **Decision:** Enforce global uniqueness for `tenants.tenant_id` (Entra Tenant ID) and bind it to exactly one workspace (the workspace_id on the tenant).
- **Rationale:** Matches FR-011 and the clarification decision (“global uniqueness bound to one workspace”).
- **Alternatives considered:** Allow the same Entra Tenant ID in multiple workspaces.
- **Why rejected:** Violates the clarified requirement and complicates deny-as-not-found behavior.
### 3) Canonical onboarding entry point = `/admin/onboarding` (only)
- **Decision:** Provide `/admin/onboarding` as the sole onboarding entry point.
- **Rationale:** Keeps a single user-facing URL for enterprise workflows; avoids fragmented legacy entry points.
- **Alternatives considered:** Workspace-scoped onboarding route (`/admin/w/{workspace}/...`).
- **Why rejected:** Conflicts with clarified spec (canonical `/admin/onboarding` only).
### 4) Tenantless operations viewer = existing `OperationRunResource` route `/admin/operations/{run}`
- **Decision:** Keep the route shape `/admin/operations/{run}` (already provided by `OperationRunResource` slug `operations`) and make it compliant by changing authorization + middleware behavior.
- **Rationale:** Minimizes routing surface area and leverages existing Monitoring → Operations UI.
- **Alternatives considered:** Create a separate “run viewer” page outside the resource.
- **Why rejected:** Duplicates infolist rendering and complicates observability conventions.
### 5) `/admin/operations/{run}` must not require selected workspace or auto-switch
- **Decision:** Exempt `/admin/operations/{run}` from forced workspace selection and from any “auto selection” side effects that would prevent tenantless viewing.
- **Rationale:** Spec requires (a) no workspace in the URL, (b) no pre-selected workspace required, (c) no auto-switching.
- **Alternatives considered:** Keep current `EnsureWorkspaceSelected` behavior (redirect to choose workspace).
- **Why rejected:** Violates FR-017a and can leak resource existence via redirects.
### 6) OperationRun authorization = workspace membership (non-member → 404)
- **Decision:** Authorize viewing a run by checking membership in the runs workspace; non-member gets deny-as-not-found (404).
- **Rationale:** FR-017a defines access semantics; runs must be viewable tenantlessly before activation.
- **Alternatives considered:** Authorize by `Tenant::current()` + matching `run.tenant_id`.
- **Why rejected:** Requires tenant routing/selection and breaks tenantless viewing.
### 7) OperationRun schema = add `workspace_id`, allow tenantless runs, preserve idempotency
- **Decision:** Add `operation_runs.workspace_id` (FK) and allow `tenant_id` to be nullable for pre-activation operations. Preserve DB-level dedupe using two partial unique indexes:
- Tenant-bound runs: `UNIQUE (tenant_id, run_identity_hash) WHERE tenant_id IS NOT NULL AND status IN ('queued', 'running')`
- Tenantless runs: `UNIQUE (workspace_id, run_identity_hash) WHERE tenant_id IS NULL AND status IN ('queued', 'running')`
- **Rationale:** Enables tenantless operations while preserving race-safe idempotency guarantees.
- **Alternatives considered:** Keep `tenant_id` required and always derive workspace via join.
- **Why rejected:** Blocks tenantless flows and makes authorization join-dependent.
### 8) Provider connection ownership = workspace-owned, default 1:1 binding
- **Decision:** Align Provider Connections to be workspace-owned and (by default) bound to exactly one managed tenant; reuse is disabled by default and policy-gated.
- **Rationale:** Matches FR-022/022a/022b and reduces blast radius of credential reuse.
- **Alternatives considered:** Keep provider connections tenant-owned.
- **Why rejected:** Conflicts with clarified spec ownership model.
## Open Questions
- None for planning; implementation will need to reconcile existing DB schema and policies with the decisions above.