diff --git a/server/private/lib/checkOrgAccessPolicy.ts b/server/private/lib/checkOrgAccessPolicy.ts index bc2d9cca..5c212ac8 100644 --- a/server/private/lib/checkOrgAccessPolicy.ts +++ b/server/private/lib/checkOrgAccessPolicy.ts @@ -95,6 +95,13 @@ export async function checkOrgAccessPolicy( } } + if (props.session.userId !== props.user.userId) { + return { + allowed: false, + error: "Session does not belong to the user" + }; + } + // now check the policies const policies: CheckOrgAccessPolicyResult["policies"] = {}; diff --git a/server/private/routers/hybrid.ts b/server/private/routers/hybrid.ts index df99df92..cf17cce3 100644 --- a/server/private/routers/hybrid.ts +++ b/server/private/routers/hybrid.ts @@ -73,6 +73,8 @@ import { validateResourceSessionToken } from "@server/auth/sessions/resource"; import { checkExitNodeOrg, resolveExitNodes } from "#private/lib/exitNodes"; import { maxmindLookup } from "@server/db/maxmind"; import { verifyResourceAccessToken } from "@server/auth/verifyResourceAccessToken"; +import { checkOrgAccessPolicy } from "#dynamic/lib/checkOrgAccessPolicy"; +import { CheckOrgAccessPolicyResult } from "@server/lib/checkOrgAccessPolicy"; // Zod schemas for request validation const getResourceByDomainParamsSchema = z @@ -300,7 +302,8 @@ function loadEncryptData() { return; // already loaded } - encryptionKeyPath = privateConfig.getRawPrivateConfig().server.encryption_key_path; + encryptionKeyPath = + privateConfig.getRawPrivateConfig().server.encryption_key_path; if (!fs.existsSync(encryptionKeyPath)) { throw new Error( @@ -1582,3 +1585,90 @@ hybridRouter.post( } } ); + +const getOrgAccessPolicyParamsSchema = z + .object({ + orgId: z.string().min(1), + userId: z.string().min(1) + }) + .strict(); + +const getOrgAccessPolicyBodySchema = z + .object({ + sessionId: z.string().min(1) + }) + .strict(); + +hybridRouter.get( + "/org/:orgId/user/:userId/access", + async (req: Request, res: Response, next: NextFunction) => { + try { + const parsedParams = getOrgAccessPolicyParamsSchema.safeParse( + req.params + ); + if (!parsedParams.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedParams.error).toString() + ) + ); + } + + const parsedBody = getOrgAccessPolicyBodySchema.safeParse(req.body); + if (!parsedBody.success) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + fromError(parsedBody.error).toString() + ) + ); + } + + const { orgId, userId } = parsedParams.data; + const { sessionId } = parsedBody.data; + const remoteExitNode = req.remoteExitNode; + + if (!remoteExitNode?.exitNodeId) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Remote exit node not found" + ) + ); + } + + if (await checkExitNodeOrg(remoteExitNode.exitNodeId, orgId)) { + // If the exit node is not allowed for the org, return an error + return next( + createHttpError( + HttpCode.FORBIDDEN, + "Exit node not allowed for this organization" + ) + ); + } + + const accessPolicy = await checkOrgAccessPolicy({ + orgId, + userId, + sessionId + }); + + return response(res, { + data: accessPolicy, + success: true, + error: false, + message: "Org access policy retrieved successfully", + status: HttpCode.OK + }); + } catch (error) { + logger.error(error); + return next( + createHttpError( + HttpCode.INTERNAL_SERVER_ERROR, + "Failed to get org login page" + ) + ); + } + } +); diff --git a/server/routers/badger/verifySession.ts b/server/routers/badger/verifySession.ts index 523163e6..e3a34eb1 100644 --- a/server/routers/badger/verifySession.ts +++ b/server/routers/badger/verifySession.ts @@ -37,6 +37,7 @@ import { getCountryCodeForIp } from "@server/lib/geoip"; import { getOrgTierData } from "#dynamic/lib/billing"; import { TierId } from "@server/lib/billing/tiers"; import { verifyPassword } from "@server/auth/password"; +import { checkOrgAccessPolicy } from "#dynamic/lib/checkOrgAccessPolicy"; // We'll see if this speeds anything up const cache = new NodeCache({ @@ -313,7 +314,8 @@ export async function verifyResourceSession( return allowed(res); } - if ( // we dont want to redirect if this is the only auth method and we did not pass here + if ( + // we dont want to redirect if this is the only auth method and we did not pass here !sso && !pincode && !password && @@ -589,6 +591,18 @@ async function isUserAllowedToAccessResource( return null; } + const accessPolicy = await checkOrgAccessPolicy({ + orgId: resource.orgId, + userId: user.userId, + sessionId: session.sessionId + }); + if (!accessPolicy.allowed || accessPolicy.error) { + logger.debug(`User not allowed by org access policy because`, { + accessPolicy + }); + return null; + } + const roleResourceAccess = await getRoleResourceAccess( resource.resourceId, userOrgRole.roleId diff --git a/src/app/[orgId]/policy/page.tsx b/src/app/[orgId]/policy/page.tsx deleted file mode 100644 index ea8d32ba..00000000 --- a/src/app/[orgId]/policy/page.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export async function OrgPolicyPage() { - return
Org Policy Page
; -}