diff --git a/server/lib/blueprints/resourcePolicies.ts b/server/lib/blueprints/resourcePolicies.ts index aa5f4bc9b..87e9101cc 100644 --- a/server/lib/blueprints/resourcePolicies.ts +++ b/server/lib/blueprints/resourcePolicies.ts @@ -1,7 +1,5 @@ import { db, - idp, - idpOrg, resourcePolicies, resourcePolicyHeaderAuth, resourcePolicyPassword, @@ -20,6 +18,7 @@ import { Config, ResourcePolicyData } from "./types"; import logger from "@server/logger"; import { getUniqueResourcePolicyName } from "@server/db/names"; import { hashPassword } from "@server/auth/password"; +import { idpExistsForOrg } from "@server/lib/idp/idpExistsForOrg"; import { isValidCIDR, isValidIP, isValidUrlGlobPattern } from "../validators"; import { isLicensedOrSubscribed } from "#dynamic/lib/isLicencedOrSubscribed"; import { tierMatrix } from "../billing/tierMatrix"; @@ -71,19 +70,13 @@ export async function updateResourcePolicies( // Validate auto-login-idp if provided if (policyData["auto-login-idp"]) { - const [provider] = await trx - .select() - .from(idp) - .innerJoin(idpOrg, eq(idpOrg.idpId, idp.idpId)) - .where( - and( - eq(idp.idpId, policyData["auto-login-idp"]), - eq(idpOrg.orgId, orgId) - ) - ) - .limit(1); + const providerExists = await idpExistsForOrg( + policyData["auto-login-idp"], + orgId, + trx + ); - if (!provider) { + if (!providerExists) { throw new Error( `Identity provider not found for policy '${policyNiceId}' in this organization` ); diff --git a/server/lib/idp/idpExistsForOrg.ts b/server/lib/idp/idpExistsForOrg.ts new file mode 100644 index 000000000..52530e7ba --- /dev/null +++ b/server/lib/idp/idpExistsForOrg.ts @@ -0,0 +1,36 @@ +import { db, idp, idpOrg, Transaction } from "@server/db"; +import { and, eq } from "drizzle-orm"; + +export function isOrgIdentityProviderMode(): boolean { + return process.env.IDENTITY_PROVIDER_MODE === "org"; +} + +/** + * Checks whether an identity provider can be used for the given org. + * In org IdP mode, the provider must be linked via idpOrg. + * In global IdP mode, the provider only needs to exist. + */ +export async function idpExistsForOrg( + idpId: number, + orgId: string, + dbOrTrx: typeof db | Transaction = db +): Promise { + if (isOrgIdentityProviderMode()) { + const [provider] = await dbOrTrx + .select({ idpId: idp.idpId }) + .from(idp) + .innerJoin(idpOrg, eq(idpOrg.idpId, idp.idpId)) + .where(and(eq(idp.idpId, idpId), eq(idpOrg.orgId, orgId))) + .limit(1); + + return !!provider; + } + + const [provider] = await dbOrTrx + .select({ idpId: idp.idpId }) + .from(idp) + .where(eq(idp.idpId, idpId)) + .limit(1); + + return !!provider; +} diff --git a/server/private/routers/policy/createResourcePolicy.ts b/server/private/routers/policy/createResourcePolicy.ts index 78eace74c..5cca3f446 100644 --- a/server/private/routers/policy/createResourcePolicy.ts +++ b/server/private/routers/policy/createResourcePolicy.ts @@ -14,8 +14,6 @@ import { hashPassword } from "@server/auth/password"; import { db, - idp, - idpOrg, orgs, resourcePolicies, resourcePolicyHeaderAuth, @@ -31,6 +29,7 @@ import { type ResourcePolicy } from "@server/db"; import { getUniqueResourcePolicyName } from "@server/db/names"; +import { idpExistsForOrg } from "@server/lib/idp/idpExistsForOrg"; import response from "@server/lib/response"; import { getResourceRuleValueValidationError, @@ -204,14 +203,9 @@ export async function createResourcePolicy( // Check if Identity provider in `skipToIdpId` exists if (skipToIdpId) { - const [provider] = await db - .select() - .from(idp) - .innerJoin(idpOrg, eq(idpOrg.idpId, idp.idpId)) - .where(and(eq(idp.idpId, skipToIdpId), eq(idpOrg.orgId, orgId))) - .limit(1); + const providerExists = await idpExistsForOrg(skipToIdpId, orgId); - if (!provider) { + if (!providerExists) { return next( createHttpError( HttpCode.INTERNAL_SERVER_ERROR, diff --git a/server/routers/policy/setResourcePolicyAccessControl.ts b/server/routers/policy/setResourcePolicyAccessControl.ts index 3af1c6eac..d4b7fcc02 100644 --- a/server/routers/policy/setResourcePolicyAccessControl.ts +++ b/server/routers/policy/setResourcePolicyAccessControl.ts @@ -2,8 +2,6 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db, - idp, - idpOrg, resourcePolicies, rolePolicies, roles, @@ -18,6 +16,7 @@ import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { and, eq, inArray, ne } from "drizzle-orm"; import { OpenAPITags, registry } from "@server/openApi"; +import { idpExistsForOrg } from "@server/lib/idp/idpExistsForOrg"; const setResourcePolicyAcccessControlBodySchema = z.strictObject({ sso: z.boolean(), @@ -27,7 +26,7 @@ const setResourcePolicyAcccessControlBodySchema = z.strictObject({ }), skipToIdpId: z.int().positive().optional().nullable().openapi({ type: "integer", - description: "Page number to retrieve" + description: "Default identity provider ID to skip to on login" }) }); @@ -36,8 +35,8 @@ const setResourcePolicyAccessControlParamsSchema = z.strictObject({ }); registry.registerPath({ - method: "post", - path: "/resource-policy/{resourceId}/access-control", + method: "put", + path: "/resource-policy/{resourcePolicyId}/access-control", description: "Set access control users for a resource policy, including SSO, users, roles, Identity provider.", tags: [OpenAPITags.PublicResourcePolicyLegacy], @@ -163,16 +162,9 @@ export async function setResourcePolicyAccessControl( // Check if Identity provider in `skipToIdpId` exists if (idpId) { - const [provider] = await db - .select() - .from(idp) - .innerJoin(idpOrg, eq(idpOrg.idpId, idp.idpId)) - .where( - and(eq(idp.idpId, idpId), eq(idpOrg.orgId, policy.orgId)) - ) - .limit(1); + const providerExists = await idpExistsForOrg(idpId, policy.orgId); - if (!provider) { + if (!providerExists) { return next( createHttpError( HttpCode.INTERNAL_SERVER_ERROR,