From bd4964f70f073b7b13f919743904ebf46fb681c1 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Mon, 8 Dec 2025 00:11:24 -0600 Subject: [PATCH] feat(user): implement organization membership checks for API key creation and user organization queries - Added verification to ensure users are members of the specified organization when creating API keys. - Implemented checks to restrict organization queries to users who are either checking their own organizations or are admins/owners of the active organization. - Enhanced error handling to return FORBIDDEN responses for unauthorized access attempts. --- apps/dokploy/server/api/routers/user.ts | 47 ++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/apps/dokploy/server/api/routers/user.ts b/apps/dokploy/server/api/routers/user.ts index 217090678..a6bd81a01 100644 --- a/apps/dokploy/server/api/routers/user.ts +++ b/apps/dokploy/server/api/routers/user.ts @@ -430,6 +430,23 @@ export const userRouter = createTRPCRouter({ createApiKey: protectedProcedure .input(apiCreateApiKey) .mutation(async ({ input, ctx }) => { + // Verify user is a member of the organization specified in metadata + if (input.metadata?.organizationId) { + const userMember = await db.query.member.findFirst({ + where: and( + eq(member.organizationId, input.metadata.organizationId), + eq(member.userId, ctx.user.id), + ), + }); + + if (!userMember) { + throw new TRPCError({ + code: "FORBIDDEN", + message: "You are not a member of this organization", + }); + } + } + const apiKey = await createApiKey(ctx.user.id, input); return apiKey; }), @@ -440,7 +457,35 @@ export const userRouter = createTRPCRouter({ userId: z.string(), }), ) - .query(async ({ input }) => { + .query(async ({ input, ctx }) => { + // Users can check their own organizations + // Admins and owners can check organizations of members in their active organization + if (input.userId !== ctx.user.id) { + // Verify the target user is a member of the active organization + const targetMember = await db.query.member.findFirst({ + where: and( + eq(member.userId, input.userId), + eq(member.organizationId, ctx.session?.activeOrganizationId || ""), + ), + }); + + if (!targetMember) { + throw new TRPCError({ + code: "FORBIDDEN", + message: "User is not a member of your active organization", + }); + } + + // Only admins and owners can check other users' organizations + if (ctx.user.role !== "owner" && ctx.user.role !== "admin") { + throw new TRPCError({ + code: "FORBIDDEN", + message: + "Only admins and owners can check other users' organizations", + }); + } + } + const organizations = await db.query.member.findMany({ where: eq(member.userId, input.userId), });