Initial pass at rules

This commit is contained in:
Owen 2025-02-06 21:42:18 -05:00
parent b92639647a
commit 2f49be69fe
No known key found for this signature in database
GPG key ID: 8271FDFFD9E0CCBD
2 changed files with 52 additions and 0 deletions

View file

@ -414,3 +414,4 @@ export type ResourceOtp = InferSelectModel<typeof resourceOtp>;
export type ResourceAccessToken = InferSelectModel<typeof resourceAccessToken>; export type ResourceAccessToken = InferSelectModel<typeof resourceAccessToken>;
export type ResourceWhitelist = InferSelectModel<typeof resourceWhitelist>; export type ResourceWhitelist = InferSelectModel<typeof resourceWhitelist>;
export type VersionMigration = InferSelectModel<typeof versionMigrations>; export type VersionMigration = InferSelectModel<typeof versionMigrations>;
export type BadgerRule = InferSelectModel<typeof badgerRules>;

View file

@ -6,6 +6,8 @@ import { fromError } from "zod-validation-error";
import { response } from "@server/lib/response"; import { response } from "@server/lib/response";
import db from "@server/db"; import db from "@server/db";
import { import {
BadgerRule,
badgerRules,
ResourceAccessToken, ResourceAccessToken,
ResourcePassword, ResourcePassword,
resourcePassword, resourcePassword,
@ -28,6 +30,7 @@ import logger from "@server/logger";
import { verifyResourceAccessToken } from "@server/auth/verifyResourceAccessToken"; import { verifyResourceAccessToken } from "@server/auth/verifyResourceAccessToken";
import NodeCache from "node-cache"; import NodeCache from "node-cache";
import { generateSessionToken } from "@server/auth/sessions/app"; import { generateSessionToken } from "@server/auth/sessions/app";
import { isIpInCidr } from "@server/lib/ip";
// We'll see if this speeds anything up // We'll see if this speeds anything up
const cache = new NodeCache({ const cache = new NodeCache({
@ -79,6 +82,7 @@ export async function verifyResourceSession(
host, host,
originalRequestURL, originalRequestURL,
requestIp, requestIp,
path,
accessToken: token accessToken: token
} = parsedBody.data; } = parsedBody.data;
@ -146,6 +150,15 @@ export async function verifyResourceSession(
return allowed(res); return allowed(res);
} }
// check the rules
if (
resource.applyRules &&
(await checkRules(resource.resourceId, clientIp, path))
) {
logger.debug("Resource allowed because rules are satisfied");
return allowed(res);
}
const redirectUrl = `${config.getRawConfig().app.dashboard_url}/auth/resource/${encodeURIComponent(resource.resourceId)}?redirect=${encodeURIComponent(originalRequestURL)}`; const redirectUrl = `${config.getRawConfig().app.dashboard_url}/auth/resource/${encodeURIComponent(resource.resourceId)}?redirect=${encodeURIComponent(originalRequestURL)}`;
// check for access token // check for access token
@ -438,3 +451,41 @@ async function isUserAllowedToAccessResource(
return false; return false;
} }
async function checkRules(
resourceId: number,
clientIp: string | undefined,
path: string | undefined
): Promise<boolean> {
const ruleCacheKey = `rules:${resourceId}`;
let rules: BadgerRule[] | undefined = cache.get(ruleCacheKey);
if (!rules) {
rules = await db
.select()
.from(badgerRules)
.where(eq(badgerRules.resourceId, resourceId));
cache.set(ruleCacheKey, rules);
}
if (rules.length === 0) {
logger.debug("No rules found for resource", resourceId);
return false;
}
for (const rule of rules) {
if (clientIp && rule.match == "IP" && isIpInCidr(clientIp, rule.value)) {
return rule.action == "ACCEPT";
} else if (path && rule.match == "PATH") {
// rule.value is a regex, match on the path and see if it matches
const re = new RegExp(rule.value);
if (re.test(path)) {
return rule.action == "ACCEPT";
}
}
}
return false;
}