fix: allow members with git providers permission to create and delete their own

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
This commit is contained in:
Mauricio Siu
2026-06-30 15:56:13 -06:00
parent c2a95870f5
commit 81adbcb8f9
4 changed files with 59 additions and 0 deletions

View File

@@ -183,4 +183,29 @@ describe("legacy boolean overrides for member", () => {
memberToReturn = mockMemberData("member");
await expect(checkPermission(ctx, { docker: ["read"] })).rejects.toThrow();
});
it("member passes gitProviders.create with canAccessToGitProviders=true", async () => {
memberToReturn = mockMemberData("member", {
canAccessToGitProviders: true,
});
await expect(
checkPermission(ctx, { gitProviders: ["create"] }),
).resolves.toBeUndefined();
});
it("member passes gitProviders.delete with canAccessToGitProviders=true", async () => {
memberToReturn = mockMemberData("member", {
canAccessToGitProviders: true,
});
await expect(
checkPermission(ctx, { gitProviders: ["delete"] }),
).resolves.toBeUndefined();
});
it("member fails gitProviders.create with canAccessToGitProviders=false", async () => {
memberToReturn = mockMemberData("member");
await expect(
checkPermission(ctx, { gitProviders: ["create"] }),
).rejects.toThrow();
});
});

View File

@@ -143,6 +143,24 @@ describe("free-tier resources for member", () => {
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", () => {

View File

@@ -5,6 +5,7 @@ import {
updateGitProvider,
} from "@dokploy/server";
import { db } from "@dokploy/server/db";
import { findMemberByUserId } from "@dokploy/server/services/permission";
import { hasValidLicense } from "@dokploy/server/services/proprietary/license-key";
import { TRPCError } from "@trpc/server";
import { desc, eq, inArray } from "drizzle-orm";
@@ -144,6 +145,19 @@ export const gitProviderRouter = createTRPCRouter({
message: "You are not allowed to delete this Git provider",
});
}
const memberRecord = await findMemberByUserId(
ctx.user.id,
ctx.session.activeOrganizationId,
);
const isPrivileged =
memberRecord.role === "owner" || memberRecord.role === "admin";
if (!isPrivileged && gitProvider.userId !== ctx.session.userId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You can only delete your own Git providers",
});
}
await audit(ctx, {
action: "delete",
resourceType: "gitProvider",

View File

@@ -170,6 +170,8 @@ const getLegacyOverrides = (
},
gitProviders: {
read: !!memberRecord.canAccessToGitProviders,
create: !!memberRecord.canAccessToGitProviders,
delete: !!memberRecord.canAccessToGitProviders,
},
};
};