mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-07-11 17:05:25 +02:00
The canAccessToGitProviders legacy override only granted read access, so members with the Git Providers toggle enabled could not add providers — the create/delete endpoints require gitProviders.create / gitProviders.delete. This mirrors how the SSH Keys toggle already grants read/create/delete. The git-provider remove endpoint now restricts non owner/admin roles to deleting only their own providers (matching the ownership model used for visibility and sharing), while owner/admin can still delete any provider in the organization. Closes #4695
180 lines
5.9 KiB
TypeScript
180 lines
5.9 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
const mockMemberData = (
|
|
role: string,
|
|
overrides: Record<string, boolean> = {},
|
|
) => ({
|
|
id: "member-1",
|
|
role,
|
|
userId: "user-1",
|
|
organizationId: "org-1",
|
|
accessedProjects: [] as string[],
|
|
accessedServices: [] as string[],
|
|
accessedEnvironments: [] as string[],
|
|
canCreateProjects: overrides.canCreateProjects ?? false,
|
|
canDeleteProjects: overrides.canDeleteProjects ?? false,
|
|
canCreateServices: overrides.canCreateServices ?? false,
|
|
canDeleteServices: overrides.canDeleteServices ?? false,
|
|
canCreateEnvironments: overrides.canCreateEnvironments ?? false,
|
|
canDeleteEnvironments: overrides.canDeleteEnvironments ?? false,
|
|
canAccessToTraefikFiles: overrides.canAccessToTraefikFiles ?? false,
|
|
canAccessToDocker: overrides.canAccessToDocker ?? false,
|
|
canAccessToAPI: overrides.canAccessToAPI ?? false,
|
|
canAccessToSSHKeys: overrides.canAccessToSSHKeys ?? false,
|
|
canAccessToGitProviders: overrides.canAccessToGitProviders ?? false,
|
|
user: { id: "user-1", email: "test@test.com" },
|
|
});
|
|
|
|
let memberToReturn: ReturnType<typeof mockMemberData> =
|
|
mockMemberData("member");
|
|
|
|
vi.mock("@dokploy/server/db", () => ({
|
|
db: {
|
|
query: {
|
|
member: {
|
|
findFirst: vi.fn(() => Promise.resolve(memberToReturn)),
|
|
findMany: vi.fn(() => Promise.resolve([])),
|
|
},
|
|
organizationRole: {
|
|
findFirst: vi.fn(),
|
|
findMany: vi.fn(() => Promise.resolve([])),
|
|
},
|
|
},
|
|
},
|
|
}));
|
|
|
|
vi.mock("@dokploy/server/services/proprietary/license-key", () => ({
|
|
hasValidLicense: vi.fn(() => Promise.resolve(false)),
|
|
}));
|
|
|
|
const { resolvePermissions } = await import(
|
|
"@dokploy/server/services/permission"
|
|
);
|
|
const { enterpriseOnlyResources, statements } = await import(
|
|
"@dokploy/server/lib/access-control"
|
|
);
|
|
|
|
const ctx = {
|
|
user: { id: "user-1" },
|
|
session: { activeOrganizationId: "org-1" },
|
|
};
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
describe("enterprise resources for static roles", () => {
|
|
it("owner gets true for all enterprise resources", async () => {
|
|
memberToReturn = mockMemberData("owner");
|
|
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);
|
|
}
|
|
}
|
|
});
|
|
|
|
it("admin gets true for all enterprise resources", async () => {
|
|
memberToReturn = mockMemberData("admin");
|
|
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);
|
|
}
|
|
}
|
|
});
|
|
|
|
it("member gets true for service-level enterprise resources", async () => {
|
|
memberToReturn = mockMemberData("member");
|
|
const perms = await resolvePermissions(ctx);
|
|
|
|
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);
|
|
});
|
|
});
|
|
|
|
describe("free-tier resources for member", () => {
|
|
it("member gets service.read=true", async () => {
|
|
memberToReturn = mockMemberData("member");
|
|
const perms = await resolvePermissions(ctx);
|
|
expect(perms.service.read).toBe(true);
|
|
});
|
|
|
|
it("member gets project.create=false without legacy override", async () => {
|
|
memberToReturn = mockMemberData("member");
|
|
const perms = await resolvePermissions(ctx);
|
|
expect(perms.project.create).toBe(false);
|
|
});
|
|
|
|
it("member gets project.create=true with canCreateProjects", async () => {
|
|
memberToReturn = mockMemberData("member", { canCreateProjects: true });
|
|
const perms = await resolvePermissions(ctx);
|
|
expect(perms.project.create).toBe(true);
|
|
});
|
|
|
|
it("member gets docker.read=false without legacy override", async () => {
|
|
memberToReturn = mockMemberData("member");
|
|
const perms = await resolvePermissions(ctx);
|
|
expect(perms.docker.read).toBe(false);
|
|
});
|
|
|
|
it("member gets docker.read=true with canAccessToDocker", async () => {
|
|
memberToReturn = mockMemberData("member", { canAccessToDocker: true });
|
|
const perms = await resolvePermissions(ctx);
|
|
expect(perms.docker.read).toBe(true);
|
|
});
|
|
|
|
it("member gets gitProviders create/delete=false without legacy override", async () => {
|
|
memberToReturn = mockMemberData("member");
|
|
const perms = await resolvePermissions(ctx);
|
|
expect(perms.gitProviders.read).toBe(false);
|
|
expect(perms.gitProviders.create).toBe(false);
|
|
expect(perms.gitProviders.delete).toBe(false);
|
|
});
|
|
|
|
it("member gets gitProviders read/create/delete=true with canAccessToGitProviders", async () => {
|
|
memberToReturn = mockMemberData("member", {
|
|
canAccessToGitProviders: true,
|
|
});
|
|
const perms = await resolvePermissions(ctx);
|
|
expect(perms.gitProviders.read).toBe(true);
|
|
expect(perms.gitProviders.create).toBe(true);
|
|
expect(perms.gitProviders.delete).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("free-tier resources for owner", () => {
|
|
it("owner gets all free-tier permissions as true", async () => {
|
|
memberToReturn = mockMemberData("owner");
|
|
const perms = await resolvePermissions(ctx);
|
|
expect(perms.project.create).toBe(true);
|
|
expect(perms.project.delete).toBe(true);
|
|
expect(perms.service.create).toBe(true);
|
|
expect(perms.service.read).toBe(true);
|
|
expect(perms.service.delete).toBe(true);
|
|
expect(perms.docker.read).toBe(true);
|
|
expect(perms.traefikFiles.read).toBe(true);
|
|
expect(perms.traefikFiles.write).toBe(true);
|
|
});
|
|
});
|