import { db } from "@dokploy/server/db";import { gitProvider, member } from "@dokploy/server/db/schema";import { hasValidLicense } from "@dokploy/server/services/proprietary/license-key";import { TRPCError } from "@trpc/server";import { and, eq } from "drizzle-orm"; export type GitProvider = typeof gitProvider.$inferSelect; export const removeGitProvider = async (gitProviderId: string) => { const result = await db .delete(gitProvider) .where(eq(gitProvider.gitProviderId, gitProviderId)) .returning(); return result[0];}; export const findGitProviderById = async (gitProviderId: string) => { const result = await db.query.gitProvider.findFirst({ where: eq(gitProvider.gitProviderId, gitProviderId), }); if (!result) { throw new TRPCError({ code: "NOT_FOUND", message: "Git Provider not found", }); } return result;}; export const updateGitProvider = async ( gitProviderId: string, input: Partial<GitProvider>,) => { return await db .update(gitProvider) .set({ ...input, }) .where(eq(gitProvider.gitProviderId, gitProviderId)) .returning() .then((response) => response[0]);}; // Returns true if the user can edit the git source configuration of an existing// deploy that is connected to the given provider.// Owner/admin: always yes.// Member: only if they own the provider or it's shared with the org.// Being in accessedGitProviders only grants permission to connect NEW deploys,// not to modify the git config of an existing deploy owned by someone else.export const canEditDeployGitSource = async ( gitProviderId: string, session: { userId: string; activeOrganizationId: string },): Promise<boolean> => { const { userId, activeOrganizationId } = session; const memberRecord = await db.query.member.findFirst({ where: and( eq(member.userId, userId), eq(member.organizationId, activeOrganizationId), ), columns: { role: true }, }); if (memberRecord?.role === "owner") return true; const provider = await db.query.gitProvider.findFirst({ where: eq(gitProvider.gitProviderId, gitProviderId), columns: { userId: true, sharedWithOrganization: true }, }); if (!provider) return false; return provider.userId === userId || provider.sharedWithOrganization;}; export const getAccessibleGitProviderIds = async (session: { userId: string; activeOrganizationId: string;}): Promise<Set<string>> => { const { userId, activeOrganizationId } = session; const allOrgProviders = await db.query.gitProvider.findMany({ where: eq(gitProvider.organizationId, activeOrganizationId), columns: { gitProviderId: true, userId: true, sharedWithOrganization: true, }, }); const memberRecord = await db.query.member.findFirst({ where: and( eq(member.userId, userId), eq(member.organizationId, activeOrganizationId), ), columns: { accessedGitProviders: true, role: true }, }); if (memberRecord?.role === "owner" || memberRecord?.role === "admin") { return new Set(allOrgProviders.map((p) => p.gitProviderId)); } const licensed = await hasValidLicense(activeOrganizationId); const assignedSet = licensed ? new Set(memberRecord?.accessedGitProviders ?? []) : new Set<string>(); const result = new Set<string>(); for (const p of allOrgProviders) { if ( p.userId === userId || p.sharedWithOrganization || assignedSet.has(p.gitProviderId) ) { result.add(p.gitProviderId); } } return result;}; /** * Authorizes read access to a specific git provider for the current session. * Throws if the provider belongs to a different organization (cross-org IDOR) * or if the caller is not entitled to it within the active organization. * Must be called before returning any git-provider record that carries secrets * (OAuth tokens, app private keys, webhook secrets). */export const assertGitProviderAccess = async ( session: { userId: string; activeOrganizationId: string }, provider: { gitProviderId: string; organizationId: string },) => { if (provider.organizationId !== session.activeOrganizationId) { throw new TRPCError({ code: "NOT_FOUND", message: "Git provider not found", }); } const accessibleIds = await getAccessibleGitProviderIds(session); if (!accessibleIds.has(provider.gitProviderId)) { throw new TRPCError({ code: "FORBIDDEN", message: "You don't have access to this git provider", }); }};
import { db } from "@dokploy/server/db";
import { gitProvider, member } from "@dokploy/server/db/schema";
import { hasValidLicense } from "@dokploy/server/services/proprietary/license-key";
import { TRPCError } from "@trpc/server";
import { and, eq } from "drizzle-orm";
export type GitProvider = typeof gitProvider.$inferSelect;
export const removeGitProvider = async (gitProviderId: string) => {
const result = await db
.delete(gitProvider)
.where(eq(gitProvider.gitProviderId, gitProviderId))
.returning();
return result[0];
};
export const findGitProviderById = async (gitProviderId: string) => {
const result = await db.query.gitProvider.findFirst({
where: eq(gitProvider.gitProviderId, gitProviderId),
});
if (!result) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Git Provider not found",
});
}
return result;
};
export const updateGitProvider = async (
gitProviderId: string,
input: Partial<GitProvider>,
) => {
return await db
.update(gitProvider)
.set({
...input,
})
.where(eq(gitProvider.gitProviderId, gitProviderId))
.returning()
.then((response) => response[0]);
};
// Returns true if the user can edit the git source configuration of an existing
// deploy that is connected to the given provider.
// Owner/admin: always yes.
// Member: only if they own the provider or it's shared with the org.
// Being in accessedGitProviders only grants permission to connect NEW deploys,
// not to modify the git config of an existing deploy owned by someone else.
export const canEditDeployGitSource = async (
gitProviderId: string,
session: { userId: string; activeOrganizationId: string },
): Promise<boolean> => {
const { userId, activeOrganizationId } = session;
const memberRecord = await db.query.member.findFirst({
where: and(
eq(member.userId, userId),
eq(member.organizationId, activeOrganizationId),
),
columns: { role: true },
});
if (memberRecord?.role === "owner") return true;
const provider = await db.query.gitProvider.findFirst({
where: eq(gitProvider.gitProviderId, gitProviderId),
columns: { userId: true, sharedWithOrganization: true },
});
if (!provider) return false;
return provider.userId === userId || provider.sharedWithOrganization;
};
export const getAccessibleGitProviderIds = async (session: {
userId: string;
activeOrganizationId: string;
}): Promise<Set<string>> => {
const { userId, activeOrganizationId } = session;
const allOrgProviders = await db.query.gitProvider.findMany({
where: eq(gitProvider.organizationId, activeOrganizationId),
columns: {
gitProviderId: true,
userId: true,
sharedWithOrganization: true,
},
});
const memberRecord = await db.query.member.findFirst({
where: and(
eq(member.userId, userId),
eq(member.organizationId, activeOrganizationId),
),
columns: { accessedGitProviders: true, role: true },
});
if (memberRecord?.role === "owner" || memberRecord?.role === "admin") {
return new Set(allOrgProviders.map((p) => p.gitProviderId));
}
const licensed = await hasValidLicense(activeOrganizationId);
const assignedSet = licensed
? new Set(memberRecord?.accessedGitProviders ?? [])
: new Set<string>();
const result = new Set<string>();
for (const p of allOrgProviders) {
if (
p.userId === userId ||
p.sharedWithOrganization ||
assignedSet.has(p.gitProviderId)
) {
result.add(p.gitProviderId);
}
}
return result;
};
/**
* Authorizes read access to a specific git provider for the current session.
* Throws if the provider belongs to a different organization (cross-org IDOR)
* or if the caller is not entitled to it within the active organization.
* Must be called before returning any git-provider record that carries secrets
* (OAuth tokens, app private keys, webhook secrets).
*/
export const assertGitProviderAccess = async (
session: { userId: string; activeOrganizationId: string },
provider: { gitProviderId: string; organizationId: string },
) => {
if (provider.organizationId !== session.activeOrganizationId) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Git provider not found",
});
}
const accessibleIds = await getAccessibleGitProviderIds(session);
if (!accessibleIds.has(provider.gitProviderId)) {
throw new TRPCError({
code: "FORBIDDEN",
message: "You don't have access to this git provider",
});
}
};