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 <noreply@anthropic.com>
This commit is contained in:
2026-04-09 19:18:39 +02:00
parent 070be70848
commit d737a251b2
@@ -42,21 +42,11 @@ const BatchCreatePublicHolidaysSchema = z.object({
replaceExisting: z.boolean().default(false), replaceExisting: z.boolean().default(false),
}); });
async function findVacationActor(
db: Parameters<Parameters<typeof protectedProcedure["query"]>[0]>[0]["ctx"]["db"],
email: string | null | undefined,
) {
return db.user.findUnique({
where: { email: email ?? "" },
select: { id: true, systemRole: true },
});
}
export const vacationManagementProcedures = { export const vacationManagementProcedures = {
approve: managerProcedure approve: managerProcedure
.input(z.object({ id: z.string() })) .input(z.object({ id: z.string() }))
.mutation(async ({ ctx, input }) => { .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 audit = makeAuditLogger(ctx.db, userRecord?.id);
const result = await approveVacation( const result = await approveVacation(
@@ -117,7 +107,7 @@ export const vacationManagementProcedures = {
emitVacationUpdated({ id: updated.id, resourceId: updated.resourceId, status: updated.status }); 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); const audit = makeAuditLogger(ctx.db, userRecord?.id);
await completeVacationApprovalTasks(ctx.db, input.id, userRecord?.id); await completeVacationApprovalTasks(ctx.db, input.id, userRecord?.id);
@@ -144,7 +134,7 @@ export const vacationManagementProcedures = {
batchApprove: managerProcedure batchApprove: managerProcedure
.input(z.object({ ids: z.array(z.string()).min(1).max(100) })) .input(z.object({ ids: z.array(z.string()).min(1).max(100) }))
.mutation(async ({ ctx, input }) => { .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 audit = makeAuditLogger(ctx.db, userRecord?.id);
const result = await batchApproveVacations( const result = await batchApproveVacations(
@@ -187,7 +177,7 @@ export const vacationManagementProcedures = {
}), }),
) )
.mutation(async ({ ctx, input }) => { .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 audit = makeAuditLogger(ctx.db, userRecord?.id);
const result = await batchRejectVacations( const result = await batchRejectVacations(
@@ -224,7 +214,7 @@ export const vacationManagementProcedures = {
cancel: protectedProcedure cancel: protectedProcedure
.input(z.object({ id: z.string() })) .input(z.object({ id: z.string() }))
.mutation(async ({ ctx, input }) => { .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 audit = makeAuditLogger(ctx.db, userRecord?.id);
if (!userRecord) { if (!userRecord) {
throw new TRPCError({ code: "UNAUTHORIZED" }); throw new TRPCError({ code: "UNAUTHORIZED" });
@@ -274,10 +264,7 @@ export const vacationManagementProcedures = {
batchCreatePublicHolidays: adminProcedure batchCreatePublicHolidays: adminProcedure
.input(BatchCreatePublicHolidaysSchema) .input(BatchCreatePublicHolidaysSchema)
.mutation(async ({ ctx, input }) => { .mutation(async ({ ctx, input }) => {
const adminUser = await ctx.db.user.findUnique({ const adminUser = ctx.dbUser;
where: { email: ctx.session.user?.email ?? "" },
select: { id: true },
});
if (!adminUser) { if (!adminUser) {
throw new TRPCError({ code: "UNAUTHORIZED" }); throw new TRPCError({ code: "UNAUTHORIZED" });
} }
@@ -309,7 +296,7 @@ export const vacationManagementProcedures = {
throw new TRPCError({ code: "NOT_FOUND", message: "Vacation not found" }); 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); const audit = makeAuditLogger(ctx.db, userRecord?.id);
if (!userRecord) { if (!userRecord) {
throw new TRPCError({ code: "UNAUTHORIZED" }); throw new TRPCError({ code: "UNAUTHORIZED" });