TenantAtlas/.specify/spec.md

645 lines
32 KiB
Markdown
Raw 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.

# Feature Specification: TenantPilot v1
**Feature Branch**: `tenantpilot-v1`
**Created**: 2025-12-10
**Status**: Draft
**Input**: TenantPilot v1 scope covering Intune configuration inventory (config, compliance, scripts, apps, conditional access, endpoint security, enrollment/autopilot, RBAC), backup, version history, and defensive restore for Intune administrators.
## Scope
```yaml
scope:
description: "v1 muss folgende Intune-Objekttypen inventarisieren, sichern und je nach Risikoklasse wiederherstellen können."
supported_types:
- key: deviceConfiguration
name: "Device Configuration"
graph_resource: "deviceManagement/deviceConfigurations"
notes: "Inklusive Custom OMA-URI, Administrative Templates und Settings Catalog."
- key: deviceCompliancePolicy
name: "Device Compliance"
graph_resource: "deviceManagement/deviceCompliancePolicies"
- key: appProtectionPolicy
name: "App Protection (MAM)"
graph_resource: "deviceAppManagement/managedAppPolicies"
notes: "iOS und Android Managed App Protection."
- key: conditionalAccessPolicy
name: "Conditional Access"
graph_resource: "identity/conditionalAccess/policies"
notes: "Kritisch für Sicherheit. Policy.Read.All/Policy.ReadWrite.All nötig; v1: Restore nur mit starker Preview."
- key: deviceManagementScript
name: "PowerShell Scripts"
graph_resource: "deviceManagement/deviceManagementScripts"
notes: "scriptContent wird beim Backup base64-decoded gespeichert und beim Restore wieder encoded (vgl. FR-020)."
- key: enrollmentRestriction
name: "Enrollment Restrictions"
graph_resource: "deviceManagement/deviceEnrollmentConfigurations"
- key: windowsAutopilotDeploymentProfile
name: "Windows Autopilot Profiles"
graph_resource: "deviceManagement/windowsAutopilotDeploymentProfiles"
- key: windowsEnrollmentStatusPage
name: "Enrollment Status Page (ESP)"
graph_resource: "deviceManagement/deviceEnrollmentConfigurations"
filter: "odata.type eq '#microsoft.graph.windows10EnrollmentCompletionPageConfiguration'"
- key: endpointSecurityIntent
name: "Endpoint Security Intents"
graph_resource: "deviceManagement/intents"
notes: "Account Protection, Disk Encryption etc.; Zuordnung über bekannte Templates."
- key: mobileApp
name: "Applications (Metadata only)"
graph_resource: "deviceAppManagement/mobileApps"
notes: "Backup nur von Metadaten/Zuweisungen (kein Binary-Download in v1)."
restore_matrix:
deviceConfiguration:
backup: full
restore: enabled
risk: medium
notes: "Standard-Case für Backup+Restore; starke Preview/Audit Pflicht."
deviceCompliancePolicy:
backup: full
restore: enabled
risk: medium
notes: "Compliance-Änderungen können Zugriff beeinflussen, aber sind gut verständlich."
appProtectionPolicy:
backup: full
restore: enabled
risk: medium-high
notes: "MAM-Änderungen wirken auf Datenzugriff in Apps; Preview und Diff wichtig."
conditionalAccessPolicy:
backup: full
restore: preview-only
risk: high
notes: "Hohe Ausfallgefahr. v1: Backup, Versioning, Diff + ausführliche Preview; Restore nur manuell anhand Preview."
deviceManagementScript:
backup: full
restore: enabled
risk: medium
notes: "Script-Inhalt und Einstellungen werden gesichert; Decode/Encode beachten."
enrollmentRestriction:
backup: full
restore: preview-only
risk: high
notes: "Kann Enrollment blockieren; v1 eher nur Preview + manuelle Umsetzung."
windowsAutopilotDeploymentProfile:
backup: full
restore: enabled
risk: medium-high
notes: "Provisioning-kritisch; Preview + Audit, aber automatisierbar."
windowsEnrollmentStatusPage:
backup: full
restore: enabled
risk: medium
notes: "ESP beeinflusst OOBE UX; Änderungen klar sichtbar."
endpointSecurityIntent:
backup: full
restore: enabled
risk: high
notes: "Security-relevante Einstellungen (z. B. Credential Guard); Preview + klare Konflikt-Warnungen nötig."
mobileApp:
backup: metadata-only
restore: enabled
risk: low-medium
notes: "Nur Metadaten/Zuweisungen; kein Binary; Restore setzt Konfigurationen/Zuweisungen wieder."
```
## User Scenarios & Testing *(mandatory)*
### User Story 1 - Policy inventory listing (Priority: P1)
Admin can view supported Intune object types (as defined in the scope) with normalized metadata for selection.
**Why this priority**: Inventory is the entry point for backup/version flows. Without it, no downstream workflows are usable.
**Independent Test**: From Filament, navigate to Policies; verify supported types render with identifiers, type/category, platform metadata, and tenant scoping.
**Acceptance Scenarios**:
1. **Given** an authenticated admin, **When** they open the Policies list, **Then** they see supported object types with identifiers, type/category, platform, and last-updated metadata.
2. **Given** filtering by type/category, **When** the admin selects a type, **Then** only matching objects appear and the view remains tenant-scoped.
---
### User Story 1b - Policy detail shows readable settings (Priority: P1)
Admin can open a policy detail page and see the **effective Intune settings** in a readable, normalized way (not raw JSON dumps).
**Independent Test**: From Filament, open a policy detail view; verify a "Settings" section renders normalized key/value pairs (or tables for special cases) derived from the latest snapshot.
**Acceptance Scenarios**:
1. **Given** a policy with at least one captured snapshot, **When** the admin opens the policy detail view, **Then** they see a "Settings" section rendering the policy configuration in a readable format (grouped/labeled).
2. **Given** the snapshot contains nested structures or list-based settings (e.g., OMA-URI / Settings Catalog), **When** the admin views settings, **Then** values are flattened/grouped or rendered as tables, and irrelevant metadata keys are hidden.
---
### User Story 2 - Backup creation and browsing (Priority: P1)
Admin creates backup sets containing multiple objects (config, compliance, scripts, apps, CA, etc.) with immutable snapshots and can browse backup details in Filament.
**Why this priority**: Backups provide safety and enable restore; immutability and audit are foundational.
**Independent Test**: Initiate a backup set selecting multiple objects; confirm immutable JSONB snapshots persisted, audit log written, and Filament shows backup detail and items.
**Acceptance Scenarios**:
1. **Given** selected objects from different categories, **When** the admin creates a backup set, **Then** backup items store immutable payload snapshots (full or metadata-only as per the restore matrix) with identifiers and types.
2. **Given** a completed backup set, **When** the admin opens its detail page, **Then** all items and metadata display along with the audit record of creation.
3. **Given** mehrere Backup-Sets existieren,
**When** der Admin ein Backup-Set auswählen oder ansehen möchte,
**Then** sieht er für jedes Set:
- einen sprechenden Namen (nicht nur Timestamp),
- das Erstellungsdatum,
- die Anzahl der enthaltenen Items,
- und optional eine kurze Beschreibung, damit er das Set sinnvoll unterscheiden kann.
---
### User Story 3 - Version history and diff (Priority: P1)
Admin can capture versions for any supported object, view timelines, and compare any two versions with meaningful diffs.
**Why this priority**: Version visibility and diffs enable rollback readiness and change comprehension.
**Independent Test**: Create multiple versions for a given object; verify timeline ordering, version metadata, and diff output (human summary + JSON diff where feasible) between any two versions.
**Acceptance Scenarios**:
1. **Given** an admin triggers version capture for an object, **When** the version is saved, **Then** an immutable snapshot and metadata (actor, time, type, tenant) are recorded.
2. **Given** two versions of the same object, **When** the admin requests a comparison, **Then** the UI shows a human-readable summary and structured JSON diff where available.
3. **Given** a saved policy version, **When** the admin opens the version detail page, **Then** the snapshot is displayed as pretty-printed JSON and, where possible, as normalized settings (not as an unreadable serialized array/string).
---
### User Story 4 - Restore with preview and confirmation (Priority: P1)
Admin can run a restore from a backup set with preview/dry-run, selective restore, clear warnings, and required confirmation before execution.
**Why this priority**: Restore is high-risk; safety features are mandatory for production readiness.
**Independent Test**: Start a restore from a backup set in preview; view change summary and warnings; select items; confirm execution; verify audit logs and outcomes recorded (success/failure/partial).
**Acceptance Scenarios**:
1. **Given** a backup set, **When** the admin initiates a restore in preview mode, **Then** the system shows a change summary with selectable items and conflict warnings.
2. **Given** selected items and explicit confirmation, **When** execution proceeds, **Then** applied changes are tenant-scoped and audit logs record start, result, and any failures.
3. **Given** mehrere Backup-Sets existieren,
**When** der Admin einen Restore Run erstellt,
**Then** zeigt die Auswahl für das "Backup set" mindestens:
- den Backup-Namen,
- das Erstellungsdatum,
- die Anzahl der Items,
damit der Admin das richtige Backup-Set sicher auswählen kann.
4. **Given** ein Restore Run wurde erstellt,
**When** der Admin die Detailseite des Restore Runs öffnet,
**Then** sieht er, welche Policies/Items in diesem Run enthalten sind
(z. B. Liste der Policies mit Name/Typ/Plattform).
---
### User Story 5 - Operational readiness and environments (Priority: P2)
Local development uses Sail; deployments target Dokploy staging then production with clear validation steps.
**Why this priority**: Ensures reproducible local setup and safe promotion to production.
**Independent Test**: Run the app locally via Sail; validate migrations on staging before production; confirm required env vars and queues/workers are documented.
### User Story 6 - Berechtigungsübersicht & Health-Status (Priority: P1)
Als Admin möchte ich für jeden Tenant sehen, welche Microsoft Graph-Berechtigungen
erforderlich sind, welche bereits erteilt wurden und welche fehlen, damit ich
sicherstellen kann, dass alle Funktionen von TenantPilot sicher und vollständig
arbeiten.
**Why this priority**: Jede neue Funktion kann zusätzliche Berechtigungen benötigen.
Ohne transparente Übersicht und Abgleich besteht das Risiko, dass Features still
kaputt sind oder unsicher laufen.
**Acceptance Scenarios**:
1. **Given** ein Tenant ist in TenantPilot hinterlegt,
**When** der Admin die Tenant-Detailseite öffnet,
**Then** sieht er eine Liste aller *erforderlichen* Berechtigungen mit Status
(z. B. OK, fehlt).
2. **Given** neue Funktionen wurden eingeführt, die zusätzliche Berechtigungen benötigen
und diese wurden in der zentralen Permissions-Liste hinzugefügt,
**When** der Admin die Tenant-Detailseite öffnet,
**Then** erscheinen die neuen Berechtigungen automatisch in der Übersicht und
fehlende Berechtigungen werden klar als fehlend markiert.
3. **Given** der Admin klickt auf "Verify configuration",
**When** TenantPilot einen Graph-Twestcall und/oder das Permission-Setup prüft,
**Then** wird der Status der Berechtigungen aktualisiert (OK/fehlt/Fehler) und
es wird ein Audit-Eintrag erstellt.
4. **Given** ein Tenant hat fehlende kritische Berechtigungen,
**When** andere Features (Policy-Sync, Backup, Restore) diesen Tenant verwenden,
**Then** kann TenantPilot dem Admin entsprechende Warnungen anzeigen oder die
Funktion mit einem klaren Fehler abbrechen.
**Acceptance Scenarios**:
1. **Given** a fresh checkout, **When** Sail commands run (`./vendor/bin/sail up -d`, `./vendor/bin/sail artisan migrate`), **Then** the app boots with PostgreSQL and Filament admin available.
2. **Given** a pending release, **When** migrations and restore flows are validated on staging, **Then** production deployment proceeds with documented steps and environment parity.
### Edge Cases
- Graph permissions missing or expired, causing policy fetch/restore failures with clear error mapping and audit entries.
- Large policy payloads or many items in a backup set; ensure JSONB storage and pagination handle load without timeouts.
- Restore conflicts when target tenant already has newer versions; preview must surface warnings and allow skip.
- Partial restore failures; audits must capture per-item outcomes and surface retry guidance.
- Diff generation for incompatible or malformed payloads should fail gracefully with admin-facing messaging.
- Retention/size concerns for snapshots; document defaults and guard against unbounded growth.
- Snapshots stored as serialized strings or array-only dumps (keys lost) must be detected; UI should show a clear warning and fall back to raw display.
- Policies whose `@odata.type` does not match the expected platform/type mapping should be flagged to prevent wrong restore previews (e.g., stored as Windows but snapshot indicates Android).
## Requirements *(mandatory)*
### Functional Requirements
- **FR-001**: System MUST list all Intune objects defined in the `scope.supported_types` section with normalized metadata and tenant scoping for selection.
- **FR-002**: System MUST allow admins to create backup sets containing multiple objects (configuration, compliance, scripts, apps, conditional access, etc.) with immutable JSONB payload snapshots.
- **FR-003**: Backup creation MUST log audit events including actor, timestamp, tenant, items, and outcome.
- **FR-004**: System MUST capture policy versions on demand and present per-policy timelines.
- **FR-005**: Users MUST be able to diff any two versions with a human-readable summary and structured JSON diff where feasible.
- **FR-006**: Restore MUST support preview/dry-run, selective item restore, and explicit confirmation before applying changes, within the per-type restore level defined in `scope.restore_matrix`.
- **FR-007**: Restore execution MUST produce audit logs covering success, failure, and partial outcomes.
- **FR-008**: Graph integration MUST route through a dedicated abstraction layer with standardized error mapping, safe retries, and high-level logging without secrets.
- **FR-009**: All policy, version, backup, and restore data MUST be tenant-aware; queries enforce tenant isolation.
- **FR-010**: Application MUST run locally via Laravel Sail with PostgreSQL and provide Filament admin flows.
- **FR-011**: Deployments MUST target Dokploy staging before production with documented migration and worker implications.
- **FR-012**: Tests MUST cover backup composition rules, version immutability, audit events, and Filament backup/restore flows (with Graph boundaries mocked).
- **FR-013**: Raw policy snapshots and backup payloads MUST be stored as JSONB with indexes justified by query needs (e.g., FK and time-based; GIN when filters require).
- **FR-014**: UI MUST provide clear warnings for potential restore conflicts and require confirmation for destructive operations; for types with `restore: preview-only` in `scope.restore_matrix` no direct apply action MAY be offered.
- **FR-015**: Admins MUST be able to safely delete (archive) backup sets that are no
longer needed. Deletion is implemented as soft-delete with audit logging, and
backup sets referenced by completed restore runs cannot be removed.
- **FR-016**: Admins MUST be able to delete individual policy versions for housekeeping.
Deletion is implemented as soft-delete with audit logging.
- **FR-017**: Admins MUST be able to deactivate (soft-delete) a tenant.
Deactivated
tenants:
- do not appear in default lists,
- cannot be used for new sync/backup/restore operations,
- keep their historical data and audit logs for traceability.
- **FR-018**: Admins MAY soft-delete restore runs to keep the UI clean; underlying
backup and policy data remains untouched.
- **FR-019**: The system MUST normalize different payload structures for display via a `PolicyNormalizer` (or equivalent): OMA-URI/custom policies as path/value tables, Settings Catalog policies as flattened structures, and standard objects as key-value views, aligned with `scope.supported_types`.
- **FR-019a**: Policy detail views MUST display a "Settings" section derived from the latest available snapshot (using the normalizer output when available).
- **FR-019b**: Policy version detail views MUST render snapshots as pretty-printed JSON (monospace, copyable) and SHOULD also render normalized settings via the same normalizer.
- **FR-020**: For PowerShell script objects (`deviceManagementScript` in `scope.supported_types`), the `scriptContent` MUST be base64-decoded when stored in backups/versions for readability/diffing and encoded again when sent back to Graph during restore.
- **FR-021**: Restore behavior MUST follow the per-type configuration in `scope.restore_matrix`: `backup` determines full vs metadata-only snapshots; `restore` determines whether automated restore is enabled or preview-only; `risk` informs warning/confirmation UX.
- **FR-022**: For high-risk types with `restore: preview-only` in `scope.restore_matrix` (e.g., `conditionalAccessPolicy`, `enrollmentRestriction`), TenantPilot MUST provide full backups, version history, and diffs plus detailed restore previews, but MUST NOT expose direct Graph apply actions; restore is manual, guided by the preview.
### Key Entities *(include if feature involves data)*
- **tenants**: Represents the deployment tenant context; referenced by all scoped data.
- **policies**: Normalized metadata for supported Intune policies.
- **policy_versions**: Immutable snapshots with metadata (actor, timestamp, tenant, policy type).
- **backup_sets**: Group of backup items with creator, timestamp, and tenant context.
- **backup_items**: Individual policy snapshots within a backup set (immutable JSONB payload + identifiers).
- **restore_runs**: Execution records for restores, including preview/actual flags and outcomes.
- **audit_logs**: Audit trail entries for backups, restores, version captures, and significant Graph actions.
## Success Criteria *(mandatory)*
### Measurable Outcomes
- **SC-001**: Admin can create a backup set selecting multiple policies and view immutable backup items with audit logs in Filament.
- **SC-002**: Policy version history timeline is available per policy and supports comparing any two versions with summary and JSON diff outputs.
- **SC-003**: Restore preview shows change summaries and conflict warnings; execution requires explicit confirmation and produces audit logs for all outcomes.
- **SC-004**: Core flows run locally via Sail; staging validation of migrations and restore paths completes before production deployments.
- **SC-005**: Automated tests covering backup composition, version immutability, audit logging, and Filament backup/restore flows pass via `./vendor/bin/sail artisan test`.
### Technical Story Enforce Single Current Tenant ("Highlander Principle")
**Context**
Aktuell können mehrere Tenants `status = active` sein. Graph-Operationen (Policy Sync,
Backup, Restore) wählen den Kontext über Heuristiken (`findOrCreateDefault`,
`local-tenant`), was zu falschen Tenants und Fehlern führt.
**Goal**
Es soll **immer genau einen klar definierten "current" Tenant** geben, über den
alle Graph-Operationen laufen. Die Auswahl dieses Tenants ist explizit und
transparent (UI + Env), nicht implizit.
**Requirements**
- Es gibt ein Flag `is_current` in `tenants`, das den aktuell verwendeten Kontext
markiert.
- Die Datenbank erzwingt per partiellem Unique Index, dass höchstens ein
nicht-gelöschter Tenant `is_current = true` haben kann.
- `Tenant::current()` liefert:
- falls `INTUNE_TENANT_ID` gesetzt ist, **genau diesen** Tenant (Fehler, wenn
er nicht existiert oder deaktiviert ist),
- sonst den Tenant mit `is_current = true` und `status = active`.
- falls keiner gefunden wird, eine klare Exception (“No current tenant selected”);
es werden keine Dummy-Tenants erzeugt.
- In der Tenant-Verwaltung gibt es eine Action "Make current", die:
- in einer Transaktion alle anderen Tenants auf `is_current = false` setzt
und den gewählten Tenant auf `is_current = true`,
- nur für aktive Tenants verfügbar ist.
- Der frühere Placeholder `local-tenant` darf nicht mehr als Graph-Kontext genutzt
werden; sobald ein echter Tenant existiert, wird er archiviert und ist nie
`is_current`.
- Alle Graph-basierten Funktionen (Policy Sync, Backup, Restore) verwenden
konsistent `Tenant::current()` oder einen explizit übergebenen Tenant.
Tenant-level actions such as "Admin consent" and "Verify configuration"
MUST be exposed on the tenant detail view (and/or row actions), not as a
global button without explicit tenant context.
### UX Guideline Table Actions / Dropdowns
- Tabellen in Filament mit mehr als zwei Zeilen-Aktionen (z.B. View, Edit,
Admin consent, Verify, Deactivate, Force delete) MÜSSEN ihre Aktionen in
einem kompakten Dropdown / ActionGroup bündeln, statt alle Buttons nebeneinander
anzuzeigen.
- Ausnahmen: besonders häufige, nicht-destruktive Aktionen (z.B. "View")
dürfen weiterhin als einzelner Button sichtbar bleiben; alle weiteren
Aktionen (z.B. Admin-Aktionen, Housekeeping) sollen im Dropdown liegen.
- Ziel: die Tabellen bleiben übersichtlich, Spaltenbreite wird begrenzt,
und Admins bekommen eine konsistente "⋯"-Interaktion für erweiterte Aktionen.
## User Story 7 Intune RBAC Onboarding Wizard (Delegated Admin Login) *(Priority: P1)*
### Problem / Context
TenantPilot arbeitet primär **app-only (Client Credentials)** gegen Microsoft Graph.
Für viele Intune-Objekte reicht „Graph App Permissions + Admin Consent“ allein nicht aus: Intune kann zusätzlich über **Intune RBAC**
blockieren, wenn der **Service Principal** (Enterprise App) keine passende **Intune Role Assignment** inkl. Scope hat.
Das äußert sich typischerweise als **403** mit „Application is not authorized to perform this operation“.
Dieses Setup ist ein **Bootstrap-Problem**:
- Ohne RBAC-Zuweisung sind Intune Reads/Writes blockiert.
- Ohne ausreichende Rechte kann TenantPilot die RBAC-Zuweisung nicht „self-service“ per app-only herstellen.
**Ziel:** TenantPilot bietet pro Tenant einen **Onboarding-Wizard**, bei dem ein Admin sich **interaktiv (delegated)** anmeldet,
und TenantPilot automatisiert (idempotent) die erforderliche Intune-RBAC-Konfiguration für die konfigurierte Enterprise App herstellt.
Danach funktionieren Policy Sync / Backup / Restore (gemäß Restore Matrix) zuverlässig.
---
### User Value
- Admins können RBAC-Probleme direkt in TenantPilot beheben (kein “Portal-Rätselraten”).
- Klarer, auditierter Ablauf (wer hat wann welche Rechte/Sopes gesetzt).
- Minimiert Ausfälle bei Policy-Sync/Backup/Restore und reduziert Support-Aufwand.
---
### In Scope (v1)
- Wizard in Filament auf der **Tenant-Detailseite** (tenant-scoped).
- Delegated Admin Login (interaktiv).
- Idempotente Ausführung:
- Service Principal (zu `tenant.app_client_id`) auflösen
- RBAC-Membership via **Security Group (recommended)** herstellen
- Intune Role Assignment erstellen/aktualisieren (Rolle + Scope)
- Abschließender Verify-Run (Health/Permissions aktualisieren)
- Vollständige Audit-Logs pro Step.
---
### Out of Scope (v1)
- Vollautomatisches “Self-heal” ohne Admin-Interaktion.
- Zeitgesteuerte Jobs, die RBAC-Rechte vergeben (ohne explizite Admin-Aktion).
- Unterstützung mehrerer paralleler RBAC-Profile pro Tenant (nur ein “recommended setup”).
---
## UX / Entry Points
### Entry Point: Tenant Detail (Filament)
Auf der `TenantResource` Detailseite im Action-Dropdown:
- `Setup Intune RBAC` (Wizard)
- `Admin consent`
- `Verify configuration`
**Visibility rules:**
- Nur für `status=active` Tenants.
- Nur wenn `app_client_id` gesetzt ist.
- Optional: Badge/Hint “RBAC missing” aus Health-Check.
**Copy/Help:**
- Kurze Erklärung: “Graph Permissions ≠ Intune RBAC”.
- Hinweis auf Least Privilege.
- Klarer Hinweis, dass Änderungen tenantweit wirken (je nach Scope).
---
## Wizard Flow
### Step 1 — Configuration (Role / Scope / Group)
**Inputs:**
- **Role** (Dropdown):
- Default: `Policy and Profile Manager` (Least Privilege für Policy/Config-Workflows)
- Optional: `Intune Administrator` (mit Warnung)
- **Scope** (Dropdown):
- Default: `Global / All devices` (wenn verfügbar)
- Optional: Auswahl einer Scope Group / Device Group (falls euer Modell das nutzt)
- **Group Mode**:
- Default: `Use Security Group (recommended)`
- Options:
- `Create new group` (Default-Name: `TenantPilot-Intune-RBAC`)
- `Use existing group` (Picker)
**UI Requirements:**
- “Review screen” zeigt *genau*, was erstellt/geändert wird (Role, Scope, Group).
### Step 2 — Delegated Admin Login
- Admin führt interaktiven Login durch (delegated).
- Wizard zeigt klar:
- welcher Tenant
- welche App (Client ID / Display Name, sofern auflösbar)
- dass nur kurzzeitig ein User-Token genutzt wird
**Security rule (mandatory):**
- Delegated Access Tokens werden **nicht persistiert** (keine Speicherung in DB/Cache).
- Tokens existieren nur im Request-Kontext / Session und werden nach Abschluss verworfen.
### Step 3 — Execute Setup (Idempotent + Safe)
Wizard führt folgenden Ablauf aus (alle Operationen tenant-scoped, über Graph-Abstraktion, mit Error-Mapping):
1) **Resolve Service Principal**
- Auflösen des Service Principals zur `tenant.app_client_id`.
- Wenn nicht gefunden:
- Wizard stoppt mit Hinweis: “Enterprise App ist im Tenant nicht vorhanden. Bitte zuerst Admin Consent durchführen.”
- Audit log: `tenant.rbac.setup.failed` (reason: sp_not_found)
2) **Ensure Security Group**
- Falls “Create new group”:
- Security Group erstellen (securityEnabled=true, mailEnabled=false).
- Wenn bereits vorhanden (gleiches displayName): wiederverwenden (oder per gespeicherter `rbac_group_id`).
- Falls “Use existing group”:
- Validieren: `securityEnabled=true`.
- Ergebnis-IDs werden gespeichert:
- `tenants.rbac_group_id` (neu, optional)
- `tenants.rbac_group_name` (optional, nur für UX)
3) **Ensure Membership (SP ∈ Group)**
- Service Principal als Member hinzufügen, wenn nicht vorhanden.
- Konflikte (already exists) müssen als OK behandelt werden.
4) **Ensure Intune Role Assignment**
- Suche nach existierendem Role Assignment, das:
- die gewünschte RoleDefinition referenziert
- die Group als Member enthält
- den gewünschten Scope abdeckt
- Wenn vorhanden: **Patch/Update** (z. B. Scope ergänzen)
- Wenn nicht vorhanden: **Create** Role Assignment
5) **Post-Verify (mandatory)**
- Direkt nach Setup:
- `Verify configuration` ausführen (inkl. Permission-Matrix Update)
- Zusätzlich 12 “Canary Calls” gegen Intune-Endpunkte, die für v1 kritisch sind (Read-Only reicht).
- Ergebnisse werden in Tenant-Health gespeichert (`app_status`, permissions health).
**Execution mode (v1):**
- Wizard führt die Steps synchron aus (kein Queue-Job), um Token-Probleme zu vermeiden.
- Timeouts: klare Fehlermeldung + Audit.
### Step 4 — Summary
- Wizard zeigt:
- Group (Name + ObjectId)
- Role (Name)
- Scope (global / group id)
- RoleAssignmentId (falls verfügbar)
- Verify result (OK / Partial / Failed)
- CTA: “Retry policy sync”
---
## Data Model Additions (minimal)
Erweiterung `tenants` (optional aber empfohlen, für Transparenz & Idempotenz):
- `rbac_group_id` (nullable string/GUID)
- `rbac_role_assignment_id` (nullable string/GUID)
- `rbac_role_key` (nullable string; z. B. `policy_profile_manager`)
- `rbac_scope_mode` (nullable string; z. B. `global|group`)
- `rbac_scope_id` (nullable string/GUID)
> Hinweis: Wenn ihr strikt ohne zusätzliche Felder arbeiten wollt, geht es auch rein über Discovery,
> aber gespeicherte IDs machen den Wizard deutlich stabiler und schneller.
---
## Functional Requirements (additions)
- **FR-023**: System MUST expose a per-tenant onboarding wizard “Setup Intune RBAC” in Filament.
- **FR-024**: Wizard MUST use delegated admin login and MUST NOT store delegated tokens.
- **FR-025**: Wizard MUST be idempotent (re-run safe) and MUST converge to the desired RBAC state.
- **FR-026**: Wizard MUST support group-based RBAC membership (recommended) and MUST ensure the service principal is a member.
- **FR-027**: Wizard MUST create or update Intune role assignments with an explicit role + scope.
- **FR-028**: Wizard MUST run a post-setup verification that updates tenant health and permissions UI.
- **FR-029**: Wizard MUST write audit logs for start, each step outcome, and final result (success/failed/partial).
- **FR-030**: Wizard MUST enforce tenant isolation and use explicit tenant context (no implicit defaults).
---
## Non-Functional / Safety Requirements
- Least Privilege:
- Default role selection is non-global admin (Policy/Profile manager).
- Selecting higher-privilege roles shows a warning and requires explicit confirmation.
- Clear Failure UX:
- Every failure must map to an actionable message (e.g., “Admin consent missing”, “Insufficient directory permissions”).
- No secrets:
- No access tokens, secrets, or payloads in logs/audits.
- Deterministic logging:
- Audit entries include tenant_id, actor_user_id, action_key, resource IDs (group, roleAssignment), and status.
---
## Acceptance Scenarios
1) **Missing RBAC → Wizard fixes it**
- **Given** ein aktiver Tenant mit konfigurierter App (`app_client_id`) und Admin Consent,
aber Intune Calls liefern RBAC-403,
- **When** der Admin den Wizard ausführt,
- **Then** wird Gruppe+Membership+RoleAssignment hergestellt, Verify wird OK, und Policy Sync funktioniert.
2) **Admin Consent fehlt**
- **Given** `app_client_id` ist gesetzt, aber der Service Principal kann nicht aufgelöst werden,
- **When** Wizard startet,
- **Then** bricht er mit “Bitte zuerst Admin Consent durchführen” ab und schreibt Audit `sp_not_found`.
3) **Idempotenz**
- **Given** Wizard wurde bereits erfolgreich ausgeführt,
- **When** Wizard erneut mit gleichen Einstellungen ausgeführt wird,
- **Then** werden keine Duplikate erzeugt, und die Summary zeigt “No changes / Already compliant”.
4) **Insufficient privileges**
- **Given** ein Admin loggt sich ein, aber hat nicht die nötigen Rechte,
- **When** Setup ausgeführt wird,
- **Then** stoppt der Wizard mit klarer Fehlermeldung pro Step (z. B. group create / role assignment create),
und Audit enthält den Step und Fehlercode.
5) **Restricted scope**
- **Given** Admin wählt eine eingeschränkte Scope Group,
- **When** Setup abgeschlossen ist,
- **Then** Verify markiert ggf. “Partial” mit Hinweis “Inventory limited by scope”.
---
## Implementation Notes (for plan.md linkage)
- Reuse existing services pattern:
- `IntuneRbacSetupService` (new) in `app/Services/Intune/`
- Uses `GraphClientInterface` and existing error mapping/logging hooks.
- Uses `AuditLogger` for stepwise audit events.
- Extend `TenantPermissionService` (User Story 7 in tasks) to include an RBAC check state:
- status: `ok|missing|error`
- message: “Intune RBAC role assignment missing (Wizard required)”
- Add Filament wizard page/action under `TenantResource`.
---
## Edge Cases
- Group exists but is not security-enabled → fail with actionable message.
- Role assignment exists but wrong scope → patch and warn.
- Multiple “similar” groups by name → prefer stored `rbac_group_id` if present, else prompt.
- Tenant mismatch: Wizard must never operate on non-selected tenant (enforce `Tenant::current()` or explicit tenant param).
- Token expiry mid-run → show “Please retry” + audit partial.
Previous draft archived under spechistory/spec.md