Drei Befunde aus einer Durchsicht der gespeicherten personenbezogenen Daten: Der Import legte bisher jede Spalte der hochgeladenen Datei dauerhaft ab, auch Mailadressen oder Rechnungsdaten, die nie zugeordnet wurden. Nach dem Buchen oder Verwerfen bleiben jetzt nur noch die gemappten Spalten stehen. Ein fehlgeschlagener Login protokollierte den eingetippten Benutzernamen. Wer sein Passwort ins Namensfeld tippt, hatte es damit im Klartext im Audit-Log. Der Name wird nur noch gespeichert, wenn das Konto existiert. Das Loeschen eines Spielers liess Spuren in den Import-Zeilen und in den Details frueherer Audit-Eintraege zurueck. Beides wird jetzt mit abgeraeumt; die Vorgaenge bleiben als Eintrag bestehen, nur ohne Inhalt. Dazu eine Aufbewahrungsfrist fuer das Audit-Log (Standard 12 Monate, ueber AUDIT_RETENTION_DAYS einstellbar). Die Guthaben-Historie ist davon nicht betroffen - sie bleibt die Quelle der Wahrheit fuer bezahlte Ansprueche.
52 lines
1.5 KiB
TypeScript
52 lines
1.5 KiB
TypeScript
import type { Prisma } from '@prisma/client';
|
|
import { prisma } from '../db.js';
|
|
import { env } from '../env.js';
|
|
|
|
export interface ActionContext {
|
|
// Beide null fuer Vorgaenge ohne bekanntes Konto, etwa einen fehlgeschlagenen
|
|
// Login auf einen Benutzernamen, den es gar nicht gibt.
|
|
actorId: number | null;
|
|
actorName: string | null;
|
|
ip?: string | undefined;
|
|
}
|
|
|
|
export interface AuditEntry {
|
|
action: string;
|
|
targetType: string;
|
|
targetId?: string | number | null;
|
|
details?: Prisma.InputJsonValue;
|
|
}
|
|
|
|
type Client = Pick<typeof prisma, 'auditLog'>;
|
|
|
|
export async function recordAudit(
|
|
client: Client,
|
|
ctx: ActionContext,
|
|
entry: AuditEntry,
|
|
): Promise<void> {
|
|
await client.auditLog.create({
|
|
data: {
|
|
actorUserId: ctx.actorId,
|
|
actorName: ctx.actorName ?? null,
|
|
action: entry.action,
|
|
targetType: entry.targetType,
|
|
targetId: entry.targetId == null ? null : String(entry.targetId),
|
|
details: entry.details ?? undefined,
|
|
ip: ctx.ip ?? null,
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Entfernt Audit-Eintraege, die aelter sind als die eingestellte Frist. Die
|
|
* Guthaben-Historie bleibt unangetastet - sie ist die Quelle der Wahrheit fuer
|
|
* bezahlte Ansprueche und hat eine andere Aufbewahrung als das Protokoll.
|
|
*/
|
|
export async function cleanupOldAuditEntries(): Promise<number> {
|
|
if (env.AUDIT_RETENTION_DAYS === 0) return 0;
|
|
|
|
const cutoff = new Date(Date.now() - env.AUDIT_RETENTION_DAYS * 24 * 3600 * 1000);
|
|
const result = await prisma.auditLog.deleteMany({ where: { createdAt: { lt: cutoff } } });
|
|
return result.count;
|
|
}
|