import { FastifyInstance, FastifyRequest } from "fastify" import { and, eq, or, isNull, inArray } from "drizzle-orm" import { authRoles, authRolePermissions, authUserRoles, } from "../../db/schema" import { McpContext, McpTool } from "./types" export async function loadTenantPermissions( server: FastifyInstance, userId: string, tenantId: number ) { const roleRows = await server.db .select({ roleId: authUserRoles.role_id, }) .from(authUserRoles) .innerJoin( authRoles, and( eq(authRoles.id, authUserRoles.role_id), or(isNull(authRoles.tenant_id), eq(authRoles.tenant_id, tenantId)) ) ) .where( and( eq(authUserRoles.user_id, userId), eq(authUserRoles.tenant_id, tenantId) ) ) const roleIds = Array.from(new Set(roleRows.map((row) => row.roleId))) if (roleIds.length === 0) return [] const permissionRows = await server.db .select({ permission: authRolePermissions.permission, }) .from(authRolePermissions) .where(inArray(authRolePermissions.role_id, roleIds)) return Array.from(new Set(permissionRows.map((row) => row.permission))) } export async function createMcpContext( server: FastifyInstance, request: FastifyRequest ): Promise { const user = request.user if (!user?.user_id) { throw Object.assign(new Error("Authentication required"), { statusCode: 401 }) } if (!user.tenant_id) { throw Object.assign(new Error("MCP benötigt einen aktiven Mandanten"), { statusCode: 403 }) } const permissions = await loadTenantPermissions(server, user.user_id, user.tenant_id) return { server, request, tenantId: user.tenant_id, userId: user.user_id, isAdmin: Boolean(user.is_admin), permissions, } } export function assertToolPermission(context: McpContext, tool: McpTool) { if (context.isAdmin) return const allowed = tool.requiredPermissions.every((permission) => context.permissions.includes(permission) ) if (!allowed) { throw Object.assign( new Error(`Fehlende Berechtigung für ${tool.name}: ${tool.requiredPermissions.join(", ")}`), { statusCode: 403 } ) } }