mirror of
https://github.com/fosrl/pangolin.git
synced 2025-08-30 22:49:27 +02:00
add feature parity
This commit is contained in:
parent
a512148348
commit
5b0200154a
92 changed files with 353 additions and 759 deletions
|
@ -6,7 +6,7 @@ import { createNextServer } from "./nextServer";
|
|||
import { createInternalServer } from "./internalServer";
|
||||
import { ApiKey, ApiKeyOrg, Session, User, UserOrg } from "./db/schemas";
|
||||
import { createIntegrationApiServer } from "./integrationApiServer";
|
||||
import license from "./license/license.js";
|
||||
import config from "@server/lib/config";
|
||||
|
||||
async function startServers() {
|
||||
await runSetupFunctions();
|
||||
|
@ -17,7 +17,7 @@ async function startServers() {
|
|||
const nextServer = await createNextServer();
|
||||
|
||||
let integrationServer;
|
||||
if (await license.isUnlocked()) {
|
||||
if (config.getRawConfig().flags?.enable_integration_api) {
|
||||
integrationServer = createIntegrationApiServer();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import express from "express";
|
||||
import cors from "cors";
|
||||
import cookieParser from "cookie-parser";
|
||||
|
@ -11,7 +6,6 @@ import logger from "@server/logger";
|
|||
import {
|
||||
errorHandlerMiddleware,
|
||||
notFoundMiddleware,
|
||||
verifyValidLicense
|
||||
} from "@server/middlewares";
|
||||
import { authenticated, unauthenticated } from "@server/routers/integration";
|
||||
import { logIncomingMiddleware } from "./middlewares/logIncoming";
|
||||
|
@ -26,8 +20,6 @@ const externalPort = config.getRawConfig().server.integration_port;
|
|||
export function createIntegrationApiServer() {
|
||||
const apiServer = express();
|
||||
|
||||
apiServer.use(verifyValidLicense);
|
||||
|
||||
if (config.getRawConfig().server.trust_proxy) {
|
||||
apiServer.set("trust proxy", 1);
|
||||
}
|
||||
|
|
|
@ -216,7 +216,8 @@ const configSchema = z.object({
|
|||
disable_user_create_org: z.boolean().optional(),
|
||||
allow_raw_resources: z.boolean().optional(),
|
||||
allow_base_domain_resources: z.boolean().optional(),
|
||||
allow_local_sites: z.boolean().optional()
|
||||
allow_local_sites: z.boolean().optional(),
|
||||
enable_integration_api: z.boolean().optional()
|
||||
})
|
||||
.optional()
|
||||
});
|
||||
|
|
|
@ -2,7 +2,7 @@ import path from "path";
|
|||
import { fileURLToPath } from "url";
|
||||
|
||||
// This is a placeholder value replaced by the build process
|
||||
export const APP_VERSION = "1.3.2";
|
||||
export const APP_VERSION = "1.4.0";
|
||||
|
||||
export const __FILENAME = fileURLToPath(import.meta.url);
|
||||
export const __DIRNAME = path.dirname(__FILENAME);
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import db from "@server/db";
|
||||
import { hostMeta, licenseKey, sites } from "@server/db/schemas";
|
||||
import logger from "@server/logger";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import * as crypto from "crypto";
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
export * from "./verifyApiKey";
|
||||
export * from "./verifyApiKeyOrgAccess";
|
||||
export * from "./verifyApiKeyHasAction";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { db } from "@server/db";
|
||||
import { resourceAccessToken, resources, apiKeyOrg } from "@server/db/schemas";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { verifyPassword } from "@server/auth/password";
|
||||
import db from "@server/db";
|
||||
import { apiKeys } from "@server/db/schemas";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { db } from "@server/db";
|
||||
import { apiKeys, apiKeyOrg } from "@server/db/schemas";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import createHttpError from "http-errors";
|
||||
import HttpCode from "@server/types/HttpCode";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import logger from "@server/logger";
|
||||
import HttpCode from "@server/types/HttpCode";
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { db } from "@server/db";
|
||||
import { apiKeyOrg } from "@server/db/schemas";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { db } from "@server/db";
|
||||
import { resources, apiKeyOrg } from "@server/db/schemas";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { db } from "@server/db";
|
||||
import { roles, apiKeyOrg } from "@server/db/schemas";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { db } from "@server/db";
|
||||
import { userOrgs } from "@server/db/schemas";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { db } from "@server/db";
|
||||
import {
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { db } from "@server/db";
|
||||
import { resources, targets, apiKeyOrg } from "@server/db/schemas";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { db } from "@server/db";
|
||||
import { userOrgs } from "@server/db/schemas";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { db } from "@server/db";
|
||||
import { userOrgs, apiKeys, apiKeyOrg } from "@server/db/schemas";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import createHttpError from "http-errors";
|
||||
import HttpCode from "@server/types/HttpCode";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { NextFunction, Request, Response } from "express";
|
||||
import db from "@server/db";
|
||||
import HttpCode from "@server/types/HttpCode";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { NextFunction, Request, Response } from "express";
|
||||
import db from "@server/db";
|
||||
import HttpCode from "@server/types/HttpCode";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { z } from "zod";
|
||||
import { db } from "@server/db";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { z } from "zod";
|
||||
import { db } from "@server/db";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { z } from "zod";
|
||||
import { db } from "@server/db";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
export * from "./createRootApiKey";
|
||||
export * from "./deleteApiKey";
|
||||
export * from "./getApiKey";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { db } from "@server/db";
|
||||
import { actions, apiKeyActions, apiKeyOrg, apiKeys } from "@server/db/schemas";
|
||||
import logger from "@server/logger";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { db } from "@server/db";
|
||||
import { apiKeyOrg, apiKeys } from "@server/db/schemas";
|
||||
import logger from "@server/logger";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { db } from "@server/db";
|
||||
import { apiKeys } from "@server/db/schemas";
|
||||
import logger from "@server/logger";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { z } from "zod";
|
||||
import { db } from "@server/db";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { z } from "zod";
|
||||
import { db } from "@server/db";
|
||||
|
|
|
@ -30,7 +30,6 @@ import {
|
|||
verifyUserIsServerAdmin,
|
||||
verifyIsLoggedInUser,
|
||||
verifyApiKeyAccess,
|
||||
verifyValidLicense
|
||||
} from "@server/middlewares";
|
||||
import { verifyUserHasAction } from "../middlewares/verifyUserHasAction";
|
||||
import { ActionsEnum } from "@server/auth/actions";
|
||||
|
@ -531,28 +530,24 @@ authenticated.get("/idp/:idpId", verifyUserIsServerAdmin, idp.getIdp);
|
|||
|
||||
authenticated.put(
|
||||
"/idp/:idpId/org/:orgId",
|
||||
verifyValidLicense,
|
||||
verifyUserIsServerAdmin,
|
||||
idp.createIdpOrgPolicy
|
||||
);
|
||||
|
||||
authenticated.post(
|
||||
"/idp/:idpId/org/:orgId",
|
||||
verifyValidLicense,
|
||||
verifyUserIsServerAdmin,
|
||||
idp.updateIdpOrgPolicy
|
||||
);
|
||||
|
||||
authenticated.delete(
|
||||
"/idp/:idpId/org/:orgId",
|
||||
verifyValidLicense,
|
||||
verifyUserIsServerAdmin,
|
||||
idp.deleteIdpOrgPolicy
|
||||
);
|
||||
|
||||
authenticated.get(
|
||||
"/idp/:idpId/org",
|
||||
verifyValidLicense,
|
||||
verifyUserIsServerAdmin,
|
||||
idp.listIdpOrgPolicies
|
||||
);
|
||||
|
@ -586,49 +581,42 @@ authenticated.post(
|
|||
|
||||
authenticated.get(
|
||||
`/api-key/:apiKeyId`,
|
||||
verifyValidLicense,
|
||||
verifyUserIsServerAdmin,
|
||||
apiKeys.getApiKey
|
||||
);
|
||||
|
||||
authenticated.put(
|
||||
`/api-key`,
|
||||
verifyValidLicense,
|
||||
verifyUserIsServerAdmin,
|
||||
apiKeys.createRootApiKey
|
||||
);
|
||||
|
||||
authenticated.delete(
|
||||
`/api-key/:apiKeyId`,
|
||||
verifyValidLicense,
|
||||
verifyUserIsServerAdmin,
|
||||
apiKeys.deleteApiKey
|
||||
);
|
||||
|
||||
authenticated.get(
|
||||
`/api-keys`,
|
||||
verifyValidLicense,
|
||||
verifyUserIsServerAdmin,
|
||||
apiKeys.listRootApiKeys
|
||||
);
|
||||
|
||||
authenticated.get(
|
||||
`/api-key/:apiKeyId/actions`,
|
||||
verifyValidLicense,
|
||||
verifyUserIsServerAdmin,
|
||||
apiKeys.listApiKeyActions
|
||||
);
|
||||
|
||||
authenticated.post(
|
||||
`/api-key/:apiKeyId/actions`,
|
||||
verifyValidLicense,
|
||||
verifyUserIsServerAdmin,
|
||||
apiKeys.setApiKeyActions
|
||||
);
|
||||
|
||||
authenticated.get(
|
||||
`/org/:orgId/api-keys`,
|
||||
verifyValidLicense,
|
||||
verifyOrgAccess,
|
||||
verifyUserHasAction(ActionsEnum.listApiKeys),
|
||||
apiKeys.listOrgApiKeys
|
||||
|
@ -636,7 +624,6 @@ authenticated.get(
|
|||
|
||||
authenticated.post(
|
||||
`/org/:orgId/api-key/:apiKeyId/actions`,
|
||||
verifyValidLicense,
|
||||
verifyOrgAccess,
|
||||
verifyApiKeyAccess,
|
||||
verifyUserHasAction(ActionsEnum.setApiKeyActions),
|
||||
|
@ -645,7 +632,6 @@ authenticated.post(
|
|||
|
||||
authenticated.get(
|
||||
`/org/:orgId/api-key/:apiKeyId/actions`,
|
||||
verifyValidLicense,
|
||||
verifyOrgAccess,
|
||||
verifyApiKeyAccess,
|
||||
verifyUserHasAction(ActionsEnum.listApiKeyActions),
|
||||
|
@ -654,7 +640,6 @@ authenticated.get(
|
|||
|
||||
authenticated.put(
|
||||
`/org/:orgId/api-key`,
|
||||
verifyValidLicense,
|
||||
verifyOrgAccess,
|
||||
verifyUserHasAction(ActionsEnum.createApiKey),
|
||||
apiKeys.createOrgApiKey
|
||||
|
@ -662,7 +647,6 @@ authenticated.put(
|
|||
|
||||
authenticated.delete(
|
||||
`/org/:orgId/api-key/:apiKeyId`,
|
||||
verifyValidLicense,
|
||||
verifyOrgAccess,
|
||||
verifyApiKeyAccess,
|
||||
verifyUserHasAction(ActionsEnum.deleteApiKey),
|
||||
|
@ -671,7 +655,6 @@ authenticated.delete(
|
|||
|
||||
authenticated.get(
|
||||
`/org/:orgId/api-key/:apiKeyId`,
|
||||
verifyValidLicense,
|
||||
verifyOrgAccess,
|
||||
verifyApiKeyAccess,
|
||||
verifyUserHasAction(ActionsEnum.getApiKey),
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { z } from "zod";
|
||||
import { db } from "@server/db";
|
||||
|
|
|
@ -81,10 +81,6 @@ export async function createOidcIdp(
|
|||
autoProvision
|
||||
} = parsedBody.data;
|
||||
|
||||
if (!(await license.isUnlocked())) {
|
||||
autoProvision = false;
|
||||
}
|
||||
|
||||
const key = config.getRawConfig().server.secret;
|
||||
|
||||
const encryptedSecret = encrypt(clientSecret, key);
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { z } from "zod";
|
||||
import { db } from "@server/db";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { z } from "zod";
|
||||
import { db } from "@server/db";
|
||||
|
|
|
@ -1,233 +0,0 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import {
|
||||
createSession,
|
||||
generateId,
|
||||
generateSessionToken,
|
||||
serializeSessionCookie
|
||||
} from "@server/auth/sessions/app";
|
||||
import db from "@server/db";
|
||||
import { Idp, idpOrg, orgs, roles, User, userOrgs, users } from "@server/db/schemas";
|
||||
import logger from "@server/logger";
|
||||
import { UserType } from "@server/types/UserTypes";
|
||||
import { eq, and, inArray } from "drizzle-orm";
|
||||
import jmespath from "jmespath";
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export async function oidcAutoProvision({
|
||||
idp,
|
||||
claims,
|
||||
existingUser,
|
||||
userIdentifier,
|
||||
email,
|
||||
name,
|
||||
req,
|
||||
res
|
||||
}: {
|
||||
idp: Idp;
|
||||
claims: any;
|
||||
existingUser?: User;
|
||||
userIdentifier: string;
|
||||
email?: string;
|
||||
name?: string;
|
||||
req: Request;
|
||||
res: Response;
|
||||
}) {
|
||||
const allOrgs = await db.select().from(orgs);
|
||||
|
||||
const defaultRoleMapping = idp.defaultRoleMapping;
|
||||
const defaultOrgMapping = idp.defaultOrgMapping;
|
||||
|
||||
let userOrgInfo: { orgId: string; roleId: number }[] = [];
|
||||
for (const org of allOrgs) {
|
||||
const [idpOrgRes] = await db
|
||||
.select()
|
||||
.from(idpOrg)
|
||||
.where(
|
||||
and(eq(idpOrg.idpId, idp.idpId), eq(idpOrg.orgId, org.orgId))
|
||||
);
|
||||
|
||||
let roleId: number | undefined = undefined;
|
||||
|
||||
const orgMapping = idpOrgRes?.orgMapping || defaultOrgMapping;
|
||||
const hydratedOrgMapping = hydrateOrgMapping(orgMapping, org.orgId);
|
||||
|
||||
if (hydratedOrgMapping) {
|
||||
logger.debug("Hydrated Org Mapping", {
|
||||
hydratedOrgMapping
|
||||
});
|
||||
const orgId = jmespath.search(claims, hydratedOrgMapping);
|
||||
logger.debug("Extraced Org ID", { orgId });
|
||||
if (orgId !== true && orgId !== org.orgId) {
|
||||
// user not allowed to access this org
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const roleMapping = idpOrgRes?.roleMapping || defaultRoleMapping;
|
||||
if (roleMapping) {
|
||||
logger.debug("Role Mapping", { roleMapping });
|
||||
const roleName = jmespath.search(claims, roleMapping);
|
||||
|
||||
if (!roleName) {
|
||||
logger.error("Role name not found in the ID token", {
|
||||
roleName
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
const [roleRes] = await db
|
||||
.select()
|
||||
.from(roles)
|
||||
.where(
|
||||
and(eq(roles.orgId, org.orgId), eq(roles.name, roleName))
|
||||
);
|
||||
|
||||
if (!roleRes) {
|
||||
logger.error("Role not found", {
|
||||
orgId: org.orgId,
|
||||
roleName
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
roleId = roleRes.roleId;
|
||||
|
||||
userOrgInfo.push({
|
||||
orgId: org.orgId,
|
||||
roleId
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("User org info", { userOrgInfo });
|
||||
|
||||
let existingUserId = existingUser?.userId;
|
||||
|
||||
// sync the user with the orgs and roles
|
||||
await db.transaction(async (trx) => {
|
||||
let userId = existingUser?.userId;
|
||||
|
||||
// create user if not exists
|
||||
if (!existingUser) {
|
||||
userId = generateId(15);
|
||||
|
||||
await trx.insert(users).values({
|
||||
userId,
|
||||
username: userIdentifier,
|
||||
email: email || null,
|
||||
name: name || null,
|
||||
type: UserType.OIDC,
|
||||
idpId: idp.idpId,
|
||||
emailVerified: true, // OIDC users are always verified
|
||||
dateCreated: new Date().toISOString()
|
||||
});
|
||||
} else {
|
||||
// set the name and email
|
||||
await trx
|
||||
.update(users)
|
||||
.set({
|
||||
username: userIdentifier,
|
||||
email: email || null,
|
||||
name: name || null
|
||||
})
|
||||
.where(eq(users.userId, userId!));
|
||||
}
|
||||
|
||||
existingUserId = userId;
|
||||
|
||||
// get all current user orgs
|
||||
const currentUserOrgs = await trx
|
||||
.select()
|
||||
.from(userOrgs)
|
||||
.where(eq(userOrgs.userId, userId!));
|
||||
|
||||
// Delete orgs that are no longer valid
|
||||
const orgsToDelete = currentUserOrgs.filter(
|
||||
(currentOrg) =>
|
||||
!userOrgInfo.some((newOrg) => newOrg.orgId === currentOrg.orgId)
|
||||
);
|
||||
|
||||
if (orgsToDelete.length > 0) {
|
||||
await trx.delete(userOrgs).where(
|
||||
and(
|
||||
eq(userOrgs.userId, userId!),
|
||||
inArray(
|
||||
userOrgs.orgId,
|
||||
orgsToDelete.map((org) => org.orgId)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Update roles for existing orgs where the role has changed
|
||||
const orgsToUpdate = currentUserOrgs.filter((currentOrg) => {
|
||||
const newOrg = userOrgInfo.find(
|
||||
(newOrg) => newOrg.orgId === currentOrg.orgId
|
||||
);
|
||||
return newOrg && newOrg.roleId !== currentOrg.roleId;
|
||||
});
|
||||
|
||||
if (orgsToUpdate.length > 0) {
|
||||
for (const org of orgsToUpdate) {
|
||||
const newRole = userOrgInfo.find(
|
||||
(newOrg) => newOrg.orgId === org.orgId
|
||||
);
|
||||
if (newRole) {
|
||||
await trx
|
||||
.update(userOrgs)
|
||||
.set({ roleId: newRole.roleId })
|
||||
.where(
|
||||
and(
|
||||
eq(userOrgs.userId, userId!),
|
||||
eq(userOrgs.orgId, org.orgId)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add new orgs that don't exist yet
|
||||
const orgsToAdd = userOrgInfo.filter(
|
||||
(newOrg) =>
|
||||
!currentUserOrgs.some(
|
||||
(currentOrg) => currentOrg.orgId === newOrg.orgId
|
||||
)
|
||||
);
|
||||
|
||||
if (orgsToAdd.length > 0) {
|
||||
await trx.insert(userOrgs).values(
|
||||
orgsToAdd.map((org) => ({
|
||||
userId: userId!,
|
||||
orgId: org.orgId,
|
||||
roleId: org.roleId,
|
||||
dateCreated: new Date().toISOString()
|
||||
}))
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const token = generateSessionToken();
|
||||
const sess = await createSession(token, existingUserId!);
|
||||
const isSecure = req.protocol === "https";
|
||||
const cookie = serializeSessionCookie(
|
||||
token,
|
||||
isSecure,
|
||||
new Date(sess.expiresAt)
|
||||
);
|
||||
|
||||
res.appendHeader("Set-Cookie", cookie);
|
||||
}
|
||||
|
||||
function hydrateOrgMapping(
|
||||
orgMapping: string | null,
|
||||
orgId: string
|
||||
): string | undefined {
|
||||
if (!orgMapping) {
|
||||
return undefined;
|
||||
}
|
||||
return orgMapping.split("{{orgId}}").join(orgId);
|
||||
}
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { z } from "zod";
|
||||
import { db } from "@server/db";
|
||||
|
|
|
@ -100,10 +100,6 @@ export async function updateOidcIdp(
|
|||
defaultOrgMapping
|
||||
} = parsedBody.data;
|
||||
|
||||
if (!(await license.isUnlocked())) {
|
||||
autoProvision = false;
|
||||
}
|
||||
|
||||
// Check if IDP exists and is of type OIDC
|
||||
const [existingIdp] = await db
|
||||
.select()
|
||||
|
|
|
@ -6,7 +6,15 @@ import HttpCode from "@server/types/HttpCode";
|
|||
import createHttpError from "http-errors";
|
||||
import logger from "@server/logger";
|
||||
import { fromError } from "zod-validation-error";
|
||||
import { idp, idpOidcConfig, users } from "@server/db/schemas";
|
||||
import {
|
||||
idp,
|
||||
idpOidcConfig,
|
||||
idpOrg,
|
||||
orgs,
|
||||
roles,
|
||||
userOrgs,
|
||||
users
|
||||
} from "@server/db/schemas";
|
||||
import { and, eq, inArray } from "drizzle-orm";
|
||||
import * as arctic from "arctic";
|
||||
import { generateOidcRedirectUrl } from "@server/lib/idp/generateRedirectUrl";
|
||||
|
@ -15,12 +23,12 @@ import jsonwebtoken from "jsonwebtoken";
|
|||
import config from "@server/lib/config";
|
||||
import {
|
||||
createSession,
|
||||
generateId,
|
||||
generateSessionToken,
|
||||
serializeSessionCookie
|
||||
} from "@server/auth/sessions/app";
|
||||
import { decrypt } from "@server/lib/crypto";
|
||||
import { oidcAutoProvision } from "./oidcAutoProvision";
|
||||
import license from "@server/license/license";
|
||||
import { UserType } from "@server/types/UserTypes";
|
||||
|
||||
const ensureTrailingSlash = (url: string): string => {
|
||||
return url;
|
||||
|
@ -212,25 +220,203 @@ export async function validateOidcCallback(
|
|||
);
|
||||
|
||||
if (existingIdp.idp.autoProvision) {
|
||||
if (!(await license.isUnlocked())) {
|
||||
return next(
|
||||
createHttpError(
|
||||
HttpCode.FORBIDDEN,
|
||||
"Auto-provisioning is not available"
|
||||
)
|
||||
const allOrgs = await db.select().from(orgs);
|
||||
|
||||
const defaultRoleMapping = existingIdp.idp.defaultRoleMapping;
|
||||
const defaultOrgMapping = existingIdp.idp.defaultOrgMapping;
|
||||
|
||||
let userOrgInfo: { orgId: string; roleId: number }[] = [];
|
||||
for (const org of allOrgs) {
|
||||
const [idpOrgRes] = await db
|
||||
.select()
|
||||
.from(idpOrg)
|
||||
.where(
|
||||
and(
|
||||
eq(idpOrg.idpId, existingIdp.idp.idpId),
|
||||
eq(idpOrg.orgId, org.orgId)
|
||||
)
|
||||
);
|
||||
|
||||
let roleId: number | undefined = undefined;
|
||||
|
||||
const orgMapping = idpOrgRes?.orgMapping || defaultOrgMapping;
|
||||
const hydratedOrgMapping = hydrateOrgMapping(
|
||||
orgMapping,
|
||||
org.orgId
|
||||
);
|
||||
|
||||
if (hydratedOrgMapping) {
|
||||
logger.debug("Hydrated Org Mapping", {
|
||||
hydratedOrgMapping
|
||||
});
|
||||
const orgId = jmespath.search(claims, hydratedOrgMapping);
|
||||
logger.debug("Extraced Org ID", { orgId });
|
||||
if (orgId !== true && orgId !== org.orgId) {
|
||||
// user not allowed to access this org
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const roleMapping =
|
||||
idpOrgRes?.roleMapping || defaultRoleMapping;
|
||||
if (roleMapping) {
|
||||
logger.debug("Role Mapping", { roleMapping });
|
||||
const roleName = jmespath.search(claims, roleMapping);
|
||||
|
||||
if (!roleName) {
|
||||
logger.error("Role name not found in the ID token", {
|
||||
roleName
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
const [roleRes] = await db
|
||||
.select()
|
||||
.from(roles)
|
||||
.where(
|
||||
and(
|
||||
eq(roles.orgId, org.orgId),
|
||||
eq(roles.name, roleName)
|
||||
)
|
||||
);
|
||||
|
||||
if (!roleRes) {
|
||||
logger.error("Role not found", {
|
||||
orgId: org.orgId,
|
||||
roleName
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
roleId = roleRes.roleId;
|
||||
|
||||
userOrgInfo.push({
|
||||
orgId: org.orgId,
|
||||
roleId
|
||||
});
|
||||
}
|
||||
}
|
||||
await oidcAutoProvision({
|
||||
idp: existingIdp.idp,
|
||||
userIdentifier,
|
||||
email,
|
||||
name,
|
||||
claims,
|
||||
existingUser,
|
||||
req,
|
||||
res
|
||||
|
||||
logger.debug("User org info", { userOrgInfo });
|
||||
|
||||
let existingUserId = existingUser?.userId;
|
||||
|
||||
// sync the user with the orgs and roles
|
||||
await db.transaction(async (trx) => {
|
||||
let userId = existingUser?.userId;
|
||||
|
||||
// create user if not exists
|
||||
if (!existingUser) {
|
||||
userId = generateId(15);
|
||||
|
||||
await trx.insert(users).values({
|
||||
userId,
|
||||
username: userIdentifier,
|
||||
email: email || null,
|
||||
name: name || null,
|
||||
type: UserType.OIDC,
|
||||
idpId: existingIdp.idp.idpId,
|
||||
emailVerified: true, // OIDC users are always verified
|
||||
dateCreated: new Date().toISOString()
|
||||
});
|
||||
} else {
|
||||
// set the name and email
|
||||
await trx
|
||||
.update(users)
|
||||
.set({
|
||||
username: userIdentifier,
|
||||
email: email || null,
|
||||
name: name || null
|
||||
})
|
||||
.where(eq(users.userId, userId!));
|
||||
}
|
||||
|
||||
existingUserId = userId;
|
||||
|
||||
// get all current user orgs
|
||||
const currentUserOrgs = await trx
|
||||
.select()
|
||||
.from(userOrgs)
|
||||
.where(eq(userOrgs.userId, userId!));
|
||||
|
||||
// Delete orgs that are no longer valid
|
||||
const orgsToDelete = currentUserOrgs.filter(
|
||||
(currentOrg) =>
|
||||
!userOrgInfo.some(
|
||||
(newOrg) => newOrg.orgId === currentOrg.orgId
|
||||
)
|
||||
);
|
||||
|
||||
if (orgsToDelete.length > 0) {
|
||||
await trx.delete(userOrgs).where(
|
||||
and(
|
||||
eq(userOrgs.userId, userId!),
|
||||
inArray(
|
||||
userOrgs.orgId,
|
||||
orgsToDelete.map((org) => org.orgId)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Update roles for existing orgs where the role has changed
|
||||
const orgsToUpdate = currentUserOrgs.filter((currentOrg) => {
|
||||
const newOrg = userOrgInfo.find(
|
||||
(newOrg) => newOrg.orgId === currentOrg.orgId
|
||||
);
|
||||
return newOrg && newOrg.roleId !== currentOrg.roleId;
|
||||
});
|
||||
|
||||
if (orgsToUpdate.length > 0) {
|
||||
for (const org of orgsToUpdate) {
|
||||
const newRole = userOrgInfo.find(
|
||||
(newOrg) => newOrg.orgId === org.orgId
|
||||
);
|
||||
if (newRole) {
|
||||
await trx
|
||||
.update(userOrgs)
|
||||
.set({ roleId: newRole.roleId })
|
||||
.where(
|
||||
and(
|
||||
eq(userOrgs.userId, userId!),
|
||||
eq(userOrgs.orgId, org.orgId)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add new orgs that don't exist yet
|
||||
const orgsToAdd = userOrgInfo.filter(
|
||||
(newOrg) =>
|
||||
!currentUserOrgs.some(
|
||||
(currentOrg) => currentOrg.orgId === newOrg.orgId
|
||||
)
|
||||
);
|
||||
|
||||
if (orgsToAdd.length > 0) {
|
||||
await trx.insert(userOrgs).values(
|
||||
orgsToAdd.map((org) => ({
|
||||
userId: userId!,
|
||||
orgId: org.orgId,
|
||||
roleId: org.roleId,
|
||||
dateCreated: new Date().toISOString()
|
||||
}))
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const token = generateSessionToken();
|
||||
const sess = await createSession(token, existingUserId!);
|
||||
const isSecure = req.protocol === "https";
|
||||
const cookie = serializeSessionCookie(
|
||||
token,
|
||||
isSecure,
|
||||
new Date(sess.expiresAt)
|
||||
);
|
||||
|
||||
res.appendHeader("Set-Cookie", cookie);
|
||||
|
||||
return response<ValidateOidcUrlCallbackResponse>(res, {
|
||||
data: {
|
||||
redirectUrl: postAuthRedirectUrl
|
||||
|
@ -278,3 +464,13 @@ export async function validateOidcCallback(
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
function hydrateOrgMapping(
|
||||
orgMapping: string | null,
|
||||
orgId: string
|
||||
): string | undefined {
|
||||
if (!orgMapping) {
|
||||
return undefined;
|
||||
}
|
||||
return orgMapping.split("{{orgId}}").join(orgId);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import * as site from "./site";
|
||||
import * as org from "./org";
|
||||
import * as resource from "./resource";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import HttpCode from "@server/types/HttpCode";
|
||||
import createHttpError from "http-errors";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import HttpCode from "@server/types/HttpCode";
|
||||
import createHttpError from "http-errors";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import HttpCode from "@server/types/HttpCode";
|
||||
import createHttpError from "http-errors";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
export * from "./getLicenseStatus";
|
||||
export * from "./activateLicense";
|
||||
export * from "./listLicenseKeys";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import HttpCode from "@server/types/HttpCode";
|
||||
import createHttpError from "http-errors";
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
// This file is licensed under the Fossorial Commercial License.
|
||||
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
|
||||
//
|
||||
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import HttpCode from "@server/types/HttpCode";
|
||||
import createHttpError from "http-errors";
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue