feat: Add Admin Groups Management Module
- Introduced a new module for managing user groups (`usrgrp`) in the administration interface. - Implemented CRUD operations for user groups: list, create, rename, and delete. - Added authorization checks to ensure only users with appropriate permissions can manage groups. - Created corresponding frontend JavaScript and SCSS files for the new module. - Updated documentation to reflect the new group management functionality and its authorization model. - Added SQL stored procedures for group management operations. - Established a new login script for database access, ensuring proper security practices.
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
---
|
||||
status: Active
|
||||
lastUpdated: 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"
|
||||
relatedDecisions:
|
||||
- "0021-user-group-management.md"
|
||||
- "0014-admin-user-management.md"
|
||||
- "0015-split-admin-user-management-module.md"
|
||||
---
|
||||
|
||||
# Group Management module (`usrgrp`)
|
||||
|
||||
## Summary
|
||||
The **Gruppenverwaltung** module lets a privileged operator see every non-deleted usergroup,
|
||||
create a new one, rename it, and delete it. It's the catalog-management counterpart to the `usr`
|
||||
module's per-user "Gruppen" dialog, which toggles an individual user's membership in these groups
|
||||
but cannot create/rename/delete the groups themselves. Same sidemenu-visibility and server-side
|
||||
authorization gate as `usr`/`admin` (`fds_sys > 4`), own `fn=usrgrp`, own frontend bundle, own
|
||||
controller partial. See [0021](../Decisions/0021-user-group-management.md) for why this is a
|
||||
separate module rather than folded into `usr`.
|
||||
|
||||
## How it works
|
||||
|
||||
### Authorization (two independent axes — same as `usr`, read [0014](../Decisions/0014-admin-user-management.md) first)
|
||||
1. **Menu visibility + `fds_sys` gate** — `fis_main_menu.js#addAdminMenuButtonsIfAuthorized`
|
||||
checks `fds_sys > 4` once and, if authorized, adds the "Administration" sidemenu dropdown with
|
||||
three entries: Benutzerverwaltung (`init:usr`), Gruppenverwaltung (`init:usrgrp`) and
|
||||
Systemüberwachung (`init:admin`). Every `/do/usrgrp/*` endpoint re-checks `fds_sys > 4`
|
||||
server-side (`Do_Process_AdminGroups`, reusing `AdminMinAuthExclusive`/`GetSystemAdminAuthAsync`
|
||||
from `IntranetController.Admin.cs`) and 401s otherwise.
|
||||
2. **Separate SQL-layer gate** — every `fis_admin_*` proc/function called below independently
|
||||
checks the caller's `intranet_admin` setting via `fis_admin_getUserAuth(@authaccount) > 1`. An
|
||||
account with `fds_sys > 4` but no `intranet_admin` setting reaches every `usrgrp` endpoint but
|
||||
gets empty results / silent no-op writes — not an error. This also applies to
|
||||
`fis_admin_addGroupMember`/`remGroupMember` (used by the `usr` module's membership dialog,
|
||||
documented there) — all five group-related `fis_admin_*` write/read objects now carry this
|
||||
check as of [0021](../Decisions/0021-user-group-management.md), which closed a real gap where
|
||||
three of them had none.
|
||||
|
||||
### Request flow
|
||||
```
|
||||
click "Gruppenverwaltung" → $ocms.init('usrgrp')
|
||||
POST /do/usrgrp/auth → { manage, level } (manage>0 ⇒ authorized)
|
||||
load /web/fis.usrgrp.de.js + /web/fis.usrgrp.css
|
||||
$ocms.usrgrp.init2() → init3() → renderLst()
|
||||
POST /do/usrgrp/list → { groups: [...] } every non-deleted group
|
||||
base ribbon (eM) → "Anlegen" (always available)
|
||||
POST /do/usrgrp/create {name} → { ok, usergroup_id, name }
|
||||
row click → per-row ribbon (iMn): Umbenennen / Löschen
|
||||
POST /do/usrgrp/rename {usergroup_id, name} → { ok, usergroup_id, name }
|
||||
POST /do/usrgrp/delete {usergroup_id} → { ok }
|
||||
```
|
||||
|
||||
### Backend (`IntranetController.AdminGroups.cs`)
|
||||
- `list` → `fis_admin_getUserGroupList(@authaccount, NULL)`, all non-deleted groups with
|
||||
decrypted names.
|
||||
- `create`/`rename` → both call `fis_admin_setUserGroup(@authaccount, @usergroup_id, @name,
|
||||
@enc_key)`; `create` passes `@usergroup_id = NULL` (the proc generates a new id via
|
||||
`fis_fn_id`), `rename` passes the existing id.
|
||||
- `delete` → `fis_admin_delUserGroup(@authaccount, @usergroup_id)` (soft-delete: sets
|
||||
`UserDeleted`/`DateDeleted`). No check for remaining members — a deleted group's membership
|
||||
rows simply stop being returned by `fis_admin_getUserGroupMembership` (filtered on the group's
|
||||
own `DateDeleted`), see [0021](../Decisions/0021-user-group-management.md) Consequences.
|
||||
|
||||
### Frontend (`fis.usrgrp.js` + `fis.usrgrp_txt_de.js` + `fis.usrgrp.scss`)
|
||||
Same list+ribbon pattern as `fis.usr.js`: `renderLst` builds a single-column card-style row list;
|
||||
a row click toggles `.selected` and swaps the `#topbar` ribbon between the base ribbon (`eM`:
|
||||
"Anlegen") and the per-row actions (`iMn`: Umbenennen/Löschen). `createDlg`/`renameDlg` use
|
||||
`$ocms.dlgform`; `deleteGroup` follows the `confirm(...)`-before-destructive-action pattern (no
|
||||
dialog, nothing to collect beyond the confirmation).
|
||||
|
||||
## Key files
|
||||
- `Fuchs/Controllers/IntranetController.AdminGroups.cs` — `Do_Process_AdminGroups` dispatch.
|
||||
- `Fuchs/Controllers/IntranetController.Admin.cs` — shared `AdminMinAuthExclusive`/
|
||||
`GetSystemAdminAuthAsync`/`AdminJson` this module reuses.
|
||||
- `Fuchs/js/intranet/modules/fis.usrgrp.js`, `fis.usrgrp_txt_de.js`, `fis.usrgrp.scss` — the
|
||||
module, texts, styles (bundled via `bdlconfig.json`, `intranet:usrgrp` context).
|
||||
- `Fuchs/js/intranet/fis_main_menu.js` (`addAdminMenuButtonsIfAuthorized`) — sidemenu wiring.
|
||||
- `Fuchs_Database/dbo/Functions/fis_admin_getUserGroupList.sql` — read-side group query.
|
||||
- `Fuchs_Database/dbo/Stored Procedures/fis_admin_setUserGroup.sql`,
|
||||
`fis_admin_delUserGroup.sql` — write-side group procs.
|
||||
|
||||
## Related decisions
|
||||
- [0021 — User group management (new module, closed the missing intranet_admin gate)](../Decisions/0021-user-group-management.md)
|
||||
- [0014 — Admin user management (dual auth axis, original decisions)](../Decisions/0014-admin-user-management.md)
|
||||
- [0015 — Split Admin into separate Status/User-Management modules](../Decisions/0015-split-admin-user-management-module.md)
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
status: Active
|
||||
lastUpdated: 2026-09-14
|
||||
lastUpdated: 2026-09-16
|
||||
applyTo:
|
||||
- "Fuchs/Controllers/IntranetController.AdminUsers.cs"
|
||||
- "Fuchs/js/intranet/modules/fis.usr*.js"
|
||||
@@ -12,6 +12,7 @@ relatedDecisions:
|
||||
- "0014-admin-user-management.md"
|
||||
- "0015-split-admin-user-management-module.md"
|
||||
- "0020-user-management-create-and-delete.md"
|
||||
- "0021-user-group-management.md"
|
||||
---
|
||||
|
||||
# User Management module (`usr`)
|
||||
@@ -51,7 +52,8 @@ click "Benutzerverwaltung" → $ocms.init('usr')
|
||||
POST /do/usr/auth → { manage, level } (manage>0 ⇒ authorized)
|
||||
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
|
||||
POST /do/usr/list → { users: [...] } grid of every non-deleted account (incl. `groups` csv)
|
||||
POST /do/usr/list {useraccount_id} → { users: [one] } single-row refresh (refreshRow)
|
||||
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
|
||||
@@ -63,8 +65,12 @@ click "Benutzerverwaltung" → $ocms.init('usr')
|
||||
```
|
||||
|
||||
### Backend (`IntranetController.AdminUsers.cs`)
|
||||
- `list` → `fis_admin_getUserAccountList(@authaccount, NULL, NULL, NULL)`, all non-deleted
|
||||
accounts with decrypted name/email/disabled flag.
|
||||
- `list` → `fis_admin_getUserAccountList(@authaccount, @useraccount_id, NULL, NULL)`, all non-deleted
|
||||
accounts with decrypted name/email/disabled flag plus a `groups` column — a `', '`-separated,
|
||||
alphabetically ordered csv of the account's active group memberships (`STRING_AGG` over
|
||||
decrypted `fis_usergroups.name_enc`, deleted groups/memberships excluded). An optional
|
||||
`useraccount_id` form field narrows the result to one account so the frontend can refresh a
|
||||
single row without re-rendering the grid.
|
||||
- `pwdreset` → the admin may type a specific password or leave it blank; validated with a plain
|
||||
`ValidatePassword(minLength: 6, numUpper: 0, numLower: 0, numNumbers: 0, numSpecial: 0)` — no
|
||||
hidden composition requirements, matching what the UI actually tells the admin (a prior bug
|
||||
@@ -82,7 +88,9 @@ click "Benutzerverwaltung" → $ocms.init('usr')
|
||||
changed (`@password = NULL`, so the `WHEN MATCHED` update path leaves `password_enc` alone). A
|
||||
duplicate email (`THROW 60001` in the proc) surfaces as HTTP 409 `{ error: "duplicate_email" }`.
|
||||
- `groups` → the new `fis_admin_getUserGroupMembership(@authaccount, @useraccount_id)` function
|
||||
returns every group plus a `member` bit for that one user.
|
||||
returns every group plus a `member` bit for that one user. The group catalog itself (create/
|
||||
rename/delete) is managed in the separate `usrgrp` module — see
|
||||
[admin-group-management.md](admin-group-management.md).
|
||||
- `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,
|
||||
@@ -101,7 +109,11 @@ click "Benutzerverwaltung" → $ocms.init('usr')
|
||||
### 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 a card-style row grid (avatar
|
||||
initials badge, name/email columns, a status pill) rather than a plain `<table>`; a row click
|
||||
initials badge, name/email/groups columns, a status pill) rather than a plain `<table>`, with
|
||||
each row produced by `buildRow(rw)`. Changes that only affect one account (`emailDlg`, every
|
||||
checkbox toggle in `groupsDlg`) call `refreshRow(rw)`, which re-posts `usr/list` with that
|
||||
`useraccount_id` and swaps the row in place — selection and the per-row ribbon survive, and the
|
||||
groups csv is always current. Create/delete/password-reset still do a full `renderLst`. 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).
|
||||
|
||||
Reference in New Issue
Block a user