--- 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.