From 22545cac8b6cf5b539fd18c57b5019b7bf153e10 Mon Sep 17 00:00:00 2001 From: Owen Date: Tue, 12 Aug 2025 13:40:59 -0700 Subject: [PATCH] Basic verify session breakout --- server/db/queries/verifySessionQueries.ts | 211 ++++++++++++++++++++++ server/routers/badger/verifySession.ts | 101 +++-------- 2 files changed, 240 insertions(+), 72 deletions(-) create mode 100644 server/db/queries/verifySessionQueries.ts diff --git a/server/db/queries/verifySessionQueries.ts b/server/db/queries/verifySessionQueries.ts new file mode 100644 index 00000000..1e159304 --- /dev/null +++ b/server/db/queries/verifySessionQueries.ts @@ -0,0 +1,211 @@ +import { db } from "@server/db"; +import { + Resource, + ResourcePassword, + ResourcePincode, + ResourceRule, + resourcePassword, + resourcePincode, + resourceRules, + resources, + roleResources, + sessions, + userOrgs, + userResources, + users +} from "@server/db"; +import { and, eq } from "drizzle-orm"; +import axios from "axios"; + +export type ResourceWithAuth = { + resource: Resource | null; + pincode: ResourcePincode | null; + password: ResourcePassword | null; +}; + +export type UserSessionWithUser = { + session: any; + user: any; +}; + +const MODE = "remote"; +const remoteEndpoint = "https://api.example.com"; + +/** + * Get resource by domain with pincode and password information + */ +export async function getResourceByDomain( + domain: string +): Promise { + if (MODE === "remote") { + try { + const response = await axios.get(`${remoteEndpoint}/resource/domain/${domain}`); + return response.data; + } catch (error) { + console.error("Error fetching resource by domain:", error); + return null; + } + } + + const [result] = await db + .select() + .from(resources) + .leftJoin( + resourcePincode, + eq(resourcePincode.resourceId, resources.resourceId) + ) + .leftJoin( + resourcePassword, + eq(resourcePassword.resourceId, resources.resourceId) + ) + .where(eq(resources.fullDomain, domain)) + .limit(1); + + if (!result) { + return null; + } + + return { + resource: result.resources, + pincode: result.resourcePincode, + password: result.resourcePassword + }; +} + +/** + * Get user session with user information + */ +export async function getUserSessionWithUser( + userSessionId: string +): Promise { + if (MODE === "remote") { + try { + const response = await axios.get(`${remoteEndpoint}/session/${userSessionId}`); + return response.data; + } catch (error) { + console.error("Error fetching user session:", error); + return null; + } + } + + const [res] = await db + .select() + .from(sessions) + .leftJoin(users, eq(users.userId, sessions.userId)) + .where(eq(sessions.sessionId, userSessionId)); + + if (!res) { + return null; + } + + return { + session: res.session, + user: res.user + }; +} + +/** + * Get user organization role + */ +export async function getUserOrgRole(userId: string, orgId: string) { + if (MODE === "remote") { + try { + const response = await axios.get(`${remoteEndpoint}/user/${userId}/org/${orgId}/role`); + return response.data; + } catch (error) { + console.error("Error fetching user org role:", error); + return null; + } + } + + const userOrgRole = await db + .select() + .from(userOrgs) + .where( + and( + eq(userOrgs.userId, userId), + eq(userOrgs.orgId, orgId) + ) + ) + .limit(1); + + return userOrgRole.length > 0 ? userOrgRole[0] : null; +} + +/** + * Check if role has access to resource + */ +export async function getRoleResourceAccess(resourceId: number, roleId: number) { + if (MODE === "remote") { + try { + const response = await axios.get(`${remoteEndpoint}/role/${roleId}/resource/${resourceId}/access`); + return response.data; + } catch (error) { + console.error("Error fetching role resource access:", error); + return null; + } + } + + const roleResourceAccess = await db + .select() + .from(roleResources) + .where( + and( + eq(roleResources.resourceId, resourceId), + eq(roleResources.roleId, roleId) + ) + ) + .limit(1); + + return roleResourceAccess.length > 0 ? roleResourceAccess[0] : null; +} + +/** + * Check if user has direct access to resource + */ +export async function getUserResourceAccess(userId: string, resourceId: number) { + if (MODE === "remote") { + try { + const response = await axios.get(`${remoteEndpoint}/user/${userId}/resource/${resourceId}/access`); + return response.data; + } catch (error) { + console.error("Error fetching user resource access:", error); + return null; + } + } + + const userResourceAccess = await db + .select() + .from(userResources) + .where( + and( + eq(userResources.userId, userId), + eq(userResources.resourceId, resourceId) + ) + ) + .limit(1); + + return userResourceAccess.length > 0 ? userResourceAccess[0] : null; +} + +/** + * Get resource rules for a given resource + */ +export async function getResourceRules(resourceId: number): Promise { + if (MODE === "remote") { + try { + const response = await axios.get(`${remoteEndpoint}/resource/${resourceId}/rules`); + return response.data; + } catch (error) { + console.error("Error fetching resource rules:", error); + return []; + } + } + + const rules = await db + .select() + .from(resourceRules) + .where(eq(resourceRules.resourceId, resourceId)); + + return rules; +} diff --git a/server/routers/badger/verifySession.ts b/server/routers/badger/verifySession.ts index 48d7c064..54a2e0c9 100644 --- a/server/routers/badger/verifySession.ts +++ b/server/routers/badger/verifySession.ts @@ -6,20 +6,21 @@ import { } from "@server/auth/sessions/resource"; import { verifyResourceAccessToken } from "@server/auth/verifyResourceAccessToken"; import { db } from "@server/db"; +import { + getResourceByDomain, + getUserSessionWithUser, + getUserOrgRole, + getRoleResourceAccess, + getUserResourceAccess, + getResourceRules +} from "@server/db/queries/verifySessionQueries"; import { Resource, ResourceAccessToken, ResourcePassword, - resourcePassword, ResourcePincode, - resourcePincode, ResourceRule, - resourceRules, - resources, - roleResources, sessions, - userOrgs, - userResources, users } from "@server/db"; import config from "@server/lib/config"; @@ -27,7 +28,6 @@ import { isIpInCidr } from "@server/lib/ip"; import { response } from "@server/lib/response"; import logger from "@server/logger"; import HttpCode from "@server/types/HttpCode"; -import { and, eq } from "drizzle-orm"; import { NextFunction, Request, Response } from "express"; import createHttpError from "http-errors"; import NodeCache from "node-cache"; @@ -137,31 +137,14 @@ export async function verifyResourceSession( | undefined = cache.get(resourceCacheKey); if (!resourceData) { - const [result] = await db - .select() - .from(resources) - .leftJoin( - resourcePincode, - eq(resourcePincode.resourceId, resources.resourceId) - ) - .leftJoin( - resourcePassword, - eq(resourcePassword.resourceId, resources.resourceId) - ) - .where(eq(resources.fullDomain, cleanHost)) - .limit(1); + const result = await getResourceByDomain(cleanHost); if (!result) { logger.debug("Resource not found", cleanHost); return notAllowed(res); } - resourceData = { - resource: result.resources, - pincode: result.resourcePincode, - password: result.resourcePassword - }; - + resourceData = result; cache.set(resourceCacheKey, resourceData); } @@ -529,14 +512,13 @@ async function isUserAllowedToAccessResource( userSessionId: string, resource: Resource ): Promise { - const [res] = await db - .select() - .from(sessions) - .leftJoin(users, eq(users.userId, sessions.userId)) - .where(eq(sessions.sessionId, userSessionId)); + const result = await getUserSessionWithUser(userSessionId); - const user = res.user; - const session = res.session; + if (!result) { + return null; + } + + const { user, session } = result; if (!user || !session) { return null; @@ -549,33 +531,18 @@ async function isUserAllowedToAccessResource( return null; } - const userOrgRole = await db - .select() - .from(userOrgs) - .where( - and( - eq(userOrgs.userId, user.userId), - eq(userOrgs.orgId, resource.orgId) - ) - ) - .limit(1); + const userOrgRole = await getUserOrgRole(user.userId, resource.orgId); - if (userOrgRole.length === 0) { + if (!userOrgRole) { return null; } - const roleResourceAccess = await db - .select() - .from(roleResources) - .where( - and( - eq(roleResources.resourceId, resource.resourceId), - eq(roleResources.roleId, userOrgRole[0].roleId) - ) - ) - .limit(1); + const roleResourceAccess = await getRoleResourceAccess( + resource.resourceId, + userOrgRole.roleId + ); - if (roleResourceAccess.length > 0) { + if (roleResourceAccess) { return { username: user.username, email: user.email, @@ -583,18 +550,12 @@ async function isUserAllowedToAccessResource( }; } - const userResourceAccess = await db - .select() - .from(userResources) - .where( - and( - eq(userResources.userId, user.userId), - eq(userResources.resourceId, resource.resourceId) - ) - ) - .limit(1); + const userResourceAccess = await getUserResourceAccess( + user.userId, + resource.resourceId + ); - if (userResourceAccess.length > 0) { + if (userResourceAccess) { return { username: user.username, email: user.email, @@ -615,11 +576,7 @@ async function checkRules( let rules: ResourceRule[] | undefined = cache.get(ruleCacheKey); if (!rules) { - rules = await db - .select() - .from(resourceRules) - .where(eq(resourceRules.resourceId, resourceId)); - + rules = await getResourceRules(resourceId); cache.set(ruleCacheKey, rules); }