diff --git a/messages/en-US.json b/messages/en-US.json index ba22ff77..ffd167a6 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -205,6 +205,7 @@ "resourceSetting": "{resourceName} Settings", "alwaysAllow": "Always Allow", "alwaysDeny": "Always Deny", + "passToAuth": "Pass to Auth", "orgSettingsDescription": "Configure your organization's general settings", "orgGeneralSettings": "Organization Settings", "orgGeneralSettingsDescription": "Manage your organization details and configuration", @@ -545,6 +546,7 @@ "rulesActions": "Actions", "rulesActionAlwaysAllow": "Always Allow: Bypass all authentication methods", "rulesActionAlwaysDeny": "Always Deny: Block all requests; no authentication can be attempted", + "rulesActionPassToAuth": "Pass to Auth: Allow authentication methods to be attempted", "rulesMatchCriteria": "Matching Criteria", "rulesMatchCriteriaIpAddress": "Match a specific IP address", "rulesMatchCriteriaIpAddressRange": "Match a range of IP addresses in CIDR notation", diff --git a/server/db/pg/schema.ts b/server/db/pg/schema.ts index fcbf2621..8e725ab1 100644 --- a/server/db/pg/schema.ts +++ b/server/db/pg/schema.ts @@ -430,7 +430,7 @@ export const resourceRules = pgTable("resourceRules", { .references(() => resources.resourceId, { onDelete: "cascade" }), enabled: boolean("enabled").notNull().default(true), priority: integer("priority").notNull(), - action: varchar("action").notNull(), // ACCEPT, DROP + action: varchar("action").notNull(), // ACCEPT, DROP, PASS match: varchar("match").notNull(), // CIDR, PATH, IP value: varchar("value").notNull() }); diff --git a/server/db/sqlite/schema.ts b/server/db/sqlite/schema.ts index 5db6bfdd..c3e79291 100644 --- a/server/db/sqlite/schema.ts +++ b/server/db/sqlite/schema.ts @@ -570,7 +570,7 @@ export const resourceRules = sqliteTable("resourceRules", { .references(() => resources.resourceId, { onDelete: "cascade" }), enabled: integer("enabled", { mode: "boolean" }).notNull().default(true), priority: integer("priority").notNull(), - action: text("action").notNull(), // ACCEPT, DROP + action: text("action").notNull(), // ACCEPT, DROP, PASS match: text("match").notNull(), // CIDR, PATH, IP value: text("value").notNull() }); diff --git a/server/routers/badger/verifySession.ts b/server/routers/badger/verifySession.ts index 62574a45..5f80de16 100644 --- a/server/routers/badger/verifySession.ts +++ b/server/routers/badger/verifySession.ts @@ -178,6 +178,9 @@ export async function verifyResourceSession( } else if (action == "DROP") { logger.debug("Resource denied by rule"); return notAllowed(res); + } else if (action == "PASS") { + logger.debug("Resource passed by rule, continuing to auth checks"); + // Continue to authentication checks below } // otherwise its undefined and we pass @@ -581,7 +584,7 @@ async function checkRules( resourceId: number, clientIp: string | undefined, path: string | undefined -): Promise<"ACCEPT" | "DROP" | undefined> { +): Promise<"ACCEPT" | "DROP" | "PASS" | undefined> { const ruleCacheKey = `rules:${resourceId}`; let rules: ResourceRule[] | undefined = cache.get(ruleCacheKey); diff --git a/server/routers/resource/createResourceRule.ts b/server/routers/resource/createResourceRule.ts index 6651eee2..affd7625 100644 --- a/server/routers/resource/createResourceRule.ts +++ b/server/routers/resource/createResourceRule.ts @@ -17,7 +17,7 @@ import { OpenAPITags, registry } from "@server/openApi"; const createResourceRuleSchema = z .object({ - action: z.enum(["ACCEPT", "DROP"]), + action: z.enum(["ACCEPT", "DROP", "PASS"]), match: z.enum(["CIDR", "IP", "PATH"]), value: z.string().min(1), priority: z.number().int(), diff --git a/server/routers/resource/updateResourceRule.ts b/server/routers/resource/updateResourceRule.ts index 449a92ef..c2b6a47a 100644 --- a/server/routers/resource/updateResourceRule.ts +++ b/server/routers/resource/updateResourceRule.ts @@ -29,7 +29,7 @@ const updateResourceRuleParamsSchema = z // Define Zod schema for request body validation const updateResourceRuleSchema = z .object({ - action: z.enum(["ACCEPT", "DROP"]).optional(), + action: z.enum(["ACCEPT", "DROP", "PASS"]).optional(), match: z.enum(["CIDR", "IP", "PATH"]).optional(), value: z.string().min(1).optional(), priority: z.number().int(), diff --git a/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx index 21c74311..424d7973 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/rules/page.tsx @@ -76,7 +76,7 @@ import { useTranslations } from "next-intl"; // Schema for rule validation const addRuleSchema = z.object({ - action: z.string(), + action: z.enum(["ACCEPT", "DROP", "PASS"]), match: z.string(), value: z.string(), priority: z.coerce.number().int().optional() @@ -104,7 +104,8 @@ export default function ResourceRules(props: { const RuleAction = { ACCEPT: t('alwaysAllow'), - DROP: t('alwaysDeny') + DROP: t('alwaysDeny'), + PASS: t('passToAuth') } as const; const RuleMatch = { @@ -113,7 +114,7 @@ export default function ResourceRules(props: { CIDR: t('ipAddressRange') } as const; - const addRuleForm = useForm({ + const addRuleForm = useForm>({ resolver: zodResolver(addRuleSchema), defaultValues: { action: "ACCEPT", @@ -437,7 +438,7 @@ export default function ResourceRules(props: { cell: ({ row }) => ( ) @@ -629,6 +631,9 @@ export default function ResourceRules(props: { {RuleAction.DROP} + + {RuleAction.PASS} +