From 232ccc913967e28d58635a989a80cd77733d9f96 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Fri, 24 Apr 2026 12:47:51 -0600 Subject: [PATCH] feat: add organization-level authorization checks to WebSocket servers - Implemented checks in the WebSocket server setups for Docker container logs, terminal, and deployment logs to ensure users can only access resources associated with their active organization. - Enhanced security by closing WebSocket connections if the organization ID does not match the session's active organization ID. --- apps/dokploy/server/api/routers/cluster.ts | 66 ++++++++++++---------- 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/apps/dokploy/server/api/routers/cluster.ts b/apps/dokploy/server/api/routers/cluster.ts index dad67a3d0..3dc07935e 100644 --- a/apps/dokploy/server/api/routers/cluster.ts +++ b/apps/dokploy/server/api/routers/cluster.ts @@ -11,20 +11,6 @@ import { audit } from "@/server/api/utils/audit"; import { getLocalServerIp } from "@/server/wss/terminal"; import { createTRPCRouter, withPermission } from "../trpc"; -const assertServerBelongsToOrg = async ( - serverId: string | undefined, - activeOrganizationId: string, -) => { - if (!serverId) return; - const targetServer = await findServerById(serverId); - if (targetServer.organizationId !== activeOrganizationId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You don't have access to this server.", - }); - } -}; - export const clusterRouter = createTRPCRouter({ getNodes: withPermission("server", "read") .input( @@ -33,10 +19,15 @@ export const clusterRouter = createTRPCRouter({ }), ) .query(async ({ input, ctx }) => { - await assertServerBelongsToOrg( - input.serverId, - ctx.session.activeOrganizationId, - ); + if (input.serverId) { + const targetServer = await findServerById(input.serverId); + if (targetServer.organizationId !== ctx.session.activeOrganizationId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You don't have access to this server.", + }); + } + } const docker = await getRemoteDocker(input.serverId); const workers: DockerNode[] = await docker.listNodes(); return workers; @@ -50,10 +41,15 @@ export const clusterRouter = createTRPCRouter({ }), ) .mutation(async ({ input, ctx }) => { - await assertServerBelongsToOrg( - input.serverId, - ctx.session.activeOrganizationId, - ); + if (input.serverId) { + const targetServer = await findServerById(input.serverId); + if (targetServer.organizationId !== ctx.session.activeOrganizationId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You don't have access to this server.", + }); + } + } try { const drainCommand = `docker node update --availability drain ${input.nodeId}`; const removeCommand = `docker node rm ${input.nodeId} --force`; @@ -88,10 +84,15 @@ export const clusterRouter = createTRPCRouter({ }), ) .query(async ({ input, ctx }) => { - await assertServerBelongsToOrg( - input.serverId, - ctx.session.activeOrganizationId, - ); + if (input.serverId) { + const targetServer = await findServerById(input.serverId); + if (targetServer.organizationId !== ctx.session.activeOrganizationId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You don't have access to this server.", + }); + } + } const docker = await getRemoteDocker(input.serverId); const result = await docker.swarmInspect(); const docker_version = await docker.version(); @@ -115,10 +116,15 @@ export const clusterRouter = createTRPCRouter({ }), ) .query(async ({ input, ctx }) => { - await assertServerBelongsToOrg( - input.serverId, - ctx.session.activeOrganizationId, - ); + if (input.serverId) { + const targetServer = await findServerById(input.serverId); + if (targetServer.organizationId !== ctx.session.activeOrganizationId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You don't have access to this server.", + }); + } + } const docker = await getRemoteDocker(input.serverId); const result = await docker.swarmInspect(); const docker_version = await docker.version();