mirror of
https://github.com/fosrl/pangolin.git
synced 2025-08-18 08:18:43 +02:00
Initial pass at rules
This commit is contained in:
parent
b92639647a
commit
2f49be69fe
2 changed files with 52 additions and 0 deletions
|
@ -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>;
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue