From a01b7bc99e63186d46ab3736823e3b6c35e34000 Mon Sep 17 00:00:00 2001 From: Ajay Ghanwat Date: Tue, 4 Aug 2026 16:43:08 +0530 Subject: [PATCH] fix(authz): cover grant/deny scope isolation and revoke scope-isolation in conformance suite --- packages/authz/test/store-conformance.ts | 42 ++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/packages/authz/test/store-conformance.ts b/packages/authz/test/store-conformance.ts index 1a923695..f0660950 100644 --- a/packages/authz/test/store-conformance.ts +++ b/packages/authz/test/store-conformance.ts @@ -85,6 +85,43 @@ export function runStoreConformance(name: string, makeStore: () => Promise { + await store.grant("u1", "post:write", "allow", { tenantId: "t1" }); + expect((await store.assignmentsFor("u1", { tenantId: "t1" })).grants).toEqual(["post:write"]); + expect((await store.assignmentsFor("u1", { tenantId: "t2" })).grants).toEqual([]); + }); + + test("a tenant-scoped deny does not leak into another tenant", async () => { + await store.grant("u1", "post:delete", "deny", { tenantId: "t1" }); + expect((await store.assignmentsFor("u1", { tenantId: "t1" })).denies).toEqual([ + "post:delete", + ]); + expect((await store.assignmentsFor("u1", { tenantId: "t2" })).denies).toEqual([]); + }); + + test("a global grant is visible inside every tenant", async () => { + await store.grant("u1", "post:publish", "allow"); + expect((await store.assignmentsFor("u1", { tenantId: "t1" })).grants).toEqual([ + "post:publish", + ]); + }); + + test("revokeGrant is scope-isolated: revoking a tenant-scoped grant leaves the global grant intact", async () => { + await store.grant("u1", "post:write", "allow"); + await store.grant("u1", "post:write", "allow", { tenantId: "t1" }); + await store.revokeGrant("u1", "post:write", { tenantId: "t1" }); + expect((await store.assignmentsFor("u1")).grants).toEqual(["post:write"]); + expect((await store.assignmentsFor("u1", { tenantId: "t1" })).grants).toEqual(["post:write"]); + }); + + test("revokeRole is scope-isolated: revoking a tenant-scoped role leaves the global role intact", async () => { + await store.assignRole("u1", "editor"); + await store.assignRole("u1", "editor", { tenantId: "t1" }); + await store.revokeRole("u1", "editor", { tenantId: "t1" }); + expect((await store.assignmentsFor("u1")).roles).toEqual(["editor"]); + expect((await store.assignmentsFor("u1", { tenantId: "t1" })).roles).toEqual(["editor"]); + }); + test("listSubjects returns everyone with an assignment in scope", async () => { await store.assignRole("u1", "editor", { tenantId: "t1" }); await store.assignRole("u2", "editor", { tenantId: "t1" }); @@ -97,5 +134,10 @@ export function runStoreConformance(name: string, makeStore: () => Promise { + await store.grant("g1", "post:write", "allow", { tenantId: "t1" }); + expect((await store.listSubjects({ tenantId: "t1" })).sort()).toEqual(["g1"]); + }); }); }