Basic verify session breakout

This commit is contained in:
Owen 2025-08-12 13:40:59 -07:00
parent fd605d9c81
commit 22545cac8b
No known key found for this signature in database
GPG key ID: 8271FDFFD9E0CCBD
2 changed files with 240 additions and 72 deletions

View file

@ -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<ResourceWithAuth | null> {
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<UserSessionWithUser | null> {
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<ResourceRule[]> {
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;
}

View file

@ -6,20 +6,21 @@ import {
} from "@server/auth/sessions/resource"; } from "@server/auth/sessions/resource";
import { verifyResourceAccessToken } from "@server/auth/verifyResourceAccessToken"; import { verifyResourceAccessToken } from "@server/auth/verifyResourceAccessToken";
import { db } from "@server/db"; import { db } from "@server/db";
import {
getResourceByDomain,
getUserSessionWithUser,
getUserOrgRole,
getRoleResourceAccess,
getUserResourceAccess,
getResourceRules
} from "@server/db/queries/verifySessionQueries";
import { import {
Resource, Resource,
ResourceAccessToken, ResourceAccessToken,
ResourcePassword, ResourcePassword,
resourcePassword,
ResourcePincode, ResourcePincode,
resourcePincode,
ResourceRule, ResourceRule,
resourceRules,
resources,
roleResources,
sessions, sessions,
userOrgs,
userResources,
users users
} from "@server/db"; } from "@server/db";
import config from "@server/lib/config"; import config from "@server/lib/config";
@ -27,7 +28,6 @@ import { isIpInCidr } from "@server/lib/ip";
import { response } from "@server/lib/response"; import { response } from "@server/lib/response";
import logger from "@server/logger"; import logger from "@server/logger";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import { and, eq } from "drizzle-orm";
import { NextFunction, Request, Response } from "express"; import { NextFunction, Request, Response } from "express";
import createHttpError from "http-errors"; import createHttpError from "http-errors";
import NodeCache from "node-cache"; import NodeCache from "node-cache";
@ -137,31 +137,14 @@ export async function verifyResourceSession(
| undefined = cache.get(resourceCacheKey); | undefined = cache.get(resourceCacheKey);
if (!resourceData) { if (!resourceData) {
const [result] = await db const result = await getResourceByDomain(cleanHost);
.select()
.from(resources)
.leftJoin(
resourcePincode,
eq(resourcePincode.resourceId, resources.resourceId)
)
.leftJoin(
resourcePassword,
eq(resourcePassword.resourceId, resources.resourceId)
)
.where(eq(resources.fullDomain, cleanHost))
.limit(1);
if (!result) { if (!result) {
logger.debug("Resource not found", cleanHost); logger.debug("Resource not found", cleanHost);
return notAllowed(res); return notAllowed(res);
} }
resourceData = { resourceData = result;
resource: result.resources,
pincode: result.resourcePincode,
password: result.resourcePassword
};
cache.set(resourceCacheKey, resourceData); cache.set(resourceCacheKey, resourceData);
} }
@ -529,14 +512,13 @@ async function isUserAllowedToAccessResource(
userSessionId: string, userSessionId: string,
resource: Resource resource: Resource
): Promise<BasicUserData | null> { ): Promise<BasicUserData | null> {
const [res] = await db const result = await getUserSessionWithUser(userSessionId);
.select()
.from(sessions)
.leftJoin(users, eq(users.userId, sessions.userId))
.where(eq(sessions.sessionId, userSessionId));
const user = res.user; if (!result) {
const session = res.session; return null;
}
const { user, session } = result;
if (!user || !session) { if (!user || !session) {
return null; return null;
@ -549,33 +531,18 @@ async function isUserAllowedToAccessResource(
return null; return null;
} }
const userOrgRole = await db const userOrgRole = await getUserOrgRole(user.userId, resource.orgId);
.select()
.from(userOrgs)
.where(
and(
eq(userOrgs.userId, user.userId),
eq(userOrgs.orgId, resource.orgId)
)
)
.limit(1);
if (userOrgRole.length === 0) { if (!userOrgRole) {
return null; return null;
} }
const roleResourceAccess = await db const roleResourceAccess = await getRoleResourceAccess(
.select() resource.resourceId,
.from(roleResources) userOrgRole.roleId
.where( );
and(
eq(roleResources.resourceId, resource.resourceId),
eq(roleResources.roleId, userOrgRole[0].roleId)
)
)
.limit(1);
if (roleResourceAccess.length > 0) { if (roleResourceAccess) {
return { return {
username: user.username, username: user.username,
email: user.email, email: user.email,
@ -583,18 +550,12 @@ async function isUserAllowedToAccessResource(
}; };
} }
const userResourceAccess = await db const userResourceAccess = await getUserResourceAccess(
.select() user.userId,
.from(userResources) resource.resourceId
.where( );
and(
eq(userResources.userId, user.userId),
eq(userResources.resourceId, resource.resourceId)
)
)
.limit(1);
if (userResourceAccess.length > 0) { if (userResourceAccess) {
return { return {
username: user.username, username: user.username,
email: user.email, email: user.email,
@ -615,11 +576,7 @@ async function checkRules(
let rules: ResourceRule[] | undefined = cache.get(ruleCacheKey); let rules: ResourceRule[] | undefined = cache.get(ruleCacheKey);
if (!rules) { if (!rules) {
rules = await db rules = await getResourceRules(resourceId);
.select()
.from(resourceRules)
.where(eq(resourceRules.resourceId, resourceId));
cache.set(ruleCacheKey, rules); cache.set(ruleCacheKey, rules);
} }