import { Response } from 'express'; import { AuthRequest } from '../types/index.js'; import * as auditService from '../services/audit.service.js'; import { logChange } from '../services/audit.service.js'; import { AuditAction, AuditSensitivity } from '@prisma/client'; /** * Audit-Logs mit Filtern abrufen */ export async function getAuditLogs(req: AuthRequest, res: Response) { try { const { userId, customerId, dataSubjectId, action, sensitivity, resourceType, resourceId, startDate, endDate, success, search, page, limit, } = req.query; const result = await auditService.searchAuditLogs({ userId: userId ? parseInt(userId as string) : undefined, customerId: customerId ? parseInt(customerId as string) : undefined, dataSubjectId: dataSubjectId ? parseInt(dataSubjectId as string) : undefined, action: action as AuditAction | undefined, sensitivity: sensitivity as AuditSensitivity | undefined, resourceType: resourceType as string | undefined, resourceId: resourceId as string | undefined, startDate: startDate ? new Date(startDate as string) : undefined, endDate: endDate ? new Date(endDate as string) : undefined, success: success !== undefined ? success === 'true' : undefined, search: search as string | undefined, page: page ? parseInt(page as string) : 1, limit: limit ? parseInt(limit as string) : 50, }); res.json({ success: true, ...result }); } catch (error) { console.error('Fehler beim Abrufen der Audit-Logs:', error); res.status(500).json({ success: false, error: 'Fehler beim Abrufen der Audit-Logs' }); } } /** * Einzelnes Audit-Log abrufen */ export async function getAuditLogById(req: AuthRequest, res: Response) { try { const id = parseInt(req.params.id); // Nicht-numerische :id (z.B. GET /audit-logs/verify statt POST /verify) // landete sonst als NaN in Prisma findUnique → 500. Sauberer 400. // (Pentest R120 – gleiche „400 statt 500"-Klasse wie R64.1/R104.1.) if (Number.isNaN(id)) { return res.status(400).json({ success: false, error: 'Ungültige Audit-Log-ID' }); } const log = await auditService.getAuditLogById(id); if (!log) { return res.status(404).json({ success: false, error: 'Audit-Log nicht gefunden' }); } res.json({ success: true, data: log }); } catch (error) { console.error('Fehler beim Abrufen des Audit-Logs:', error); res.status(500).json({ success: false, error: 'Fehler beim Abrufen des Audit-Logs' }); } } /** * Audit-Logs für einen Kunden abrufen (DSGVO) */ export async function getAuditLogsByCustomer(req: AuthRequest, res: Response) { try { const customerId = parseInt(req.params.customerId); if (Number.isNaN(customerId)) { return res.status(400).json({ success: false, error: 'Ungültige Kunden-ID' }); } const logs = await auditService.getAuditLogsByDataSubject(customerId); res.json({ success: true, data: logs }); } catch (error) { console.error('Fehler beim Abrufen der Kunden-Audit-Logs:', error); res.status(500).json({ success: false, error: 'Fehler beim Abrufen der Audit-Logs' }); } } /** * Audit-Logs exportieren */ export async function exportAuditLogs(req: AuthRequest, res: Response) { try { const format = (req.query.format as 'json' | 'csv') || 'json'; const { action, sensitivity, resourceType, startDate, endDate, } = req.query; const content = await auditService.exportAuditLogs( { action: action as AuditAction | undefined, sensitivity: sensitivity as AuditSensitivity | undefined, resourceType: resourceType as string | undefined, startDate: startDate ? new Date(startDate as string) : undefined, endDate: endDate ? new Date(endDate as string) : undefined, }, format ); if (format === 'csv') { const filename = `audit-logs-${new Date().toISOString().split('T')[0]}.csv`; res.setHeader('Content-Type', 'text/csv; charset=utf-8'); res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); // BOM für Excel UTF-8 Erkennung res.send('\uFEFF' + content); } else { res.json({ success: true, data: JSON.parse(content) }) } } catch (error) { console.error('Fehler beim Exportieren der Audit-Logs:', error); res.status(500).json({ success: false, error: 'Fehler beim Exportieren' }); } } /** * Hash-Ketten-Integrität prüfen */ export async function verifyIntegrity(req: AuthRequest, res: Response) { try { const { fromId, toId } = req.query; const result = await auditService.verifyIntegrity( fromId ? parseInt(fromId as string) : undefined, toId ? parseInt(toId as string) : undefined ); res.json({ success: true, data: { valid: result.valid, checkedCount: result.checkedCount, invalidEntries: result.invalidEntries, message: result.valid ? 'Alle Einträge sind valide' : `${result.invalidEntries.length} manipulierte Einträge gefunden`, }, }); } catch (error) { console.error('Fehler bei der Integritätsprüfung:', error); res.status(500).json({ success: false, error: 'Fehler bei der Integritätsprüfung' }); } } /** * Hash-Kette reparieren (alle Hashes neu berechnen) */ export async function rehashAll(req: AuthRequest, res: Response) { try { const result = await auditService.rehashAll(); res.json({ success: true, data: result, message: `${result.rehashedCount} Einträge neu gehasht. Kette ist jetzt intakt.`, }); } catch (error) { console.error('Fehler beim Re-Hashing:', error); res.status(500).json({ success: false, error: 'Fehler beim Re-Hashing' }); } } /** * Retention-Policies abrufen */ export async function getRetentionPolicies(req: AuthRequest, res: Response) { try { const policies = await auditService.getRetentionPolicies(); res.json({ success: true, data: policies }); } catch (error) { console.error('Fehler beim Abrufen der Retention-Policies:', error); res.status(500).json({ success: false, error: 'Fehler beim Abrufen der Policies' }); } } /** * Retention-Policy aktualisieren */ export async function updateRetentionPolicy(req: AuthRequest, res: Response) { try { const id = parseInt(req.params.id); if (Number.isNaN(id)) { return res.status(400).json({ success: false, error: 'Ungültige Policy-ID' }); } const { retentionDays, description, legalBasis, isActive } = req.body; const policy = await auditService.updateRetentionPolicy(id, { retentionDays, description, legalBasis, isActive, }); await logChange({ req, action: 'UPDATE', resourceType: 'RetentionPolicy', resourceId: id.toString(), label: `Aufbewahrungsrichtlinie aktualisiert`, }); res.json({ success: true, data: policy }); } catch (error) { console.error('Fehler beim Aktualisieren der Retention-Policy:', error); res.status(500).json({ success: false, error: 'Fehler beim Aktualisieren' }); } } /** * Retention-Cleanup manuell ausführen */ export async function runRetentionCleanup(req: AuthRequest, res: Response) { try { const result = await auditService.runRetentionCleanup(); res.json({ success: true, data: result, message: `${result.deletedCount} alte Audit-Logs wurden gelöscht`, }); } catch (error) { console.error('Fehler beim Retention-Cleanup:', error); res.status(500).json({ success: false, error: 'Fehler beim Cleanup' }); } }