diff --git a/backend/prisma/migrations/20260822100000_user_service_account/migration.sql b/backend/prisma/migrations/20260822100000_user_service_account/migration.sql new file mode 100644 index 00000000..503d2ff0 --- /dev/null +++ b/backend/prisma/migrations/20260822100000_user_service_account/migration.sql @@ -0,0 +1,9 @@ +-- Dienstkonto-Kennzeichen (Pentest R182). +-- +-- Das Gegenbuch meldet sich stuendlich an. Diese Anmeldung wurde bisher als +-- Authentication/CRITICAL protokolliert - also das vorhersagbarste Ereignis im +-- System auf der hoechsten Stufe. Damit trainiert die Routine den Betreiber +-- darauf, CRITICAL wegzuklicken, und der erste ECHTE Vorfall erbt diesen +-- Reflex. Mit dem Kennzeichen werden solche Anmeldungen als Routine gefuehrt. +ALTER TABLE `User` + ADD COLUMN IF NOT EXISTS `isServiceAccount` BOOLEAN NOT NULL DEFAULT false; diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index bd0d505e..6f5251d7 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -76,6 +76,11 @@ model User { firstName String lastName String isActive Boolean @default(true) + /// Dienstkonto (z. B. das Gegenbuch). Meldet sich planmaessig und haeufig an. + /// Solche Anmeldungen werden im Audit-Log als Routine gefuehrt statt als + /// CRITICAL - sonst trainiert das vorhersagbarste Ereignis im System den + /// Betreiber darauf, die hoechste Stufe wegzuklicken (Pentest R182). + isServiceAccount Boolean @default(false) tokenInvalidatedAt DateTime? // Zeitpunkt ab dem alle Tokens ungültig sind (für Zwangslogout bei Rechteänderung) // Passwort-Reset diff --git a/backend/src/middleware/audit.ts b/backend/src/middleware/audit.ts index 96dd94ce..4382e0be 100644 --- a/backend/src/middleware/audit.ts +++ b/backend/src/middleware/audit.ts @@ -167,6 +167,21 @@ const ACTION_LABELS: Record = { TOKEN_REFRESH: 'Sitzung verlängert', }; +/** + * Ist das die planmaessige Anmeldung eines Dienstkontos? (Pentest R182) + * + * Vorhersagbare Ereignisse sind miserable Alarme und exzellente Grundlinien. + * Das Gegenbuch meldet sich stuendlich an – als CRITICAL gefuehrt trainiert + * genau diese Routine den Betreiber darauf, die hoechste Stufe wegzuklicken, + * und der erste ECHTE Vorfall erbt den Reflex. Der Eintrag bleibt (man soll + * sehen, dass das Gegenbuch arbeitet), aber als Routine. + */ +function istDienstkontoAnmeldung(responseBody: unknown, success: boolean): boolean { + if (!success || !responseBody || typeof responseBody !== 'object') return false; + const data = (responseBody as { data?: { user?: { isServiceAccount?: boolean } } }).data; + return data?.user?.isServiceAccount === true; +} + /** * Unterscheidet die drei Ausgaenge von POST /auth/refresh (Pentest R166-03). * @@ -219,6 +234,9 @@ function generateHumanLabel( // Auth if (path.includes('/auth/login') || path.includes('/auth/customer-login')) { const email = req.body?.email || ''; + if (action === 'LOGIN' && istDienstkontoAnmeldung(responseBody, true)) { + return `Dienstkonto ${email} angemeldet (planmäßig)`; + } return action === 'LOGIN' ? `Benutzer ${email} hat sich angemeldet` : `Anmeldung fehlgeschlagen für ${email}`; @@ -486,7 +504,9 @@ export function auditMiddleware(req: AuthRequest, res: Response, next: NextFunct // Andere Auth-Events behalten ihre Default-Sensitivität (Authentication → CRITICAL). sensitivity: action === 'TOKEN_REFRESH' ? (refreshOutcome(responseBody, responseSuccess) === 'rejected' ? 'HIGH' : 'LOW') - : undefined, + : action === 'LOGIN' && istDienstkontoAnmeldung(responseBody, responseSuccess) + ? 'LOW' + : undefined, resourceType: mapping.type, resourceId, resourceLabel, diff --git a/backend/src/services/auth.service.ts b/backend/src/services/auth.service.ts index fda403b1..72476f71 100644 --- a/backend/src/services/auth.service.ts +++ b/backend/src/services/auth.service.ts @@ -333,6 +333,9 @@ export async function login(email: string, password: string) { permissions: Array.from(permissions), customerId: user.customerId, isCustomerPortal: false, + // Wird von der Audit-Middleware gelesen, um planmaessige + // Dienstkonto-Anmeldungen als Routine einzustufen (R182). + isServiceAccount: user.isServiceAccount, }, }; }