From 97c9af7f141fd730ba5d2027171e829412f4c48d Mon Sep 17 00:00:00 2001 From: Matthias Hochmeister Date: Mon, 23 Mar 2026 17:54:19 +0100 Subject: [PATCH] new features --- backend/src/controllers/permission.controller.ts | 12 ++++++++++-- backend/src/services/audit.service.ts | 10 ++++++++-- backend/src/services/permission.service.ts | 14 +++++++++++++- .../src/components/admin/PermissionMatrixTab.tsx | 8 ++++++-- frontend/src/services/permissions.ts | 5 +++-- 5 files changed, 40 insertions(+), 9 deletions(-) diff --git a/backend/src/controllers/permission.controller.ts b/backend/src/controllers/permission.controller.ts index 6fcdcdc..195e100 100644 --- a/backend/src/controllers/permission.controller.ts +++ b/backend/src/controllers/permission.controller.ts @@ -112,8 +112,16 @@ class PermissionController { } } - await permissionService.setMultipleGroupPermissions(updates, req.user!.id); - res.json({ success: true, message: 'Berechtigungen aktualisiert' }); + const result = await permissionService.setMultipleGroupPermissions(updates, req.user!.id); + if (result.droppedPermissions.length > 0) { + res.json({ + success: true, + message: `Berechtigungen aktualisiert. Warnung: ${result.droppedPermissions.length} Berechtigung(en) existieren nicht in der Datenbank und wurden ignoriert: ${result.droppedPermissions.join(', ')}`, + droppedPermissions: result.droppedPermissions, + }); + } else { + res.json({ success: true, message: 'Berechtigungen aktualisiert' }); + } } catch (error) { logger.error('Failed to set bulk permissions', { error }); res.status(500).json({ success: false, message: 'Fehler beim Speichern der Berechtigungen' }); diff --git a/backend/src/services/audit.service.ts b/backend/src/services/audit.service.ts index e6f0b82..6f3f650 100644 --- a/backend/src/services/audit.service.ts +++ b/backend/src/services/audit.service.ts @@ -248,11 +248,17 @@ class AuditService { const perm = meta?.required_permission ?? '?'; const path = meta?.attempted_path ?? ''; const method = meta?.attempted_method ?? ''; - nachricht = `${entry.user_email ?? 'Unbekannt'}: ${method} ${path} — benötigt "${perm}"`; + const userLabel = entry.user_email ?? 'Unbekannt'; + nachricht = `${userLabel}: ${method} ${path} — benötigt "${perm}"`; } else { nachricht = `${entry.action} auf ${entry.resource_type}${entry.resource_id ? ' ' + entry.resource_id : ''} durch ${entry.user_email ?? 'System'}`; } + // Include user name/email in quell_id so each user+action combo is unique + const quellId = entry.action === 'PERMISSION_DENIED' + ? `${entry.action}_${(entry.metadata as any)?.required_permission ?? ''}_${entry.user_id ?? Date.now()}` + : `${entry.action}_${entry.resource_type}_${entry.resource_id ?? Date.now()}`; + for (const admin of admins) { // Don't notify the admin about their own actions if (admin.id === entry.user_id) continue; @@ -264,7 +270,7 @@ class AuditService { nachricht, schwere: entry.action === 'PERMISSION_DENIED' ? 'warnung' : 'info', quell_typ: 'audit_alert', - quell_id: `${entry.action}_${entry.resource_type}_${entry.resource_id ?? Date.now()}`, + quell_id: quellId, }); } } catch (error) { diff --git a/backend/src/services/permission.service.ts b/backend/src/services/permission.service.ts index c42d4c3..2b3a6e1 100644 --- a/backend/src/services/permission.service.ts +++ b/backend/src/services/permission.service.ts @@ -262,7 +262,7 @@ class PermissionService { async setMultipleGroupPermissions( updates: { group: string; permissions: string[] }[], grantedBy: string, - ): Promise { + ): Promise<{ droppedPermissions: string[] }> { const client = await pool.connect(); try { await client.query('BEGIN'); @@ -282,8 +282,18 @@ class PermissionService { validSet = new Set(validResult.rows.map((r: any) => r.id)); } + const allDropped: string[] = []; + for (const { group, permissions } of updates) { const validPermIds = permissions.filter(p => validSet.has(p)); + const droppedPermIds = permissions.filter(p => !validSet.has(p)); + if (droppedPermIds.length > 0) { + logger.warn('Permissions dropped during save — not found in permissions table', { + group, + droppedPermIds, + }); + allDropped.push(...droppedPermIds); + } await client.query('DELETE FROM group_permissions WHERE authentik_group = $1', [group]); @@ -307,6 +317,8 @@ class PermissionService { groupCount: updates.length, grantedBy, }); + + return { droppedPermissions: [...new Set(allDropped)] }; } catch (error) { await client.query('ROLLBACK'); throw error; diff --git a/frontend/src/components/admin/PermissionMatrixTab.tsx b/frontend/src/components/admin/PermissionMatrixTab.tsx index ef542eb..258f047 100644 --- a/frontend/src/components/admin/PermissionMatrixTab.tsx +++ b/frontend/src/components/admin/PermissionMatrixTab.tsx @@ -168,10 +168,14 @@ function PermissionMatrixTab() { const permissionMutation = useMutation({ mutationFn: (updates: { group: string; permissions: string[] }[]) => permissionsApi.setBulkPermissions(updates), - onSuccess: () => { + onSuccess: (result) => { queryClient.invalidateQueries({ queryKey: ['admin-permission-matrix'] }); queryClient.invalidateQueries({ queryKey: ['my-permissions'] }); - showSuccess('Berechtigungen gespeichert'); + if (result?.droppedPermissions && result.droppedPermissions.length > 0) { + showError(`Berechtigungen gespeichert, aber ${result.droppedPermissions.length} Berechtigung(en) existieren nicht in der DB und wurden ignoriert: ${result.droppedPermissions.join(', ')}`); + } else { + showSuccess('Berechtigungen gespeichert'); + } }, onError: () => showError('Fehler beim Speichern der Berechtigungen'), }); diff --git a/frontend/src/services/permissions.ts b/frontend/src/services/permissions.ts index 91a3649..927b66c 100644 --- a/frontend/src/services/permissions.ts +++ b/frontend/src/services/permissions.ts @@ -25,8 +25,9 @@ export const permissionsApi = { await api.delete(`/api/permissions/admin/group/${encodeURIComponent(group)}`); }, - setBulkPermissions: async (updates: { group: string; permissions: string[] }[]): Promise => { - await api.put('/api/permissions/admin/bulk', { updates }); + setBulkPermissions: async (updates: { group: string; permissions: string[] }[]): Promise<{ droppedPermissions?: string[] }> => { + const r = await api.put('/api/permissions/admin/bulk', { updates }); + return r.data; }, setMaintenanceFlag: async (featureGroup: string, active: boolean): Promise => {