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),
});
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 = {
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" });