fix(blueprint): require planning access for global field defs

This commit is contained in:
2026-03-30 12:18:59 +02:00
parent 649c8feb22
commit c9a35452dc
3 changed files with 80 additions and 2 deletions
@@ -26,6 +26,14 @@ function createProtectedContext(
};
}
function createUnauthenticatedContext(db: Record<string, unknown>) {
return {
session: null,
db: db as never,
dbUser: null,
};
}
describe("master-data router authorization", () => {
it("requires planning read access for blueprint summaries with project counts", async () => {
const findMany = vi.fn();
@@ -166,6 +174,75 @@ describe("master-data router authorization", () => {
});
});
it("requires authenticated planning access for global blueprint field definitions", async () => {
const findMany = vi.fn();
const unauthenticatedCaller = createCallerFactory(blueprintRouter)(createUnauthenticatedContext({
blueprint: {
findMany,
},
}));
const authenticatedCaller = createCallerFactory(blueprintRouter)(createProtectedContext({
blueprint: {
findMany,
},
}));
await expect(unauthenticatedCaller.getGlobalFieldDefs({ target: BlueprintTarget.PROJECT })).rejects.toMatchObject({
code: "UNAUTHORIZED",
message: "Authentication required",
});
await expect(authenticatedCaller.getGlobalFieldDefs({ target: BlueprintTarget.PROJECT })).rejects.toMatchObject({
code: "FORBIDDEN",
message: "Planning read access required",
});
expect(findMany).not.toHaveBeenCalled();
});
it("allows global blueprint field definitions for users with planning access", async () => {
const findMany = vi.fn().mockResolvedValue([
{
id: "bp_project_global",
name: "Global Project Blueprint",
fieldDefs: [
{ key: "market", label: "Market", type: "text" },
{ key: "deliveryModel", label: "Delivery Model", type: "select" },
],
},
]);
const caller = createCallerFactory(blueprintRouter)(createProtectedContext({
blueprint: {
findMany,
},
}, {
granted: [PermissionKey.VIEW_PLANNING],
}));
const result = await caller.getGlobalFieldDefs({ target: BlueprintTarget.PROJECT });
expect(result).toEqual([
{
key: "market",
label: "Market",
type: "text",
blueprintId: "bp_project_global",
blueprintName: "Global Project Blueprint",
},
{
key: "deliveryModel",
label: "Delivery Model",
type: "select",
blueprintId: "bp_project_global",
blueprintName: "Global Project Blueprint",
},
]);
expect(findMany).toHaveBeenCalledWith({
where: { target: "PROJECT", isGlobal: true, isActive: true },
select: { id: true, name: true, fieldDefs: true },
});
});
it("keeps country lists available to authenticated users as safe lookup data", async () => {
const findMany = vi.fn().mockResolvedValue([
{