feat(auth): tighten allocation read audiences
This commit is contained in:
@@ -62,6 +62,123 @@ function createManagerCaller(db: Record<string, unknown>) {
|
||||
});
|
||||
}
|
||||
|
||||
function createControllerCaller(db: Record<string, unknown>) {
|
||||
return createCaller({
|
||||
session: {
|
||||
user: { email: "controller@example.com", name: "Controller", image: null },
|
||||
expires: "2026-03-13T00:00:00.000Z",
|
||||
},
|
||||
db: db as never,
|
||||
dbUser: {
|
||||
id: "user_controller",
|
||||
systemRole: SystemRole.CONTROLLER,
|
||||
permissionOverrides: null,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function createProtectedCaller(db: Record<string, unknown>) {
|
||||
return createCaller({
|
||||
session: {
|
||||
user: { email: "user@example.com", name: "User", image: null },
|
||||
expires: "2026-03-13T00:00:00.000Z",
|
||||
},
|
||||
db: db as never,
|
||||
dbUser: {
|
||||
id: "user_1",
|
||||
systemRole: SystemRole.USER,
|
||||
permissionOverrides: null,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
describe("allocation router authorization", () => {
|
||||
const planningWindow = {
|
||||
resourceId: "resource_1",
|
||||
startDate: new Date("2026-04-01T00:00:00.000Z"),
|
||||
endDate: new Date("2026-04-02T00:00:00.000Z"),
|
||||
hoursPerDay: 8,
|
||||
};
|
||||
|
||||
it("allows controllers to read assignment lists through the planning audience", async () => {
|
||||
const assignment = {
|
||||
id: "assignment_1",
|
||||
demandRequirementId: null,
|
||||
resourceId: "resource_1",
|
||||
projectId: "project_1",
|
||||
startDate: new Date("2026-04-01T00:00:00.000Z"),
|
||||
endDate: new Date("2026-04-02T00:00:00.000Z"),
|
||||
hoursPerDay: 8,
|
||||
percentage: 100,
|
||||
role: "Compositor",
|
||||
roleId: "role_comp",
|
||||
dailyCostCents: 40000,
|
||||
status: AllocationStatus.ACTIVE,
|
||||
metadata: {},
|
||||
resource: null,
|
||||
project: { id: "project_1", name: "Project One", shortCode: "PRJ" },
|
||||
roleEntity: null,
|
||||
demandRequirement: null,
|
||||
};
|
||||
const db = {
|
||||
assignment: {
|
||||
findMany: vi.fn().mockResolvedValue([assignment]),
|
||||
},
|
||||
systemSettings: {
|
||||
findUnique: vi.fn().mockResolvedValue(null),
|
||||
},
|
||||
};
|
||||
|
||||
const caller = createControllerCaller(db);
|
||||
const result = await caller.listAssignments({});
|
||||
|
||||
expect(result).toEqual([assignment]);
|
||||
expect(db.assignment.findMany).toHaveBeenCalledWith({
|
||||
where: {},
|
||||
include: expect.any(Object),
|
||||
orderBy: { startDate: "asc" },
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ name: "list", invoke: (caller: ReturnType<typeof createProtectedCaller>) => caller.list({}) },
|
||||
{ name: "listView", invoke: (caller: ReturnType<typeof createProtectedCaller>) => caller.listView({}) },
|
||||
{ name: "listDemands", invoke: (caller: ReturnType<typeof createProtectedCaller>) => caller.listDemands({}) },
|
||||
{ name: "listAssignments", invoke: (caller: ReturnType<typeof createProtectedCaller>) => caller.listAssignments({}) },
|
||||
{
|
||||
name: "getAssignmentById",
|
||||
invoke: (caller: ReturnType<typeof createProtectedCaller>) => caller.getAssignmentById({ id: "assignment_1" }),
|
||||
},
|
||||
{
|
||||
name: "resolveAssignment",
|
||||
invoke: (caller: ReturnType<typeof createProtectedCaller>) => caller.resolveAssignment({ assignmentId: "assignment_1" }),
|
||||
},
|
||||
{
|
||||
name: "getDemandRequirementById",
|
||||
invoke: (caller: ReturnType<typeof createProtectedCaller>) => caller.getDemandRequirementById({ id: "demand_1" }),
|
||||
},
|
||||
{
|
||||
name: "checkResourceAvailability",
|
||||
invoke: (caller: ReturnType<typeof createProtectedCaller>) => caller.checkResourceAvailability(planningWindow),
|
||||
},
|
||||
{
|
||||
name: "getResourceAvailabilityView",
|
||||
invoke: (caller: ReturnType<typeof createProtectedCaller>) => caller.getResourceAvailabilityView(planningWindow),
|
||||
},
|
||||
{
|
||||
name: "getResourceAvailabilitySummary",
|
||||
invoke: (caller: ReturnType<typeof createProtectedCaller>) => caller.getResourceAvailabilitySummary(planningWindow),
|
||||
},
|
||||
])("requires planning read access for $name", async ({ invoke }) => {
|
||||
const caller = createProtectedCaller({});
|
||||
|
||||
await expect(invoke(caller)).rejects.toMatchObject({
|
||||
code: "FORBIDDEN",
|
||||
message: "Planning read access required",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function createDemandWorkflowDb(overrides: Record<string, unknown> = {}) {
|
||||
const db = {
|
||||
project: {
|
||||
|
||||
Reference in New Issue
Block a user