import { Injectable, Logger } from '@nestjs/common'; import { DatabaseService } from '../database/database.service'; /** Definierte Audit-Aktionen der Plattform. */ export const AUDIT_ACTIONS = { LOGIN_SUCCESS: 'LOGIN_SUCCESS', LOGIN_FAILED: 'LOGIN_FAILED', LOGIN_LOCKED: 'LOGIN_FAILED_LOCKED', LOGOUT: 'LOGOUT', USER_CREATED: 'USER_CREATED', USER_UPDATED: 'USER_UPDATED', USER_ENABLED: 'USER_ENABLED', USER_DISABLED: 'USER_DISABLED', USER_DELETED: 'USER_DELETED', USER_PASSWORD_RESET: 'USER_PASSWORD_RESET', USER_PASSWORD_CHANGED: 'USER_PASSWORD_CHANGED', MODULE_INSTALLED: 'MODULE_INSTALLED', MODULE_UPDATED: 'MODULE_UPDATED', MODULE_REMOVED: 'MODULE_REMOVED', MODULE_STARTED: 'MODULE_STARTED', MODULE_STOPPED: 'MODULE_STOPPED', MODULE_RESTARTED: 'MODULE_RESTARTED', MODULE_CONFIG_UPDATED: 'MODULE_CONFIG_UPDATED', MODULE_ENABLED: 'MODULE_ENABLED', MODULE_DISABLED: 'MODULE_DISABLED', PERMISSION_GRANTED: 'PERMISSION_GRANTED', PERMISSION_REVOKED: 'PERMISSION_REVOKED', SETTINGS_CHANGED: 'SETTINGS_CHANGED', } as const; export type AuditAction = (typeof AUDIT_ACTIONS)[keyof typeof AUDIT_ACTIONS]; /** * Zentrales Audit-Logging: Sicherheitsrelevante Ereignisse werden * nachvollziehbar aufgezeichnet. Es werden niemals Passwörter, * Tokens oder personenbezogene Daten geloggt. */ @Injectable() export class AuditService { private readonly logger = new Logger('Audit'); constructor(private readonly database: DatabaseService) {} async record(input: { userId: string | null; username: string; action: AuditAction; details?: Record; ipAddress?: string | null; }): Promise { try { await this.database.query( `INSERT INTO audit_logs (user_id, username, action, details, ip_address) VALUES ($1, $2, $3, $4::jsonb, $5)`, [ input.userId, input.username, input.action, JSON.stringify(input.details ?? {}), input.ipAddress ?? null, ], ); } catch (error) { // Audit-Fehler dürfen den eigentlichen Request niemals blockieren. this.logger.error( `Audit-Log fehlgeschlagen (${input.action})`, error instanceof Error ? error.stack : String(error), ); } } }