policies and policy on resource structure in a good place

This commit is contained in:
miloschwartz
2026-06-07 12:19:33 -07:00
parent aa47f522ef
commit 3b675f7de1
36 changed files with 1579 additions and 1147 deletions

View File

@@ -1,5 +1,7 @@
import z from "zod";
import ipaddr from "ipaddr.js";
import { COUNTRIES } from "@server/db/countries";
import { isValidRegionId } from "@server/db/regions";
export function isValidCIDR(cidr: string): boolean {
return (
@@ -67,6 +69,45 @@ export function isValidUrlGlobPattern(pattern: string): boolean {
return true;
}
export const RESOURCE_RULE_MATCH_TYPES = [
"CIDR",
"IP",
"PATH",
"COUNTRY",
"ASN",
"REGION"
] as const;
export type ResourceRuleMatchType = (typeof RESOURCE_RULE_MATCH_TYPES)[number];
export function getResourceRuleValueValidationError(
match: ResourceRuleMatchType,
value: string
): string | null {
switch (match) {
case "CIDR":
return isValidCIDR(value) ? null : "Invalid CIDR provided";
case "IP":
return isValidIP(value) ? null : "Invalid IP provided";
case "PATH":
return isValidUrlGlobPattern(value)
? null
: "Invalid URL glob pattern provided";
case "REGION":
return isValidRegionId(value) ? null : "Invalid region ID provided";
case "COUNTRY":
return COUNTRIES.some((country) => country.code === value)
? null
: "Invalid country code provided";
case "ASN":
return /^AS\d+$/i.test(value.trim())
? null
: "Invalid ASN provided";
default:
return "Invalid rule match type provided";
}
}
export function isUrlValid(url: string | undefined) {
if (!url) return true; // the link is optional in the schema so if it's empty it's valid
var pattern = new RegExp(

View File

@@ -33,9 +33,8 @@ import {
import { getUniqueResourcePolicyName } from "@server/db/names";
import response from "@server/lib/response";
import {
isValidCIDR,
isValidIP,
isValidUrlGlobPattern
getResourceRuleValueValidationError,
RESOURCE_RULE_MATCH_TYPES
} from "@server/lib/validators";
import logger from "@server/logger";
import { OpenAPITags, registry } from "@server/openApi";
@@ -56,9 +55,9 @@ const ruleSchema = z.strictObject({
enum: ["ACCEPT", "DROP", "PASS"],
description: "rule action"
}),
match: z.enum(["CIDR", "IP", "PATH"]).openapi({
match: z.enum(RESOURCE_RULE_MATCH_TYPES).openapi({
type: "string",
enum: ["CIDR", "IP", "PATH"],
enum: [...RESOURCE_RULE_MATCH_TYPES],
description: "rule match"
}),
value: z.string().min(1),
@@ -261,26 +260,13 @@ export async function createResourcePolicy(
const niceId = await getUniqueResourcePolicyName(orgId);
for (const rule of rules) {
if (rule.match === "CIDR" && !isValidCIDR(rule.value)) {
const validationError = getResourceRuleValueValidationError(
rule.match,
rule.value
);
if (validationError) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
"Invalid CIDR provided"
)
);
} else if (rule.match === "IP" && !isValidIP(rule.value)) {
return next(
createHttpError(HttpCode.BAD_REQUEST, "Invalid IP provided")
);
} else if (
rule.match === "PATH" &&
!isValidUrlGlobPattern(rule.value)
) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
"Invalid URL glob pattern provided"
)
createHttpError(HttpCode.BAD_REQUEST, validationError)
);
}
}

View File

@@ -666,6 +666,13 @@ authenticated.get(
resource.getResourcePolicies
);
authenticated.get(
"/resource-policy/:resourcePolicyId",
verifyResourcePolicyAccess,
verifyUserHasAction(ActionsEnum.getResourcePolicy),
policy.getResourcePolicy
);
authenticated.put(
"/resource-policy/:resourcePolicyId",
verifyResourcePolicyAccess,

View File

@@ -8,9 +8,8 @@ import createHttpError from "http-errors";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
import {
isValidCIDR,
isValidIP,
isValidUrlGlobPattern
getResourceRuleValueValidationError,
RESOURCE_RULE_MATCH_TYPES
} from "@server/lib/validators";
import { OpenAPITags, registry } from "@server/openApi";
@@ -20,9 +19,9 @@ const ruleSchema = z.strictObject({
enum: ["ACCEPT", "DROP", "PASS"],
description: "rule action"
}),
match: z.enum(["CIDR", "IP", "PATH"]).openapi({
match: z.enum(RESOURCE_RULE_MATCH_TYPES).openapi({
type: "string",
enum: ["CIDR", "IP", "PATH"],
enum: [...RESOURCE_RULE_MATCH_TYPES],
description: "rule match"
}),
value: z.string().min(1),
@@ -105,26 +104,13 @@ export async function setResourcePolicyRules(
}
for (const rule of rules) {
if (rule.match === "CIDR" && !isValidCIDR(rule.value)) {
const validationError = getResourceRuleValueValidationError(
rule.match,
rule.value
);
if (validationError) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
"Invalid CIDR provided"
)
);
} else if (rule.match === "IP" && !isValidIP(rule.value)) {
return next(
createHttpError(HttpCode.BAD_REQUEST, "Invalid IP provided")
);
} else if (
rule.match === "PATH" &&
!isValidUrlGlobPattern(rule.value)
) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
"Invalid URL glob pattern provided"
)
createHttpError(HttpCode.BAD_REQUEST, validationError)
);
}
}