mirror of
https://github.com/fosrl/pangolin.git
synced 2026-01-28 22:00:51 +00:00
463 lines
13 KiB
TypeScript
463 lines
13 KiB
TypeScript
/*
|
|
* 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 * as certificates from "#private/routers/certificates";
|
|
import { createStore } from "#private/lib/rateLimitStore";
|
|
import * as billing from "#private/routers/billing";
|
|
import * as remoteExitNode from "#private/routers/remoteExitNode";
|
|
import * as loginPage from "#private/routers/loginPage";
|
|
import * as orgIdp from "#private/routers/orgIdp";
|
|
import * as domain from "#private/routers/domain";
|
|
import * as auth from "#private/routers/auth";
|
|
import * as license from "#private/routers/license";
|
|
import * as generateLicense from "./generatedLicense";
|
|
import * as logs from "#private/routers/auditLogs";
|
|
import * as misc from "#private/routers/misc";
|
|
import * as reKey from "#private/routers/re-key";
|
|
|
|
import {
|
|
verifyOrgAccess,
|
|
verifyUserHasAction,
|
|
verifyUserIsServerAdmin,
|
|
verifySiteAccess,
|
|
verifyClientAccess
|
|
} from "@server/middlewares";
|
|
import { ActionsEnum } from "@server/auth/actions";
|
|
import {
|
|
logActionAudit,
|
|
verifyCertificateAccess,
|
|
verifyIdpAccess,
|
|
verifyLoginPageAccess,
|
|
verifyRemoteExitNodeAccess,
|
|
verifyValidSubscription
|
|
} from "#private/middlewares";
|
|
import rateLimit, { ipKeyGenerator } from "express-rate-limit";
|
|
import createHttpError from "http-errors";
|
|
import HttpCode from "@server/types/HttpCode";
|
|
import { verifyValidLicense } from "../middlewares/verifyValidLicense";
|
|
import { build } from "@server/build";
|
|
import {
|
|
unauthenticated as ua,
|
|
authenticated as a,
|
|
authRouter as aa
|
|
} from "@server/routers/external";
|
|
|
|
export const authenticated = a;
|
|
export const unauthenticated = ua;
|
|
export const authRouter = aa;
|
|
|
|
unauthenticated.post(
|
|
"/remote-exit-node/quick-start",
|
|
verifyValidLicense,
|
|
rateLimit({
|
|
windowMs: 60 * 60 * 1000,
|
|
max: 5,
|
|
keyGenerator: (req) => `${req.path}:${ipKeyGenerator(req.ip || "")}`,
|
|
handler: (req, res, next) => {
|
|
const message = `You can only create 5 remote exit nodes every hour. Please try again later.`;
|
|
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
|
},
|
|
store: createStore()
|
|
}),
|
|
remoteExitNode.quickStartRemoteExitNode
|
|
);
|
|
|
|
authenticated.put(
|
|
"/org/:orgId/idp/oidc",
|
|
verifyValidLicense,
|
|
verifyOrgAccess,
|
|
verifyUserHasAction(ActionsEnum.createIdp),
|
|
logActionAudit(ActionsEnum.createIdp),
|
|
orgIdp.createOrgOidcIdp
|
|
);
|
|
|
|
authenticated.post(
|
|
"/org/:orgId/idp/:idpId/oidc",
|
|
verifyValidLicense,
|
|
verifyOrgAccess,
|
|
verifyIdpAccess,
|
|
verifyUserHasAction(ActionsEnum.updateIdp),
|
|
logActionAudit(ActionsEnum.updateIdp),
|
|
orgIdp.updateOrgOidcIdp
|
|
);
|
|
|
|
authenticated.delete(
|
|
"/org/:orgId/idp/:idpId",
|
|
verifyValidLicense,
|
|
verifyOrgAccess,
|
|
verifyIdpAccess,
|
|
verifyUserHasAction(ActionsEnum.deleteIdp),
|
|
logActionAudit(ActionsEnum.deleteIdp),
|
|
orgIdp.deleteOrgIdp
|
|
);
|
|
|
|
authenticated.get(
|
|
"/org/:orgId/idp/:idpId",
|
|
verifyValidLicense,
|
|
verifyOrgAccess,
|
|
verifyIdpAccess,
|
|
verifyUserHasAction(ActionsEnum.getIdp),
|
|
orgIdp.getOrgIdp
|
|
);
|
|
|
|
authenticated.get(
|
|
"/org/:orgId/idp",
|
|
verifyValidLicense,
|
|
verifyOrgAccess,
|
|
verifyUserHasAction(ActionsEnum.listIdps),
|
|
orgIdp.listOrgIdps
|
|
);
|
|
|
|
authenticated.get("/org/:orgId/idp", orgIdp.listOrgIdps); // anyone can see this; it's just a list of idp names and ids
|
|
|
|
authenticated.get(
|
|
"/org/:orgId/certificate/:domainId/:domain",
|
|
verifyValidLicense,
|
|
verifyOrgAccess,
|
|
verifyCertificateAccess,
|
|
verifyUserHasAction(ActionsEnum.getCertificate),
|
|
certificates.getCertificate
|
|
);
|
|
|
|
authenticated.post(
|
|
"/org/:orgId/certificate/:certId/restart",
|
|
verifyValidLicense,
|
|
verifyOrgAccess,
|
|
verifyCertificateAccess,
|
|
verifyUserHasAction(ActionsEnum.restartCertificate),
|
|
logActionAudit(ActionsEnum.restartCertificate),
|
|
certificates.restartCertificate
|
|
);
|
|
|
|
if (build === "saas") {
|
|
unauthenticated.post(
|
|
"/quick-start",
|
|
rateLimit({
|
|
windowMs: 15 * 60 * 1000,
|
|
max: 100,
|
|
keyGenerator: (req) => req.path,
|
|
handler: (req, res, next) => {
|
|
const message = `We're too busy right now. Please try again later.`;
|
|
return next(
|
|
createHttpError(HttpCode.TOO_MANY_REQUESTS, message)
|
|
);
|
|
},
|
|
store: createStore()
|
|
}),
|
|
auth.quickStart
|
|
);
|
|
|
|
authenticated.post(
|
|
"/org/:orgId/billing/create-checkout-session",
|
|
verifyOrgAccess,
|
|
verifyUserHasAction(ActionsEnum.billing),
|
|
logActionAudit(ActionsEnum.billing),
|
|
billing.createCheckoutSession
|
|
);
|
|
|
|
authenticated.post(
|
|
"/org/:orgId/billing/create-portal-session",
|
|
verifyOrgAccess,
|
|
verifyUserHasAction(ActionsEnum.billing),
|
|
logActionAudit(ActionsEnum.billing),
|
|
billing.createPortalSession
|
|
);
|
|
|
|
authenticated.get(
|
|
"/org/:orgId/billing/subscription",
|
|
verifyOrgAccess,
|
|
verifyUserHasAction(ActionsEnum.billing),
|
|
billing.getOrgSubscription
|
|
);
|
|
|
|
authenticated.get(
|
|
"/org/:orgId/billing/usage",
|
|
verifyOrgAccess,
|
|
verifyUserHasAction(ActionsEnum.billing),
|
|
billing.getOrgUsage
|
|
);
|
|
|
|
authenticated.get(
|
|
"/org/:orgId/license",
|
|
verifyOrgAccess,
|
|
generateLicense.listSaasLicenseKeys
|
|
);
|
|
|
|
authenticated.put(
|
|
"/org/:orgId/license",
|
|
verifyOrgAccess,
|
|
generateLicense.generateNewLicense
|
|
);
|
|
|
|
authenticated.post(
|
|
"/send-support-request",
|
|
rateLimit({
|
|
windowMs: 15 * 60 * 1000,
|
|
max: 3,
|
|
keyGenerator: (req) =>
|
|
`sendSupportRequest:${req.user?.userId || ipKeyGenerator(req.ip || "")}`,
|
|
handler: (req, res, next) => {
|
|
const message = `You can only send 3 support requests every 15 minutes. Please try again later.`;
|
|
return next(
|
|
createHttpError(HttpCode.TOO_MANY_REQUESTS, message)
|
|
);
|
|
},
|
|
store: createStore()
|
|
}),
|
|
misc.sendSupportEmail
|
|
);
|
|
}
|
|
|
|
authenticated.get(
|
|
"/domain/namespaces",
|
|
verifyValidLicense,
|
|
domain.listDomainNamespaces
|
|
);
|
|
|
|
authenticated.get(
|
|
"/domain/check-namespace-availability",
|
|
verifyValidLicense,
|
|
domain.checkDomainNamespaceAvailability
|
|
);
|
|
|
|
authenticated.put(
|
|
"/org/:orgId/remote-exit-node",
|
|
verifyValidLicense,
|
|
verifyOrgAccess,
|
|
verifyUserHasAction(ActionsEnum.createRemoteExitNode),
|
|
logActionAudit(ActionsEnum.createRemoteExitNode),
|
|
remoteExitNode.createRemoteExitNode
|
|
);
|
|
|
|
authenticated.get(
|
|
"/org/:orgId/remote-exit-nodes",
|
|
verifyValidLicense,
|
|
verifyOrgAccess,
|
|
verifyUserHasAction(ActionsEnum.listRemoteExitNode),
|
|
remoteExitNode.listRemoteExitNodes
|
|
);
|
|
|
|
authenticated.get(
|
|
"/org/:orgId/remote-exit-node/:remoteExitNodeId",
|
|
verifyValidLicense,
|
|
verifyOrgAccess,
|
|
verifyRemoteExitNodeAccess,
|
|
verifyUserHasAction(ActionsEnum.getRemoteExitNode),
|
|
remoteExitNode.getRemoteExitNode
|
|
);
|
|
|
|
authenticated.get(
|
|
"/org/:orgId/pick-remote-exit-node-defaults",
|
|
verifyValidLicense,
|
|
verifyOrgAccess,
|
|
verifyUserHasAction(ActionsEnum.createRemoteExitNode),
|
|
remoteExitNode.pickRemoteExitNodeDefaults
|
|
);
|
|
|
|
authenticated.delete(
|
|
"/org/:orgId/remote-exit-node/:remoteExitNodeId",
|
|
verifyValidLicense,
|
|
verifyOrgAccess,
|
|
verifyRemoteExitNodeAccess,
|
|
verifyUserHasAction(ActionsEnum.deleteRemoteExitNode),
|
|
logActionAudit(ActionsEnum.deleteRemoteExitNode),
|
|
remoteExitNode.deleteRemoteExitNode
|
|
);
|
|
|
|
authenticated.put(
|
|
"/org/:orgId/login-page",
|
|
verifyValidLicense,
|
|
verifyOrgAccess,
|
|
verifyUserHasAction(ActionsEnum.createLoginPage),
|
|
logActionAudit(ActionsEnum.createLoginPage),
|
|
loginPage.createLoginPage
|
|
);
|
|
|
|
authenticated.post(
|
|
"/org/:orgId/login-page/:loginPageId",
|
|
verifyValidLicense,
|
|
verifyOrgAccess,
|
|
verifyLoginPageAccess,
|
|
verifyUserHasAction(ActionsEnum.updateLoginPage),
|
|
logActionAudit(ActionsEnum.updateLoginPage),
|
|
loginPage.updateLoginPage
|
|
);
|
|
|
|
authenticated.delete(
|
|
"/org/:orgId/login-page/:loginPageId",
|
|
verifyValidLicense,
|
|
verifyOrgAccess,
|
|
verifyLoginPageAccess,
|
|
verifyUserHasAction(ActionsEnum.deleteLoginPage),
|
|
logActionAudit(ActionsEnum.deleteLoginPage),
|
|
loginPage.deleteLoginPage
|
|
);
|
|
|
|
authenticated.get(
|
|
"/org/:orgId/login-page",
|
|
verifyValidLicense,
|
|
verifyOrgAccess,
|
|
verifyUserHasAction(ActionsEnum.getLoginPage),
|
|
loginPage.getLoginPage
|
|
);
|
|
|
|
authenticated.get(
|
|
"/org/:orgId/login-page-branding",
|
|
verifyValidLicense,
|
|
verifyOrgAccess,
|
|
verifyUserHasAction(ActionsEnum.getLoginPage),
|
|
logActionAudit(ActionsEnum.getLoginPage),
|
|
loginPage.getLoginPageBranding
|
|
);
|
|
|
|
authenticated.put(
|
|
"/org/:orgId/login-page-branding",
|
|
verifyValidLicense,
|
|
verifyOrgAccess,
|
|
verifyUserHasAction(ActionsEnum.updateLoginPage),
|
|
logActionAudit(ActionsEnum.updateLoginPage),
|
|
loginPage.upsertLoginPageBranding
|
|
);
|
|
|
|
authenticated.delete(
|
|
"/org/:orgId/login-page-branding",
|
|
verifyValidLicense,
|
|
verifyOrgAccess,
|
|
verifyUserHasAction(ActionsEnum.deleteLoginPage),
|
|
logActionAudit(ActionsEnum.deleteLoginPage),
|
|
loginPage.deleteLoginPageBranding
|
|
);
|
|
|
|
authRouter.post(
|
|
"/remoteExitNode/get-token",
|
|
verifyValidLicense,
|
|
rateLimit({
|
|
windowMs: 15 * 60 * 1000,
|
|
max: 900,
|
|
keyGenerator: (req) =>
|
|
`remoteExitNodeGetToken:${req.body.newtId || ipKeyGenerator(req.ip || "")}`,
|
|
handler: (req, res, next) => {
|
|
const message = `You can only request an remoteExitNodeToken token ${900} times every ${15} minutes. Please try again later.`;
|
|
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
|
},
|
|
store: createStore()
|
|
}),
|
|
remoteExitNode.getRemoteExitNodeToken
|
|
);
|
|
|
|
authRouter.post(
|
|
"/transfer-session-token",
|
|
verifyValidLicense,
|
|
rateLimit({
|
|
windowMs: 1 * 60 * 1000,
|
|
max: 60,
|
|
keyGenerator: (req) =>
|
|
`transferSessionToken:${ipKeyGenerator(req.ip || "")}`,
|
|
handler: (req, res, next) => {
|
|
const message = `You can only transfer a session token ${5} times every ${1} minute. Please try again later.`;
|
|
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
|
},
|
|
store: createStore()
|
|
}),
|
|
auth.transferSession
|
|
);
|
|
|
|
authenticated.post(
|
|
"/license/activate",
|
|
verifyUserIsServerAdmin,
|
|
license.activateLicense
|
|
);
|
|
|
|
authenticated.get(
|
|
"/license/keys",
|
|
verifyUserIsServerAdmin,
|
|
license.listLicenseKeys
|
|
);
|
|
|
|
authenticated.delete(
|
|
"/license/:licenseKey",
|
|
verifyUserIsServerAdmin,
|
|
license.deleteLicenseKey
|
|
);
|
|
|
|
authenticated.post(
|
|
"/license/recheck",
|
|
verifyUserIsServerAdmin,
|
|
license.recheckStatus
|
|
);
|
|
|
|
authenticated.get(
|
|
"/org/:orgId/logs/action",
|
|
verifyValidLicense,
|
|
verifyValidSubscription,
|
|
verifyOrgAccess,
|
|
verifyUserHasAction(ActionsEnum.exportLogs),
|
|
logs.queryActionAuditLogs
|
|
);
|
|
|
|
authenticated.get(
|
|
"/org/:orgId/logs/action/export",
|
|
verifyValidLicense,
|
|
verifyValidSubscription,
|
|
verifyOrgAccess,
|
|
verifyUserHasAction(ActionsEnum.exportLogs),
|
|
logActionAudit(ActionsEnum.exportLogs),
|
|
logs.exportActionAuditLogs
|
|
);
|
|
|
|
authenticated.get(
|
|
"/org/:orgId/logs/access",
|
|
verifyValidLicense,
|
|
verifyValidSubscription,
|
|
verifyOrgAccess,
|
|
verifyUserHasAction(ActionsEnum.exportLogs),
|
|
logs.queryAccessAuditLogs
|
|
);
|
|
|
|
authenticated.get(
|
|
"/org/:orgId/logs/access/export",
|
|
verifyValidLicense,
|
|
verifyValidSubscription,
|
|
verifyOrgAccess,
|
|
verifyUserHasAction(ActionsEnum.exportLogs),
|
|
logActionAudit(ActionsEnum.exportLogs),
|
|
logs.exportAccessAuditLogs
|
|
);
|
|
|
|
authenticated.post(
|
|
"/re-key/:clientId/regenerate-client-secret",
|
|
verifyClientAccess, // this is first to set the org id
|
|
verifyValidLicense,
|
|
verifyValidSubscription,
|
|
verifyUserHasAction(ActionsEnum.reGenerateSecret),
|
|
reKey.reGenerateClientSecret
|
|
);
|
|
|
|
authenticated.post(
|
|
"/re-key/:siteId/regenerate-site-secret",
|
|
verifySiteAccess, // this is first to set the org id
|
|
verifyValidLicense,
|
|
verifyValidSubscription,
|
|
verifyUserHasAction(ActionsEnum.reGenerateSecret),
|
|
reKey.reGenerateSiteSecret
|
|
);
|
|
|
|
authenticated.put(
|
|
"/re-key/:orgId/regenerate-remote-exit-node-secret",
|
|
verifyValidLicense,
|
|
verifyValidSubscription,
|
|
verifyOrgAccess,
|
|
verifyUserHasAction(ActionsEnum.reGenerateSecret),
|
|
reKey.reGenerateExitNodeSecret
|
|
);
|