--- status: Accepted date: 2026-09-16 applyTo: - "Fuchs/Controllers/IntranetController.AdminGroups.cs" - "Fuchs/js/intranet/modules/fis.usrgrp*.js" - "Fuchs/js/intranet/modules/fis.usrgrp.scss" - "Fuchs/js/intranet/fis_main_menu.js" - "Fuchs_Database/dbo/Functions/fis_admin_getUserGroupList.sql" - "Fuchs_Database/dbo/Stored Procedures/fis_admin_setUserGroup.sql" - "Fuchs_Database/dbo/Stored Procedures/fis_admin_delUserGroup.sql" - "Fuchs_Database/dbo/Stored Procedures/fis_admin_addGroupMember.sql" - "Fuchs_Database/dbo/Stored Procedures/fis_admin_remGroupMember.sql" supersededBy: "" --- # 0021 — User group management: new `usrgrp` module, close the missing `intranet_admin` gate on group writes ## Context The per-user "Gruppen" dialog in the `usr` module ([0014](0014-admin-user-management.md), [0015](0015-split-admin-user-management-module.md)) lets an admin toggle an existing user's membership in each usergroup, but there was no UI anywhere to create, rename or delete a usergroup itself — `fis_usergroups` could only be populated by running a raw SQL script by hand (`hlp/db/create user group.sql`). The DB-layer proc `fis_admin_setUserGroup` (create/rename) and the read-side `fis_admin_getUserGroupList` already existed but were wired to no controller endpoint, mirroring the exact gap [0020](0020-user-management-create-and-delete.md) closed for user accounts. While wiring this up, three of the four `fis_admin_*` group-write procedures were found missing the module's standard `intranet_admin` authorization check entirely (`fis_admin_addGroupMember`, `fis_admin_remGroupMember`, `fis_admin_delUserGroup`) — a caller with `fds_sys > 4` but no `intranet_admin` setting could add/remove a user's group membership or delete a group outright, the same real authorization hole 0020 fixed for `fis_admin_delUserAccount`. `fis_admin_setUserGroup` already had the check. ## Decision - **New standalone module, `fn=usrgrp`** (`IntranetController.AdminGroups.cs`, `fis.usrgrp.js`/`fis.usrgrp_txt_de.js`/`fis.usrgrp.scss`), added as a third entry in the existing "Administration" sidemenu dropdown alongside Benutzerverwaltung and Systemüberwachung — per [0015](0015-split-admin-user-management-module.md)'s own stated consequence that any future admin-only feature should default to its own module under that dropdown rather than be folded into an existing one. The dropdown itself already stays visible while any of its children is open, so switching between Gruppenverwaltung and Benutzerverwaltung needs no dedicated "back" control. - **Same dual-authorization gate as every other module under this dropdown**: `fds_sys > 4` server-side (`AdminMinAuthExclusive`/`GetSystemAdminAuthAsync`, shared from `IntranetController.Admin.cs`) gates whether the module is reachable/visible at all; `fis_admin_getUserAuth(@authaccount) > 1` (the `intranet_admin` setting) independently gates every read/write the `fis_admin_*` procs perform. Both together are what "only admins can see and change groups" means in this codebase — see the Consequences below for why this is enforced in four places, not one. - **New `id` cases in `Do_Process_AdminGroups`**: `list` → `fis_admin_getUserGroupList`, `create`/ `rename` → `fis_admin_setUserGroup` (same proc; `rename` supplies an existing `@usergroup_id`), `delete` → `fis_admin_delUserGroup`. No membership editing here — that stays in the `usr` module's existing per-user checkbox dialog, unchanged. - **Closed the missing `intranet_admin` gate** on `fis_admin_addGroupMember`, `fis_admin_remGroupMember` and `fis_admin_delUserGroup` by adding the same `fis_admin_getUserAuth(@authaccount) > 1` guard `fis_admin_setUserGroup` already had, following the same "no-op instead of error" style 0020 used for `fis_admin_delUserAccount`: a caller without `intranet_admin >= 2` gets an unmodified result set / a `false` success bit, not a SQL error, consistent with every other `fis_admin_*` proc in this area. - **Frontend** mirrors `fis.usr.js`'s list+ribbon pattern (`renderLst`/`eM`/`iMn`): a card-style row list with a base ribbon button ("Anlegen") and a per-row ribbon ("Umbenennen"/"Löschen") on row selection, using `$ocms.dlgform` for create/rename and a `confirm(...)` guard (no dialog) for delete — the same choice 0020 made for deleting a user account. ## Consequences - Deleting a group does **not** guard against it still having members — its `fis_usergroups_members` rows simply stop being returned (the membership query already filters on the group's own `DateDeleted`). No UI currently surfaces "this group has N members" before deletion; acceptable since deletion is a soft-delete and the group row can be recreated with the same effective membership restored via `fis_admin_addGroupMember` if this turns out to matter in practice. - Four separate `fis_admin_*` procs/functions now each independently re-check `intranet_admin > 1` (`getUserGroupList`, `setUserGroup`, `delUserGroup`, `addGroupMember`, `remGroupMember` — five, in fact) rather than a single shared gate; this is the established style in this codebase (every `fis_admin_*` object re-checks for itself, see 0014) and not something this change revisits. - Same operational note as 0014/0015/0020: an account needs **both** `fds_sys > 4` and `intranet_admin >= 2` to actually manage groups, not obvious from the UI. ## Alternatives considered - **Extend the existing per-user "Gruppen" dialog** with an inline "new group" input and a delete icon per row, instead of a new module. Rejected per explicit user direction — the user asked for a dedicated sidemenu entry for group management, separate from the per-user dialog. - **Fold into the `usr` module** as a second ribbon button. Rejected for the same reason 0015 split `usr` off `admin` in the first place: two unrelated concerns (managing individual user accounts vs. managing the group catalog) behind one button was already identified as a mistake once it happened once; the "own module under the dropdown" default from 0015 applies directly.