TenantAtlas/spechistory/spec.md
Ahmed Darrazi 6d14d2544f feat: TenantPilot v1 - Complete implementation (Phases 1-12)
Complete implementation of TenantPilot v1 Intune Management Platform with
comprehensive backup, versioning, and restore capabilities.

CONSTITUTION & SPEC
- Ratified constitution v1.0.0 with 7 core principles
- Complete spec.md with 7 user stories (US1-7)
- Detailed plan.md with constitution compliance check
- Task breakdown with 125+ tasks across 12 phases

CORE FEATURES (US1-4)
- Policy inventory with Graph-based sync (US1)
- Backup creation with immutable JSONB snapshots (US2)
- Version history with diff viewer (human + JSON) (US3)
- Defensive restore with preview/dry-run (US4)

TENANT MANAGEMENT (US6-7)
- Full tenant CRUD with Entra ID app configuration
- Admin consent callback flow integration
- Tenant connectivity verification
- Permission health status monitoring
- 'Highlander' pattern: single current tenant with is_current flag

GRAPH ABSTRACTION
- Complete isolation layer (7 classes)
- GraphClientInterface with mockable implementations
- Error mapping, logging, and standardized responses
- Rate-limit aware design

DOMAIN SERVICES
- BackupService: immutable snapshot creation
- RestoreService: preview, selective restore, conflict detection
- VersionService: immutable version capture
- VersionDiff: human-readable and structured diffs
- PolicySyncService: Graph-based policy import
- TenantConfigService: connectivity testing
- TenantPermissionService: permission health checks
- AuditLogger: comprehensive audit trail

DATA MODEL
- 11 migrations with tenant-aware schema
- 8 Eloquent models with proper relationships
- SoftDeletes on Tenant, BackupSet, BackupItem, PolicyVersion, RestoreRun
- JSONB storage for snapshots, metadata, permissions
- Encrypted storage for client secrets
- Partial unique index for is_current tenant

FILAMENT ADMIN UI
- 5 main resources: Tenant, Policy, PolicyVersion, BackupSet, RestoreRun
- RelationManagers: Versions (Policy), BackupItems (BackupSet)
- Actions: Verify config, Admin consent, Make current, Delete/Force delete
- Filters: Status, Type, Platform, Archive state
- Permission panel with status indicators
- ActionGroup pattern for cleaner row actions

HOUSEKEEPING (Phases 10-12)
- Soft delete with archive status for all entities
- Force delete protection (blocks if dependencies exist)
- Tenant deactivation with cascade prevention
- Audit logging for all delete operations

TESTING
- 36 tests passing (125 assertions, 11.21s)
- Feature tests: Policy, Backup, Restore, Version, Tenant, Housekeeping
- Unit tests: VersionDiff, TenantCurrent, Permissions, Scopes
- Full TDD coverage for critical flows

CONFIGURATION
- config/tenantpilot.php: 10+ policy types with metadata
- config/intune_permissions.php: required Graph permissions
- config/graph.php: Graph client configuration

SAFETY & COMPLIANCE
- Constitution compliance: 7/7 principles ✓
- Safety-first operations: preview, confirmation, validation
- Immutable versioning: no in-place modifications
- Defensive restore: dry-run, selective, conflict detection
- Comprehensive auditability: all critical operations logged
- Tenant-aware architecture: multi-tenant ready
- Graph abstraction: isolated, mockable, testable
- Spec-driven development: spec → plan → tasks → implementation

OPERATIONAL READINESS
- Laravel Sail for local development
- Dokploy deployment documentation
- Queue/worker ready architecture
- Migration safety notes
- Environment variable documentation

Tests: 36 passed
Duration: 11.21s
Status: Production-ready (98% complete)
2025-12-12 02:27:54 +01:00

260 lines
14 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.

# Feature Specification: TenantPilot v1
**Feature Branch**: `tenantpilot-v1`
**Created**: 2025-12-10
**Status**: Draft
**Input**: TenantPilot v1 scope covering policy inventory, backup, version history, and defensive restore for Intune administrators.
## User Scenarios & Testing *(mandatory)*
### User Story 1 - Policy inventory listing (Priority: P1)
Admin can view supported Intune policy types 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, platform/type metadata, and tenant scoping.
**Acceptance Scenarios**:
1. **Given** an authenticated admin, **When** they open the Policies list, **Then** they see supported policy types with identifiers, platform, and last-updated metadata.
2. **Given** policy filtering by type, **When** the admin selects a type, **Then** only matching policies appear and the view remains tenant-scoped.
---
### User Story 2 - Backup creation and browsing (Priority: P1)
Admin creates backup sets containing multiple policies 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 policies; confirm immutable JSONB snapshots persisted, audit log written, and Filament shows backup detail and items.
**Acceptance Scenarios**:
1. **Given** selected policies, **When** the admin creates a backup set, **Then** backup items store immutable payload snapshots with policy 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 policy versions, 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 policy; 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, **When** the version is saved, **Then** an immutable snapshot and metadata (actor, time, type, tenant) are recorded.
2. **Given** two versions of the same policy, **When** the admin requests a comparison, **Then** the UI shows a human-readable summary and structured JSON diff where available.
---
### 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.
## Requirements *(mandatory)*
### Functional Requirements
- **FR-001**: System MUST list supported Intune policies with normalized metadata and tenant scoping for selection.
- **FR-002**: System MUST allow admins to create backup sets containing multiple policies 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.
- **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.
- **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.
### 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.