feat(auth): classify planning and resource read audiences

This commit is contained in:
2026-03-30 08:51:07 +02:00
parent f6daf21983
commit db45829eca
7 changed files with 154 additions and 138 deletions
@@ -666,7 +666,7 @@ describe("project router", () => {
},
};
const caller = createProtectedCaller(db);
const caller = createControllerCaller(db);
const result = await caller.searchSummaries({ search: "Gelddruckmaschine", limit: 10 });
expect(result).toEqual([
@@ -735,6 +735,19 @@ describe("project router", () => {
).rejects.toThrow(expect.objectContaining({ code: "FORBIDDEN" }));
});
it("blocks USER role from lightweight project search summaries", async () => {
const db = {
project: {
findMany: vi.fn(),
},
};
const caller = createProtectedCaller(db);
await expect(
caller.searchSummaries({ search: "Gelddruckmaschine", limit: 10 }),
).rejects.toThrow(expect.objectContaining({ code: "FORBIDDEN" }));
});
it("returns lightweight project identifier reads from the canonical router", async () => {
const db = {
project: {
@@ -750,7 +763,7 @@ describe("project router", () => {
},
};
const caller = createProtectedCaller(db);
const caller = createControllerCaller(db);
const result = await caller.getByIdentifier({ identifier: "GDM" });
expect(result).toEqual({
@@ -854,5 +867,19 @@ describe("project router", () => {
caller.getByIdentifierDetail({ identifier: "GDM" }),
).rejects.toThrow(expect.objectContaining({ code: "FORBIDDEN" }));
});
it("blocks USER role from lightweight project identifier reads", async () => {
const db = {
project: {
findUnique: vi.fn(),
findFirst: vi.fn(),
},
};
const caller = createProtectedCaller(db);
await expect(
caller.getByIdentifier({ identifier: "GDM" }),
).rejects.toThrow(expect.objectContaining({ code: "FORBIDDEN" }));
});
});
});
@@ -780,7 +780,7 @@ describe("resource router", () => {
caller.listSummaries({ search: "Alice", limit: 10 }),
).rejects.toMatchObject({
code: "FORBIDDEN",
message: "You need resource overview access to search resource summaries",
message: "Resource overview access required",
});
expect(db.resource.findMany).not.toHaveBeenCalled();
});
@@ -980,7 +980,7 @@ describe("resource router", () => {
caller.listStaff({ limit: 10 }),
).rejects.toMatchObject({
code: "FORBIDDEN",
message: "You need resource overview access to list staff resource data",
message: "Resource overview access required",
});
expect(db.resource.findMany).not.toHaveBeenCalled();
expect(db.resource.count).not.toHaveBeenCalled();
@@ -1301,7 +1301,7 @@ describe("resource router", () => {
},
};
const caller = createProtectedCaller(db);
const caller = createProtectedCallerWithOverrides(db, { granted: [PermissionKey.VIEW_ALL_RESOURCES] });
const result = await caller.resolveResponsiblePersonName({ name: "Peter" });
expect(result).toEqual({
@@ -1310,6 +1310,25 @@ describe("resource router", () => {
});
});
it("rejects responsible-person resolution for regular users without resource overview access", async () => {
const db = {
resource: {
findFirst: vi.fn(),
findMany: vi.fn(),
},
};
const caller = createProtectedCaller(db);
await expect(
caller.resolveResponsiblePersonName({ name: "Peter" }),
).rejects.toMatchObject({
code: "FORBIDDEN",
message: "Resource overview access required",
});
expect(db.resource.findFirst).not.toHaveBeenCalled();
expect(db.resource.findMany).not.toHaveBeenCalled();
});
it("applies country filters on the staff list including explicit no-country toggle", async () => {
const db = {
resource: {