1import { schema as s } from "jazz-tools";2import { createPolicyTestApp } from "jazz-tools/testing";3import {4 defineAccessPolicies,5 groupAccess,6 groupMembershipTable,7 groupRoleCapabilities,8 privateAccess,9 sharedAccess,10 sharedGrantTable,11} from "./mod.ts";12import { assert } from "../runtime/test-assert.ts";1314const schema = {15 privateDocs: s.table({ title: s.string() }),16 sharedDocs: s.table({ title: s.string() }),17 sharedDocGrants: sharedGrantTable("sharedDocs"),18 workspaces: s.table({ name: s.string() }),19 workspaceMembers: groupMembershipTable("workspaces"),20 groupDocs: s.table({ workspaceId: s.ref("workspaces"), title: s.string() }),21};22const app = s.defineApp(schema);23const permissions = defineAccessPolicies(app, [24 privateAccess({ resource: app.privateDocs }),25 sharedAccess({ resource: app.sharedDocs, grants: app.sharedDocGrants }),26 groupAccess({27 groups: app.workspaces,28 members: app.workspaceMembers,29 resources: app.groupDocs,30 groupId: "workspaceId",31 }),32]);3334type ThrowExpectation = {35 toThrow(expected?: unknown): void;36 not: { toThrow(expected?: unknown): void };37};3839function expectLike(value: unknown): ThrowExpectation {40 const run = (): unknown => {41 if (typeof value !== "function") throw new Error("expectLike requires a callback");42 try {43 value();44 return undefined;45 } catch (error) {46 return error;47 }48 };49 const matches = (error: unknown, expected: unknown): boolean => {50 if (expected === undefined) return error !== undefined;51 const message = error instanceof Error ? error.message : String(error);52 return typeof expected === "string"53 ? message.includes(expected)54 : error instanceof (expected as typeof Error);55 };56 return {57 toThrow(expected?: unknown) {58 const error = run();59 if (!matches(error, expected)) {60 throw new Error(`expected callback to throw ${String(expected)}`);61 }62 },63 not: {64 toThrow(expected?: unknown) {65 const error = run();66 if (error !== undefined && matches(error, expected)) {67 throw new Error(`expected callback not to throw ${String(expected)}`, { cause: error });68 }69 },70 },71 };72}7374const session = (user_id: string) => ({ user_id, claims: {}, authMode: "local-first" as const });7576Deno.test("official Jazz harness enforces private and direct-share templates", async () => {77 const testApp = await createPolicyTestApp(app, permissions, expectLike);78 try {79 const owner = testApp.as(session("owner"));80 const reader = testApp.as(session("reader"));81 const editor = testApp.as(session("editor"));82 const stranger = testApp.as(session("stranger"));8384 const privateDoc = await owner.insert(app.privateDocs, { title: "private" }).wait({85 tier: "global",86 });87 assert(88 (await owner.all(app.privateDocs.where({ id: privateDoc.id }))).length === 1,89 "owner lost private row",90 );91 assert(92 (await stranger.all(app.privateDocs.where({ id: privateDoc.id }))).length === 0,93 "unrelated user read private row",94 );95 stranger.expectDenied((db) => db.update(app.privateDocs, privateDoc.id, { title: "denied" }));9697 const sharedDoc = await owner.insert(app.sharedDocs, { title: "shared" }).wait({98 tier: "global",99 });100 const readGrant = await owner.insert(app.sharedDocGrants, {101 resourceId: sharedDoc.id,102 user_id: "reader",103 can_edit: false,104 }).wait({ tier: "global" });105 await owner.insert(app.sharedDocGrants, {106 resourceId: sharedDoc.id,107 user_id: "editor",108 can_edit: true,109 }).wait({ tier: "global" });110 let ownerRows: Array<s.RowOf<typeof app.sharedDocs>> = [];111 let readerRows: Array<s.RowOf<typeof app.sharedDocs>> = [];112 let editorRows: Array<s.RowOf<typeof app.sharedDocs>> = [];113 const liveSharedDoc = app.sharedDocs.where({ id: sharedDoc.id });114 const stopOwner = owner.subscribeAll(liveSharedDoc, (delta) => ownerRows = delta.all);115 const stopReader = reader.subscribeAll(liveSharedDoc, (delta) => readerRows = delta.all);116 const stopEditor = editor.subscribeAll(liveSharedDoc, (delta) => editorRows = delta.all);117118 assert(119 (await reader.all(app.sharedDocs.where({ id: sharedDoc.id }))).length === 1,120 "reader could not read shared row",121 );122 assert(123 (await editor.all(app.sharedDocs.where({ id: sharedDoc.id }))).length === 1,124 "editor could not read shared row",125 );126 assert(127 (await stranger.all(app.sharedDocs.where({ id: sharedDoc.id }))).length === 0,128 "stranger read shared row",129 );130 reader.expectDenied((db) => db.update(app.sharedDocs, sharedDoc.id, { title: "denied" }));131 await editor.update(app.sharedDocs, sharedDoc.id, { title: "allowed" }).wait({132 tier: "global",133 });134 assert(135 ownerRows[0]?.title === "allowed" && editorRows[0]?.title === "allowed",136 "authorized live queries did not observe the shared update",137 );138 stranger.expectDenied((db) => db.delete(app.sharedDocs, sharedDoc.id));139140 await owner.delete(app.sharedDocGrants, readGrant.id).wait({ tier: "global" });141 assert(readerRows.length === 0, "revocation did not remove the row from the active query");142 assert(143 (await reader.all(app.sharedDocs.where({ id: sharedDoc.id }))).length === 0,144 "revoked reader retained access",145 );146 assert(147 ownerRows.length === 1 && editorRows.length === 1,148 "revocation affected other identities",149 );150 stopOwner();151 stopReader();152 stopEditor();153 } finally {154 await testApp.shutdown();155 }156});157158Deno.test("official Jazz harness enforces fixed group roles and membership lifecycle", async () => {159 const testApp = await createPolicyTestApp(app, permissions, expectLike);160 try {161 const admin = testApp.as(session("admin"));162 const reader = testApp.as(session("reader"));163 const contributor = testApp.as(session("contributor"));164 const writer = testApp.as(session("writer"));165 const stranger = testApp.as(session("stranger"));166167 const group = await admin.insert(app.workspaces, {168 name: "workspace",169 }).wait({170 tier: "global",171 });172 assert(173 (await admin.all(app.workspaces.where({ id: group.id }))).length === 1,174 "group creator could not read the created group",175 );176 const initialAdmin = await admin.insert(app.workspaceMembers, {177 groupId: group.id,178 user_id: "admin",179 ...groupRoleCapabilities("admin"),180 }).wait({ tier: "global" });181 assert(initialAdmin.role === "admin", "creator did not bootstrap as first admin");182 const groupId = group.id;183 const memberships = await Promise.all([184 admin.insert(app.workspaceMembers, {185 groupId,186 user_id: "reader",187 ...groupRoleCapabilities("reader"),188 }).wait({189 tier: "global",190 }),191 admin.insert(app.workspaceMembers, {192 groupId,193 user_id: "contributor",194 ...groupRoleCapabilities("contributor"),195 })196 .wait(197 { tier: "global" },198 ),199 admin.insert(app.workspaceMembers, {200 groupId,201 user_id: "writer",202 ...groupRoleCapabilities("writer"),203 }).wait({204 tier: "global",205 }),206 ]);207 assert(208 (await admin.all(app.workspaceMembers.where({ groupId, user_id: "admin", role: "admin" })))209 .length === 1,210 "admin membership was not visible to the admin",211 );212 const adminDoc = await admin.insert(app.groupDocs, { workspaceId: groupId, title: "admin row" })213 .wait({214 tier: "global",215 });216 let adminRows: Array<s.RowOf<typeof app.groupDocs>> = [];217 let readerRows: Array<s.RowOf<typeof app.groupDocs>> = [];218 let writerRows: Array<s.RowOf<typeof app.groupDocs>> = [];219 const liveGroupDocs = app.groupDocs.where({ workspaceId: groupId });220 const stopAdmin = admin.subscribeAll(liveGroupDocs, (delta) => adminRows = delta.all);221 const stopReader = reader.subscribeAll(liveGroupDocs, (delta) => readerRows = delta.all);222 const stopWriter = writer.subscribeAll(liveGroupDocs, (delta) => writerRows = delta.all);223 const liveDoc = await contributor.insert(app.groupDocs, {224 workspaceId: groupId,225 title: "live insert",226 }).wait({ tier: "global" });227 assert(228 [adminRows, readerRows, writerRows].every((rows) =>229 rows.some((row) => row.id === liveDoc.id)230 ),231 "authorized live queries did not observe the group insert",232 );233 await writer.update(app.groupDocs, liveDoc.id, { title: "live update" }).wait({234 tier: "global",235 });236 assert(237 [adminRows, readerRows, writerRows].every((rows) =>238 rows.find((row) => row.id === liveDoc.id)?.title === "live update"239 ),240 "authorized live queries did not observe the group update",241 );242 await admin.delete(app.groupDocs, liveDoc.id).wait({ tier: "global" });243 assert(244 [adminRows, readerRows, writerRows].every((rows) =>245 rows.every((row) => row.id !== liveDoc.id)246 ),247 "authorized live queries did not observe the group delete",248 );249250 reader.expectDenied((db) =>251 db.insert(app.groupDocs, { workspaceId: groupId, title: "reader cannot create" })252 );253 contributor.expectAllowed((db) =>254 db.insert(app.groupDocs, { workspaceId: groupId, title: "contributor own" })255 );256 contributor.expectDenied((db) => db.update(app.groupDocs, adminDoc.id, { title: "not own" }));257 writer.expectAllowed((db) => db.update(app.groupDocs, adminDoc.id, { title: "writer" }));258 admin.expectAllowed((db) => db.update(app.groupDocs, adminDoc.id, { title: "admin" }));259 stranger.expectDenied((db) =>260 db.insert(app.groupDocs, { workspaceId: groupId, title: "non-member" })261 );262 assert(263 (await stranger.all(app.groupDocs.where({ workspaceId: groupId }))).length === 0,264 "non-member read group rows",265 );266267 admin.expectAllowed((db) =>268 db.update(app.workspaceMembers, memberships[0].id, groupRoleCapabilities("writer"))269 );270 reader.expectDenied((db) =>271 db.update(app.workspaceMembers, memberships[0].id, groupRoleCapabilities("admin"))272 );273 admin.expectAllowed((db) => db.delete(app.workspaceMembers, memberships[1].id));274 writer.expectAllowed((db) => db.delete(app.workspaceMembers, memberships[2].id));275 stranger.expectDenied((db) => db.delete(app.workspaceMembers, memberships[2].id));276 stopAdmin();277 stopReader();278 stopWriter();279 } finally {280 await testApp.shutdown();281 }282});283284Deno.test("group-creator authority is permanent, exclusive, and survives demotion", async () => {285 const testApp = await createPolicyTestApp(app, permissions, expectLike);286 try {287 const creator = testApp.as(session("creator"));288 const successor = testApp.as(session("successor"));289 const stranger = testApp.as(session("stranger"));290291 const group = await creator.insert(app.workspaces, { name: "handover" }).wait({292 tier: "global",293 });294 // Creator authority never extends to anyone else.295 stranger.expectDenied((db) => db.update(app.workspaces, group.id, { name: "hijacked" }));296 stranger.expectDenied((db) =>297 db.insert(app.workspaceMembers, {298 groupId: group.id,299 user_id: "stranger",300 ...groupRoleCapabilities("admin"),301 })302 );303 stranger.expectDenied((db) => db.delete(app.workspaces, group.id));304305 const creatorMembership = await creator.insert(app.workspaceMembers, {306 groupId: group.id,307 user_id: "creator",308 ...groupRoleCapabilities("admin"),309 }).wait({ tier: "global" });310 await creator.insert(app.workspaceMembers, {311 groupId: group.id,312 user_id: "successor",313 ...groupRoleCapabilities("admin"),314 }).wait({ tier: "global" });315316 // The documented trust property of the template on the pinned Jazz317 // alpha.53 engine: demoting or removing the creator does NOT end their318 // authority — they retain group update and, through it, membership319 // management, so they can always restore their own admin membership.320 // The engine cannot express "creator authority only while no admin321 // exists" (see the negation canary below); when that becomes possible,322 // this contract is expected to change to a bootstrap-only window.323 successor.expectAllowed((db) =>324 db.update(app.workspaceMembers, creatorMembership.id, groupRoleCapabilities("reader"))325 );326 creator.expectAllowed((db) =>327 db.update(app.workspaceMembers, creatorMembership.id, groupRoleCapabilities("admin"))328 );329 successor.expectAllowed((db) => db.delete(app.workspaceMembers, creatorMembership.id));330 creator.expectAllowed((db) =>331 db.insert(app.workspaceMembers, {332 groupId: group.id,333 user_id: "creator",334 ...groupRoleCapabilities("admin"),335 })336 );337 } finally {338 await testApp.shutdown();339 }340});341342Deno.test("engine canary: pinned alpha.53 silently drops Not around existence conditions", async () => {343 // Guard rule that SHOULD deny deletion while an admin membership exists:344 // Not(ExistsRel(members WHERE groupId = <this group> AND can_manage)).345 // The pinned engine ignores the negation, so the delete is ALLOWED below.346 // WHEN THIS TEST FAILS after a jazz-tools upgrade: negation started347 // working — replace permanent group-creator authority with the348 // bootstrap-only window design in349 // docs/decisions/group-creator-authority-alpha53.md and update the350 // permanent-authority contract test above.351 const canaryPermissions = s.definePermissions(app, (rawContext) => {352 const { policy } = rawContext as unknown as {353 policy: Record<354 string,355 {356 allowRead: { always(): unknown };357 allowInsert: { always(): unknown };358 allowDelete: { where(input: unknown): unknown };359 }360 >;361 };362 policy.workspaces.allowRead.always();363 policy.workspaces.allowInsert.always();364 policy.workspaceMembers.allowRead.always();365 policy.workspaceMembers.allowInsert.always();366 policy.workspaces.allowDelete.where({367 type: "Not",368 expr: {369 type: "ExistsRel",370 rel: {371 Filter: {372 input: { TableScan: { table: "workspaceMembers" } },373 predicate: {374 And: [375 {376 Cmp: {377 left: { column: "groupId" },378 op: "Eq",379 right: { OuterColumn: { column: "id" } },380 },381 },382 {383 Cmp: {384 left: { column: "can_manage" },385 op: "Eq",386 right: { Literal: true },387 },388 },389 ],390 },391 },392 },393 },394 });395 });396 const testApp = await createPolicyTestApp(app, canaryPermissions, expectLike);397 try {398 const creator = testApp.as(session("creator"));399 const group = await creator.insert(app.workspaces, { name: "canary" }).wait({400 tier: "global",401 });402 await creator.insert(app.workspaceMembers, {403 groupId: group.id,404 user_id: "creator",405 ...groupRoleCapabilities("admin"),406 }).wait({ tier: "global" });407 // A working Not would DENY this delete (an admin membership exists).408 creator.expectAllowed((db) => db.delete(app.workspaces, group.id));409 } finally {410 await testApp.shutdown();411 }412});413414Deno.test("a group without its first admin remains deletable by its creator only", async () => {415 const testApp = await createPolicyTestApp(app, permissions, expectLike);416 try {417 const creator = testApp.as(session("creator"));418 const stranger = testApp.as(session("stranger"));419 // A group whose first-admin insert failed: the row exists, no membership.420 const orphan = await creator.insert(app.workspaces, { name: "orphan" }).wait({421 tier: "global",422 });423 stranger.expectDenied((db) => db.delete(app.workspaces, orphan.id));424 await creator.delete(app.workspaces, orphan.id).wait({ tier: "global" });425 assert(426 (await creator.all(app.workspaces.where({ id: orphan.id }))).length === 0,427 "the creator's rollback delete did not remove the orphaned group",428 );429 } finally {430 await testApp.shutdown();431 }432});433434Deno.test("access templates reject malformed relationship tables with actionable errors", () => {435 const malformed = s.defineApp({436 docs: s.table({ title: s.string() }),437 grants: s.table({ resourceId: s.string(), user_id: s.string(), can_edit: s.boolean() }),438 });439 let message = "";440 try {441 defineAccessPolicies(malformed, [442 sharedAccess({ resource: malformed.docs, grants: malformed.grants }),443 ]);444 } catch (error) {445 message = error instanceof Error ? error.message : String(error);446 }447 assert(448 message.includes("grants") && message.includes("resourceId"),449 "configuration error was not actionable",450 );451});