From 5afff3c6628609263f594ad2ce50246441ccccc1 Mon Sep 17 00:00:00 2001 From: miloschwartz Date: Wed, 3 Dec 2025 15:50:24 -0500 Subject: [PATCH] add extra org policy checks to middlewares --- server/index.ts | 1 + server/middlewares/verifyAccessTokenAccess.ts | 19 ++++++++++++ server/middlewares/verifyAdmin.ts | 19 ++++++++++++ server/middlewares/verifyApiKeyAccess.ts | 19 ++++++++++++ server/middlewares/verifyClientAccess.ts | 19 ++++++++++++ server/middlewares/verifyDomainAccess.ts | 19 ++++++++++++ server/middlewares/verifyOrgAccess.ts | 30 ++++++++++--------- server/middlewares/verifyResourceAccess.ts | 26 ++++++++++++---- server/middlewares/verifyRoleAccess.ts | 29 +++++++++++++++++- .../middlewares/verifySetResourceClients.ts | 27 +++++++++++++++-- server/middlewares/verifySetResourceUsers.ts | 19 ++++++++++++ server/middlewares/verifySiteAccess.ts | 27 ++++++++++++----- .../middlewares/verifySiteResourceAccess.ts | 19 ++++++++++++ server/middlewares/verifyTargetAccess.ts | 21 +++++++++++++ server/middlewares/verifyUserAccess.ts | 19 ++++++++++++ server/routers/olm/handleOlmPingMessage.ts | 2 +- .../routers/olm/handleOlmRegisterMessage.ts | 2 +- .../olm/handleOlmServerPeerAddMessage.ts | 2 +- 18 files changed, 285 insertions(+), 34 deletions(-) diff --git a/server/index.ts b/server/index.ts index 012a4bbb..a61daca7 100644 --- a/server/index.ts +++ b/server/index.ts @@ -79,6 +79,7 @@ declare global { userOrgIds?: string[]; remoteExitNode?: RemoteExitNode; siteResource?: SiteResource; + orgPolicyAllowed?: boolean; } } } diff --git a/server/middlewares/verifyAccessTokenAccess.ts b/server/middlewares/verifyAccessTokenAccess.ts index 92873524..033b326d 100644 --- a/server/middlewares/verifyAccessTokenAccess.ts +++ b/server/middlewares/verifyAccessTokenAccess.ts @@ -5,6 +5,7 @@ import { and, eq } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; import { canUserAccessResource } from "@server/auth/canUserAccessResource"; +import { checkOrgAccessPolicy } from "#dynamic/lib/checkOrgAccessPolicy"; export async function verifyAccessTokenAccess( req: Request, @@ -96,6 +97,24 @@ export async function verifyAccessTokenAccess( req.userOrgId = resource[0].orgId!; } + if (req.orgPolicyAllowed === undefined && req.userOrg.orgId) { + const policyCheck = await checkOrgAccessPolicy({ + orgId: req.userOrg.orgId, + userId, + session: req.session + }); + req.orgPolicyAllowed = policyCheck.allowed; + if (!policyCheck.allowed || policyCheck.error) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "Failed organization access policy check: " + + (policyCheck.error || "Unknown error") + ) + ); + } + } + const resourceAllowed = await canUserAccessResource({ userId, resourceId, diff --git a/server/middlewares/verifyAdmin.ts b/server/middlewares/verifyAdmin.ts index 60f7334c..253bfc2d 100644 --- a/server/middlewares/verifyAdmin.ts +++ b/server/middlewares/verifyAdmin.ts @@ -4,6 +4,7 @@ import { roles, userOrgs } from "@server/db"; import { and, eq } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; +import { checkOrgAccessPolicy } from "#dynamic/lib/checkOrgAccessPolicy"; export async function verifyAdmin( req: Request, @@ -43,6 +44,24 @@ export async function verifyAdmin( ); } + if (req.orgPolicyAllowed === undefined && req.userOrg.orgId) { + const policyCheck = await checkOrgAccessPolicy({ + orgId: req.userOrg.orgId, + userId, + session: req.session + }); + req.orgPolicyAllowed = policyCheck.allowed; + if (!policyCheck.allowed || policyCheck.error) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "Failed organization access policy check: " + + (policyCheck.error || "Unknown error") + ) + ); + } + } + const userRole = await db .select() .from(roles) diff --git a/server/middlewares/verifyApiKeyAccess.ts b/server/middlewares/verifyApiKeyAccess.ts index 8ab709b6..6edc5ab8 100644 --- a/server/middlewares/verifyApiKeyAccess.ts +++ b/server/middlewares/verifyApiKeyAccess.ts @@ -4,6 +4,7 @@ import { userOrgs, apiKeys, apiKeyOrg } from "@server/db"; import { and, eq, or } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; +import { checkOrgAccessPolicy } from "#dynamic/lib/checkOrgAccessPolicy"; export async function verifyApiKeyAccess( req: Request, @@ -84,6 +85,24 @@ export async function verifyApiKeyAccess( ); } + if (req.orgPolicyAllowed === undefined && req.userOrg.orgId) { + const policyCheck = await checkOrgAccessPolicy({ + orgId: req.userOrg.orgId, + userId, + session: req.session + }); + req.orgPolicyAllowed = policyCheck.allowed; + if (!policyCheck.allowed || policyCheck.error) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "Failed organization access policy check: " + + (policyCheck.error || "Unknown error") + ) + ); + } + } + const userOrgRoleId = req.userOrg.roleId; req.userOrgRoleId = userOrgRoleId; diff --git a/server/middlewares/verifyClientAccess.ts b/server/middlewares/verifyClientAccess.ts index df45b541..ab65ba2a 100644 --- a/server/middlewares/verifyClientAccess.ts +++ b/server/middlewares/verifyClientAccess.ts @@ -4,6 +4,7 @@ import { userOrgs, clients, roleClients, userClients } from "@server/db"; import { and, eq } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; +import { checkOrgAccessPolicy } from "#dynamic/lib/checkOrgAccessPolicy"; export async function verifyClientAccess( req: Request, @@ -75,6 +76,24 @@ export async function verifyClientAccess( ); } + if (req.orgPolicyAllowed === undefined && req.userOrg.orgId) { + const policyCheck = await checkOrgAccessPolicy({ + orgId: req.userOrg.orgId, + userId, + session: req.session + }); + req.orgPolicyAllowed = policyCheck.allowed; + if (!policyCheck.allowed || policyCheck.error) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "Failed organization access policy check: " + + (policyCheck.error || "Unknown error") + ) + ); + } + } + const userOrgRoleId = req.userOrg.roleId; req.userOrgRoleId = userOrgRoleId; req.userOrgId = client.orgId; diff --git a/server/middlewares/verifyDomainAccess.ts b/server/middlewares/verifyDomainAccess.ts index a6daf451..88ffe678 100644 --- a/server/middlewares/verifyDomainAccess.ts +++ b/server/middlewares/verifyDomainAccess.ts @@ -4,6 +4,7 @@ import { userOrgs, apiKeyOrg } from "@server/db"; import { and, eq } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; +import { checkOrgAccessPolicy } from "#dynamic/lib/checkOrgAccessPolicy"; export async function verifyDomainAccess( req: Request, @@ -78,6 +79,24 @@ export async function verifyDomainAccess( ); } + if (req.orgPolicyAllowed === undefined && req.userOrg.orgId) { + const policyCheck = await checkOrgAccessPolicy({ + orgId: req.userOrg.orgId, + userId, + session: req.session + }); + req.orgPolicyAllowed = policyCheck.allowed; + if (!policyCheck.allowed || policyCheck.error) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "Failed organization access policy check: " + + (policyCheck.error || "Unknown error") + ) + ); + } + } + const userOrgRoleId = req.userOrg.roleId; req.userOrgRoleId = userOrgRoleId; diff --git a/server/middlewares/verifyOrgAccess.ts b/server/middlewares/verifyOrgAccess.ts index 3acdad6c..c5224ae5 100644 --- a/server/middlewares/verifyOrgAccess.ts +++ b/server/middlewares/verifyOrgAccess.ts @@ -47,20 +47,22 @@ export async function verifyOrgAccess( ); } - const policyCheck = await checkOrgAccessPolicy({ - orgId, - userId, - session: req.session - }); - - if (!policyCheck.allowed || policyCheck.error) { - return next( - createHttpError( - HttpCode.FORBIDDEN, - "Failed organization access policy check: " + - (policyCheck.error || "Unknown error") - ) - ); + if (req.orgPolicyAllowed === undefined) { + const policyCheck = await checkOrgAccessPolicy({ + orgId, + userId, + session: req.session + }); + req.orgPolicyAllowed = policyCheck.allowed; + if (!policyCheck.allowed || policyCheck.error) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "Failed organization access policy check: " + + (policyCheck.error || "Unknown error") + ) + ); + } } // User has access, attach the user's role to the request for potential future use diff --git a/server/middlewares/verifyResourceAccess.ts b/server/middlewares/verifyResourceAccess.ts index 5c88139d..9b0763ab 100644 --- a/server/middlewares/verifyResourceAccess.ts +++ b/server/middlewares/verifyResourceAccess.ts @@ -1,14 +1,10 @@ import { Request, Response, NextFunction } from "express"; import { db } from "@server/db"; -import { - resources, - userOrgs, - userResources, - roleResources, -} from "@server/db"; +import { resources, userOrgs, userResources, roleResources } from "@server/db"; import { and, eq } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; +import { checkOrgAccessPolicy } from "#dynamic/lib/checkOrgAccessPolicy"; export async function verifyResourceAccess( req: Request, @@ -73,6 +69,24 @@ export async function verifyResourceAccess( ); } + if (req.orgPolicyAllowed === undefined && req.userOrg.orgId) { + const policyCheck = await checkOrgAccessPolicy({ + orgId: req.userOrg.orgId, + userId, + session: req.session + }); + req.orgPolicyAllowed = policyCheck.allowed; + if (!policyCheck.allowed || policyCheck.error) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "Failed organization access policy check: " + + (policyCheck.error || "Unknown error") + ) + ); + } + } + const userOrgRoleId = req.userOrg.roleId; req.userOrgRoleId = userOrgRoleId; req.userOrgId = resource[0].orgId; diff --git a/server/middlewares/verifyRoleAccess.ts b/server/middlewares/verifyRoleAccess.ts index 98681644..91adf07c 100644 --- a/server/middlewares/verifyRoleAccess.ts +++ b/server/middlewares/verifyRoleAccess.ts @@ -5,6 +5,7 @@ import { and, eq, inArray } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; import logger from "@server/logger"; +import { checkOrgAccessPolicy } from "#dynamic/lib/checkOrgAccessPolicy"; export async function verifyRoleAccess( req: Request, @@ -105,6 +106,33 @@ export async function verifyRoleAccess( req.userOrgRoleId = userOrg[0].roleId; } + if (!req.userOrg) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "User does not have access to this organization" + ) + ); + } + + if (req.orgPolicyAllowed === undefined && req.userOrg.orgId) { + const policyCheck = await checkOrgAccessPolicy({ + orgId: req.userOrg.orgId, + userId, + session: req.session + }); + req.orgPolicyAllowed = policyCheck.allowed; + if (!policyCheck.allowed || policyCheck.error) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "Failed organization access policy check: " + + (policyCheck.error || "Unknown error") + ) + ); + } + } + return next(); } catch (error) { logger.error("Error verifying role access:", error); @@ -116,4 +144,3 @@ export async function verifyRoleAccess( ); } } - diff --git a/server/middlewares/verifySetResourceClients.ts b/server/middlewares/verifySetResourceClients.ts index d078391d..8f9c1eca 100644 --- a/server/middlewares/verifySetResourceClients.ts +++ b/server/middlewares/verifySetResourceClients.ts @@ -4,6 +4,7 @@ import { clients } from "@server/db"; import { and, eq, inArray } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; +import { checkOrgAccessPolicy } from "#dynamic/lib/checkOrgAccessPolicy"; export async function verifySetResourceClients( req: Request, @@ -11,9 +12,12 @@ export async function verifySetResourceClients( next: NextFunction ) { const userId = req.user!.userId; - const singleClientId = req.params.clientId || req.body.clientId || req.query.clientId; + const singleClientId = + req.params.clientId || req.body.clientId || req.query.clientId; const { clientIds } = req.body; - const allClientIds = clientIds || (singleClientId ? [parseInt(singleClientId as string)] : []); + const allClientIds = + clientIds || + (singleClientId ? [parseInt(singleClientId as string)] : []); if (!userId) { return next( @@ -30,6 +34,24 @@ export async function verifySetResourceClients( ); } + if (req.orgPolicyAllowed === undefined && req.userOrg.orgId) { + const policyCheck = await checkOrgAccessPolicy({ + orgId: req.userOrg.orgId, + userId, + session: req.session + }); + req.orgPolicyAllowed = policyCheck.allowed; + if (!policyCheck.allowed || policyCheck.error) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "Failed organization access policy check: " + + (policyCheck.error || "Unknown error") + ) + ); + } + } + if (allClientIds.length === 0) { return next(); } @@ -66,4 +88,3 @@ export async function verifySetResourceClients( ); } } - diff --git a/server/middlewares/verifySetResourceUsers.ts b/server/middlewares/verifySetResourceUsers.ts index be6d21fc..94600b9b 100644 --- a/server/middlewares/verifySetResourceUsers.ts +++ b/server/middlewares/verifySetResourceUsers.ts @@ -4,6 +4,7 @@ import { userOrgs } from "@server/db"; import { and, eq, inArray, or } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; +import { checkOrgAccessPolicy } from "#dynamic/lib/checkOrgAccessPolicy"; export async function verifySetResourceUsers( req: Request, @@ -28,6 +29,24 @@ export async function verifySetResourceUsers( ); } + if (req.orgPolicyAllowed === undefined && req.userOrg.orgId) { + const policyCheck = await checkOrgAccessPolicy({ + orgId: req.userOrg.orgId, + userId, + session: req.session + }); + req.orgPolicyAllowed = policyCheck.allowed; + if (!policyCheck.allowed || policyCheck.error) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "Failed organization access policy check: " + + (policyCheck.error || "Unknown error") + ) + ); + } + } + if (!userIds) { return next(createHttpError(HttpCode.BAD_REQUEST, "Invalid user IDs")); } diff --git a/server/middlewares/verifySiteAccess.ts b/server/middlewares/verifySiteAccess.ts index 6d01392f..06f06a93 100644 --- a/server/middlewares/verifySiteAccess.ts +++ b/server/middlewares/verifySiteAccess.ts @@ -1,16 +1,11 @@ import { Request, Response, NextFunction } from "express"; import { db } from "@server/db"; -import { - sites, - userOrgs, - userSites, - roleSites, - roles, -} from "@server/db"; +import { sites, userOrgs, userSites, roleSites, roles } from "@server/db"; import { and, eq, or } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; import logger from "@server/logger"; +import { checkOrgAccessPolicy } from "#dynamic/lib/checkOrgAccessPolicy"; export async function verifySiteAccess( req: Request, @@ -82,6 +77,24 @@ export async function verifySiteAccess( ); } + if (req.orgPolicyAllowed === undefined && req.userOrg.orgId) { + const policyCheck = await checkOrgAccessPolicy({ + orgId: req.userOrg.orgId, + userId, + session: req.session + }); + req.orgPolicyAllowed = policyCheck.allowed; + if (!policyCheck.allowed || policyCheck.error) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "Failed organization access policy check: " + + (policyCheck.error || "Unknown error") + ) + ); + } + } + const userOrgRoleId = req.userOrg.roleId; req.userOrgRoleId = userOrgRoleId; req.userOrgId = site[0].orgId; diff --git a/server/middlewares/verifySiteResourceAccess.ts b/server/middlewares/verifySiteResourceAccess.ts index 82b0a3ce..ca7d37fb 100644 --- a/server/middlewares/verifySiteResourceAccess.ts +++ b/server/middlewares/verifySiteResourceAccess.ts @@ -5,6 +5,7 @@ import { eq, and } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; import logger from "@server/logger"; +import { checkOrgAccessPolicy } from "#dynamic/lib/checkOrgAccessPolicy"; export async function verifySiteResourceAccess( req: Request, @@ -90,6 +91,24 @@ export async function verifySiteResourceAccess( ); } + if (req.orgPolicyAllowed === undefined && req.userOrg.orgId) { + const policyCheck = await checkOrgAccessPolicy({ + orgId: req.userOrg.orgId, + userId, + session: req.session + }); + req.orgPolicyAllowed = policyCheck.allowed; + if (!policyCheck.allowed || policyCheck.error) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "Failed organization access policy check: " + + (policyCheck.error || "Unknown error") + ) + ); + } + } + const userOrgRoleId = req.userOrg.roleId; req.userOrgRoleId = userOrgRoleId; req.userOrgId = siteResource.orgId; diff --git a/server/middlewares/verifyTargetAccess.ts b/server/middlewares/verifyTargetAccess.ts index 50563d6e..7e433fcb 100644 --- a/server/middlewares/verifyTargetAccess.ts +++ b/server/middlewares/verifyTargetAccess.ts @@ -5,6 +5,7 @@ import { and, eq } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; import { canUserAccessResource } from "../auth/canUserAccessResource"; +import { checkOrgAccessPolicy } from "#dynamic/lib/checkOrgAccessPolicy"; export async function verifyTargetAccess( req: Request, @@ -102,6 +103,26 @@ export async function verifyTargetAccess( req.userOrgId = resource[0].orgId!; } + const orgId = req.userOrg.orgId; + + if (req.orgPolicyAllowed === undefined && orgId) { + const policyCheck = await checkOrgAccessPolicy({ + orgId, + userId, + session: req.session + }); + req.orgPolicyAllowed = policyCheck.allowed; + if (!policyCheck.allowed || policyCheck.error) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "Failed organization access policy check: " + + (policyCheck.error || "Unknown error") + ) + ); + } + } + const resourceAllowed = await canUserAccessResource({ userId, resourceId, diff --git a/server/middlewares/verifyUserAccess.ts b/server/middlewares/verifyUserAccess.ts index 3ef0f0ba..fcc4d0cb 100644 --- a/server/middlewares/verifyUserAccess.ts +++ b/server/middlewares/verifyUserAccess.ts @@ -4,6 +4,7 @@ import { userOrgs } from "@server/db"; import { and, eq, or } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; +import { checkOrgAccessPolicy } from "#dynamic/lib/checkOrgAccessPolicy"; export async function verifyUserAccess( req: Request, @@ -47,6 +48,24 @@ export async function verifyUserAccess( ); } + if (req.orgPolicyAllowed === undefined && req.userOrg.orgId) { + const policyCheck = await checkOrgAccessPolicy({ + orgId: req.userOrg.orgId, + userId, + session: req.session + }); + req.orgPolicyAllowed = policyCheck.allowed; + if (!policyCheck.allowed || policyCheck.error) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "Failed organization access policy check: " + + (policyCheck.error || "Unknown error") + ) + ); + } + } + return next(); } catch (error) { return next( diff --git a/server/routers/olm/handleOlmPingMessage.ts b/server/routers/olm/handleOlmPingMessage.ts index 4bcbbb8b..632b0f3f 100644 --- a/server/routers/olm/handleOlmPingMessage.ts +++ b/server/routers/olm/handleOlmPingMessage.ts @@ -5,7 +5,7 @@ import { clients, Olm } from "@server/db"; import { eq, lt, isNull, and, or } from "drizzle-orm"; import logger from "@server/logger"; import { validateSessionToken } from "@server/auth/sessions/app"; -import { checkOrgAccessPolicy } from "@server/lib/checkOrgAccessPolicy"; +import { checkOrgAccessPolicy } from "#dynamic/lib/checkOrgAccessPolicy"; import { sendTerminateClient } from "../client/terminate"; // Track if the offline checker interval is running diff --git a/server/routers/olm/handleOlmRegisterMessage.ts b/server/routers/olm/handleOlmRegisterMessage.ts index 696da748..4dd49ea6 100644 --- a/server/routers/olm/handleOlmRegisterMessage.ts +++ b/server/routers/olm/handleOlmRegisterMessage.ts @@ -32,7 +32,7 @@ import { } from "@server/lib/ip"; import { generateRemoteSubnets } from "@server/lib/ip"; import { rebuildClientAssociationsFromClient } from "@server/lib/rebuildClientAssociations"; -import { checkOrgAccessPolicy } from "@server/lib/checkOrgAccessPolicy"; +import { checkOrgAccessPolicy } from "#dynamic/lib/checkOrgAccessPolicy"; import { validateSessionToken } from "@server/auth/sessions/app"; import config from "@server/lib/config"; diff --git a/server/routers/olm/handleOlmServerPeerAddMessage.ts b/server/routers/olm/handleOlmServerPeerAddMessage.ts index 2e5009eb..a6d73099 100644 --- a/server/routers/olm/handleOlmServerPeerAddMessage.ts +++ b/server/routers/olm/handleOlmServerPeerAddMessage.ts @@ -32,7 +32,7 @@ import { } from "@server/lib/ip"; import { generateRemoteSubnets } from "@server/lib/ip"; import { rebuildClientAssociationsFromClient } from "@server/lib/rebuildClientAssociations"; -import { checkOrgAccessPolicy } from "@server/lib/checkOrgAccessPolicy"; +import { checkOrgAccessPolicy } from "#dynamic/lib/checkOrgAccessPolicy"; import { validateSessionToken } from "@server/auth/sessions/app"; import config from "@server/lib/config"; import {