Say user throughout, noting Ory's identity naming in the docs
CI / full-gate (push) Successful in 2m32s

This commit is contained in:
2026-08-03 17:13:20 +02:00
parent 076d1f6926
commit 9097552065
37 changed files with 259 additions and 251 deletions
@@ -18,7 +18,7 @@ import type { ExpandTree, RelationTuple } from "#plugin-api";
const uid = (n: number) => `01902d5e-7b6c-7e3a-9f21-3c8d1e0a4b${String(n).padStart(2, "0")}`;
const userTuple = (permission: string, n: number): RelationTuple =>
({ namespace: "Permission", object: permission, relation: "granted", subject_id: `identity:${uid(n)}` });
({ namespace: "Permission", object: permission, relation: "granted", subject_id: `user:${uid(n)}` });
const groupTuple = (permission: string, group: string): RelationTuple =>
({ namespace: "Permission", object: permission, relation: "granted", subject_set: { namespace: "Group", object: group, relation: "members" } });
@@ -26,14 +26,14 @@ test("isValidRoleName + permissionGrantTuple map the form value to a Permission
for (const ok of ["admin", "editor", "team-a", "a1_b9"]) assert.equal(isValidRoleName(ok), true, ok);
for (const bad of ["", "Admin", "a b", "-bad", "a".repeat(65)]) assert.equal(isValidRoleName(bad), false, bad);
assert.deepEqual(permissionGrantTuple("editor", `identity:${uid(2)}`), { namespace: "Permission", object: "editor", relation: "granted", subject_id: `identity:${uid(2)}` });
assert.deepEqual(permissionGrantTuple("editor", `user:${uid(2)}`), { namespace: "Permission", object: "editor", relation: "granted", subject_id: `user:${uid(2)}` });
assert.deepEqual(permissionGrantTuple("editor", "group:eng"), { namespace: "Permission", object: "editor", relation: "granted", subject_set: { namespace: "Group", object: "eng", relation: "members" } });
for (const bad of ["", "identity:not-a-uuid", "group:Bad Name", "nope:x"]) assert.equal(permissionGrantTuple("editor", bad), null, bad);
for (const bad of ["", "user:not-a-uuid", "group:Bad Name", "nope:x"]) assert.equal(permissionGrantTuple("editor", bad), null, bad);
});
test("expandToEffectiveUsers flattens an expand tree → sorted distinct user ids, transitive through groups", () => {
// The subject rides on each node's `tuple` (Keto v26.2.0 shape, verified live).
const leaf = (n: number): ExpandTree => ({ tuple: { namespace: "", object: "", relation: "", subject_id: `identity:${uid(n)}` }, type: "leaf" });
const leaf = (n: number): ExpandTree => ({ tuple: { namespace: "", object: "", relation: "", subject_id: `user:${uid(n)}` }, type: "leaf" });
const tree: ExpandTree = {
children: [
leaf(1), // direct
@@ -71,7 +71,7 @@ test("buildPermissionsListModel filters by search, sorts, paginates; the name li
});
test("buildPermissionFormModel: a create form with a required name field + member options (user or group)", () => {
const options = [{ label: "ada@example.com", value: `identity:${uid(1)}` }, { label: "eng (group)", value: "group:eng" }];
const options = [{ label: "ada@example.com", value: `user:${uid(1)}` }, { label: "eng (group)", value: "group:eng" }];
const m = buildPermissionFormModel({ csrfToken: "tok.sig", memberOptions: options });
assert.equal(m.title, "New permission");
assert.equal(m.form.action, "/admin/permissions");
@@ -89,8 +89,8 @@ test("buildPermissionFormModel: a create form with a required name field + membe
test("buildPermissionDetailModel: members → rows, add-options exclude current members, effective access listed, actions wired", () => {
const members = [memberView(userTuple("admin", 1), new Map([[uid(1), "ada@example.com"]])), memberView(groupTuple("admin", "eng"), new Map())];
const candidates = [
{ label: "ada@example.com", value: `identity:${uid(1)}` }, // already a member → excluded
{ label: "grace@example.com", value: `identity:${uid(2)}` },
{ label: "ada@example.com", value: `user:${uid(1)}` }, // already a member → excluded
{ label: "grace@example.com", value: `user:${uid(2)}` },
{ label: "eng (group)", value: "group:eng" }, // already a member → excluded
{ label: "ops (group)", value: "group:ops" },
];
@@ -100,7 +100,7 @@ test("buildPermissionDetailModel: members → rows, add-options exclude current
assert.equal(m.members.rows.length, 2);
assert.equal(m.members.action, "/admin/permissions/admin/members/delete");
assert.equal(m.add.action, "/admin/permissions/admin/members");
assert.deepEqual(m.add.options.map((o) => o.value), [`identity:${uid(2)}`, "group:ops"]);
assert.deepEqual(m.add.options.map((o) => o.value), [`user:${uid(2)}`, "group:ops"]);
assert.deepEqual(m.effective.map((e) => e.label), ["ada@example.com", "grace@example.com"]);
assert.equal(m.delete.action, "/admin/permissions/admin/delete");
});