check idp org ownership on save policy closes #3290

This commit is contained in:
miloschwartz
2026-07-29 09:38:15 -04:00
parent 9a9ae649ef
commit f47c94d05b
4 changed files with 52 additions and 37 deletions
+7 -14
View File
@@ -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`
);
+36
View File
@@ -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<boolean> {
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;
}
@@ -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,
@@ -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,