From 8f96d0795c4edb0302bcb66da4e3dcc4616e17ad Mon Sep 17 00:00:00 2001 From: Owen Date: Sat, 8 Feb 2025 17:10:37 -0500 Subject: [PATCH] Add update --- server/auth/actions.ts | 3 +- server/routers/external.ts | 7 + server/routers/resource/index.ts | 3 +- server/routers/resource/updateResourceRule.ts | 130 ++++++++++++++++++ 4 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 server/routers/resource/updateResourceRule.ts diff --git a/server/auth/actions.ts b/server/auth/actions.ts index 7c0f5d28..001b9a6c 100644 --- a/server/auth/actions.ts +++ b/server/auth/actions.ts @@ -60,7 +60,8 @@ export enum ActionsEnum { listAccessTokens = "listAccessTokens", createResourceRule = "createResourceRule", deleteResourceRule = "deleteResourceRule", - listResourceRules = "listResourceRules" + listResourceRules = "listResourceRules", + updateResourceRule = "updateResourceRule", } export async function checkUserActionPermission( diff --git a/server/routers/external.ts b/server/routers/external.ts index 73e32911..d7934f9e 100644 --- a/server/routers/external.ts +++ b/server/routers/external.ts @@ -199,6 +199,13 @@ authenticated.get( verifyUserHasAction(ActionsEnum.listResourceRules), resource.listResourceRules ); +authenticated.post( + "/resource/:resourceId/:ruleId", + verifyResourceAccess, + verifyUserHasAction(ActionsEnum.updateResourceRule), + resource.updateResourceRule +); + authenticated.delete( "/resource/:resourceId/:ruleId", verifyResourceAccess, diff --git a/server/routers/resource/index.ts b/server/routers/resource/index.ts index b3e8016f..03c9ffbe 100644 --- a/server/routers/resource/index.ts +++ b/server/routers/resource/index.ts @@ -20,4 +20,5 @@ export * from "./transferResource"; export * from "./getExchangeToken"; export * from "./createResourceRule"; export * from "./deleteResourceRule"; -export * from "./listResourceRules"; \ No newline at end of file +export * from "./listResourceRules"; +export * from "./updateResourceRule"; \ No newline at end of file diff --git a/server/routers/resource/updateResourceRule.ts b/server/routers/resource/updateResourceRule.ts new file mode 100644 index 00000000..f96ea0fa --- /dev/null +++ b/server/routers/resource/updateResourceRule.ts @@ -0,0 +1,130 @@ +import { Request, Response, NextFunction } from "express"; +import { z } from "zod"; +import { db } from "@server/db"; +import { resourceRules, resources } from "@server/db/schema"; +import { eq } from "drizzle-orm"; +import response from "@server/lib/response"; +import HttpCode from "@server/types/HttpCode"; +import createHttpError from "http-errors"; +import logger from "@server/logger"; +import { fromError } from "zod-validation-error"; + +// Define Zod schema for request parameters validation +const updateResourceRuleParamsSchema = z + .object({ + ruleId: z + .string() + .transform(Number) + .pipe(z.number().int().positive()), + resourceId: z + .string() + .transform(Number) + .pipe(z.number().int().positive()) + }) + .strict(); + +// Define Zod schema for request body validation +const updateResourceRuleSchema = z + .object({ + action: z.enum(["ACCEPT", "DROP"]).optional(), + match: z.enum(["CIDR", "PATH"]).optional(), + value: z.string().min(1).optional() + }) + .strict() + .refine((data) => Object.keys(data).length > 0, { + message: "At least one field must be provided for update" + }); + +export async function updateResourceRule( + req: Request, + res: Response, + next: NextFunction +): Promise { + try { + // Validate path parameters + const parsedParams = updateResourceRuleParamsSchema.safeParse(req.params); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + // Validate request body + const parsedBody = updateResourceRuleSchema.safeParse(req.body); + if (!parsedBody.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedBody.error).toString() + ) + ); + } + + const { ruleId, resourceId } = parsedParams.data; + const updateData = parsedBody.data; + + // Verify that the resource exists + const [resource] = await db + .select() + .from(resources) + .where(eq(resources.resourceId, resourceId)) + .limit(1); + + if (!resource) { + return next( + createHttpError( + HttpCode.NOT_FOUND, + `Resource with ID ${resourceId} not found` + ) + ); + } + + // Verify that the rule exists and belongs to the specified resource + const [existingRule] = await db + .select() + .from(resourceRules) + .where(eq(resourceRules.ruleId, ruleId)) + .limit(1); + + if (!existingRule) { + return next( + createHttpError( + HttpCode.NOT_FOUND, + `Resource rule with ID ${ruleId} not found` + ) + ); + } + + if (existingRule.resourceId !== resourceId) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + `Resource rule ${ruleId} does not belong to resource ${resourceId}` + ) + ); + } + + // Update the rule + const [updatedRule] = await db + .update(resourceRules) + .set(updateData) + .where(eq(resourceRules.ruleId, ruleId)) + .returning(); + + return response(res, { + data: updatedRule, + success: true, + error: false, + message: "Resource rule updated successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") + ); + } +} \ No newline at end of file