mirror of
https://github.com/andwati/dokploy.git
synced 2026-10-25 22:42:18 +00:00
The .one / getRepositories / getBranches / testConnection handlers for github, gitlab, gitea and bitbucket resolved a provider by bare id under protectedProcedure and returned the full row (OAuth tokens, app private keys, webhook secrets) with no organization or entitlement check, letting any authenticated user read another org's git-provider credentials (IDOR). Adds assertGitProviderAccess() in the git-provider service (rejects cross-org with NOT_FOUND and non-entitled same-org with FORBIDDEN, reusing the existing getAccessibleGitProviderIds entitlement logic) and calls it in every read handler before returning secret-bearing data. Closes GHSA-66r5-5m5f-57vg
222 lines
5.1 KiB
TypeScript
222 lines
5.1 KiB
TypeScript
import {
|
|
assertGitProviderAccess,
|
|
createGitea,
|
|
findGiteaById,
|
|
getAccessibleGitProviderIds,
|
|
getGiteaBranches,
|
|
getGiteaRepositories,
|
|
haveGiteaRequirements,
|
|
testGiteaConnection,
|
|
updateGitea,
|
|
updateGitProvider,
|
|
} from "@dokploy/server";
|
|
import { db } from "@dokploy/server/db";
|
|
import { TRPCError } from "@trpc/server";
|
|
import {
|
|
createTRPCRouter,
|
|
protectedProcedure,
|
|
withPermission,
|
|
} from "@/server/api/trpc";
|
|
import { audit } from "@/server/api/utils/audit";
|
|
import {
|
|
apiCreateGitea,
|
|
apiFindGiteaBranches,
|
|
apiFindOneGitea,
|
|
apiGiteaTestConnection,
|
|
apiUpdateGitea,
|
|
} from "@/server/db/schema";
|
|
|
|
export const giteaRouter = createTRPCRouter({
|
|
create: withPermission("gitProviders", "create")
|
|
.input(apiCreateGitea)
|
|
.mutation(async ({ input, ctx }) => {
|
|
try {
|
|
const result = await createGitea(
|
|
input,
|
|
ctx.session.activeOrganizationId,
|
|
ctx.session.userId,
|
|
);
|
|
|
|
await audit(ctx, {
|
|
action: "create",
|
|
resourceType: "gitProvider",
|
|
resourceId: result.giteaId,
|
|
resourceName: input.name,
|
|
});
|
|
|
|
return result;
|
|
} catch (error) {
|
|
throw new TRPCError({
|
|
code: "BAD_REQUEST",
|
|
message: "Error creating this Gitea provider",
|
|
cause: error,
|
|
});
|
|
}
|
|
}),
|
|
|
|
one: protectedProcedure
|
|
.input(apiFindOneGitea)
|
|
.query(async ({ input, ctx }) => {
|
|
const gitea = await findGiteaById(input.giteaId);
|
|
await assertGitProviderAccess(ctx.session, gitea.gitProvider);
|
|
return gitea;
|
|
}),
|
|
|
|
giteaProviders: protectedProcedure.query(async ({ ctx }) => {
|
|
const accessibleIds = await getAccessibleGitProviderIds(ctx.session);
|
|
|
|
let result = await db.query.gitea.findMany({
|
|
with: {
|
|
gitProvider: true,
|
|
},
|
|
});
|
|
|
|
result = result.filter(
|
|
(provider) =>
|
|
provider.gitProvider.organizationId ===
|
|
ctx.session.activeOrganizationId &&
|
|
accessibleIds.has(provider.gitProvider.gitProviderId),
|
|
);
|
|
|
|
const filtered = result
|
|
.filter((provider) => haveGiteaRequirements(provider))
|
|
.map((provider) => {
|
|
return {
|
|
giteaId: provider.giteaId,
|
|
gitProvider: {
|
|
...provider.gitProvider,
|
|
},
|
|
};
|
|
});
|
|
|
|
return filtered;
|
|
}),
|
|
|
|
getGiteaRepositories: protectedProcedure
|
|
.input(apiFindOneGitea)
|
|
.query(async ({ input, ctx }) => {
|
|
const { giteaId } = input;
|
|
|
|
if (!giteaId) {
|
|
throw new TRPCError({
|
|
code: "BAD_REQUEST",
|
|
message: "Gitea provider ID is required.",
|
|
});
|
|
}
|
|
|
|
const gitea = await findGiteaById(giteaId);
|
|
await assertGitProviderAccess(ctx.session, gitea.gitProvider);
|
|
|
|
try {
|
|
const repositories = await getGiteaRepositories(giteaId);
|
|
return repositories;
|
|
} catch (error) {
|
|
console.error("Error fetching Gitea repositories:", error);
|
|
throw new TRPCError({
|
|
code: "BAD_REQUEST",
|
|
message: error instanceof Error ? error.message : String(error),
|
|
});
|
|
}
|
|
}),
|
|
|
|
getGiteaBranches: protectedProcedure
|
|
.input(apiFindGiteaBranches)
|
|
.query(async ({ input, ctx }) => {
|
|
const { giteaId, owner, repositoryName } = input;
|
|
|
|
if (!giteaId || !owner || !repositoryName) {
|
|
throw new TRPCError({
|
|
code: "BAD_REQUEST",
|
|
message:
|
|
"Gitea provider ID, owner, and repository name are required.",
|
|
});
|
|
}
|
|
|
|
const gitea = await findGiteaById(giteaId);
|
|
await assertGitProviderAccess(ctx.session, gitea.gitProvider);
|
|
|
|
try {
|
|
return await getGiteaBranches({
|
|
giteaId,
|
|
owner,
|
|
repo: repositoryName,
|
|
});
|
|
} catch (error) {
|
|
console.error("Error fetching Gitea branches:", error);
|
|
throw new TRPCError({
|
|
code: "BAD_REQUEST",
|
|
message: error instanceof Error ? error.message : String(error),
|
|
});
|
|
}
|
|
}),
|
|
|
|
testConnection: protectedProcedure
|
|
.input(apiGiteaTestConnection)
|
|
.mutation(async ({ input, ctx }) => {
|
|
const giteaId = input.giteaId ?? "";
|
|
|
|
const gitea = await findGiteaById(giteaId);
|
|
await assertGitProviderAccess(ctx.session, gitea.gitProvider);
|
|
|
|
try {
|
|
const result = await testGiteaConnection({
|
|
giteaId,
|
|
});
|
|
|
|
return `Found ${result} repositories`;
|
|
} catch (error) {
|
|
console.error("Gitea connection test error:", error);
|
|
throw new TRPCError({
|
|
code: "BAD_REQUEST",
|
|
message: error instanceof Error ? error.message : String(error),
|
|
});
|
|
}
|
|
}),
|
|
|
|
update: withPermission("gitProviders", "create")
|
|
.input(apiUpdateGitea)
|
|
.mutation(async ({ input, ctx }) => {
|
|
if (input.name) {
|
|
await updateGitProvider(input.gitProviderId, {
|
|
name: input.name,
|
|
organizationId: ctx.session.activeOrganizationId,
|
|
});
|
|
|
|
await updateGitea(input.giteaId, {
|
|
...input,
|
|
});
|
|
} else {
|
|
await updateGitea(input.giteaId, {
|
|
...input,
|
|
});
|
|
}
|
|
|
|
await audit(ctx, {
|
|
action: "update",
|
|
resourceType: "gitProvider",
|
|
resourceId: input.giteaId,
|
|
resourceName: input.name,
|
|
});
|
|
|
|
return { success: true };
|
|
}),
|
|
|
|
getGiteaUrl: protectedProcedure
|
|
.input(apiFindOneGitea)
|
|
.query(async ({ input }) => {
|
|
const { giteaId } = input;
|
|
|
|
if (!giteaId) {
|
|
throw new TRPCError({
|
|
code: "BAD_REQUEST",
|
|
message: "Gitea provider ID is required.",
|
|
});
|
|
}
|
|
|
|
const giteaProvider = await findGiteaById(giteaId);
|
|
|
|
// Return the base URL of the Gitea instance
|
|
return giteaProvider.giteaUrl;
|
|
}),
|
|
});
|