Files
Fuchs_Intranet/Fuchs/Docs/Decisions/0014-admin-user-management.md
StefanandClaude Sonnet 5 076290cbeb
Playwright Tests / test (push) Canceled after 0s
Split Admin module into Status and User Management under one sidemenu dropdown
Splits fn=admin into two independent modules: the original status
dashboard stays at fn=admin, user management moves to a new fn=usr
(IntranetController.AdminUsers.cs, fis.usr.js). Both are now reached
through a single "Administration" sidemenu dropdown (Systemüberwachung /
Benutzerverwaltung) gated by one shared fds_sys > 4 check, per ADR 0015
(supersedes 0014's "fold into one module" choice).

Also fixes the password-reset validation to check only a plain 6-char
minimum instead of silently also requiring ValidatePassword's default
2-uppercase/2-lowercase/2-digit composition, which contradicted the
"mind. 6 Zeichen" message shown to the admin.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-09 12:09:44 +02:00

83 lines
5.4 KiB
Markdown

---
status: Superseded
date: 2026-09-09
applyTo:
- "Fuchs/Controllers/IntranetController.Admin.cs"
- "Fuchs/js/intranet/modules/fis.admin*.js"
- "Fuchs/js/intranet/modules/fis.admin.scss"
- "Fuchs_Database/dbo/Functions/fis_admin_*.sql"
- "Fuchs_Database/dbo/Stored Procedures/fis_admin_*.sql"
supersededBy: "0015-split-admin-user-management-module.md"
---
# 0014 — Admin user management: folded into the `admin` module, dual auth axis, password always emailed
## Context
An operator needs to see the list of non-deleted intranet user accounts (name + email), and
per account reset the password, change the email address, and view/change group assignments.
The database layer for this already existed (`fis_admin_getUserAccountList`,
`fis_admin_getUserGroupList`, `fis_admin_setUserAccount`, `fis_admin_resetUserPassword`,
`fis_admin_addGroupMember`/`remGroupMember`) but was completely unwired from any controller.
Those procs gate on a **different** authorization mechanism than the rest of the app: a
per-user `intranet_admin` setting read via `fis_admin_getUserAuth(@authaccount) > 1`, entirely
independent of the `fis_getModuleAuth`/`fis_module_auth` mechanism the rest of the app
(including the existing Admin module, see
[0011](0011-admin-module-system-status.md)) uses.
## Decision
- **Fold user management into the existing `admin` module** (`fn=admin`) rather than create a
new top-level module. New `id` cases (`users`, `userpwdreset`, `useremail`, `usergroups`,
`usergroupset`) sit in `Do_Process_Admin` alongside the system-status diagnostics, behind the
**same** `fds_sys > 4` server-side gate as the rest of that module.
- **Known, accepted gap: two independent authorization axes.** `fds_sys > 4` controls whether
the endpoints are reachable at all; the underlying `fis_admin_*` SQL objects separately
require the caller's `intranet_admin` setting to be `> 1` (`fis_admin_getUserAuth`). An
account with `fds_sys > 4` but no `intranet_admin` setting will reach these endpoints but get
empty result sets / no-op writes from SQL — not an error, just silently nothing. **Operational
consequence:** any account meant to do user management must be provisioned with both
`fds_sys > 4` and `intranet_admin >= 2` (via `fis_useraccounts_settings`). This was a deliberate
simplicity trade-off (see Alternatives) rather than unifying the two permission systems.
- **Password reset always emails the result; never displays it.** The ribbon's "Passwort
zurücksetzen" action lets the admin either type a specific new password or leave the field
blank to auto-generate one (`fis_admin_resetUserPassword` gained an optional `@newpassword`
parameter for this). Either way, the resulting plaintext password is sent only to the target
user's stored email address via `IComService` (respecting the `Fuchs:Email:OverrideRecipient`
dev safety net) — the admin never sees it on screen.
- **Change email reuses `fis_admin_setUserAccount`** (prefilling the other fields from the
existing row, `@password = NULL` so the update path leaves the password untouched) instead of
adding a narrower proc — its `WHEN MATCHED` branch never touches `password_enc`, and its
existing duplicate-email `THROW 60001` is mapped to an HTTP 409 in the controller.
- **New `fis_admin_getUserGroupMembership` function** returns every group plus a `member` bit
for one target user (mirrors `fis_admin_getUserGroupList`'s auth-gate style) — needed for the
per-user group checkbox dialog; toggling a checkbox calls `fis_admin_addGroupMember`/
`remGroupMember` immediately (no separate save step, consistent with the rest of the module's
ribbon actions).
- Per the [exception-user-notification rule](0003-interrupting-exceptions-notify-the-user.md),
a failed password reset, a failed reset-email delivery, or a target with no usable email all
call `IEventService.UserIssueAsync` so the admin is never left believing an operation
succeeded silently.
## Consequences
- Deploying/onboarding an admin for user management is now a two-step provisioning task
(`fds_sys` **and** `intranet_admin`), not obvious from the UI — document this whenever
granting the permission.
- `fis_admin_resetUserPassword`'s signature changed (added optional `@newpassword`) and its
result set gained a `newpassword` column; it was previously unwired from any caller, so this
is a safe in-place change.
- The Admin module's frontend now mixes two concerns (system diagnostics + user management)
under one ribbon/menu entry; if user management grows substantially, revisit splitting it into
its own module (see Alternatives).
## Alternatives considered
- **Unify the two authorization axes** (e.g. make `fis_admin_*` procs check `fis_getModuleAuth`
instead of `fis_admin_getUserAuth`, or vice versa). Rejected for this change — it would touch
every existing `fis_admin_*` proc/function (all already gate on `fis_admin_getUserAuth`) for a
feature that only needed to wire up what already existed; revisit if a real conflict surfaces
in practice.
- **New standalone top-level module** (`fn=usr`) with its own menu button and its own
`fis_admin_getUserAuth`-based gate. Rejected per explicit user direction — folding into the
existing Administration module was preferred over a second admin-ish menu entry.
- **Show the new/reset password on screen instead of emailing it.** Rejected per explicit user
direction — the password must only ever reach the user's own inbox, never be visible to the
admin performing the reset.