mirror of
https://github.com/fosrl/pangolin.git
synced 2026-08-11 06:58:28 +02:00
@@ -1,8 +1,8 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { resourceRules, resources } from "@server/db";
|
import { resourcePolicyRules, resourceRules, resources } from "@server/db";
|
||||||
import { eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
@@ -22,13 +22,20 @@ const updateResourceRuleParamsSchema = z.strictObject({
|
|||||||
resourceId: z.coerce.number().int().positive()
|
resourceId: z.coerce.number().int().positive()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const resourceRuleMatchSchema = z.enum([
|
||||||
|
"CIDR",
|
||||||
|
"IP",
|
||||||
|
"PATH",
|
||||||
|
"COUNTRY",
|
||||||
|
"ASN",
|
||||||
|
"REGION"
|
||||||
|
]);
|
||||||
|
|
||||||
// Define Zod schema for request body validation
|
// Define Zod schema for request body validation
|
||||||
const updateResourceRuleSchema = z
|
const updateResourceRuleSchema = z
|
||||||
.strictObject({
|
.strictObject({
|
||||||
action: z.enum(["ACCEPT", "DROP", "PASS"]).optional(),
|
action: z.enum(["ACCEPT", "DROP", "PASS"]).optional(),
|
||||||
match: z
|
match: resourceRuleMatchSchema.optional(),
|
||||||
.enum(["CIDR", "IP", "PATH", "COUNTRY", "ASN", "REGION"])
|
|
||||||
.optional(),
|
|
||||||
value: z.string().min(1).optional(),
|
value: z.string().min(1).optional(),
|
||||||
priority: z.int(),
|
priority: z.int(),
|
||||||
enabled: z.boolean().optional()
|
enabled: z.boolean().optional()
|
||||||
@@ -123,37 +130,102 @@ export async function updateResourceRule(
|
|||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
HttpCode.BAD_REQUEST,
|
||||||
"Cannot create rule for non-http resource"
|
"Cannot update rule for non-http resource"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that the rule exists and belongs to the specified resource
|
const isInlinePolicy =
|
||||||
const [existingRule] = await db
|
resource.resourcePolicyId === null &&
|
||||||
.select()
|
resource.defaultResourcePolicyId !== null;
|
||||||
.from(resourceRules)
|
|
||||||
.where(eq(resourceRules.ruleId, ruleId))
|
|
||||||
.limit(1);
|
|
||||||
|
|
||||||
if (!existingRule) {
|
let existingMatch:
|
||||||
return next(
|
| "CIDR"
|
||||||
createHttpError(
|
| "IP"
|
||||||
HttpCode.NOT_FOUND,
|
| "PATH"
|
||||||
`Resource rule with ID ${ruleId} not found`
|
| "COUNTRY"
|
||||||
)
|
| "ASN"
|
||||||
|
| "REGION";
|
||||||
|
|
||||||
|
if (isInlinePolicy) {
|
||||||
|
const policyId = resource.defaultResourcePolicyId!;
|
||||||
|
const [existingRule] = await db
|
||||||
|
.select()
|
||||||
|
.from(resourcePolicyRules)
|
||||||
|
.where(eq(resourcePolicyRules.ruleId, ruleId))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (!existingRule) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.NOT_FOUND,
|
||||||
|
`Resource rule with ID ${ruleId} not found`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existingRule.resourcePolicyId !== policyId) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.FORBIDDEN,
|
||||||
|
`Resource rule ${ruleId} does not belong to resource ${resourceId}`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsedExistingMatch = resourceRuleMatchSchema.safeParse(
|
||||||
|
existingRule.match
|
||||||
);
|
);
|
||||||
|
if (!parsedExistingMatch.success) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"Resource rule has invalid match type"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
existingMatch = parsedExistingMatch.data;
|
||||||
|
} else {
|
||||||
|
// 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}`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsedExistingMatch = resourceRuleMatchSchema.safeParse(
|
||||||
|
existingRule.match
|
||||||
|
);
|
||||||
|
if (!parsedExistingMatch.success) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"Resource rule has invalid match type"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
existingMatch = parsedExistingMatch.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existingRule.resourceId !== resourceId) {
|
const match = updateData.match || existingMatch;
|
||||||
return next(
|
|
||||||
createHttpError(
|
|
||||||
HttpCode.FORBIDDEN,
|
|
||||||
`Resource rule ${ruleId} does not belong to resource ${resourceId}`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const match = updateData.match || existingRule.match;
|
|
||||||
const { value } = updateData;
|
const { value } = updateData;
|
||||||
|
|
||||||
if (value !== undefined) {
|
if (value !== undefined) {
|
||||||
@@ -197,11 +269,30 @@ export async function updateResourceRule(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update the rule
|
// Update the rule
|
||||||
const [updatedRule] = await db
|
const [updatedRule] = isInlinePolicy
|
||||||
.update(resourceRules)
|
? await db
|
||||||
.set(updateData)
|
.update(resourcePolicyRules)
|
||||||
.where(eq(resourceRules.ruleId, ruleId))
|
.set(updateData)
|
||||||
.returning();
|
.where(
|
||||||
|
and(
|
||||||
|
eq(resourcePolicyRules.ruleId, ruleId),
|
||||||
|
eq(
|
||||||
|
resourcePolicyRules.resourcePolicyId,
|
||||||
|
resource.defaultResourcePolicyId!
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.returning()
|
||||||
|
: await db
|
||||||
|
.update(resourceRules)
|
||||||
|
.set(updateData)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(resourceRules.ruleId, ruleId),
|
||||||
|
eq(resourceRules.resourceId, resourceId)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.returning();
|
||||||
|
|
||||||
return response(res, {
|
return response(res, {
|
||||||
data: updatedRule,
|
data: updatedRule,
|
||||||
|
|||||||
Reference in New Issue
Block a user