Chungus 2.0

This commit is contained in:
Owen
2025-10-10 11:27:15 -07:00
parent f64a477c3d
commit d92b87b7c8
224 changed files with 1507 additions and 1586 deletions

View File

@@ -0,0 +1,18 @@
/*
* This file is part of a proprietary work.
*
* Copyright (c) 2025 Fossorial, Inc.
* All rights reserved.
*
* This file is licensed under the Fossorial Commercial License.
* You may not use this file except in compliance with the License.
* Unauthorized use, copying, modification, or distribution is strictly prohibited.
*
* This file is not licensed under the AGPLv3.
*/
export * from "./verifyCertificateAccess";
export * from "./verifyRemoteExitNodeAccess";
export * from "./verifyIdpAccess";
export * from "./verifyLoginPageAccess";
export * from "../../lib/corsWithLoginPage";

View File

@@ -0,0 +1,126 @@
/*
* This file is part of a proprietary work.
*
* Copyright (c) 2025 Fossorial, Inc.
* All rights reserved.
*
* This file is licensed under the Fossorial Commercial License.
* You may not use this file except in compliance with the License.
* Unauthorized use, copying, modification, or distribution is strictly prohibited.
*
* This file is not licensed under the AGPLv3.
*/
import { Request, Response, NextFunction } from "express";
import { db, domainNamespaces } from "@server/db";
import { certificates } from "@server/db";
import { domains, orgDomains } from "@server/db";
import { eq, and } from "drizzle-orm";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";
import logger from "@server/logger";
export async function verifyCertificateAccess(
req: Request,
res: Response,
next: NextFunction
) {
try {
// Assume user/org access is already verified
const orgId = req.params.orgId;
const certId = req.params.certId || req.body?.certId || req.query?.certId;
let domainId =
req.params.domainId || req.body?.domainId || req.query?.domainId;
if (!orgId) {
return next(
createHttpError(HttpCode.BAD_REQUEST, "Invalid organization ID")
);
}
if (!domainId) {
if (!certId) {
return next(
createHttpError(HttpCode.BAD_REQUEST, "Must provide either certId or domainId")
);
}
// Get the certificate and its domainId
const [cert] = await db
.select()
.from(certificates)
.where(eq(certificates.certId, Number(certId)))
.limit(1);
if (!cert) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Certificate with ID ${certId} not found`
)
);
}
domainId = cert.domainId;
if (!domainId) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Certificate with ID ${certId} does not have a domain`
)
);
}
}
if (!domainId) {
return next(
createHttpError(HttpCode.BAD_REQUEST, "Must provide either certId or domainId")
);
}
// Check if the domain is a namespace domain
const [namespaceDomain] = await db
.select()
.from(domainNamespaces)
.where(eq(domainNamespaces.domainId, domainId))
.limit(1);
if (namespaceDomain) {
// If it's a namespace domain, we can skip the org check
return next();
}
// Check if the domain is associated with the org
const [orgDomain] = await db
.select()
.from(orgDomains)
.where(
and(
eq(orgDomains.orgId, orgId),
eq(orgDomains.domainId, domainId)
)
)
.limit(1);
if (!orgDomain) {
return next(
createHttpError(
HttpCode.FORBIDDEN,
"Organization does not have access to this certificate"
)
);
}
return next();
} catch (error) {
logger.error(error);
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"Error verifying certificate access"
)
);
}
}
export default verifyCertificateAccess;

View File

