A throwaway role on the production server confirmed the create shape, one-pointer changes to permissions, bases and name together, the grant refusal, and the in-use refusal when another role builds on it. It also showed that a permission name Stalwart does not know fails the whole update -- which is how jmapEmailSet, carried by the mock since Accounts was built, turned out not to exist. The mock uses jmapEmailUpdate and now refuses unknown names against the 0.16.22 snapshot. Some permissions an administrator holds are never listed by /api/account (sysLogCreate was granted without complaint), so the picker locks their Allow; KNOWN-ISSUES says so.
42 lines
2.4 KiB
TypeScript
42 lines
2.4 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { permissionSet } from "@/lib/adminAccess";
|
|
import { canBuildOn, effectivePermissions, inherited, roleOutranks, setPatch, type DirectoryRole } from "@/lib/adminRoles";
|
|
|
|
const flags = (...n: string[]) => Object.fromEntries(n.map((x) => [x, true]));
|
|
const roles = new Map<string, DirectoryRole>([
|
|
["user", { id: "user", description: "User", enabledPermissions: flags("jmapEmailGet", "jmapEmailUpdate") }],
|
|
["help", { id: "help", description: "Helpdesk", enabledPermissions: flags("sysAccountGet"), disabledPermissions: flags("jmapEmailUpdate"), roleIds: flags("user") }],
|
|
["lead", { id: "lead", description: "Lead", enabledPermissions: flags("sysAccountUpdate"), roleIds: flags("help") }],
|
|
]);
|
|
|
|
describe("what a role holds", () => {
|
|
it("follows every base, and a denial anywhere in the tree wins", () => {
|
|
// Stalwart unions enabled with enabled and disabled with disabled across
|
|
// the tree, then takes the disabled away (permissions.rs).
|
|
expect([...effectivePermissions(roles.get("lead")!, roles, "lead")].sort()).toEqual(["jmapEmailGet", "sysAccountGet", "sysAccountUpdate"]);
|
|
const { granted, denied } = inherited(["help"], roles, "lead");
|
|
expect(granted.get("jmapEmailGet")).toBe("help");
|
|
expect(denied.get("jmapEmailUpdate")).toBe("help");
|
|
});
|
|
|
|
it("changes a set one pointer at a time", () => {
|
|
expect(setPatch("enabledPermissions", ["a", "b"], new Set(["b", "c"]))).toEqual({ "enabledPermissions/a": null, "enabledPermissions/c": true });
|
|
expect(setPatch("roleIds", [], [])).toEqual({});
|
|
});
|
|
|
|
it("will not build on itself, or on a role already built on it", () => {
|
|
expect(canBuildOn("help", "help", roles)).toBe(false);
|
|
expect(canBuildOn("help", "lead", roles)).toBe(false);
|
|
expect(canBuildOn("lead", "user", roles)).toBe(true);
|
|
expect(canBuildOn(null, "lead", roles)).toBe(true);
|
|
});
|
|
|
|
it("is read-only to a viewer missing anything enabled in its tree, denied or not", () => {
|
|
const viewer = permissionSet(["jmapEmailGet", "sysAccountGet", "sysAccountUpdate"]);
|
|
// jmapEmailUpdate is denied on Helpdesk but enabled on User beneath it: a
|
|
// grant Stalwart would check, and a delete it would not.
|
|
expect(roleOutranks(viewer, roles.get("lead")!, roles)).toBe(true);
|
|
expect(roleOutranks(permissionSet([...viewer, "jmapEmailUpdate"]), roles.get("lead")!, roles)).toBe(false);
|
|
});
|
|
});
|