diff --git a/apps/dokploy/__test__/permissions/resolve-permissions.test.ts b/apps/dokploy/__test__/permissions/resolve-permissions.test.ts index 8ea6b9596..759c8dad8 100644 --- a/apps/dokploy/__test__/permissions/resolve-permissions.test.ts +++ b/apps/dokploy/__test__/permissions/resolve-permissions.test.ts @@ -88,16 +88,28 @@ describe("enterprise resources for static roles", () => { } }); - it("member gets true for all enterprise resources", async () => { + it("member gets true for service-level enterprise resources", async () => { memberToReturn = mockMemberData("member"); const perms = await resolvePermissions(ctx); - for (const resource of enterpriseOnlyResources) { - const actions = statements[resource as keyof typeof statements]; - for (const action of actions) { - expect((perms as any)[resource][action]).toBe(true); - } - } + expect(perms.deployment.read).toBe(true); + expect(perms.deployment.create).toBe(true); + expect(perms.domain.read).toBe(true); + expect(perms.backup.read).toBe(true); + expect(perms.logs.read).toBe(true); + expect(perms.monitoring.read).toBe(true); + }); + + it("member gets false for org-level enterprise resources", async () => { + memberToReturn = mockMemberData("member"); + const perms = await resolvePermissions(ctx); + + expect(perms.server.read).toBe(false); + expect(perms.registry.read).toBe(false); + expect(perms.certificate.read).toBe(false); + expect(perms.destination.read).toBe(false); + expect(perms.notification.read).toBe(false); + expect(perms.auditLog.read).toBe(false); }); }); diff --git a/apps/dokploy/components/dashboard/settings/audit-logs/columns.tsx b/apps/dokploy/components/proprietary/audit-logs/columns.tsx similarity index 100% rename from apps/dokploy/components/dashboard/settings/audit-logs/columns.tsx rename to apps/dokploy/components/proprietary/audit-logs/columns.tsx diff --git a/apps/dokploy/components/dashboard/settings/audit-logs/data-table.tsx b/apps/dokploy/components/proprietary/audit-logs/data-table.tsx similarity index 100% rename from apps/dokploy/components/dashboard/settings/audit-logs/data-table.tsx rename to apps/dokploy/components/proprietary/audit-logs/data-table.tsx diff --git a/apps/dokploy/components/dashboard/settings/audit-logs/show-audit-logs.tsx b/apps/dokploy/components/proprietary/audit-logs/show-audit-logs.tsx similarity index 100% rename from apps/dokploy/components/dashboard/settings/audit-logs/show-audit-logs.tsx rename to apps/dokploy/components/proprietary/audit-logs/show-audit-logs.tsx diff --git a/apps/dokploy/components/dashboard/settings/users/manage-custom-roles.tsx b/apps/dokploy/components/proprietary/roles/manage-custom-roles.tsx similarity index 100% rename from apps/dokploy/components/dashboard/settings/users/manage-custom-roles.tsx rename to apps/dokploy/components/proprietary/roles/manage-custom-roles.tsx diff --git a/apps/dokploy/pages/dashboard/settings/audit-logs.tsx b/apps/dokploy/pages/dashboard/settings/audit-logs.tsx index 6a5acf012..1172b2c02 100644 --- a/apps/dokploy/pages/dashboard/settings/audit-logs.tsx +++ b/apps/dokploy/pages/dashboard/settings/audit-logs.tsx @@ -3,8 +3,8 @@ import { createServerSideHelpers } from "@trpc/react-query/server"; import type { GetServerSidePropsContext } from "next"; import type { ReactElement } from "react"; import superjson from "superjson"; -import { ShowAuditLogs } from "@/components/dashboard/settings/audit-logs/show-audit-logs"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; +import { ShowAuditLogs } from "@/components/proprietary/audit-logs/show-audit-logs"; import { appRouter } from "@/server/api/root"; const Page = () => { diff --git a/apps/dokploy/pages/dashboard/settings/users.tsx b/apps/dokploy/pages/dashboard/settings/users.tsx index b92cf539a..43c014279 100644 --- a/apps/dokploy/pages/dashboard/settings/users.tsx +++ b/apps/dokploy/pages/dashboard/settings/users.tsx @@ -3,10 +3,10 @@ import { createServerSideHelpers } from "@trpc/react-query/server"; import type { GetServerSidePropsContext } from "next"; import type { ReactElement } from "react"; import superjson from "superjson"; -import { ManageCustomRoles } from "@/components/dashboard/settings/users/manage-custom-roles"; +import { DashboardLayout } from "@/components/layouts/dashboard-layout"; +import { ManageCustomRoles } from "@/components/proprietary/roles/manage-custom-roles"; import { ShowInvitations } from "@/components/dashboard/settings/users/show-invitations"; import { ShowUsers } from "@/components/dashboard/settings/users/show-users"; -import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; diff --git a/packages/server/src/lib/access-control.ts b/packages/server/src/lib/access-control.ts index 9b8f62ee7..d49fd2d45 100644 --- a/packages/server/src/lib/access-control.ts +++ b/packages/server/src/lib/access-control.ts @@ -168,21 +168,23 @@ export const memberRole = ac.newRole({ gitProviders: [], traefikFiles: [], api: [], - volume: [], - deployment: [], - envVars: [], - projectEnvVars: [], - environmentEnvVars: [], + // Service-level enterprise resources — member can do everything within services they have access to + volume: ["read", "create", "delete"], + deployment: ["read", "create", "cancel"], + envVars: ["read", "write"], + projectEnvVars: ["read", "write"], + environmentEnvVars: ["read", "write"], + backup: ["read", "create", "update", "delete", "restore"], + volumeBackup: ["read", "create", "update", "delete", "restore"], + schedule: ["read", "create", "update", "delete"], + domain: ["read", "create", "delete"], + logs: ["read"], + monitoring: ["read"], + // Org-level enterprise resources — member cannot manage these server: [], registry: [], certificate: [], - backup: [], - volumeBackup: [], - schedule: [], - domain: [], destination: [], notification: [], - logs: [], - monitoring: [], auditLog: [], }); diff --git a/packages/server/src/services/permission.ts b/packages/server/src/services/permission.ts index 4a4800495..3088842b6 100644 --- a/packages/server/src/services/permission.ts +++ b/packages/server/src/services/permission.ts @@ -182,13 +182,14 @@ export const resolvePermissions = async ( const legacyOverrides = memberRecord.role === "member" ? getLegacyOverrides(memberRecord) : {}; - const isStaticRole = memberRecord.role in staticRoles; + const isPrivilegedRole = + memberRecord.role === "owner" || memberRecord.role === "admin"; const result = {} as ResolvedPermissions; for (const [resource, actions] of Object.entries(statements)) { const resourcePerms = {} as Record; for (const action of actions) { - if (isStaticRole && enterpriseOnlyResources.has(resource)) { + if (isPrivilegedRole && enterpriseOnlyResources.has(resource)) { resourcePerms[action] = true; continue; }