@@ -0,0 +1,102 @@
/*
* This file is part of a proprietary work.
*
* Copyright (c) 2025 Fossorial, Inc.
* All rights reserved.
*
* This file is licensed under the Fossorial Commercial License.
* You may not use this file except in compliance with the License.
* Unauthorized use, copying, modification, or distribution is strictly prohibited.
*
* This file is not licensed under the AGPLv3.
*/
import { Request, Response, NextFunction } from "express";
import { userOrgs, db, idp, idpOrg } from "@server/db";
import { and, eq, or } from "drizzle-orm";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";
export async function verifyIdpAccess(
req: Request,
res: Response,
next: NextFunction
) {
try {
const userId = req.user!.userId;
const idpId =
req.params.idpId || req.body.idpId || req.query.idpId;
const orgId = req.params.orgId;
if (!userId) {
return next(
createHttpError(HttpCode.UNAUTHORIZED, "User not authenticated")
);
}
if (!orgId) {
return next(
createHttpError(HttpCode.BAD_REQUEST, "Invalid organization ID")
);
}
if (!idpId) {
return next(
createHttpError(HttpCode.BAD_REQUEST, "Invalid key ID")
);
}
const [idpRes] = await db
.select()
.from(idp)
.innerJoin(idpOrg, eq(idp.idpId, idpOrg.idpId))
.where(
and(eq(idp.idpId, idpId), eq(idpOrg.orgId, orgId))
)
.limit(1);
if (!idpRes || !idpRes.idp || !idpRes.idpOrg) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`IdP with ID ${idpId} not found for organization ${orgId}`
)
);
}
if (!req.userOrg) {
const userOrgRole = await db
.select()
.from(userOrgs)
.where(
and(
eq(userOrgs.userId, userId),
eq(userOrgs.orgId, idpRes.idpOrg.orgId)
)
)
.limit(1);
req.userOrg = userOrgRole[0];
}
if (!req.userOrg) {
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have access to this organization"
)
);
}
const userOrgRoleId = req.userOrg.roleId;
req.userOrgRoleId = userOrgRoleId;
return next();
} catch (error) {
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"Error verifying idp access"
)
);
}
}

View File

@@ -0,0 +1,81 @@
/*
* This file is part of a proprietary work.
*
* Copyright (c) 2025 Fossorial, Inc.
* All rights reserved.
*
* This file is licensed under the Fossorial Commercial License.
* You may not use this file except in compliance with the License.
* Unauthorized use, copying, modification, or distribution is strictly prohibited.
*
* This file is not licensed under the AGPLv3.
*/
import { Request, Response, NextFunction } from "express";
import { userOrgs, db, loginPageOrg } from "@server/db";
import { and, eq, inArray } from "drizzle-orm";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";
export async function verifyLoginPageAccess(
req: Request,
res: Response,
next: NextFunction
) {
try {
const userId = req.user!.userId;
const loginPageId =
req.params.loginPageId ||
req.body.loginPageId ||
req.query.loginPageId;
if (!userId) {
return next(
createHttpError(HttpCode.UNAUTHORIZED, "User not authenticated")
);
}
if (!loginPageId) {
return next(
createHttpError(HttpCode.BAD_REQUEST, "Invalid login page ID")
);
}
const loginPageOrgs = await db
.select({
orgId: loginPageOrg.orgId
})
.from(loginPageOrg)
.where(eq(loginPageOrg.loginPageId, loginPageId));
const orgIds = loginPageOrgs.map((lpo) => lpo.orgId);
const existingUserOrgs = await db
.select()
.from(userOrgs)
.where(
and(
eq(userOrgs.userId, userId),
inArray(userOrgs.orgId, orgIds)
)
);
if (existingUserOrgs.length === 0) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Login page with ID ${loginPageId} not found for user's organizations`
)
);
}
return next();
} catch (error) {
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"Error verifying login page access"
)
);
}
}

View File

