diff --git a/backend/src/services/user.service.ts b/backend/src/services/user.service.ts index f58c9f8a..75cedc35 100644 --- a/backend/src/services/user.service.ts +++ b/backend/src/services/user.service.ts @@ -44,6 +44,7 @@ export async function getAllUsers(filters: UserFilters) { firstName: true, lastName: true, isActive: true, + isServiceAccount: true, customerId: true, whatsappNumber: true, telegramUsername: true, @@ -97,6 +98,7 @@ export async function getUserById(id: number) { firstName: true, lastName: true, isActive: true, + isServiceAccount: true, customerId: true, whatsappNumber: true, telegramUsername: true, @@ -170,6 +172,7 @@ export async function createUser(data: { firstName: true, lastName: true, isActive: true, + isServiceAccount: true, customerId: true, roles: { include: { role: true }, @@ -198,6 +201,7 @@ export async function updateUser( firstName?: string; lastName?: string; isActive?: boolean; + isServiceAccount?: boolean; roleIds?: number[]; customerId?: number; hasDeveloperAccess?: boolean; diff --git a/backend/src/utils/sanitize.ts b/backend/src/utils/sanitize.ts index 7ed34210..4034916b 100644 --- a/backend/src/utils/sanitize.ts +++ b/backend/src/utils/sanitize.ts @@ -640,6 +640,10 @@ const USER_UPDATABLE_FIELDS = [ 'firstName', 'lastName', 'isActive', + // Kennzeichnet planmaessig arbeitende Konten (z. B. das Gegenbuch). Ihre + // Anmeldungen werden als Routine statt CRITICAL gefuehrt, dafuer wacht der + // Heartbeat-Monitor ueber ihr AUSBLEIBEN (Pentest R182/R183). + 'isServiceAccount', 'whatsappNumber', 'telegramUsername', 'signalNumber', diff --git a/docs/todo.md b/docs/todo.md index 9be264f7..5b7767f3 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -97,6 +97,20 @@ isolierte Instanz (keine Multi-Tenancy im Code), Provisioning + Abrechnung ## ✅ Erledigt +- [x] **🖱️ Dienstkonto-Kennzeichen in der Benutzerverwaltung** (2026-08-24) + - Nachgezogen: Das Feld `isServiceAccount` lag zwar in der Datenbank, war + aber **nirgends setzbar** – weder im Formular noch ueber die API. Der + Betreiber haette es nicht aktivieren koennen, damit waere die + R182-Einstufung wirkungslos geblieben. + - Jetzt: Ankreuzfeld „Dienstkonto“ im Benutzerformular unter den + zusaetzlichen Berechtigungen, mit Erklaerung im Klartext (planmaessige + Anmeldungen als Routine, dafuer Meldung bei AUSBLEIBEN). Feld in der + Whitelist (`USER_UPDATABLE_FIELDS`, damit auch beim Anlegen), im + Service-Typ und in allen drei `select`-Bloecken, damit es beim Bearbeiten + vorbelegt wird. + - Verifiziert: Whitelist laesst das Feld durch und blockt Fremdfelder + weiterhin; Wert ueber Prisma lesbar; `tsc` und `vite build` gruen. + - [x] **🚨 Retention-Governance + Heartbeat-Wachhund (Pentest R183, R182-Rest)** (2026-08-24) - **R183-01 (HIGH)** – Die geladene Waffe war ungegatet, der Abzug gegatet: `PUT /retention-policies/{id}` nahm `retentionDays: 0` **ohne diff --git a/frontend/src/pages/users/UserList.tsx b/frontend/src/pages/users/UserList.tsx index 0d7fb07b..4f9b38b0 100644 --- a/frontend/src/pages/users/UserList.tsx +++ b/frontend/src/pages/users/UserList.tsx @@ -247,6 +247,7 @@ function UserModal({ isActive: true, hasDeveloperAccess: false, hasGdprAccess: false, + isServiceAccount: false, whatsappNumber: '', telegramUsername: '', signalNumber: '', @@ -267,6 +268,7 @@ function UserModal({ isActive: (user as any).isActive ?? true, hasDeveloperAccess: (user as any).hasDeveloperAccess ?? false, hasGdprAccess: (user as any).hasGdprAccess ?? false, + isServiceAccount: (user as any).isServiceAccount ?? false, whatsappNumber: (user as any).whatsappNumber || '', telegramUsername: (user as any).telegramUsername || '', signalNumber: (user as any).signalNumber || '', @@ -324,6 +326,7 @@ function UserModal({ isActive: formData.isActive, hasDeveloperAccess: formData.hasDeveloperAccess, hasGdprAccess: formData.hasGdprAccess, + isServiceAccount: formData.isServiceAccount, whatsappNumber: formData.whatsappNumber || undefined, telegramUsername: formData.telegramUsername || undefined, signalNumber: formData.signalNumber || undefined, @@ -359,6 +362,7 @@ function UserModal({ roleIds: formData.roleIds, hasDeveloperAccess: formData.hasDeveloperAccess, hasGdprAccess: formData.hasGdprAccess, + isServiceAccount: formData.isServiceAccount, whatsappNumber: formData.whatsappNumber || undefined, telegramUsername: formData.telegramUsername || undefined, signalNumber: formData.signalNumber || undefined, @@ -487,6 +491,22 @@ function UserModal({
+