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.
28 lines
1.6 KiB
TypeScript
28 lines
1.6 KiB
TypeScript
import { test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { interpretServerAccount, normalizePermission } from "./upstream.js";
|
|
|
|
/**
|
|
* `/api/account` is the only place Stalwart lists what an account may do, and
|
|
* ihasmail used to read the edition out of it and throw the rest away.
|
|
*/
|
|
test("the account's permissions are kept alongside the edition", () => {
|
|
const info = interpretServerAccount({ edition: "enterprise", permissions: ["sysAccountGet", "sysAccountQuery"], locale: "en_US" });
|
|
assert.deepEqual(info, { edition: "enterprise", permissions: ["sysAccountGet", "sysAccountQuery"] });
|
|
});
|
|
|
|
test("permission names read the same whichever case the server uses", () => {
|
|
// The source serialises camelCase; the documentation shows kebab-case.
|
|
assert.equal(normalizePermission("sys-account-get"), "sysAccountGet");
|
|
assert.equal(normalizePermission("sysAccountGet"), "sysAccountGet");
|
|
assert.equal(normalizePermission("sys-dkim-signature-create"), "sysDkimSignatureCreate");
|
|
assert.deepEqual(interpretServerAccount({ permissions: ["sys-account-get", "sysAccountGet"] }).permissions, ["sysAccountGet"]);
|
|
});
|
|
|
|
test("a body without a usable list yields no permissions rather than failing", () => {
|
|
assert.deepEqual(interpretServerAccount({ edition: "oss" }), { edition: "oss", permissions: [] });
|
|
assert.deepEqual(interpretServerAccount({ permissions: "sysAccountGet" }), { edition: null, permissions: [] });
|
|
assert.deepEqual(interpretServerAccount({ permissions: [1, null, "sysDomainGet"] }).permissions, ["sysDomainGet"]);
|
|
assert.deepEqual(interpretServerAccount(null), { edition: null, permissions: [] });
|
|
});
|