Ver código fonte

Initial pass at rules

Owen 5 meses atrás
pai
commit
2f49be69fe
2 arquivos alterados com 52 adições e 0 exclusões
  1. 1 0
      server/db/schema.ts
  2. 51 0
      server/routers/badger/verifySession.ts

+ 1 - 0
server/db/schema.ts

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

+ 51 - 0
server/routers/badger/verifySession.ts

@@ -6,6 +6,8 @@ import { fromError } from "zod-validation-error";
 import { response } from "@server/lib/response";
 import db from "@server/db";
 import {
+    BadgerRule,
+    badgerRules,
     ResourceAccessToken,
     ResourcePassword,
     resourcePassword,
@@ -28,6 +30,7 @@ import logger from "@server/logger";
 import { verifyResourceAccessToken } from "@server/auth/verifyResourceAccessToken";
 import NodeCache from "node-cache";
 import { generateSessionToken } from "@server/auth/sessions/app";
+import { isIpInCidr } from "@server/lib/ip";
 
 // We'll see if this speeds anything up
 const cache = new NodeCache({
@@ -79,6 +82,7 @@ export async function verifyResourceSession(
             host,
             originalRequestURL,
             requestIp,
+            path,
             accessToken: token
         } = parsedBody.data;
 
@@ -146,6 +150,15 @@ export async function verifyResourceSession(
             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)}`;
 
         // check for access token
@@ -438,3 +451,41 @@ async function isUserAllowedToAccessResource(
 
     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;
+}