feat(backend): enforce user-owned sessions

This commit is contained in:
User
2026-07-16 18:32:52 +02:00
parent 72db6b823d
commit 458eb13c89
15 changed files with 626 additions and 105 deletions
+47 -9
View File
@@ -1,25 +1,63 @@
import type { FastifyInstance } from "fastify";
import type { ThtRunner } from "../tht/tht-runner.js";
import { getPrincipal } from "../auth/auth.js";
import type { PrincipalContext } from "../auth/principal.js";
import type { Settings } from "../settings/settings-store.js";
export function sqlRoutes(app: FastifyInstance, deps: { tht: ThtRunner }): void {
export function sqlRoutes(app: FastifyInstance, deps: {
tht: ThtRunner; getSettings: (principal: PrincipalContext) => Promise<Settings>;
}): void {
const runnerFor = (principal: PrincipalContext): any => {
const runner = deps.tht as any;
return typeof runner.withPrincipal === "function" ? runner.withPrincipal(principal) : runner;
};
const authorize = async (principal: PrincipalContext, id: string, workspace?: string) => {
try {
const runner = runnerFor(principal);
if (typeof runner.sessionShow !== "function") return {};
return await runner.sessionShow(id, workspace);
}
catch (error) {
if (/not found|non trovata|inesistente|404/i.test(error instanceof Error ? error.message : String(error))) return undefined;
throw error;
}
};
app.post("/sessions/:id/sql/preview", async (req, reply) => {
const id = (req.params as any).id as string;
const { limit, offset } = (req.body as any) ?? {};
let principal: PrincipalContext;
let workspace: string | undefined;
try {
const result = await deps.tht.sqlPreview(id, { limit, offset });
return result;
} catch (err: any) {
return reply.code(500).send({ error: err.message ?? String(err) });
principal = getPrincipal(req);
const settings = await deps.getSettings(principal);
workspace = settings.workspace;
if (!await authorize(principal, id, settings.workspace)) return reply.code(404).send({ error: "session not found" });
} catch {
return reply.code(503).send({ error: "session storage is unavailable" });
}
try {
return await runnerFor(principal).sqlPreview(id, { limit, offset }, workspace);
} catch (error: any) {
return reply.code(500).send({ error: error.message ?? String(error) });
}
});
app.post("/sessions/:id/sql/export", async (req, reply) => {
const id = (req.params as any).id as string;
let principal: PrincipalContext;
let workspace: string | undefined;
try {
const result = await deps.tht.sqlExport(id);
return result;
} catch (err: any) {
return reply.code(500).send({ error: err.message ?? String(err) });
principal = getPrincipal(req);
const settings = await deps.getSettings(principal);
workspace = settings.workspace;
if (!await authorize(principal, id, settings.workspace)) return reply.code(404).send({ error: "session not found" });
} catch {
return reply.code(503).send({ error: "session storage is unavailable" });
}
try {
return await runnerFor(principal).sqlExport(id, workspace);
} catch (error: any) {
return reply.code(500).send({ error: error.message ?? String(error) });
}
});
}