Let admins create and delete users in Benutzerverwaltung, modernize list
Playwright Tests / test (push) Canceled after 0s

Wire up two previously-unwired stored procedures for the usr module:
- fis_admin_createUserAccount gained the missing @authaccount +
  intranet_admin auth gate, a duplicate-email check, and now returns
  the (possibly auto-generated) password so it can be emailed to the
  new user, mirroring fis_admin_resetUserPassword's pattern.
- fis_admin_delUserAccount gained the same missing auth gate plus a
  guard against an admin deleting their own account.

New /do/usr/create and /do/usr/delete endpoints in
IntranetController.AdminUsers.cs, a "Neuer Benutzer" button and a
"Benutzer löschen" ribbon action in fis.usr.js, and unit tests for the
new welcome-email content (BuildWelcomeEmail).

Also restyle the user list (fis.usr.scss) from a plain bordered table
to a card-style row grid with avatar-initial badges and status pills,
matching the Admin dashboard's visual language.

Documented as ADR 0020 and in the admin-user-management concept doc.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Stefan
2026-09-14 14:51:59 +02:00
co-authored by Claude Sonnet 5
parent 38145a917e
commit 4386a6336a
14 changed files with 716 additions and 138 deletions
+40 -18
View File
@@ -1,6 +1,6 @@
---
status: Active
lastUpdated: 2026-09-09
lastUpdated: 2026-09-14
applyTo:
- "Fuchs/Controllers/IntranetController.AdminUsers.cs"
- "Fuchs/js/intranet/modules/fis.usr*.js"
@@ -11,20 +11,22 @@ applyTo:
relatedDecisions:
- "0014-admin-user-management.md"
- "0015-split-admin-user-management-module.md"
- "0020-user-management-create-and-delete.md"
---
# User Management module (`usr`)
## Summary
The **Benutzerverwaltung** module lets a privileged operator see every non-deleted intranet
user account (name + email + active/disabled), reset a password, change an email address, and
view/change group assignments. It shares its sidemenu-visibility and server-side authorization
gate with the separate Admin/status-dashboard module (`fds_sys > 4`) but is otherwise fully
independent — own `fn=usr`, own frontend bundle, own controller partial. See
[0015](../Decisions/0015-split-admin-user-management-module.md) for why these were split from
one module into two, and [0014](../Decisions/0014-admin-user-management.md) for the original
decisions this one carries forward (password always emailed, dual auth axis, reuse of
`fis_admin_setUserAccount` for email changes).
user account (name + email + active/disabled), create a new account, delete an existing one,
reset a password, change an email address, and view/change group assignments. It shares its
sidemenu-visibility and server-side authorization gate with the separate Admin/status-dashboard
module (`fds_sys > 4`) but is otherwise fully independent — own `fn=usr`, own frontend bundle,
own controller partial. See [0015](../Decisions/0015-split-admin-user-management-module.md) for
why these were split from one module into two, [0014](../Decisions/0014-admin-user-management.md)
for the original decisions this one carries forward (password always emailed, dual auth axis,
reuse of `fis_admin_setUserAccount` for email changes), and
[0020](../Decisions/0020-user-management-create-and-delete.md) for create/delete.
## How it works
@@ -50,11 +52,14 @@ click "Benutzerverwaltung" → $ocms.init('usr')
load /web/fis.usr.de.js + /web/fis.usr.css
$ocms.usr.init2() → init3() → renderLst()
POST /do/usr/list → { users: [...] } grid of every non-deleted account
row click → per-row ribbon (iMn): Passwort zurücksetzen / E-Mail ändern / Gruppenzugehörigkeit
base ribbon (eM) → "Neuer Benutzer" (always available, not just when a row is selected)
POST /do/usr/create {name, firstname, email, title?, gender?, mobile?} → { ok, useraccount_id, emailed } | 409 duplicate_email
row click → per-row ribbon (iMn): Passwort zurücksetzen / E-Mail ändern / Gruppenzugehörigkeit / Benutzer löschen
POST /do/usr/pwdreset {useraccount_id, newpassword?} → { ok, emailed }
POST /do/usr/email {useraccount_id, email} → { ok } | 409 duplicate_email
POST /do/usr/groups {useraccount_id} → { groups: [...] }
POST /do/usr/groupset {useraccount_id, usergroup_id, member} → { ok, member }
POST /do/usr/delete {useraccount_id} → { ok } | 409 cannot_delete_self
```
### Backend (`IntranetController.AdminUsers.cs`)
@@ -80,26 +85,43 @@ click "Benutzerverwaltung" → $ocms.init('usr')
returns every group plus a `member` bit for that one user.
- `groupset` → calls `fis_admin_addGroupMember`/`remGroupMember` depending on the posted
`member` flag — applied immediately per checkbox toggle, no separate save step.
- `create` → `fis_admin_createUserAccount(@authaccount, @name, @firstname, @title, @gender,
@email, @mobile, @password, @enc_key)`. Only name/firstname/email are required from the
dialog; the admin may leave password blank (the proc auto-generates one and returns it as
`newpassword`, mirroring `fis_admin_resetUserPassword`'s pattern). The controller then emails
the new account's credentials via `BuildWelcomeEmail` — same "emailed, never shown on screen"
rule as `pwdreset` — and returns `emailed` so the UI can tell the admin whether delivery
succeeded. Duplicate email → 409 `duplicate_email`, same mapping as `email`.
- `delete` → `fis_admin_delUserAccount(@authaccount, @useraccount_id)` (soft-delete: sets
`UserDeleted`/`DateDeleted`). The controller rejects a self-delete with 409
`cannot_delete_self` before even calling SQL; the proc enforces the same guard independently.
See [0020](../Decisions/0020-user-management-create-and-delete.md) for why both procs needed
the `intranet_admin` auth gate added when they were wired up.
### Frontend (`fis.usr.js` + `fis.usr_txt_de.js` + `fis.usr.scss`)
A self-contained list+ribbon module modeled on Banking (`fis.bam.js`)'s `eM`/`iMn` pattern
rather than the Admin dashboard's card layout: `renderLst` builds the table via
`$fis.frm_list`/`$$.tblset`; a row click toggles `.selected` and swaps the `#topbar` ribbon
between the base "Neu Laden" button (`eM`) and the three per-row actions (`iMn`). Each action is
a dialog (`pwdResetDlg`/`emailDlg`/`groupsDlg`) using `$ocms.dlgform`/`$ocms.dlg` with a manual
rather than the Admin dashboard's card layout: `renderLst` builds a card-style row grid (avatar
initials badge, name/email columns, a status pill) rather than a plain `<table>`; a row click
toggles `.selected` and swaps the `#topbar` ribbon between the base ribbon (`eM`: "Neuer
Benutzer" always available, plus "Neu Laden" once a row was selected) and the four per-row
actions (`iMn`: Passwort zurücksetzen / E-Mail ändern / Gruppenzugehörigkeit / Benutzer löschen).
`pwdResetDlg`/`emailDlg`/`groupsDlg`/`createDlg` use `$ocms.dlgform`/`$ocms.dlg` with a manual
`submit`/`postXT` (not the built-in `success`-URL wiring) so a 409's `error` code can be mapped
to a specific German message via `$uct.err`.
to a specific German message via `$uct.err`; `deleteUser` instead follows Banking's
`confirm(...)`-before-destructive-action pattern (`fis.bam.js`'s `smd`/`mbd`) since there's
nothing to collect beyond the confirmation itself.
## Key files
- `Fuchs/Controllers/IntranetController.AdminUsers.cs` — `Do_Process_AdminUsers` dispatch + `BuildPasswordResetEmail` + `NotifyAdminUserActionFailedAsync`.
- `Fuchs/Controllers/IntranetController.AdminUsers.cs` — `Do_Process_AdminUsers` dispatch + `BuildPasswordResetEmail`/`BuildWelcomeEmail` + `NotifyAdminUserActionFailedAsync`.
- `Fuchs/Controllers/IntranetController.Admin.cs` — shared `AdminMinAuthExclusive`/`GetSystemAdminAuthAsync`/`AdminJson` this module reuses.
- `Fuchs/js/intranet/modules/fis.usr.js`, `fis.usr_txt_de.js`, `fis.usr.scss` — the module, texts, styles (bundled via `bdlconfig.json`, `intranet:usr` context).
- `Fuchs/js/intranet/fis_main_menu.js` (`addAdminMenuButtonsIfAuthorized`) + `fis_main_go.js` — conditional menu buttons (this module and `admin`).
- `Fuchs_Database/dbo/Functions/fis_admin_getUserAccountList.sql`, `fis_admin_getUserGroupList.sql`, `fis_admin_getUserGroupMembership.sql` — read-side user/group queries.
- `Fuchs_Database/dbo/Stored Procedures/fis_admin_resetUserPassword.sql`, `fis_admin_setUserAccount.sql`, `fis_admin_addGroupMember.sql`, `fis_admin_remGroupMember.sql` — write-side user/group procs.
- `Fuchs.Tests/AdminUserManagementTests.cs` — password-reset email content tests.
- `Fuchs_Database/dbo/Stored Procedures/fis_admin_resetUserPassword.sql`, `fis_admin_setUserAccount.sql`, `fis_admin_addGroupMember.sql`, `fis_admin_remGroupMember.sql`, `fis_admin_createUserAccount.sql`, `fis_admin_delUserAccount.sql` — write-side user/group procs.
- `Fuchs.Tests/AdminUserManagementTests.cs` — password-reset and welcome-email content tests.
## Related decisions
- [0014 — Admin user management (original decisions, still in force)](../Decisions/0014-admin-user-management.md)
- [0020 — Wire up create/delete, welcome email on create, guard delete](../Decisions/0020-user-management-create-and-delete.md)
- [0015 — Split Admin into separate Status/User-Management modules](../Decisions/0015-split-admin-user-management-module.md)
- [0003 — Interrupting exceptions notify the user](../Decisions/0003-interrupting-exceptions-notify-the-user.md)