From 1493b84787b9e3e873de2b8b633cb8ba53fab263 Mon Sep 17 00:00:00 2001 From: "CTO (LegalAI)" Date: Thu, 9 Apr 2026 14:55:20 +0000 Subject: [PATCH] fix: extract tenantId from session auth instead of request body/headers AI routes now use requirePermission() + ctx.tenantId to get the tenant, ensuring getModelForTenant() is always called with the correct tenant ID so that stored API keys are used. Fixes norms/parse (was falling back to getModel()) and analyses/structured (was trusting x-tenant-id header). Co-Authored-By: Paperclip --- src/app/api/analyses/structured/route.ts | 17 ++++++----------- src/app/api/norms/parse/route.ts | 24 ++++++++++-------------- 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/src/app/api/analyses/structured/route.ts b/src/app/api/analyses/structured/route.ts index f7570c5..c656579 100644 --- a/src/app/api/analyses/structured/route.ts +++ b/src/app/api/analyses/structured/route.ts @@ -4,19 +4,14 @@ import { type NextRequest } from 'next/server'; import { runStructuredAnalysis } from '@/lib/ai/structured-analysis'; import { AnalyseMode } from '@/types'; +import { requirePermission } from '@/lib/auth/rbac'; const VALID_MODES = new Set(Object.values(AnalyseMode)); export async function POST(request: NextRequest) { - const tenantId = request.headers.get('x-tenant-id'); - const userId = request.headers.get('x-user-id'); - - if (!tenantId || !userId) { - return Response.json( - { error: 'Missing x-tenant-id or x-user-id header' }, - { status: 401 }, - ); - } + const auth = await requirePermission('analyses:create'); + if ('response' in auth) return auth.response; + const { ctx } = auth; const body = await request.json(); const { @@ -46,8 +41,8 @@ export async function POST(request: NextRequest) { try { const result = await runStructuredAnalysis({ - tenantId, - userId, + tenantId: ctx.tenantId, + userId: ctx.userId, caseId, mode, title, diff --git a/src/app/api/norms/parse/route.ts b/src/app/api/norms/parse/route.ts index a25f406..0d27915 100644 --- a/src/app/api/norms/parse/route.ts +++ b/src/app/api/norms/parse/route.ts @@ -1,8 +1,8 @@ // POST /api/norms/parse // Accepts raw law text (Fließtext) or a PDF file and uses AI to parse it into structured provisions. // -// JSON body: { text: string, tenantId?: string } -// OR multipart/form-data: file (PDF/TXT), tenantId (optional) +// JSON body: { text: string } +// OR multipart/form-data: file (PDF/TXT) // // Returns: { // provisions: Array<{ @@ -13,7 +13,8 @@ // } import { generateText } from 'ai'; -import { getModelForTenant, getModel } from '@/lib/ai/providers'; +import { getModelForTenant } from '@/lib/ai/providers'; +import { requirePermission } from '@/lib/auth/rbac'; const PARSE_SYSTEM_PROMPT = `Du bist ein Experte fuer deutsches Recht und Gesetzestexte. Deine Aufgabe ist es, einen Fliesstext eines Gesetzes, Tarifvertrags oder einer anderen Rechtsquelle in einzelne Paragraphen zu zerlegen. @@ -45,15 +46,17 @@ async function extractTextFromPdf(buffer: Buffer): Promise { } export async function POST(request: Request) { + const auth = await requirePermission('norms:write'); + if ('response' in auth) return auth.response; + const { ctx } = auth; + let text: string; - let tenantId: string | undefined; const contentType = request.headers.get('content-type') ?? ''; if (contentType.includes('multipart/form-data')) { const formData = await request.formData(); const file = formData.get('file'); - tenantId = (formData.get('tenantId') as string) || undefined; if (!file || !(file instanceof File)) { return Response.json({ error: 'file field is required.' }, { status: 400 }); @@ -76,7 +79,7 @@ export async function POST(request: Request) { text = new TextDecoder('utf-8').decode(buffer); } } else { - let body: { text?: string; tenantId?: string }; + let body: { text?: string }; try { body = await request.json(); } catch { @@ -84,7 +87,6 @@ export async function POST(request: Request) { } text = body.text ?? ''; - tenantId = body.tenantId; } if (!text || typeof text !== 'string' || text.trim().length === 0) { @@ -102,13 +104,7 @@ export async function POST(request: Request) { } try { - let model; - if (tenantId) { - const result = await getModelForTenant(tenantId); - model = result.model; - } else { - model = getModel(); - } + const { model } = await getModelForTenant(ctx.tenantId); const result = await generateText({ model,