TenantAtlas/.specify/tasks.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

319 lines
18 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.

---
description: "Task list for TenantPilot v1 implementation"
---
# Tasks: TenantPilot v1
**Input**: Design documents from `.specify/spec.md` and `.specify/plan.md`
**Prerequisites**: plan.md (complete), spec.md (complete)
## Phase 1: Setup (Shared Infrastructure)
- [x] T001 [P] [Shared] Confirm Sail/Env ready; ensure `.env` has PostgreSQL settings for Sail and Filament admin user seeded (if missing) in `database/seeders/`.
- [x] T002 [P] [Shared] Add baseline docs for local dev and staging promotion notes in `README.md` (Sail commands, staging-before-prod reminder).
## Phase 2: Foundational (Blocking Prerequisites)
- [x] T003 [Shared] Add tenant-aware migrations for `tenants`, `policies`, `policy_versions`, `backup_sets`, `backup_items`, `restore_runs`, `audit_logs` with JSONB payloads and FK/time indexes in `database/migrations/`.
- [x] T004 [Shared] Create models with relationships and guarded attributes for the above entities in `app/Models/`.
- [x] T005 [Shared] Implement Graph abstraction contracts (`GraphClientInterface`, error mapping, logging hooks) in `app/Services/Graph/` with a mockable adapter.
- [x] T006 [Shared] Add audit logging service/helper to capture actor, tenant, operation, resources, outcome in `app/Services/Intune/AuditLogger.php`.
- [x] T007 [Shared] Seed supported policy types/metadata for initial scope in `database/seeders/PoliciesSeeder.php` and ensure tenant scoping.
## Phase 3: User Story 1 - Policy inventory listing (Priority: P1)
### Tests for User Story 1
- [x] T008 [P] [US1] Feature test for Filament policy listing and filtering (tenant-scoped) in `tests/Feature/Filament/PolicyListingTest.php` using mocked Graph sync.
### Implementation for User Story 1
- [x] T009 [US1] Implement policy sync/import orchestrator using Graph abstraction in `app/Services/Intune/PolicySyncService.php` (no direct Graph in UI).
- [x] T010 [US1] Create Filament resource/table for policies with filters and metadata columns in `app/Filament/Resources/PolicyResource.php`.
- [x] T011 [US1] Add command/job to sync policies (queues-ready) in `app/Console/Commands/SyncPolicies.php` and queue job under `app/Jobs/`.
## Phase 4: User Story 2 - Backup creation and browsing (Priority: P1)
### Tests for User Story 2
- [x] T012 [P] [US2] Feature test for creating backup sets with multiple policies and verifying immutable JSONB snapshots + audit log in `tests/Feature/Filament/BackupCreationTest.php`.
### Implementation for User Story 2
- [x] T013 [US2] Implement backup domain service to assemble snapshots from policies with Graph payload retrieval in `app/Services/Intune/BackupService.php`.
- [x] T014 [US2] Add Filament resource/pages for backup sets and items (list/detail) in `app/Filament/Resources/BackupSetResource.php`.
- [x] T131 [UX] [US2] Refactor BackupSet policy selection to RelationManager:
- Remove the multi-select policy picker from the BackupSet **Create** form (keep Create minimal: name/description).
- After create, redirect to BackupSet **Edit/View** where items can be managed.
- Add `BackupItemsRelationManager` to `BackupSetResource` showing a table with columns: Policy Name, Type (badge), Restore (badge), Risk (badge).
- Add header action “Policies hinzufügen” (searchable, multiple) that adds items/attaches policies **tenant-scoped** and prevents duplicates per BackupSet.
- Provide a remove action (detach/soft-delete as per domain rules).
- [x] T132 [P] [US2] Update/extend `tests/Feature/Filament/BackupCreationTest.php` to cover the new UX flow:
- Create BackupSet without policies.
- Add multiple policies via RelationManager action.
- Verify immutable JSONB snapshots + audit log behavior remains correct.
- [x] T015 [US2] Wire audit logging for backup creation events in `app/Services/Intune/BackupService.php` using `AuditLogger`.
## Phase 5: User Story 3 - Version history and diff (Priority: P1)
### Tests for User Story 3
- [x] T016 [P] [US3] Feature test for version capture and timeline display in `tests/Feature/Filament/PolicyVersionTest.php`.
- [x] T017 [P] [US3] Unit test for diff generation (human summary + JSON diff) in `tests/Unit/VersionDiffTest.php`.
### Implementation for User Story 3
- [x] T018 [US3] Implement version capture service with immutable JSONB writes in `app/Services/Intune/VersionService.php`.
- [x] T019 [US3] Create diff helper (summary + structured JSON) in `app/Services/Intune/VersionDiff.php` and surface in Filament version compare view in `app/Filament/Resources/PolicyVersionResource.php`.
- [x] T020 [US3] Hook version capture into relevant flows (manual trigger + backup/restore hooks) ensuring audit logging.
## Phase 6: User Story 4 - Restore with preview and confirmation (Priority: P1)
### Tests for User Story 4
- [x] T021 [P] [US4] Feature test for restore preview (change summary, conflicts, selective items) in `tests/Feature/Filament/RestorePreviewTest.php`.
- [x] T022 [P] [US4] Feature test for confirmed restore execution capturing audit logs and per-item outcomes in `tests/Feature/Filament/RestoreExecutionTest.php`.
### Implementation for User Story 4
- [x] T023 [US4] Implement restore service with preview/dry-run and selective item application in `app/Services/Intune/RestoreService.php`, integrating Graph adapter and conflict detection.
- [x] T024 [US4] Add Filament restore UI (wizard or pages) showing preview, warnings, and confirmation gate in `app/Filament/Resources/RestoreRunResource.php`.
- [x] T025 [US4] Record restore run lifecycle (start, per-item result, completion) and audit events in `restore_runs` and `audit_logs`.
## Phase 7: User Story 5 - Operational readiness and environments (Priority: P2)
### Implementation for User Story 5
- [x] T026 [US5] Document Dokploy staging→production promotion steps, required env vars, queue/worker expectations, and migration safety notes in `README.md` or `docs/deploy.md`.
- [x] T027 [US5] Add quick Sail commands and test invocation notes to `README.md` (e.g., `./vendor/bin/sail artisan test`) and ensure sample env entries for Graph credentials.
## Phase 8: User Story 6 - Tenant hinzufügen & Entra ID App-Setup (Priority: P1)
- [x] T030 [US6] Migration für `tenants` ergänzen/prüfen:
- Felder: `name`, `tenant_id` (GUID), `domain`, `app_client_id`, `app_status`, `app_notes`,
`created_at`, `updated_at`.
- Optional: Felder für Secret/Certificate-Config (verschlüsselt), falls benötigt.
- [x] T031 [US6] Eloquent Model `Tenant`:
- Beziehungen zu `policies`, `backup_sets`, `restore_runs`, `policy_versions`, `audit_logs`
über `tenant_id`.
- Tenant-aware Scopes, falls vorhanden (z. B. `forTenant()`).
- [x] T032 [US6] Filament-Resource `TenantResource`:
- Listenansicht: Name, Tenant ID, Domain, App-Status, erstellt/am.
- Create/Edit-Form: Name, Tenant ID, Domain, App-Client-ID, optionale Notizen.
- Detailseite mit Actions:
- „Open in Entra“ (Link zur App/Tenant im Entra-Portal),
- optional: „Copy Admin Consent URL“.
- [x] T033 [US6] `TenantConfigService` (oder Erweiterung des Graph-Clients):
- Methode `testConnectivity(Tenant $tenant)`: führt einen einfachen Graph-Call aus
(z. B. `/organization` oder ähnliches) mit den App-Daten des Tenants.
- Rückgabe: DTO/Array mit `success`, `error_message` (falls vorhanden).
- [x] T034 [US6] Action „Verify configuration“ in `TenantResource`:
- Ruft `testConnectivity()` auf,
- setzt `app_status` auf z. B. `ok`, `error` oder `consent_required`,
- zeigt eine Filament-Notification mit dem Ergebnis,
- schreibt einen Audit-Log-Eintrag (`tenant.config.verified`).
- [x] T035 [US6] Tenant-Kontext in bestehende Services integrieren:
- `PolicySyncService`, `BackupService`, `RestoreService` so anpassen,
dass sie einen `Tenant` oder `tenant_id` übergeben bekommen
und den Graph-Client mit diesem Kontext verwenden.
- Sicherstellen, dass alle policy/backup/restore/audit-Datensätze `tenant_id` setzen.
- [x] T036 [US6] Feature-Test `TenantSetupTest`:
- Erstellen eines Tenants via Filament (Create-Form).
- Aufruf der Action „Verify configuration“ mit gemocktem Graph-Client:
- einmal mit erfolgreichem Call → `app_status = ok`,
- einmal mit Fehler → `app_status = error` + passende Notification.
- Prüfen, dass Audit-Logs geschrieben werden.
- [x] T037 [US6] Admin-Consent Callback Route
- Route/Controller, der als `redirect_uri` der Entra-ID-App dient.
- Liest `tenant` / `error` / `admin_consent` aus der Query.
- Ordnet das dem richtigen `Tenant` zu (z. B. via `state`).
- Aktualisiert `app_status` (z. B. `ok`, `error`, `consent_denied`).
- Zeigt eine Bestätigungs-/Fehlerseite für den Admin.
---
## Phase 9: User Story 7 - Berechtigungsübersicht & Health-Status (Priority: P1)
- [x] T040 [US7] Zentrale Permissions-Liste anlegen:
- `config/intune_permissions.php` mit allen aktuell benötigten Graph-Berechtigungen:
- technischer Name (z. B. `DeviceManagementConfiguration.ReadWrite.All`),
- Typ: `application` / `delegated`,
- kurze Beschreibung,
- Feature-Tags (z. B. `["policy-sync", "backup"]`).
- Optional: `docs/permissions.md` mit einer Tabelle Feature ↔ Permission als
menschlich lesbare Referenz.
- [x] T041 [US7] Datenmodell für Tenant-Berechtigungen:
- Variante A (einfach): JSONB-Feld `granted_permissions` in `tenants` (Liste von Permission-Keys).
- Variante B (feiner): Tabelle `tenant_permissions` mit
`(tenant_id, permission_key, status, last_checked_at)`.
- `status` mindestens: `ok`, `missing`, `error`.
- [x] T042 [US7] Service `TenantPermissionService`:
- `getRequiredPermissions(): array` liest aus `config/intune_permissions.php`.
- `getGrantedPermissions(Tenant $tenant): array` liest aus Graph oder aus
`tenant_permissions`/`granted_permissions`.
- `compare(Tenant $tenant): TenantPermissionStatusDTO` liefert pro Permission
den Status (ok/missing/error) + Gesamthealth.
- [x] T043 [US7] Integration in Tenant-Detail-UI:
- Auf der `TenantResource`-Detailseite ein Panel/Section „Permissions“:
- Liste aller **required permissions**,
- pro Zeile: Name, Typ, Feature-Tags, Status (Icon + Label: OK/fehlt/Fehler).
- Optional: Link zu Doku oder Entra-Darstellung (z. B. „How to grant these permissions“).
- [x] T044 [US7] Action „Verify configuration“ erweitern:
- Zusätzlich zu `testConnectivity()` auch `TenantPermissionService::compare()` aufrufen.
- Ergebnisse in `tenant_permissions`/`granted_permissions` speichern.
- `app_status` und Permission-Health aktualisieren.
- Audit-Log-Eintrag `tenant.permissions.checked` schreiben.
- [x] T045 [US7] Tests für Permissions:
- Unit-Tests für `TenantPermissionService::compare()`:
- Szenarien: alle ok, Permission fehlt, Graph-Error.
- Feature-Test für Tenant-Detailseite:
- required permissions werden angezeigt,
- fehlende werden als fehlend markiert,
- „Verify configuration“ aktualisiert den Status wie erwartet.
## Phase 9b: Scope-Ausrichtung auf neue Objekttypen
- [x] T028 [Scope] Konfiguration `config/tenantpilot.php` auf die in `scope.supported_types` definierten Objekttypen erweitern (type/key, endpoint, label/category, optional risk/restore-Hinweis). Sicherstellen, dass diese Liste die einzige Quelle für Policy-Sync/Backup/Restore ist.
- [x] T029 [Scope] Filament-UI an neue Typen anpassen: Tabellenfilter/Grouping nach Kategorie (z.B. Config/Compliance/Scripts/Apps/CA), Backup/Restore-Formulare mit Hinweisen zu Restore-Level aus `scope.restore_matrix` (z.B. CA/enrollment restrictions = preview-only).
## Phase 10: Housekeeping Delete-Funktionen für Backups & Versions
- [x] T060 [HK] BackupSets soft deletable machen:
- `backup_sets` (und ggf. `backup_items`) Migration/Model mit `SoftDeletes` (deleted_at).
- Sicherstellen, dass RestoreRuns keine gelöschten BackupSets verwenden; Delete nur erlauben,
wenn keine zugehörigen RestoreRuns existieren.
- [x] T061 [HK] Filament-Delete-Action für BackupSets:
- In `BackupSetResource` Delete-Action in List- und/oder Detail-View hinzufügen.
- Mit Confirmation-Dialog (“This will archive this backup set and hide it from the UI.”).
- Delete disabled/hidden, wenn `restore_runs` für das Set existieren.
- Nach Delete Audit-Log (`backup.deleted`) schreiben.
- [x] T062 [HK] PolicyVersions soft deletable machen:
- `policy_versions` Migration/Model um `SoftDeletes` erweitern.
- Alle Queries und Filament-Resources so lassen, dass standardmäßig nur non-deleted Versions
angezeigt werden.
- [x] T063 [HK] Filament-Delete-Action für PolicyVersions:
- In `PolicyVersionResource` Delete-Action hinzufügen (List/Detail).
- Confirmation + Audit-Log (`policy_version.deleted`).
- [x] T064 [HK] Tests für Housekeeping:
- Feature-Test: Löschen eines BackupSets ohne RestoreRun → `deleted_at` gesetzt, UI-Eintrag weg,
Audit-Log vorhanden.
- Feature-Test: BackupSet mit RestoreRun → Delete-Action nicht verfügbar.
- Feature-Test: Löschen einer PolicyVersion → `deleted_at` gesetzt, nicht mehr in List sichtbar.
## Phase 11: Housekeeping Tenant löschen/deaktivieren
- [x] T070 [HK] Tenants soft deletable machen:
- `tenants` Model um `SoftDeletes` erweitern, Migration ggf. `deleted_at` hinzufügen.
- Optional: Feld `status` (enum/string: `active`, `archived`) einführen; beim Delete auf `archived` setzen.
- Alle Standard-Queries für Tenants nur `active` / nicht gelöscht anzeigen.
- [x] T071 [HK] Tenant-Delete-Action (Deaktivieren) in `TenantResource`:
- Delete-/Archive-Action in der Tenant-Liste und/oder Detailseite hinzufügen.
- Deutlich machen: “Deaktiviert diesen Tenant. Historische Daten bleiben vorhanden, neue Aktionen
sind nicht mehr möglich.”
- Bei Ausführung:
- `deleted_at` setzen (und ggf. `status = archived`),
- Audit-Log `tenant.deleted` oder `tenant.archived` schreiben.
- [x] T072 [HK] Verhalten für deaktivierte Tenants:
- In `PolicySyncService`, `BackupService`, `RestoreService` prüfen, dass nur aktive Tenants
verwendet werden; bei deaktiviertem Tenant frühzeitig mit verständlicher Fehlermeldung abbrechen.
- In Filament-Navigation Tenants, Policies, Backups, Restores eines deaktivierten Tenants nicht
mehr in Standard-Listen anzeigen (es sei denn, es gibt explizite “Show archived”-Filter).
- [x] T073 [HK] (Optional) RestoreRuns soft deletable machen:
- `restore_runs` Model/Migration mit `SoftDeletes`.
- Delete-Action in `RestoreRunResource` hinzufügen (nur UI-Aufräumung, keine Folgen für Backups).
- Audit-Log `restore_run.deleted` schreiben.
- [x] T074 [HK] Tests für Tenant-Delete:
- Feature-Test: Tenant löschen/deaktivieren → Tenant taucht nicht mehr in Standardlisten auf,
`deleted_at` (und `status`) ist gesetzt, Audit-Event existiert.
- Feature-Test: Versuch, mit deaktiviertem Tenant einen Policy-Sync/Backup/Restore zu starten,
führt zu einem klaren Fehler (und kein Graph-Call wird ausgeführt).
## Phase 12: Housekeeping Hard Deletes (Force Delete)
- [x] T075 [HK] Force-Delete-Actions ergänzen:
- Filament-Listen für Tenants, BackupSets, PolicyVersions, RestoreRuns erhalten „Force delete“
Aktionen (sichtbar nur im Trashed-Filter), mit klarer Confirmation.
- BackupSets: Force delete nur, wenn keine RestoreRuns existieren; löscht Items mit.
- Tenants: Force delete nur, wenn archiviert; blockiert für aktive Tenants.
- Alle Force-Deletes schreiben Audit-Log-Einträge vor der endgültigen Löschung.
- Tests für Force-Delete-Flows (erfolgreich/blockiert) ergänzen.
## Phase 12: Single current tenant ("Highlander")
- [x] T120 [TENANT] Migration `add_is_current_to_tenants`:
- Spalte `is_current` (boolean, default false, not null) zu `tenants` hinzufügen.
- Partielle Unique-Index anlegen, z. B.:
- `UNIQUE INDEX tenants_current_unique ON tenants (is_current)
WHERE is_current = true AND deleted_at IS NULL`.
- [x] T121 [TENANT] Tenant-Model anpassen:
- Methode `makeCurrent()` implementieren:
- Transaktion: alle anderen Tenants `is_current = false`, dieser Tenant `is_current = true`.
- Methode `static current()` implementieren:
- Wenn `INTUNE_TENANT_ID` gesetzt ist → Tenant mit dieser GUID laden,
sonst Exception, wenn nicht gefunden / deaktiviert.
- Wenn nicht gesetzt → Tenant mit `is_current = true` und `status = active`
(und `deleted_at` null) zurückgeben.
- Wenn keiner → Exception “No current tenant selected”.
- `findOrCreateDefault()` deprecaten/entfernen; keine Dummy-Tenants mehr erzeugen.
- [x] T122 [TENANT] Data-Migration / Cleanup:
- Falls mindestens ein Tenant mit `app_status = ok` existiert:
- einen als `is_current = true` markieren (z. B. den ersten).
- `local-tenant` auf `status = archived`, `is_current = false` setzen.
- Sicherstellen, dass `local-tenant` nie wieder als aktueller Kontext verwendet wird.
- [x] T123 [TENANT] Filament `TenantResource` UI:
- Spalte/Badge für `is_current` in der Liste hinzufügen.
- Table-Action "Make current" ergänzen:
- nur sichtbar für aktive Tenants, die nicht `is_current` sind.
- ruft `makeCurrent()` auf und zeigt Notification.
- Alte Logik entfernen, die `local-tenant` automatisch als Default nutzt.
- [x] T124 [TENANT] Consumers refactoren:
- Alle Vorkommen von `findOrCreateDefault()` suchen und durch `Tenant::current()`
(oder expliziten Tenant) ersetzen:
- Policy-Sync (Command + Filament-Action),
- BackupSet-Erstellung,
- RestoreRun-Erstellung,
- ggf. weitere Services.
- [x] T125 [TENANT] Tests:
- Unit-Tests für `Tenant::current()`:
- INTUNE_TENANT_ID gesetzt → nimmt diesen Tenant, Fehler wenn nicht vorhanden.
- INTUNE_TENANT_ID nicht gesetzt → nimmt den mit `is_current = true`.
- kein current Tenant → Exception.
- Feature-Test für "Make current" in `TenantResource`:
- Nach der Action ist genau ein Tenant `is_current = true`, alle anderen `false`.
- Optional: Test, dass `local-tenant` nach Cleanup nicht mehr als Kontext gewählt wird.
- [x] T130 [UX] Tabellen-Aktionen in Dropdown bündeln (ActionGroup)
- In `TenantResource` (Tenants-Liste) die Zeilen-Aktionen refaktorieren:
- `View` (optional) direkt anzeigen.
- Alle weiteren Aktionen (`Edit`, `Admin consent`, `Verify configuration`,
`Deactivate`, `Force delete`) in eine `Tables\Actions\ActionGroup` mit
"⋯"-Icon verschieben.
- Prüfen, ob in anderen Ressourcen mit vielen Row-Actions (z.B. Backups,
RestoreRuns) ebenfalls eine `ActionGroup` sinnvoll ist und diese konsistent
einsetzen.