Files
dokploy/apps/dokploy/server/api/routers/registry.ts
Mauricio Siu 8127dc4536 feat: add comprehensive permission tests and enhance permission checks in components
- Introduced new test files for permission checks, including `check-permission.test.ts`, `enterprise-only-resources.test.ts`, `resolve-permissions.test.ts`, and `service-access.test.ts`.
- Implemented permission checks in various components to ensure actions are gated by user permissions, including `ShowTraefikConfig`, `UpdateTraefikConfig`, `ShowVolumes`, `ShowDomains`, and others.
- Enhanced the logic for displaying UI elements based on user permissions, ensuring that only authorized users can access or modify resources.
2026-03-15 16:42:48 -06:00

206 lines
5.2 KiB
TypeScript

import {
createRegistry,
execAsyncRemote,
execFileAsync,
findRegistryById,
IS_CLOUD,
removeRegistry,
updateRegistry,
} from "@dokploy/server";
import { db } from "@dokploy/server/db";
import { TRPCError } from "@trpc/server";
import { eq } from "drizzle-orm";
import {
apiCreateRegistry,
apiFindOneRegistry,
apiRemoveRegistry,
apiTestRegistry,
apiTestRegistryById,
apiUpdateRegistry,
registry,
} from "@/server/db/schema";
import { audit } from "@/server/api/utils/audit";
import { createTRPCRouter, withPermission } from "../trpc";
export const registryRouter = createTRPCRouter({
create: withPermission("registry", "create")
.input(apiCreateRegistry)
.mutation(async ({ ctx, input }) => {
const reg = await createRegistry(input, ctx.session.activeOrganizationId);
await audit(ctx, {
action: "create",
resourceType: "registry",
resourceId: reg.registryId,
resourceName: reg.registryName,
});
return reg;
}),
remove: withPermission("registry", "delete")
.input(apiRemoveRegistry)
.mutation(async ({ ctx, input }) => {
const registry = await findRegistryById(input.registryId);
if (registry.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to delete this registry",
});
}
await audit(ctx, {
action: "delete",
resourceType: "registry",
resourceId: registry.registryId,
resourceName: registry.registryName,
});
return await removeRegistry(input.registryId);
}),
update: withPermission("registry", "create")
.input(apiUpdateRegistry)
.mutation(async ({ input, ctx }) => {
const { registryId, ...rest } = input;
const registry = await findRegistryById(registryId);
if (registry.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to update this registry",
});
}
const application = await updateRegistry(registryId, {
...rest,
});
if (!application) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error updating registry",
});
}
await audit(ctx, {
action: "update",
resourceType: "registry",
resourceId: registryId,
resourceName: registry.registryName,
});
return true;
}),
all: withPermission("registry", "read").query(async ({ ctx }) => {
const registryResponse = await db.query.registry.findMany({
where: eq(registry.organizationId, ctx.session.activeOrganizationId),
});
return registryResponse;
}),
one: withPermission("registry", "read")
.input(apiFindOneRegistry)
.query(async ({ input, ctx }) => {
const registry = await findRegistryById(input.registryId);
if (registry.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to access this registry",
});
}
return registry;
}),
testRegistry: withPermission("registry", "read")
.input(apiTestRegistry)
.mutation(async ({ input }) => {
try {
const args = [
"login",
input.registryUrl,
"--username",
input.username,
"--password-stdin",
];
if (IS_CLOUD && !input.serverId) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Select a server to test the registry",
});
}
if (input.serverId && input.serverId !== "none") {
await execAsyncRemote(
input.serverId,
`echo ${input.password} | docker ${args.join(" ")}`,
);
} else {
await execFileAsync("docker", args, {
input: Buffer.from(input.password).toString(),
});
}
return true;
} catch (error) {
throw new TRPCError({
code: "BAD_REQUEST",
message:
error instanceof Error
? error.message
: "Error testing the registry",
cause: error,
});
}
}),
testRegistryById: withPermission("registry", "read")
.input(apiTestRegistryById)
.mutation(async ({ input, ctx }) => {
try {
const registryData = await db.query.registry.findFirst({
where: eq(registry.registryId, input.registryId ?? ""),
});
if (!registryData) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Registry not found",
});
}
if (registryData.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to test this registry",
});
}
const args = [
"login",
registryData.registryUrl,
"--username",
registryData.username,
"--password-stdin",
];
if (IS_CLOUD && !input.serverId) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Select a server to test the registry",
});
}
if (input.serverId && input.serverId !== "none") {
await execAsyncRemote(
input.serverId,
`echo ${registryData.password} | docker ${args.join(" ")}`,
);
} else {
await execFileAsync("docker", args, {
input: Buffer.from(registryData.password).toString(),
});
}
return true;
} catch (error) {
throw new TRPCError({
code: "BAD_REQUEST",
message:
error instanceof Error
? error.message
: "Error testing the registry",
cause: error,
});
}
}),
});