fix(users): DSGVO-/Entwickler-Zugriff über User-Update durchreichen
`pickUserUpdate`-Whitelist enthielt `hasGdprAccess` und `hasDeveloperAccess` nicht – sie wurden vom Mass-Assignment-Schutz aus dem Request entfernt, bevor sie den Service erreichen konnten. Damit lief `setUserGdprAccess` / `setUserDeveloperAccess` nie und die zwei versteckten Rollen blieben unzuweisbar (UI-Checkbox hatte keine Wirkung). Fix: Beide Felder zur Whitelist hinzugefügt – sie sind keine User-Spalten, der Service mappt sie auf die DSGVO-/Developer-Rollen. Bonus: Audit-Log-Diff vergleicht jetzt den Pre-State korrekt (User-Rollen in `before` mitgeladen + Field-Labels), sonst hätte der jetzt durchkommende Flag immer einen False-Positive-Change "- → Ja" produziert. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -72,8 +72,19 @@ export async function updateUser(req: Request, res: Response): Promise<void> {
|
||||
// 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<void> {
|
||||
const changes: Record<string, { von: unknown; nach: unknown }> = {};
|
||||
const fieldLabels: Record<string, string> = {
|
||||
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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user