From 5ae103e779f03a0900d0e3e9b61cb0c159f1a461 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Fri, 21 Feb 2025 00:48:04 -0600 Subject: [PATCH] refactor: update permission checks to use organization context --- apps/dokploy/components/layouts/side.tsx | 21 ++-- apps/dokploy/server/api/routers/project.ts | 25 ++++- packages/server/src/services/user.ts | 111 +++++++++++++++------ 3 files changed, 107 insertions(+), 50 deletions(-) diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index 22f86fb0..f1296ce3 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -155,7 +155,7 @@ const MENU: Menu = { // Only enabled for admins and users with access to Traefik files in non-cloud environments isEnabled: ({ auth, isCloud }) => !!( - (auth?.role === "owner" || auth?.user?.canAccessToTraefikFiles) && + (auth?.role === "owner" || auth?.canAccessToTraefikFiles) && !isCloud ), }, @@ -166,10 +166,7 @@ const MENU: Menu = { icon: BlocksIcon, // Only enabled for admins and users with access to Docker in non-cloud environments isEnabled: ({ auth, isCloud }) => - !!( - (auth?.role === "owner" || auth?.user?.canAccessToDocker) && - !isCloud - ), + !!((auth?.role === "owner" || auth?.canAccessToDocker) && !isCloud), }, { isSingle: true, @@ -178,10 +175,7 @@ const MENU: Menu = { icon: PieChart, // Only enabled for admins and users with access to Docker in non-cloud environments isEnabled: ({ auth, isCloud }) => - !!( - (auth?.role === "owner" || auth?.user?.canAccessToDocker) && - !isCloud - ), + !!((auth?.role === "owner" || auth?.canAccessToDocker) && !isCloud), }, { isSingle: true, @@ -190,10 +184,7 @@ const MENU: Menu = { icon: Forward, // Only enabled for admins and users with access to Docker in non-cloud environments isEnabled: ({ auth, isCloud }) => - !!( - (auth?.role === "owner" || auth?.user?.canAccessToDocker) && - !isCloud - ), + !!((auth?.role === "owner" || auth?.canAccessToDocker) && !isCloud), }, // Legacy unused menu, adjusted to the new structure @@ -291,7 +282,7 @@ const MENU: Menu = { url: "/dashboard/settings/ssh-keys", // Only enabled for admins and users with access to SSH keys isEnabled: ({ auth }) => - !!(auth?.role === "owner" || auth?.user?.canAccessToSSHKeys), + !!(auth?.role === "owner" || auth?.canAccessToSSHKeys), }, { isSingle: true, @@ -300,7 +291,7 @@ const MENU: Menu = { icon: GitBranch, // Only enabled for admins and users with access to Git providers isEnabled: ({ auth }) => - !!(auth?.role === "owner" || auth?.user?.canAccessToGitProviders), + !!(auth?.role === "owner" || auth?.canAccessToGitProviders), }, { isSingle: true, diff --git a/apps/dokploy/server/api/routers/project.ts b/apps/dokploy/server/api/routers/project.ts index e3c24e53..68b068bc 100644 --- a/apps/dokploy/server/api/routers/project.ts +++ b/apps/dokploy/server/api/routers/project.ts @@ -38,7 +38,11 @@ export const projectRouter = createTRPCRouter({ .mutation(async ({ ctx, input }) => { try { if (ctx.user.rol === "member") { - await checkProjectAccess(ctx.user.id, "create"); + await checkProjectAccess( + ctx.user.id, + "create", + ctx.session.activeOrganizationId, + ); } const admin = await findUserById(ctx.user.ownerId); @@ -55,7 +59,11 @@ export const projectRouter = createTRPCRouter({ ctx.session.activeOrganizationId, ); if (ctx.user.rol === "member") { - await addNewProject(ctx.user.id, project.projectId); + await addNewProject( + ctx.user.id, + project.projectId, + ctx.session.activeOrganizationId, + ); } return project; @@ -77,7 +85,12 @@ export const projectRouter = createTRPCRouter({ ctx.session.activeOrganizationId, ); - await checkProjectAccess(ctx.user.id, "access", input.projectId); + await checkProjectAccess( + ctx.user.id, + "access", + ctx.session.activeOrganizationId, + input.projectId, + ); const project = await db.query.projects.findFirst({ where: and( @@ -212,7 +225,11 @@ export const projectRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { if (ctx.user.rol === "member") { - await checkProjectAccess(ctx.user.id, "delete"); + await checkProjectAccess( + ctx.user.id, + "delete", + ctx.session.activeOrganizationId, + ); } const currentProject = await findProjectById(input.projectId); if ( diff --git a/packages/server/src/services/user.ts b/packages/server/src/services/user.ts index 9351a003..9e924e9f 100644 --- a/packages/server/src/services/user.ts +++ b/packages/server/src/services/user.ts @@ -33,32 +33,48 @@ export const findUserByAuthId = async (authId: string) => { // return userR; }; -export const addNewProject = async (userId: string, projectId: string) => { - const userR = await findUserById(userId); +export const addNewProject = async ( + userId: string, + projectId: string, + organizationId: string, +) => { + const userR = await findMemberById(userId, organizationId); - // await db - // .update(user) - // .set({ - // accessedProjects: [...userR.accessedProjects, projectId], - // }) - // .where(eq(user.authId, authId)); + await db + .update(member) + .set({ + accessedProjects: [...userR.accessedProjects, projectId], + }) + .where( + and(eq(member.id, userR.id), eq(member.organizationId, organizationId)), + ); }; -export const addNewService = async (userId: string, serviceId: string) => { - const userR = await findUserById(userId); - // await db - // .update(user) - // .set({ - // accessedServices: [...userR.accessedServices, serviceId], - // }) - // .where(eq(user.userId, userId)); +export const addNewService = async ( + userId: string, + serviceId: string, + organizationId: string, +) => { + const userR = await findMemberById(userId, organizationId); + await db + .update(member) + .set({ + accessedServices: [...userR.accessedServices, serviceId], + }) + .where( + and(eq(member.id, userR.id), eq(member.organizationId, organizationId)), + ); }; export const canPerformCreationService = async ( userId: string, projectId: string, + organizationId: string, ) => { - const { accessedProjects, canCreateServices } = await findUserById(userId); + const { accessedProjects, canCreateServices } = await findMemberById( + userId, + organizationId, + ); const haveAccessToProject = accessedProjects.includes(projectId); if (canCreateServices && haveAccessToProject) { @@ -71,8 +87,9 @@ export const canPerformCreationService = async ( export const canPerformAccessService = async ( userId: string, serviceId: string, + organizationId: string, ) => { - const { accessedServices } = await findUserById(userId); + const { accessedServices } = await findMemberById(userId, organizationId); const haveAccessToService = accessedServices.includes(serviceId); if (haveAccessToService) { @@ -85,8 +102,12 @@ export const canPerformAccessService = async ( export const canPeformDeleteService = async ( userId: string, serviceId: string, + organizationId: string, ) => { - const { accessedServices, canDeleteServices } = await findUserById(userId); + const { accessedServices, canDeleteServices } = await findMemberById( + userId, + organizationId, + ); const haveAccessToService = accessedServices.includes(serviceId); if (canDeleteServices && haveAccessToService) { @@ -96,8 +117,11 @@ export const canPeformDeleteService = async ( return false; }; -export const canPerformCreationProject = async (userId: string) => { - const { canCreateProjects } = await findUserById(userId); +export const canPerformCreationProject = async ( + userId: string, + organizationId: string, +) => { + const { canCreateProjects } = await findMemberById(userId, organizationId); if (canCreateProjects) { return true; @@ -106,8 +130,11 @@ export const canPerformCreationProject = async (userId: string) => { return false; }; -export const canPerformDeleteProject = async (userId: string) => { - const { canDeleteProjects } = await findUserById(userId); +export const canPerformDeleteProject = async ( + userId: string, + organizationId: string, +) => { + const { canDeleteProjects } = await findMemberById(userId, organizationId); if (canDeleteProjects) { return true; @@ -119,8 +146,9 @@ export const canPerformDeleteProject = async (userId: string) => { export const canPerformAccessProject = async ( userId: string, projectId: string, + organizationId: string, ) => { - const { accessedProjects } = await findUserById(userId); + const { accessedProjects } = await findMemberById(userId, organizationId); const haveAccessToProject = accessedProjects.includes(projectId); @@ -130,26 +158,45 @@ export const canPerformAccessProject = async ( return false; }; -export const canAccessToTraefikFiles = async (userId: string) => { - const { canAccessToTraefikFiles } = await findUserById(userId); +export const canAccessToTraefikFiles = async ( + userId: string, + organizationId: string, +) => { + const { canAccessToTraefikFiles } = await findMemberById( + userId, + organizationId, + ); return canAccessToTraefikFiles; }; export const checkServiceAccess = async ( userId: string, serviceId: string, + organizationId: string, action = "access" as "access" | "create" | "delete", ) => { let hasPermission = false; switch (action) { case "create": - hasPermission = await canPerformCreationService(userId, serviceId); + hasPermission = await canPerformCreationService( + userId, + serviceId, + organizationId, + ); break; case "access": - hasPermission = await canPerformAccessService(userId, serviceId); + hasPermission = await canPerformAccessService( + userId, + serviceId, + organizationId, + ); break; case "delete": - hasPermission = await canPeformDeleteService(userId, serviceId); + hasPermission = await canPeformDeleteService( + userId, + serviceId, + organizationId, + ); break; default: hasPermission = false; @@ -165,6 +212,7 @@ export const checkServiceAccess = async ( export const checkProjectAccess = async ( authId: string, action: "create" | "delete" | "access", + organizationId: string, projectId?: string, ) => { let hasPermission = false; @@ -173,13 +221,14 @@ export const checkProjectAccess = async ( hasPermission = await canPerformAccessProject( authId, projectId as string, + organizationId, ); break; case "create": - hasPermission = await canPerformCreationProject(authId); + hasPermission = await canPerformCreationProject(authId, organizationId); break; case "delete": - hasPermission = await canPerformDeleteProject(authId); + hasPermission = await canPerformDeleteProject(authId, organizationId); break; default: hasPermission = false;