From d737a251b21c70057d0a1cdcf5ebb2b850df66a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hartmut=20N=C3=B6renberg?= Date: Thu, 9 Apr 2026 19:18:39 +0200 Subject: [PATCH] fix(api): replace ctx.session.user lookups with ctx.dbUser Procedures were re-fetching the acting user from DB using the session email, which breaks if email changes between session creation and request. ctx.dbUser is populated by protectedProcedure and is always current. Also removed the now-unused findVacationActor helper function. Co-Authored-By: Claude Sonnet 4.6 --- .../router/vacation-management-procedures.ts | 27 +++++-------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/packages/api/src/router/vacation-management-procedures.ts b/packages/api/src/router/vacation-management-procedures.ts index 8e1db35..87b64a8 100644 --- a/packages/api/src/router/vacation-management-procedures.ts +++ b/packages/api/src/router/vacation-management-procedures.ts @@ -42,21 +42,11 @@ const BatchCreatePublicHolidaysSchema = z.object({ replaceExisting: z.boolean().default(false), }); -async function findVacationActor( - db: Parameters[0]>[0]["ctx"]["db"], - email: string | null | undefined, -) { - return db.user.findUnique({ - where: { email: email ?? "" }, - select: { id: true, systemRole: true }, - }); -} - export const vacationManagementProcedures = { approve: managerProcedure .input(z.object({ id: z.string() })) .mutation(async ({ ctx, input }) => { - const userRecord = await findVacationActor(ctx.db, ctx.session.user?.email); + const userRecord = ctx.dbUser; const audit = makeAuditLogger(ctx.db, userRecord?.id); const result = await approveVacation( @@ -117,7 +107,7 @@ export const vacationManagementProcedures = { emitVacationUpdated({ id: updated.id, resourceId: updated.resourceId, status: updated.status }); - const userRecord = await findVacationActor(ctx.db, ctx.session.user?.email); + const userRecord = ctx.dbUser; const audit = makeAuditLogger(ctx.db, userRecord?.id); await completeVacationApprovalTasks(ctx.db, input.id, userRecord?.id); @@ -144,7 +134,7 @@ export const vacationManagementProcedures = { batchApprove: managerProcedure .input(z.object({ ids: z.array(z.string()).min(1).max(100) })) .mutation(async ({ ctx, input }) => { - const userRecord = await findVacationActor(ctx.db, ctx.session.user?.email); + const userRecord = ctx.dbUser; const audit = makeAuditLogger(ctx.db, userRecord?.id); const result = await batchApproveVacations( @@ -187,7 +177,7 @@ export const vacationManagementProcedures = { }), ) .mutation(async ({ ctx, input }) => { - const userRecord = await findVacationActor(ctx.db, ctx.session.user?.email); + const userRecord = ctx.dbUser; const audit = makeAuditLogger(ctx.db, userRecord?.id); const result = await batchRejectVacations( @@ -224,7 +214,7 @@ export const vacationManagementProcedures = { cancel: protectedProcedure .input(z.object({ id: z.string() })) .mutation(async ({ ctx, input }) => { - const userRecord = await findVacationActor(ctx.db, ctx.session.user?.email); + const userRecord = ctx.dbUser; const audit = makeAuditLogger(ctx.db, userRecord?.id); if (!userRecord) { throw new TRPCError({ code: "UNAUTHORIZED" }); @@ -274,10 +264,7 @@ export const vacationManagementProcedures = { batchCreatePublicHolidays: adminProcedure .input(BatchCreatePublicHolidaysSchema) .mutation(async ({ ctx, input }) => { - const adminUser = await ctx.db.user.findUnique({ - where: { email: ctx.session.user?.email ?? "" }, - select: { id: true }, - }); + const adminUser = ctx.dbUser; if (!adminUser) { throw new TRPCError({ code: "UNAUTHORIZED" }); } @@ -309,7 +296,7 @@ export const vacationManagementProcedures = { throw new TRPCError({ code: "NOT_FOUND", message: "Vacation not found" }); } - const userRecord = await findVacationActor(ctx.db, ctx.session.user?.email); + const userRecord = ctx.dbUser; const audit = makeAuditLogger(ctx.db, userRecord?.id); if (!userRecord) { throw new TRPCError({ code: "UNAUTHORIZED" });