FRE-592: Fix 4 code review blockers (security + correctness)
- Add project ownership verification to relationship mutations (createRelationship, updateRelationship, deleteRelationship, getRelationshipsForCharacter) - Add project ownership verification to getCharacter and getScene - Add ownership check to projectProcedure middleware (hasProjectAccess) - Fix searchCharacters filter combination bug (accumulate conditions instead of overwriting) Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import { initTRPC, TRPCError } from '@trpc/server';
|
||||
import { z } from 'zod';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { projects } from '../../src/db/schema';
|
||||
import type { TRPCContext } from './types';
|
||||
|
||||
// Initialize tRPC with context
|
||||
@@ -21,18 +23,36 @@ const hasDb = t.middleware(({ ctx, next }) => {
|
||||
return next({ ctx: { ...ctx, db: ctx.db } });
|
||||
});
|
||||
|
||||
// Middleware for project ownership verification
|
||||
const hasProjectAccess = t.middleware(async ({ ctx, next }) => {
|
||||
if (!ctx.projectId) {
|
||||
throw new TRPCError({ code: 'FORBIDDEN', message: 'Project access required' });
|
||||
}
|
||||
if (!ctx.userId) {
|
||||
throw new TRPCError({ code: 'UNAUTHORIZED', message: 'User not authenticated' });
|
||||
}
|
||||
if (!ctx.db) {
|
||||
throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Database not available' });
|
||||
}
|
||||
const rows = await ctx.db.select({ id: projects.id, ownerId: projects.ownerId })
|
||||
.from(projects)
|
||||
.where(eq(projects.id, ctx.projectId));
|
||||
const project = rows[0];
|
||||
if (!project) {
|
||||
throw new TRPCError({ code: 'NOT_FOUND', message: `Project ${ctx.projectId} not found` });
|
||||
}
|
||||
if (project.ownerId !== ctx.userId) {
|
||||
throw new TRPCError({ code: 'FORBIDDEN', message: `You do not have access to project ${ctx.projectId}` });
|
||||
}
|
||||
return next({ ctx: { ...ctx, projectId: ctx.projectId } });
|
||||
});
|
||||
|
||||
// Base router
|
||||
export const baseRouter = t.router;
|
||||
|
||||
// Procedure builders
|
||||
export const publicProcedure = t.procedure.use(hasDb);
|
||||
export const protectedProcedure = t.procedure.use(isAuthenticated).use(hasDb);
|
||||
const hasProjectAccess = t.middleware(({ ctx, next }) => {
|
||||
if (!ctx.projectId) {
|
||||
throw new TRPCError({ code: 'FORBIDDEN', message: 'Project access required' });
|
||||
}
|
||||
return next({ ctx: { ...ctx, projectId: ctx.projectId } });
|
||||
});
|
||||
|
||||
export const projectProcedure = t.procedure
|
||||
.use(isAuthenticated)
|
||||
|
||||
Reference in New Issue
Block a user