From 8bc764a35ebdeebc1c930cbb1494f9ec8b050007 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hartmut=20N=C3=B6renberg?= Date: Tue, 31 Mar 2026 22:54:33 +0200 Subject: [PATCH] fix(api): harden optional audit and session fields --- .../role-router-planning-counts.test.ts | 43 +++++++++++++++++++ .../system-role-config-procedure-support.ts | 2 +- .../user-self-service-procedure-support.ts | 2 +- .../utilization-category-procedure-support.ts | 4 +- 4 files changed, 47 insertions(+), 4 deletions(-) diff --git a/packages/api/src/__tests__/role-router-planning-counts.test.ts b/packages/api/src/__tests__/role-router-planning-counts.test.ts index 778eeb3..3d47590 100644 --- a/packages/api/src/__tests__/role-router-planning-counts.test.ts +++ b/packages/api/src/__tests__/role-router-planning-counts.test.ts @@ -1,6 +1,49 @@ import { AllocationStatus, PermissionKey, SystemRole } from "@capakraken/shared"; import { TRPCError } from "@trpc/server"; import { describe, expect, it, vi } from "vitest"; + +vi.mock("@capakraken/application", () => ({ + countPlanningEntries: vi.fn(async ( + db: { + demandRequirement: { findMany: (args?: { where?: { projectId?: { in: string[] }; roleId?: { in: string[] } } }) => Promise> }; + assignment: { findMany: (args?: { where?: { projectId?: { in: string[] }; roleId?: { in: string[] } } }) => Promise> }; + }, + input: { projectIds?: string[]; roleIds?: string[] } = {}, + ) => { + const where = { + ...(input.projectIds?.length ? { projectId: { in: input.projectIds } } : {}), + ...(input.roleIds?.length ? { roleId: { in: input.roleIds } } : {}), + }; + const [demandRequirements, assignments] = await Promise.all([ + db.demandRequirement.findMany({ where }), + db.assignment.findMany({ where }), + ]); + + const countsByProjectId = new Map(); + const countsByRoleId = new Map(); + + for (const entry of [...demandRequirements, ...assignments]) { + countsByProjectId.set( + entry.projectId, + (countsByProjectId.get(entry.projectId) ?? 0) + 1, + ); + + if (entry.roleId) { + countsByRoleId.set( + entry.roleId, + (countsByRoleId.get(entry.roleId) ?? 0) + 1, + ); + } + } + + return { + countsByProjectId, + countsByRoleId, + totalCount: demandRequirements.length + assignments.length, + }; + }), +})); + import { roleRouter } from "../router/role.js"; import { createCallerFactory } from "../trpc.js"; diff --git a/packages/api/src/router/system-role-config-procedure-support.ts b/packages/api/src/router/system-role-config-procedure-support.ts index dc6d55c..7fcf298 100644 --- a/packages/api/src/router/system-role-config-procedure-support.ts +++ b/packages/api/src/router/system-role-config-procedure-support.ts @@ -40,7 +40,7 @@ export async function updateSystemRoleConfig( entityId: input.role, entityName: result.label, action: "UPDATE", - userId: ctx.dbUser?.id, + ...(ctx.dbUser?.id ? { userId: ctx.dbUser.id } : {}), before: (existing ?? {}) as unknown as Record, after: result as unknown as Record, source: "ui", diff --git a/packages/api/src/router/user-self-service-procedure-support.ts b/packages/api/src/router/user-self-service-procedure-support.ts index df5be97..b6ec3fd 100644 --- a/packages/api/src/router/user-self-service-procedure-support.ts +++ b/packages/api/src/router/user-self-service-procedure-support.ts @@ -157,7 +157,7 @@ export async function generateTotpSecret(ctx: UserSelfServiceContext) { const secret = new Secret({ size: 20 }); const totp = new TOTP({ issuer: "CapaKraken", - label: ctx.session.user?.email ?? ctx.dbUser!.id, + label: ctx.session?.user?.email ?? ctx.dbUser!.id, algorithm: "SHA1", digits: 6, period: 30, diff --git a/packages/api/src/router/utilization-category-procedure-support.ts b/packages/api/src/router/utilization-category-procedure-support.ts index 3e8ae29..c1700dc 100644 --- a/packages/api/src/router/utilization-category-procedure-support.ts +++ b/packages/api/src/router/utilization-category-procedure-support.ts @@ -74,7 +74,7 @@ export async function createUtilizationCategory( entityId: created.id, entityName: created.name, action: "CREATE", - userId: ctx.dbUser?.id, + ...(ctx.dbUser?.id ? { userId: ctx.dbUser.id } : {}), after: created as unknown as Record, source: "ui", }); @@ -112,7 +112,7 @@ export async function updateUtilizationCategory( entityId: updated.id, entityName: updated.name, action: "UPDATE", - userId: ctx.dbUser?.id, + ...(ctx.dbUser?.id ? { userId: ctx.dbUser.id } : {}), before, after: updated as unknown as Record, source: "ui",