Add Groups to Administration #361

Closed
opened 2026-09-15 15:31:54 +00:00 by jcoffey-dev · 0 comments
Owner

Second of the five stacked Administration PRs. This one targets feat/admin-dashboard (#360). The five are meant to merge together, in order.

What it adds: Groups under Directory, next to Accounts. To Stalwart a group is an x:Account with @type: "Group", using the same sysAccountQuery/sysAccountGet permissions, so it follows the Accounts layout: search, pages of 50, a member-count column, and a side panel.

Panel

  • Display name, address (when creating), other addresses, role, and storage limit. These save together, with only changed fields sent.
  • Members: search for a person and add them, or remove members one at a time. Each change applies immediately.
    • Stalwart stores membership on the user, so each add or remove is one update: "memberGroupIds/<group>": true | null. That changes nothing else in the user's set.
    • You can't add or remove yourself, matching the "can't change your own role" rule.
  • Role: Default or Custom. From the source, in 0.16 a user's permissions come only from their own roles; a group gives members access to what's shared with it, not permissions. Only roles the viewer could grant are offered.
  • Delete:
    • Type-the-address confirmation.
    • Members are taken out first, then the group is destroyed. This is the same keys-then-domain order Domains uses, since the registry keeps anything another object points to.
    • A role without sysAccountUpdate isn't offered a delete it could only half finish.

Mock fixes

  • Groups had a user's roles; they're now Default.
  • A memberGroupIds filter was accepted but ignored; it's now applied.
  • A delete refused because members still point to the group came back with the wrong error shape; it now matches the domain case (linkedObjects: [{object, id}]).
  • The mock now refuses nested groups, and memberships pointing at things that aren't groups.

Not tried live. Prod has no groups and every operation is a write. What was read from source is recorded in KNOWN-ISSUES, including the assumption that deleting a group with members is refused (the UI removes members first either way). I'd like to confirm on the live server with a throwaway group before these merge.

Translation: 35 new strings (2 plurals) in all nine catalogues (1489/1505 each, 16 falling back as before).

Checked

  • Typecheck, 1249 web and 212 server tests pass, as do build and i18n:check.
  • New tests cover:
    • the membership patch shape, and the member-count query;
    • create payload (no credentials or encryption settings);
    • delete order, stopping if removing members fails, and the no-members case;
    • mock filter, pointer, nesting and linked-delete behaviour;
    • panel guards (can't remove yourself, read-only role, delete blocked without update, delete passing every member).
  • In Chrome against the mock:
    • list and counts;
    • opened Support, added Grace (count 2→3, Office unchanged), removed Alan (3→2);
    • created "Sales", added a member, deleted it through the dialog ("Its 1 member is taken out…");
    • no console errors.

Merged 2026-09-15 as coffey-labs/ihasmail@5a7a8019e9

Rebuilt from: git history, session transcript.

Second of the five stacked Administration PRs. This one targets `feat/admin-dashboard` (#360). The five are meant to merge together, in order. **What it adds:** Groups under Directory, next to Accounts. To Stalwart a group is an `x:Account` with `@type: "Group"`, using the same `sysAccountQuery`/`sysAccountGet` permissions, so it follows the Accounts layout: search, pages of 50, a member-count column, and a side panel. **Panel** - Display name, address (when creating), other addresses, role, and storage limit. These save together, with only changed fields sent. - **Members:** search for a person and add them, or remove members one at a time. Each change applies immediately. - Stalwart stores membership on the user, so each add or remove is one update: `"memberGroupIds/<group>": true | null`. That changes nothing else in the user's set. - You can't add or remove yourself, matching the "can't change your own role" rule. - **Role:** `Default` or `Custom`. From the source, in 0.16 a user's permissions come only from their own roles; a group gives members access to what's shared with it, not permissions. Only roles the viewer could grant are offered. - **Delete:** - Type-the-address confirmation. - Members are taken out first, then the group is destroyed. This is the same keys-then-domain order Domains uses, since the registry keeps anything another object points to. - A role without `sysAccountUpdate` isn't offered a delete it could only half finish. **Mock fixes** - Groups had a user's roles; they're now `Default`. - A `memberGroupIds` filter was accepted but ignored; it's now applied. - A delete refused because members still point to the group came back with the wrong error shape; it now matches the domain case (`linkedObjects: [{object, id}]`). - The mock now refuses nested groups, and memberships pointing at things that aren't groups. **Not tried live.** Prod has no groups and every operation is a write. What was read from source is recorded in KNOWN-ISSUES, including the assumption that deleting a group with members is refused (the UI removes members first either way). I'd like to confirm on the live server with a throwaway group before these merge. **Translation:** 35 new strings (2 plurals) in all nine catalogues (1489/1505 each, 16 falling back as before). **Checked** - Typecheck, 1249 web and 212 server tests pass, as do build and i18n:check. - New tests cover: - the membership patch shape, and the member-count query; - create payload (no credentials or encryption settings); - delete order, stopping if removing members fails, and the no-members case; - mock filter, pointer, nesting and linked-delete behaviour; - panel guards (can't remove yourself, read-only role, delete blocked without update, delete passing every member). - In Chrome against the mock: - list and counts; - opened Support, added Grace (count 2→3, Office unchanged), removed Alan (3→2); - created "Sales", added a member, deleted it through the dialog ("Its 1 member is taken out…"); - no console errors. **Merged** 2026-09-15 as coffey-labs/ihasmail@5a7a8019e94f <sub>Rebuilt from: git history, session transcript.</sub>
This repo is archived. You cannot comment on issues.