feat: add organization tenant model

This commit is contained in:
2026-07-09 23:37:20 +08:00
parent 5d315fff21
commit 2b7aa3294f
20 changed files with 900 additions and 116 deletions
+7 -7
View File
@@ -1,5 +1,5 @@
import { describe, it, expect, beforeEach, afterAll } from "vitest";
import { prisma, resetDb } from "./helpers.js";
import { DEFAULT_ORG_ID, prisma, resetDb } from "./helpers.js";
import { canTriggerRole } from "../../src/permission.js";
describe("canTriggerRole (integration, per-role gate)", () => {
@@ -13,7 +13,7 @@ describe("canTriggerRole (integration, per-role gate)", () => {
it("allows when role is unconfigured on the project (back-compat: open)", async () => {
const project = await prisma.project.create({
data: { id: "p-role-1", name: "T", workspaceDir: "/tmp/x" },
data: { id: "p-role-1", organizationId: DEFAULT_ORG_ID, name: "T", workspaceDir: "/tmp/x" },
});
const r = await canTriggerRole(prisma, project.id, "draft", "ou_a");
expect(r.allowed).toBe(true);
@@ -21,7 +21,7 @@ describe("canTriggerRole (integration, per-role gate)", () => {
it("allows when principal holds an active grant for the role", async () => {
const project = await prisma.project.create({
data: { id: "p-role-2", name: "T", workspaceDir: "/tmp/x" },
data: { id: "p-role-2", organizationId: DEFAULT_ORG_ID, name: "T", workspaceDir: "/tmp/x" },
});
await prisma.roleTriggerGrant.create({
data: { projectId: project.id, roleId: "review", principalType: "USER", principalId: "ou_a" },
@@ -32,7 +32,7 @@ describe("canTriggerRole (integration, per-role gate)", () => {
it("denies when grants exist for the role but principal has none", async () => {
const project = await prisma.project.create({
data: { id: "p-role-3", name: "T", workspaceDir: "/tmp/x" },
data: { id: "p-role-3", organizationId: DEFAULT_ORG_ID, name: "T", workspaceDir: "/tmp/x" },
});
// Someone else has the role; ou_b does not.
await prisma.roleTriggerGrant.create({
@@ -44,7 +44,7 @@ describe("canTriggerRole (integration, per-role gate)", () => {
it("denies when the principal's grant was revoked", async () => {
const project = await prisma.project.create({
data: { id: "p-role-4", name: "T", workspaceDir: "/tmp/x" },
data: { id: "p-role-4", organizationId: DEFAULT_ORG_ID, name: "T", workspaceDir: "/tmp/x" },
});
await prisma.roleTriggerGrant.create({
data: { projectId: project.id, roleId: "review", principalType: "USER", principalId: "ou_a", revokedAt: new Date() },
@@ -54,8 +54,8 @@ describe("canTriggerRole (integration, per-role gate)", () => {
});
it("scopes grants per-project (grant on p1 does not allow on p2)", async () => {
const p1 = await prisma.project.create({ data: { id: "p-role-5a", name: "T", workspaceDir: "/tmp/x" } });
const p2 = await prisma.project.create({ data: { id: "p-role-5b", name: "T", workspaceDir: "/tmp/x" } });
const p1 = await prisma.project.create({ data: { id: "p-role-5a", organizationId: DEFAULT_ORG_ID, name: "T", workspaceDir: "/tmp/x" } });
const p2 = await prisma.project.create({ data: { id: "p-role-5b", organizationId: DEFAULT_ORG_ID, name: "T", workspaceDir: "/tmp/x" } });
await prisma.roleTriggerGrant.create({
data: { projectId: p1.id, roleId: "review", principalType: "USER", principalId: "ou_a" },
});