Rename the coarse gate from role to permission, matching RBAC
This commit is contained in:
@@ -29,13 +29,13 @@ const keto = (fetchImpl: typeof fetch) => createKetoClient({ fetchImpl, readUrl:
|
||||
|
||||
test("check GETs the read API and returns the allowed boolean (true and false)", async () => {
|
||||
const allow = recorder(() => res(200, { allowed: true }));
|
||||
assert.equal(await keto(allow.fetchImpl).check({ namespace: "Role", object: "admin", relation: "members", subject_id: USER }), true);
|
||||
assert.equal(await keto(allow.fetchImpl).check({ namespace: "Permission", object: "admin", relation: "granted", subject_id: USER }), true);
|
||||
assert.match(allow.calls[0]!.url, /^http:\/\/keto:4466\/relation-tuples\/check\?/);
|
||||
assert.match(allow.calls[0]!.url, /namespace=Role&object=admin&relation=members/);
|
||||
assert.match(allow.calls[0]!.url, /namespace=Permission&object=admin&relation=granted/);
|
||||
assert.match(allow.calls[0]!.url, new RegExp(`subject_id=${encodeURIComponent(USER).replace(/[.]/g, "\\.")}`));
|
||||
// A denied check is 403 {allowed:false} (not a 200) — both statuses carry the verdict.
|
||||
const deny = recorder(() => res(403, { allowed: false }));
|
||||
assert.equal(await keto(deny.fetchImpl).check({ namespace: "Role", object: "admin", relation: "members", subject_id: "identity:nobody" }), false);
|
||||
assert.equal(await keto(deny.fetchImpl).check({ namespace: "Permission", object: "admin", relation: "granted", subject_id: "identity:nobody" }), false);
|
||||
});
|
||||
|
||||
test("check on a subject_set builds subject_set.* params and forwards max-depth", async () => {
|
||||
@@ -51,20 +51,20 @@ test("check on a subject_set builds subject_set.* params and forwards max-depth"
|
||||
|
||||
test("check throws a KetoError carrying the status on an unexpected response", async () => {
|
||||
await assert.rejects(
|
||||
keto((async () => res(400, { error: "bad" })) as typeof fetch).check({ namespace: "Role", object: "admin", relation: "members", subject_id: USER }),
|
||||
keto((async () => res(400, { error: "bad" })) as typeof fetch).check({ namespace: "Permission", object: "admin", relation: "granted", subject_id: USER }),
|
||||
(e: unknown) => e instanceof KetoError && e.status === 400,
|
||||
);
|
||||
});
|
||||
|
||||
test("listRelations builds the filter query + pagination and parses next_page_token", async () => {
|
||||
const tuples = [{ namespace: "Role", object: "admin", relation: "members", subject_id: USER }];
|
||||
const tuples = [{ namespace: "Permission", object: "admin", relation: "granted", subject_id: USER }];
|
||||
const { calls, fetchImpl } = recorder(() => res(200, { next_page_token: "NEXT", relation_tuples: tuples }));
|
||||
const out = await keto(fetchImpl).listRelations({ namespace: "Role", object: "admin", pageSize: 10, pageToken: "CUR", relation: "members" });
|
||||
const out = await keto(fetchImpl).listRelations({ namespace: "Permission", object: "admin", pageSize: 10, pageToken: "CUR", relation: "granted" });
|
||||
assert.deepEqual(out.tuples, tuples);
|
||||
assert.equal(out.nextPageToken, "NEXT");
|
||||
const url = calls[0]!.url;
|
||||
assert.match(url, /^http:\/\/keto:4466\/relation-tuples\?/);
|
||||
assert.match(url, /namespace=Role&object=admin&relation=members/);
|
||||
assert.match(url, /namespace=Permission&object=admin&relation=granted/);
|
||||
assert.match(url, /page_size=10&page_token=CUR/);
|
||||
// No Link header / token in the body ⇒ null, empty list ⇒ [].
|
||||
const empty = await keto((async () => res(200, {})) as typeof fetch).listRelations();
|
||||
@@ -72,16 +72,16 @@ test("listRelations builds the filter query + pagination and parses next_page_to
|
||||
});
|
||||
|
||||
test("expand GETs the read API for a subject set and returns the tree (with max-depth)", async () => {
|
||||
const tree = { children: [{ tuple: { namespace: "", object: "", relation: "", subject_id: USER }, type: "leaf" }], tuple: { namespace: "", object: "", relation: "", subject_set: { namespace: "Role", object: "admin", relation: "members" } }, type: "union" };
|
||||
const tree = { children: [{ tuple: { namespace: "", object: "", relation: "", subject_id: USER }, type: "leaf" }], tuple: { namespace: "", object: "", relation: "", subject_set: { namespace: "Permission", object: "admin", relation: "granted" } }, type: "union" };
|
||||
const { calls, fetchImpl } = recorder(() => res(200, tree));
|
||||
const out = await keto(fetchImpl).expand({ namespace: "Role", object: "admin", relation: "members" }, { maxDepth: 3 });
|
||||
const out = await keto(fetchImpl).expand({ namespace: "Permission", object: "admin", relation: "granted" }, { maxDepth: 3 });
|
||||
assert.deepEqual(out, tree);
|
||||
assert.match(calls[0]!.url, /^http:\/\/keto:4466\/relation-tuples\/expand\?/);
|
||||
assert.match(calls[0]!.url, /namespace=Role&object=admin&relation=members&max-depth=3/);
|
||||
assert.match(calls[0]!.url, /namespace=Permission&object=admin&relation=granted&max-depth=3/);
|
||||
});
|
||||
|
||||
test("writeTuple PUTs the tuple as JSON to the write API (idempotent; non-2xx throws)", async () => {
|
||||
const tuple = { namespace: "Role", object: "admin", relation: "members", subject_id: USER };
|
||||
const tuple = { namespace: "Permission", object: "admin", relation: "granted", subject_id: USER };
|
||||
const { calls, fetchImpl } = recorder(() => res(201, tuple));
|
||||
await keto(fetchImpl).writeTuple(tuple);
|
||||
assert.equal(calls[0]!.method, "PUT");
|
||||
@@ -95,12 +95,12 @@ test("writeTuple PUTs the tuple as JSON to the write API (idempotent; non-2xx th
|
||||
|
||||
test("deleteTuple DELETEs the write API by query params (204 resolves; non-204 throws)", async () => {
|
||||
const { calls, fetchImpl } = recorder(() => res(204));
|
||||
await keto(fetchImpl).deleteTuple({ namespace: "Role", object: "admin", relation: "members", subject_id: USER });
|
||||
await keto(fetchImpl).deleteTuple({ namespace: "Permission", object: "admin", relation: "granted", subject_id: USER });
|
||||
assert.equal(calls[0]!.method, "DELETE");
|
||||
assert.match(calls[0]!.url, /^http:\/\/keto:4467\/admin\/relation-tuples\?/);
|
||||
assert.match(calls[0]!.url, /namespace=Role&object=admin&relation=members/);
|
||||
assert.match(calls[0]!.url, /namespace=Permission&object=admin&relation=granted/);
|
||||
await assert.rejects(
|
||||
keto((async () => res(404)) as typeof fetch).deleteTuple({ namespace: "Role", object: "x", relation: "members", subject_id: USER }),
|
||||
keto((async () => res(404)) as typeof fetch).deleteTuple({ namespace: "Permission", object: "x", relation: "granted", subject_id: USER }),
|
||||
(e: unknown) => e instanceof KetoError && e.status === 404,
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user