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)
18 KiB
| 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)
- T001 [P] [Shared] Confirm Sail/Env ready; ensure
.envhas PostgreSQL settings for Sail and Filament admin user seeded (if missing) indatabase/seeders/. - 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)
- T003 [Shared] Add tenant-aware migrations for
tenants,policies,policy_versions,backup_sets,backup_items,restore_runs,audit_logswith JSONB payloads and FK/time indexes indatabase/migrations/. - T004 [Shared] Create models with relationships and guarded attributes for the above entities in
app/Models/. - T005 [Shared] Implement Graph abstraction contracts (
GraphClientInterface, error mapping, logging hooks) inapp/Services/Graph/with a mockable adapter. - T006 [Shared] Add audit logging service/helper to capture actor, tenant, operation, resources, outcome in
app/Services/Intune/AuditLogger.php. - T007 [Shared] Seed supported policy types/metadata for initial scope in
database/seeders/PoliciesSeeder.phpand ensure tenant scoping.
Phase 3: User Story 1 - Policy inventory listing (Priority: P1)
Tests for User Story 1
- T008 [P] [US1] Feature test for Filament policy listing and filtering (tenant-scoped) in
tests/Feature/Filament/PolicyListingTest.phpusing mocked Graph sync.
Implementation for User Story 1
- T009 [US1] Implement policy sync/import orchestrator using Graph abstraction in
app/Services/Intune/PolicySyncService.php(no direct Graph in UI). - T010 [US1] Create Filament resource/table for policies with filters and metadata columns in
app/Filament/Resources/PolicyResource.php. - T011 [US1] Add command/job to sync policies (queues-ready) in
app/Console/Commands/SyncPolicies.phpand queue job underapp/Jobs/.
Phase 4: User Story 2 - Backup creation and browsing (Priority: P1)
Tests for User Story 2
- 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
-
T013 [US2] Implement backup domain service to assemble snapshots from policies with Graph payload retrieval in
app/Services/Intune/BackupService.php. -
T014 [US2] Add Filament resource/pages for backup sets and items (list/detail) in
app/Filament/Resources/BackupSetResource.php. -
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
BackupItemsRelationManagertoBackupSetResourceshowing 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).
-
T132 [P] [US2] Update/extend
tests/Feature/Filament/BackupCreationTest.phpto cover the new UX flow:- Create BackupSet without policies.
- Add multiple policies via RelationManager action.
- Verify immutable JSONB snapshots + audit log behavior remains correct.
-
T015 [US2] Wire audit logging for backup creation events in
app/Services/Intune/BackupService.phpusingAuditLogger.
Phase 5: User Story 3 - Version history and diff (Priority: P1)
Tests for User Story 3
- T016 [P] [US3] Feature test for version capture and timeline display in
tests/Feature/Filament/PolicyVersionTest.php. - T017 [P] [US3] Unit test for diff generation (human summary + JSON diff) in
tests/Unit/VersionDiffTest.php.
Implementation for User Story 3
- T018 [US3] Implement version capture service with immutable JSONB writes in
app/Services/Intune/VersionService.php. - T019 [US3] Create diff helper (summary + structured JSON) in
app/Services/Intune/VersionDiff.phpand surface in Filament version compare view inapp/Filament/Resources/PolicyVersionResource.php. - 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
- T021 [P] [US4] Feature test for restore preview (change summary, conflicts, selective items) in
tests/Feature/Filament/RestorePreviewTest.php. - 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
- 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. - T024 [US4] Add Filament restore UI (wizard or pages) showing preview, warnings, and confirmation gate in
app/Filament/Resources/RestoreRunResource.php. - T025 [US4] Record restore run lifecycle (start, per-item result, completion) and audit events in
restore_runsandaudit_logs.
Phase 7: User Story 5 - Operational readiness and environments (Priority: P2)
Implementation for User Story 5
- T026 [US5] Document Dokploy staging→production promotion steps, required env vars, queue/worker expectations, and migration safety notes in
README.mdordocs/deploy.md. - 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)
-
T030 [US6] Migration für
tenantsergä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.
- Felder:
-
T031 [US6] Eloquent Model
Tenant:- Beziehungen zu
policies,backup_sets,restore_runs,policy_versions,audit_logsübertenant_id. - Tenant-aware Scopes, falls vorhanden (z. B.
forTenant()).
- Beziehungen zu
-
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“.
-
T033 [US6]
TenantConfigService(oder Erweiterung des Graph-Clients):- Methode
testConnectivity(Tenant $tenant): führt einen einfachen Graph-Call aus (z. B./organizationoder ähnliches) mit den App-Daten des Tenants. - Rückgabe: DTO/Array mit
success,error_message(falls vorhanden).
- Methode
-
T034 [US6] Action „Verify configuration“ in
TenantResource:- Ruft
testConnectivity()auf, - setzt
app_statusauf z. B.ok,erroroderconsent_required, - zeigt eine Filament-Notification mit dem Ergebnis,
- schreibt einen Audit-Log-Eintrag (
tenant.config.verified).
- Ruft
-
T035 [US6] Tenant-Kontext in bestehende Services integrieren:
PolicySyncService,BackupService,RestoreServiceso anpassen, dass sie einenTenantodertenant_idübergeben bekommen und den Graph-Client mit diesem Kontext verwenden.- Sicherstellen, dass alle policy/backup/restore/audit-Datensätze
tenant_idsetzen.
-
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.
- einmal mit erfolgreichem Call →
- Prüfen, dass Audit-Logs geschrieben werden.
-
T037 [US6] Admin-Consent Callback Route
- Route/Controller, der als
redirect_urider Entra-ID-App dient. - Liest
tenant/error/admin_consentaus der Query. - Ordnet das dem richtigen
Tenantzu (z. B. viastate). - Aktualisiert
app_status(z. B.ok,error,consent_denied). - Zeigt eine Bestätigungs-/Fehlerseite für den Admin.
- Route/Controller, der als
Phase 9: User Story 7 - Berechtigungsübersicht & Health-Status (Priority: P1)
-
T040 [US7] Zentrale Permissions-Liste anlegen:
config/intune_permissions.phpmit 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"]).
- technischer Name (z. B.
- Optional:
docs/permissions.mdmit einer Tabelle Feature ↔ Permission als menschlich lesbare Referenz.
-
T041 [US7] Datenmodell für Tenant-Berechtigungen:
- Variante A (einfach): JSONB-Feld
granted_permissionsintenants(Liste von Permission-Keys). - Variante B (feiner): Tabelle
tenant_permissionsmit(tenant_id, permission_key, status, last_checked_at). statusmindestens:ok,missing,error.
- Variante A (einfach): JSONB-Feld
-
T042 [US7] Service
TenantPermissionService:getRequiredPermissions(): array– liest ausconfig/intune_permissions.php.getGrantedPermissions(Tenant $tenant): array– liest aus Graph oder austenant_permissions/granted_permissions.compare(Tenant $tenant): TenantPermissionStatusDTO– liefert pro Permission den Status (ok/missing/error) + Gesamthealth.
-
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“).
- Auf der
-
T044 [US7] Action „Verify configuration“ erweitern:
- Zusätzlich zu
testConnectivity()auchTenantPermissionService::compare()aufrufen. - Ergebnisse in
tenant_permissions/granted_permissionsspeichern. app_statusund Permission-Health aktualisieren.- Audit-Log-Eintrag
tenant.permissions.checkedschreiben.
- Zusätzlich zu
-
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.
- Unit-Tests für
Phase 9b: Scope-Ausrichtung auf neue Objekttypen
- T028 [Scope] Konfiguration
config/tenantpilot.phpauf die inscope.supported_typesdefinierten 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. - 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
-
T060 [HK] BackupSets soft deletable machen:
backup_sets(und ggf.backup_items) Migration/Model mitSoftDeletes(deleted_at).- Sicherstellen, dass RestoreRuns keine gelöschten BackupSets verwenden; Delete nur erlauben, wenn keine zugehörigen RestoreRuns existieren.
-
T061 [HK] Filament-Delete-Action für BackupSets:
- In
BackupSetResourceDelete-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_runsfür das Set existieren. - Nach Delete Audit-Log (
backup.deleted) schreiben.
- In
-
T062 [HK] PolicyVersions soft deletable machen:
policy_versionsMigration/Model umSoftDeleteserweitern.- Alle Queries und Filament-Resources so lassen, dass standardmäßig nur non-deleted Versions angezeigt werden.
-
T063 [HK] Filament-Delete-Action für PolicyVersions:
- In
PolicyVersionResourceDelete-Action hinzufügen (List/Detail). - Confirmation + Audit-Log (
policy_version.deleted).
- In
-
T064 [HK] Tests für Housekeeping:
- Feature-Test: Löschen eines BackupSets ohne RestoreRun →
deleted_atgesetzt, UI-Eintrag weg, Audit-Log vorhanden. - Feature-Test: BackupSet mit RestoreRun → Delete-Action nicht verfügbar.
- Feature-Test: Löschen einer PolicyVersion →
deleted_atgesetzt, nicht mehr in List sichtbar.
- Feature-Test: Löschen eines BackupSets ohne RestoreRun →
Phase 11: Housekeeping – Tenant löschen/deaktivieren
-
T070 [HK] Tenants soft deletable machen:
tenantsModel umSoftDeleteserweitern, Migration ggf.deleted_athinzufügen.- Optional: Feld
status(enum/string:active,archived) einführen; beim Delete aufarchivedsetzen. - Alle Standard-Queries für Tenants nur
active/ nicht gelöscht anzeigen.
-
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_atsetzen (und ggf.status = archived),- Audit-Log
tenant.deletedodertenant.archivedschreiben.
-
T072 [HK] Verhalten für deaktivierte Tenants:
- In
PolicySyncService,BackupService,RestoreServiceprü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).
- In
-
T073 [HK] (Optional) RestoreRuns soft deletable machen:
restore_runsModel/Migration mitSoftDeletes.- Delete-Action in
RestoreRunResourcehinzufügen (nur UI-Aufräumung, keine Folgen für Backups). - Audit-Log
restore_run.deletedschreiben.
-
T074 [HK] Tests für Tenant-Delete:
- Feature-Test: Tenant löschen/deaktivieren → Tenant taucht nicht mehr in Standardlisten auf,
deleted_at(undstatus) 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).
- Feature-Test: Tenant löschen/deaktivieren → Tenant taucht nicht mehr in Standardlisten auf,
Phase 12: Housekeeping – Hard Deletes (Force Delete)
- 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")
-
T120 [TENANT] Migration
add_is_current_to_tenants:- Spalte
is_current(boolean, default false, not null) zutenantshinzufü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.
- Spalte
-
T121 [TENANT] Tenant-Model anpassen:
- Methode
makeCurrent()implementieren:- Transaktion: alle anderen Tenants
is_current = false, dieser Tenantis_current = true.
- Transaktion: alle anderen Tenants
- Methode
static current()implementieren:- Wenn
INTUNE_TENANT_IDgesetzt ist → Tenant mit dieser GUID laden, sonst Exception, wenn nicht gefunden / deaktiviert. - Wenn nicht gesetzt → Tenant mit
is_current = trueundstatus = active(unddeleted_atnull) zurückgeben. - Wenn keiner → Exception “No current tenant selected”.
- Wenn
findOrCreateDefault()deprecaten/entfernen; keine Dummy-Tenants mehr erzeugen.
- Methode
-
T122 [TENANT] Data-Migration / Cleanup:
- Falls mindestens ein Tenant mit
app_status = okexistiert:- einen als
is_current = truemarkieren (z. B. den ersten). local-tenantaufstatus = archived,is_current = falsesetzen.
- einen als
- Sicherstellen, dass
local-tenantnie wieder als aktueller Kontext verwendet wird.
- Falls mindestens ein Tenant mit
-
T123 [TENANT] Filament
TenantResourceUI:- Spalte/Badge für
is_currentin der Liste hinzufügen. - Table-Action "Make current" ergänzen:
- nur sichtbar für aktive Tenants, die nicht
is_currentsind. - ruft
makeCurrent()auf und zeigt Notification.
- nur sichtbar für aktive Tenants, die nicht
- Alte Logik entfernen, die
local-tenantautomatisch als Default nutzt.
- Spalte/Badge für
-
T124 [TENANT] Consumers refactoren:
- Alle Vorkommen von
findOrCreateDefault()suchen und durchTenant::current()(oder expliziten Tenant) ersetzen:- Policy-Sync (Command + Filament-Action),
- BackupSet-Erstellung,
- RestoreRun-Erstellung,
- ggf. weitere Services.
- Alle Vorkommen von
-
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 anderenfalse.
- Nach der Action ist genau ein Tenant
-
Optional: Test, dass
local-tenantnach Cleanup nicht mehr als Kontext gewählt wird. -
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 eineTables\Actions\ActionGroupmit "⋯"-Icon verschieben.
-
Prüfen, ob in anderen Ressourcen mit vielen Row-Actions (z.B. Backups, RestoreRuns) ebenfalls eine
ActionGroupsinnvoll ist und diese konsistent einsetzen.
-