@@ -0,0 +1,56 @@
/*
* This file is part of a proprietary work.
*
* Copyright (c) 2025 Fossorial, Inc.
* All rights reserved.
*
* This file is licensed under the Fossorial Commercial License.
* You may not use this file except in compliance with the License.
* Unauthorized use, copying, modification, or distribution is strictly prohibited.
*
* This file is not licensed under the AGPLv3.
*/
import { NextFunction, Response } from "express";
import ErrorResponse from "@server/types/ErrorResponse";
import config from "@server/lib/config";
import { unauthorized } from "@server/auth/unauthorizedResponse";
import logger from "@server/logger";
import { validateRemoteExitNodeSessionToken } from "#private/auth/sessions/remoteExitNode";
export const verifySessionRemoteExitNodeMiddleware = async (
req: any,
res: Response<ErrorResponse>,
next: NextFunction
) => {
// get the token from the auth header
const token = req.headers["authorization"]?.split(" ")[1] || "";
const { session, remoteExitNode } = await validateRemoteExitNodeSessionToken(token);
if (!session || !remoteExitNode) {
if (config.getRawConfig().app.log_failed_attempts) {
logger.info(`Remote exit node session not found. IP: ${req.ip}.`);
}
return next(unauthorized());
}
// const existingUser = await db
// .select()
// .from(users)
// .where(eq(users.userId, user.userId));
// if (!existingUser || !existingUser[0]) {
// if (config.getRawConfig().app.log_failed_attempts) {
// logger.info(`User session not found. IP: ${req.ip}.`);
// }
// return next(
// createHttpError(HttpCode.BAD_REQUEST, "User does not exist")
// );
// }
req.session = session;
req.remoteExitNode = remoteExitNode;
next();
};

View File

@@ -0,0 +1,118 @@
/*
* This file is part of a proprietary work.
*
* Copyright (c) 2025 Fossorial, Inc.
* All rights reserved.
*
* This file is licensed under the Fossorial Commercial License.
* You may not use this file except in compliance with the License.
* Unauthorized use, copying, modification, or distribution is strictly prohibited.
*
* This file is not licensed under the AGPLv3.
*/
import { Request, Response, NextFunction } from "express";
import { db, exitNodeOrgs, exitNodes, remoteExitNodes } 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";
export async function verifyRemoteExitNodeAccess(
req: Request,
res: Response,
next: NextFunction
) {
const userId = req.user!.userId; // Assuming you have user information in the request
const orgId = req.params.orgId;
const remoteExitNodeId =
req.params.remoteExitNodeId ||
req.body.remoteExitNodeId ||
req.query.remoteExitNodeId;
if (!userId) {
return next(
createHttpError(HttpCode.UNAUTHORIZED, "User not authenticated")
);
}
try {
const [remoteExitNode] = await db
.select()
.from(remoteExitNodes)
.where(and(eq(remoteExitNodes.remoteExitNodeId, remoteExitNodeId)));
if (!remoteExitNode) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Remote exit node with ID ${remoteExitNodeId} not found`
)
);
}
if (!remoteExitNode.exitNodeId) {
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
`Remote exit node with ID ${remoteExitNodeId} does not have an exit node ID`
)
);
}
const [exitNodeOrg] = await db
.select()
.from(exitNodeOrgs)
.where(
and(
eq(exitNodeOrgs.exitNodeId, remoteExitNode.exitNodeId),
eq(exitNodeOrgs.orgId, orgId)
)
);
if (!exitNodeOrg) {
return next(
createHttpError(
HttpCode.NOT_FOUND,
`Remote exit node with ID ${remoteExitNodeId} not found in organization ${orgId}`
)
);
}
if (!req.userOrg) {
// Get user's role ID in the organization
const userOrgRole = await db
.select()
.from(userOrgs)
.where(
and(
eq(userOrgs.userId, userId),
eq(userOrgs.orgId, exitNodeOrg.orgId)
)
)
.limit(1);
req.userOrg = userOrgRole[0];
}
if (!req.userOrg) {
return next(
createHttpError(
HttpCode.FORBIDDEN,
"User does not have access to this organization"
)
);
}
const userOrgRoleId = req.userOrg.roleId;
req.userOrgRoleId = userOrgRoleId;
return next();
} catch (error) {
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"Error verifying remote exit node access"
)
);
}
}