Add Administration, starting with accounts

An account whose Stalwart role manages accounts now finds Administration in
the account menu. It lists, searches, creates and edits accounts -- display
name, other addresses, role, storage limit -- sets a new password, and
deletes, each offered only when the role holds the matching permission.

The server keeps the permissions list from GET /api/account, which it
already called for the edition and threw the rest away. Everything else is
JMAP x:Account, x:Domain and x:Role calls through the existing /api/jmap
proxy, so nothing new is stored and Stalwart decides every call.

Stalwart checks a grant against the caller's permissions but not a password
change or a delete, so an account that outranks the viewer is shown
read-only. Your own password is changed in Settings, which re-seals the
session; changing it here would strand it.

The mock server gains a directory behind the same permission names, with
MOCK_ROLE choosing admin, tenant-admin, helpdesk or user.

68 new strings, translated in all nine catalogues; strings falling back to
English stay at 16.
This commit is contained in:
2026-09-13 15:11:55 -07:00
parent b0564679e6
commit 82e217155b
34 changed files with 2684 additions and 22 deletions
+6
View File
@@ -19,6 +19,9 @@ export const CAP = {
websocket: "urn:ietf:params:jmap:websocket",
} as const;
/** Stalwart's own capability, which carries its `x:` registry methods. */
export const STALWART_CAP = "urn:stalwart:jmap";
export class JmapMethodError extends Error {
constructor(
public readonly method: string,
@@ -349,6 +352,9 @@ export class JmapClient {
/** Map method name prefix → required capability URNs. */
function usingFor(method: string): string[] {
const type = method.split("/")[0] ?? "";
// Stalwart's registry: accounts, domains, credentials. Advertised per
// account rather than in the session, which supportedUsing() allows for.
if (type.startsWith("x:")) return [STALWART_CAP];
switch (type) {
case "Mailbox":
case "Thread":
+6
View File
@@ -39,6 +39,12 @@ export interface JmapSession {
/** "oss" | "community" | "enterprise". Stalwart publishes no version. */
edition?: string | null;
};
/**
* The account's effective permissions on that server, as Stalwart reports
* them. What the client offers is shaped by these; what is allowed is
* decided by Stalwart on every call.
*/
permissions?: string[];
};
}