diff --git a/backend/src/controllers/user.controller.ts b/backend/src/controllers/user.controller.ts index 03fafaca..6ab0a816 100644 --- a/backend/src/controllers/user.controller.ts +++ b/backend/src/controllers/user.controller.ts @@ -72,8 +72,19 @@ export async function updateUser(req: Request, res: Response): Promise { // Whitelist: nur erlaubte Felder aus req.body übernehmen (Mass-Assignment-Schutz) const data = pickUserUpdate(req.body); - // Vorherigen Stand laden für Audit - const before = await prisma.user.findUnique({ where: { id: userId } }); + // Vorherigen Stand laden für Audit – inkl. Rollen, damit hasGdprAccess / + // hasDeveloperAccess (versteckte Rollen) korrekt verglichen werden. + const beforeUser = await prisma.user.findUnique({ + where: { id: userId }, + include: { roles: { include: { role: true } } }, + }); + const before = beforeUser + ? { + ...beforeUser, + hasGdprAccess: beforeUser.roles.some((ur) => ur.role.name === 'DSGVO'), + hasDeveloperAccess: beforeUser.roles.some((ur) => ur.role.name === 'Developer'), + } + : null; const user = await userService.updateUser(userId, data as any); if (user) { @@ -82,6 +93,7 @@ export async function updateUser(req: Request, res: Response): Promise { const changes: Record = {}; const fieldLabels: Record = { email: 'E-Mail', firstName: 'Vorname', lastName: 'Nachname', isActive: 'Aktiv', + hasGdprAccess: 'DSGVO-Zugriff', hasDeveloperAccess: 'Entwicklerzugriff', }; for (const [key, newVal] of Object.entries(data)) { if (['id', 'createdAt', 'updatedAt'].includes(key)) continue; diff --git a/backend/src/utils/sanitize.ts b/backend/src/utils/sanitize.ts index 7f737679..77310b0b 100644 --- a/backend/src/utils/sanitize.ts +++ b/backend/src/utils/sanitize.ts @@ -110,6 +110,12 @@ const USER_UPDATABLE_FIELDS = [ 'signalNumber', 'roleIds', 'password', // nur Admin, wird im Service gehashed + // hasGdprAccess + hasDeveloperAccess sind keine User-Spalten – der Service + // mappt sie auf die versteckten Rollen DSGVO/Developer (siehe + // setUserGdprAccess / setUserDeveloperAccess). Müssen aber auf der Whitelist + // stehen, damit pick() sie nicht aus dem Request entfernt. + 'hasGdprAccess', + 'hasDeveloperAccess', // Nicht: id, customerId, tokenInvalidatedAt, passwordResetToken, passwordResetExpiresAt ] as const;