feat(security): Zugangsdaten verschluesselt speichern (#021)

AES-256-GCM mit Schluessel aus APP_SECRET fuer SMTP-Passwort und
Webhook-Geheimnis. Bestehende Klartextwerte werden weiter gelesen und beim
naechsten Speichern umgeschluesselt. 156 Tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-17 23:50:46 +02:00
co-authored by Claude Opus 5
parent d2ebc4e096
commit db3d38aa3c
21 changed files with 265 additions and 49 deletions
+9 -2
View File
@@ -1,7 +1,12 @@
import { afterAll, beforeEach, describe, expect, it } from "vitest";
import { hash } from "@node-rs/argon2";
import { cookieOptionen, sitzungErzeugen, sitzungLesen } from "@/lib/sitzung";
import { AnmeldeFehler, anmeldeRateLimit, mitOidcAnmelden, mitPasswortAnmelden } from "@/server/auth/benutzer";
import {
AnmeldeFehler,
anmeldeRateLimit,
mitOidcAnmelden,
mitPasswortAnmelden,
} from "@/server/auth/benutzer";
import { datenbankLeeren, datenbankVerfuegbar, testPrisma } from "./helpers/db";
const GEHEIMNIS = "test-geheimnis-mit-mindestens-32-zeichen!!";
@@ -108,7 +113,9 @@ describe.runIf(datenbankVerfuegbar)("Anmeldung", () => {
await admin();
await expect(mitPasswortAnmelden("admin@example.de", "falsch", "1.2.3.4")).rejects.toThrow(AnmeldeFehler);
const protokoll = await testPrisma.auditEintrag.findFirst({ where: { aktion: "anmeldung.fehlgeschlagen" } });
const protokoll = await testPrisma.auditEintrag.findFirst({
where: { aktion: "anmeldung.fehlgeschlagen" },
});
expect(protokoll).not.toBeNull();
});