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
@@ -0,0 +1,68 @@
using LiteDB;
namespace LehrerApp.Data;
/// <summary>
/// Prüft und ändert den Passwortschutz einer LiteDB-Datei.
/// Passwort-Änderungen laufen über eine Kopie (neue Datei mit Zielpasswort, alle
/// Collections umkopiert, dann Austausch) statt über <c>LiteDatabase.Rebuild</c> mit
/// Passwort, das in LiteDB 5.0.21 nachweislich fehlschlägt (per Skript verifiziert).
/// </summary>
public class DatabaseEncryptionService
{
public bool IsEncrypted(string dbPath)
{
if (!File.Exists(dbPath)) return false;
try
{
using var db = new LiteDatabase(dbPath);
return false;
}
catch (LiteException)
{
return true;
}
}
public bool VerifyPassword(string dbPath, string password)
{
try
{
using var db = new LiteDatabase(new ConnectionString(dbPath) { Password = password });
return true;
}
catch (LiteException)
{
return false;
}
}
/// Setzt (newPassword != null), ändert oder entfernt (newPassword == null) das
/// Passwort der Datenbank. Der Aufrufer muss sicherstellen, dass keine andere
/// Verbindung (z.B. der laufende <see cref="LiteDbContext"/>) die Datei offen hält.
public void SetPassword(string dbPath, string? currentPassword, string? newPassword)
{
var tempPath = dbPath + ".reencrypt.tmp";
if (File.Exists(tempPath)) File.Delete(tempPath);
var srcConnection = currentPassword is null
? new ConnectionString(dbPath)
: new ConnectionString(dbPath) { Password = currentPassword };
var dstConnection = newPassword is null
? new ConnectionString(tempPath)
: new ConnectionString(tempPath) { Password = newPassword };
using (var src = new LiteDatabase(srcConnection))
using (var dst = new LiteDatabase(dstConnection))
{
foreach (var name in src.GetCollectionNames())
{
var docs = src.GetCollection<BsonDocument>(name).FindAll().ToList();
if (docs.Count > 0) dst.GetCollection<BsonDocument>(name).InsertBulk(docs);
}
}
File.Copy(tempPath, dbPath, overwrite: true);
File.Delete(tempPath);
}
}