Datensicherheit: Backup, Verschlüsselung, App-Sperre (Kapitel 13.3)

Automatisches rollierendes Backup der Datenbank beim Start mit
Wiederherstellung über die Einstellungen, versionierte Schema-Migration,
optionale Passwort-Verschlüsselung der LiteDB-Datei und eine App-Sperre
nach Inaktivität mit eigenem Passwort.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-13 13:07:36 +02:00
co-authored by Claude Sonnet 5
parent db1afff7e4
commit 48d07a2b99
25 changed files with 1228 additions and 21 deletions
+40 -2
View File
@@ -25,6 +25,12 @@ public static class AppBootstrapper
public static string DbPath { get; private set; } = "";
public static string AppDataPath { get; private set; } = "";
/// <summary>
/// Vor <see cref="BuildServices"/> gesetzt, wenn die Datenbank passwortgeschützt ist
/// (siehe App.axaml.cs: Passwort-Abfrage vor dem Öffnen der Datenbank).
/// </summary>
public static string? DbPassword { get; set; }
/// <summary>
/// Vor der eigentlichen DI-Konfiguration verfügbar (z.B. für den globalen
/// Exception-Handler in Program.cs, der schon vor <see cref="BuildServices"/> greifen muss).
@@ -42,19 +48,41 @@ public static class AppBootstrapper
return appData;
}
/// Vor dem Öffnen der Datenbank verfügbar (z.B. für die Passwort-Abfrage beim Start).
public static string ResolveDbPath()
{
if (string.IsNullOrEmpty(DbPath))
DbPath = Path.Combine(ResolveAppDataPath(), "lehrerapp.db");
return DbPath;
}
public static AppLogger EnsureLogger()
{
Logger ??= new AppLogger(ResolveAppDataPath());
return Logger;
}
public static bool IsDbEncrypted() =>
new DatabaseEncryptionService().IsEncrypted(ResolveDbPath());
/// Beendet den Prozess und startet ihn neu (z.B. nach Wiederherstellung eines Backups
/// oder einer Änderung der Datenbank-Verschlüsselung — die laufende LiteDB-Verbindung
/// kann nicht sicher "heiß" auf eine andere Datei umgehängt werden).
public static void RestartApplication()
{
var exePath = Environment.ProcessPath;
if (!string.IsNullOrEmpty(exePath))
System.Diagnostics.Process.Start(exePath);
Environment.Exit(0);
}
public static IServiceProvider BuildServices()
{
var services = new ServiceCollection();
// ── Pfade ─────────────────────────────────────────────────────────────
var appData = ResolveAppDataPath();
DbPath = Path.Combine(appData, "lehrerapp.db");
ResolveDbPath();
var queuePath = Path.Combine(appData, "syncqueue.db");
var keyPath = Path.Combine(appData, "sync.key");
@@ -63,8 +91,17 @@ public static class AppBootstrapper
services.AddSingleton(Logger);
services.AddSingleton<NotificationService>();
// ── Datensicherheit (13.3) ───────────────────────────────────────────
// Backup läuft vor dem Öffnen der Datenbank, rein dateibasiert (unabhängig von
// Verschlüsselung) — reine Datei-Kopie, kein offener LiteDB-Handle nötig.
var backup = new BackupService(appData);
backup.CreateBackup(DbPath);
services.AddSingleton(backup);
services.AddSingleton(_ => new AppLockService(appData));
services.AddSingleton<DatabaseEncryptionService>();
// ── Datenbank ─────────────────────────────────────────────────────────
services.AddSingleton(_ => new LiteDbContext(DbPath));
services.AddSingleton(_ => new LiteDbContext(DbPath, DbPassword));
// ── Repositories ──────────────────────────────────────────────────────
services.AddSingleton<IStudentRepository, StudentRepository>();
@@ -128,6 +165,7 @@ public static class AppBootstrapper
// ── ViewModels ────────────────────────────────────────────────────────
// Singleton: einmal erstellt, überall dieselbe Instanz
services.AddSingleton<AppLockViewModel>();
services.AddSingleton<MainWindowViewModel>();
services.AddSingleton<DashboardViewModel>();
services.AddSingleton(sp =>