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";
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<BasicUserData | null> {
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);
}