diff --git a/apps/dokploy/server/api/routers/backup.ts b/apps/dokploy/server/api/routers/backup.ts index c3633b135..75bb60f2c 100644 --- a/apps/dokploy/server/api/routers/backup.ts +++ b/apps/dokploy/server/api/routers/backup.ts @@ -458,9 +458,26 @@ export const backupRouter = createTRPCRouter({ serverId: z.string().optional(), }), ) - .query(async ({ input }) => { + .query(async ({ input, ctx }) => { try { const destination = await findDestinationById(input.destinationId); + if (destination.organizationId !== ctx.session.activeOrganizationId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You don't have access to this destination.", + }); + } + 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 rcloneFlags = getS3Credentials(destination); const bucketPath = `:s3:${destination.bucket}`; diff --git a/apps/dokploy/server/api/routers/cluster.ts b/apps/dokploy/server/api/routers/cluster.ts index afd8a0e92..dad67a3d0 100644 --- a/apps/dokploy/server/api/routers/cluster.ts +++ b/apps/dokploy/server/api/routers/cluster.ts @@ -11,6 +11,20 @@ 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( @@ -18,7 +32,11 @@ export const clusterRouter = createTRPCRouter({ serverId: z.string().optional(), }), ) - .query(async ({ input }) => { + .query(async ({ input, ctx }) => { + await assertServerBelongsToOrg( + input.serverId, + ctx.session.activeOrganizationId, + ); const docker = await getRemoteDocker(input.serverId); const workers: DockerNode[] = await docker.listNodes(); return workers; @@ -32,6 +50,10 @@ export const clusterRouter = createTRPCRouter({ }), ) .mutation(async ({ input, ctx }) => { + await assertServerBelongsToOrg( + input.serverId, + ctx.session.activeOrganizationId, + ); try { const drainCommand = `docker node update --availability drain ${input.nodeId}`; const removeCommand = `docker node rm ${input.nodeId} --force`; @@ -65,7 +87,11 @@ export const clusterRouter = createTRPCRouter({ serverId: z.string().optional(), }), ) - .query(async ({ input }) => { + .query(async ({ input, ctx }) => { + await assertServerBelongsToOrg( + input.serverId, + ctx.session.activeOrganizationId, + ); const docker = await getRemoteDocker(input.serverId); const result = await docker.swarmInspect(); const docker_version = await docker.version(); @@ -88,7 +114,11 @@ export const clusterRouter = createTRPCRouter({ serverId: z.string().optional(), }), ) - .query(async ({ input }) => { + .query(async ({ input, ctx }) => { + await assertServerBelongsToOrg( + input.serverId, + ctx.session.activeOrganizationId, + ); const docker = await getRemoteDocker(input.serverId); const result = await docker.swarmInspect(); const docker_version = await docker.version(); diff --git a/apps/dokploy/server/api/routers/deployment.ts b/apps/dokploy/server/api/routers/deployment.ts index 03cd3c935..6f3b1d1ae 100644 --- a/apps/dokploy/server/api/routers/deployment.ts +++ b/apps/dokploy/server/api/routers/deployment.ts @@ -16,6 +16,7 @@ import { checkServicePermissionAndAccess, findMemberByUserId, } from "@dokploy/server/services/permission"; +import { findServerById } from "@dokploy/server/services/server"; import { TRPCError } from "@trpc/server"; import { desc, eq } from "drizzle-orm"; import { z } from "zod"; @@ -52,7 +53,14 @@ export const deploymentRouter = createTRPCRouter({ }), allByServer: withPermission("deployment", "read") .input(apiFindAllByServer) - .query(async ({ input }) => { + .query(async ({ input, ctx }) => { + 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.", + }); + } return await findAllDeploymentsByServerId(input.serverId); }), allCentralized: withPermission("deployment", "read").query( diff --git a/apps/dokploy/server/api/routers/volume-backups.ts b/apps/dokploy/server/api/routers/volume-backups.ts index 5b50219d2..1f589d1e3 100644 --- a/apps/dokploy/server/api/routers/volume-backups.ts +++ b/apps/dokploy/server/api/routers/volume-backups.ts @@ -15,7 +15,9 @@ import { updateVolumeBackupSchema, volumeBackups, } from "@dokploy/server/db/schema"; +import { findDestinationById } from "@dokploy/server/services/destination"; import { checkServicePermissionAndAccess } from "@dokploy/server/services/permission"; +import { findServerById } from "@dokploy/server/services/server"; import { execAsyncRemote, execAsyncStream, @@ -265,7 +267,23 @@ export const volumeBackupsRouter = createTRPCRouter({ serverId: z.string().optional(), }), ) - .subscription(async ({ input }) => { + .subscription(async ({ input, ctx }) => { + const destination = await findDestinationById(input.destinationId); + if (destination.organizationId !== ctx.session.activeOrganizationId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You don't have access to this destination.", + }); + } + 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.", + }); + } + } return observable((emit) => { const runRestore = async () => { try { diff --git a/apps/dokploy/server/wss/docker-container-logs.ts b/apps/dokploy/server/wss/docker-container-logs.ts index 159bedaae..ed4541558 100644 --- a/apps/dokploy/server/wss/docker-container-logs.ts +++ b/apps/dokploy/server/wss/docker-container-logs.ts @@ -85,6 +85,11 @@ export const setupDockerContainerLogsWebSocketServer = ( if (serverId) { const server = await findServerById(serverId); + if (server.organizationId !== session.activeOrganizationId) { + ws.close(); + return; + } + if (!server.sshKeyId) return; const client = new Client(); client diff --git a/apps/dokploy/server/wss/docker-container-terminal.ts b/apps/dokploy/server/wss/docker-container-terminal.ts index a2c242d95..e752c0651 100644 --- a/apps/dokploy/server/wss/docker-container-terminal.ts +++ b/apps/dokploy/server/wss/docker-container-terminal.ts @@ -61,6 +61,12 @@ export const setupDockerContainerTerminalWebSocketServer = ( try { if (serverId) { const server = await findServerById(serverId); + + if (server.organizationId !== session.activeOrganizationId) { + ws.close(); + return; + } + if (!server.sshKeyId) throw new Error("No SSH key available for this server"); diff --git a/apps/dokploy/server/wss/listen-deployment.ts b/apps/dokploy/server/wss/listen-deployment.ts index c39fa70b7..cd9eefed6 100644 --- a/apps/dokploy/server/wss/listen-deployment.ts +++ b/apps/dokploy/server/wss/listen-deployment.ts @@ -57,6 +57,11 @@ export const setupDeploymentLogsWebSocketServer = ( if (serverId) { const server = await findServerById(serverId); + if (server.organizationId !== session.activeOrganizationId) { + ws.close(); + return; + } + if (!server.sshKeyId) { ws.close(); return; diff --git a/apps/dokploy/server/wss/terminal.ts b/apps/dokploy/server/wss/terminal.ts index 00b0e2c2c..4825f7301 100644 --- a/apps/dokploy/server/wss/terminal.ts +++ b/apps/dokploy/server/wss/terminal.ts @@ -154,6 +154,11 @@ export const setupTerminalWebSocketServer = ( return; } + if (server.organizationId !== session.activeOrganizationId) { + ws.close(); + return; + } + const { ipAddress: host, port, username, sshKey, sshKeyId } = server; if (!sshKeyId) { diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index 65dd1b01d..afbc57881 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -481,8 +481,10 @@ export const validateRequest = async (request: IncomingMessage) => { }; } - const organizationId = JSON.parse( - apiKeyRecord.metadata || "{}", + const organizationId = ( + JSON.parse(apiKeyRecord.metadata || "{}") as { + organizationId?: string; + } ).organizationId; if (!organizationId) {