Converting to use both inline and shared policy

This commit is contained in:
Owen
2026-06-03 14:41:43 -07:00
parent 18bcf40174
commit 8e9071a336
9 changed files with 444 additions and 89 deletions
+79 -17
View File
@@ -35,6 +35,7 @@ import {
resourcePolicyHeaderAuth, resourcePolicyHeaderAuth,
ResourcePolicyHeaderAuth ResourcePolicyHeaderAuth
} from "@server/db"; } from "@server/db";
import { alias } from "drizzle-orm/sqlite-core";
import { and, eq, inArray, or, sql } from "drizzle-orm"; import { and, eq, inArray, or, sql } from "drizzle-orm";
export type ResourceWithAuth = { export type ResourceWithAuth = {
@@ -67,6 +68,33 @@ export async function getResourceByDomain(
wildcardCandidates.push(`*.${parts.slice(i).join(".")}`); wildcardCandidates.push(`*.${parts.slice(i).join(".")}`);
} }
const sharedPolicy = alias(resourcePolicies, "sharedPolicy");
const defaultPolicy = alias(resourcePolicies, "defaultPolicy");
const sharedPolicyPincode = alias(
resourcePolicyPincode,
"sharedPolicyPincode"
);
const defaultPolicyPincode = alias(
resourcePolicyPincode,
"defaultPolicyPincode"
);
const sharedPolicyPassword = alias(
resourcePolicyPassword,
"sharedPolicyPassword"
);
const defaultPolicyPassword = alias(
resourcePolicyPassword,
"defaultPolicyPassword"
);
const sharedPolicyHeaderAuth = alias(
resourcePolicyHeaderAuth,
"sharedPolicyHeaderAuth"
);
const defaultPolicyHeaderAuth = alias(
resourcePolicyHeaderAuth,
"defaultPolicyHeaderAuth"
);
const potentialResults = await db const potentialResults = await db
.select() .select()
.from(resources) .from(resources)
@@ -90,28 +118,56 @@ export async function getResourceByDomain(
) )
) )
.leftJoin( .leftJoin(
resourcePolicies, sharedPolicy,
eq(resourcePolicies.resourcePolicyId, resources.resourcePolicyId) eq(sharedPolicy.resourcePolicyId, resources.resourcePolicyId)
) )
.leftJoin( .leftJoin(
resourcePolicyPincode, sharedPolicyPincode,
eq( eq(
resourcePolicyPincode.resourcePolicyId, sharedPolicyPincode.resourcePolicyId,
resourcePolicies.resourcePolicyId sharedPolicy.resourcePolicyId
) )
) )
.leftJoin( .leftJoin(
resourcePolicyPassword, sharedPolicyPassword,
eq( eq(
resourcePolicyPassword.resourcePolicyId, sharedPolicyPassword.resourcePolicyId,
resourcePolicies.resourcePolicyId sharedPolicy.resourcePolicyId
) )
) )
.leftJoin( .leftJoin(
resourcePolicyHeaderAuth, sharedPolicyHeaderAuth,
eq( eq(
resourcePolicyHeaderAuth.resourcePolicyId, sharedPolicyHeaderAuth.resourcePolicyId,
resourcePolicies.resourcePolicyId sharedPolicy.resourcePolicyId
)
)
.leftJoin(
defaultPolicy,
eq(
defaultPolicy.resourcePolicyId,
resources.defaultResourcePolicyId
)
)
.leftJoin(
defaultPolicyPincode,
eq(
defaultPolicyPincode.resourcePolicyId,
defaultPolicy.resourcePolicyId
)
)
.leftJoin(
defaultPolicyPassword,
eq(
defaultPolicyPassword.resourcePolicyId,
defaultPolicy.resourcePolicyId
)
)
.leftJoin(
defaultPolicyHeaderAuth,
eq(
defaultPolicyHeaderAuth.resourcePolicyId,
defaultPolicy.resourcePolicyId
) )
) )
.innerJoin(orgs, eq(orgs.orgId, resources.orgId)) .innerJoin(orgs, eq(orgs.orgId, resources.orgId))
@@ -143,18 +199,24 @@ export async function getResourceByDomain(
return null; return null;
} }
const effectivePolicyPincode =
result.sharedPolicyPincode ?? result.defaultPolicyPincode ?? null;
const effectivePolicyPassword =
result.sharedPolicyPassword ?? result.defaultPolicyPassword ?? null;
const effectivePolicyHeaderAuth =
result.sharedPolicyHeaderAuth ?? result.defaultPolicyHeaderAuth ?? null;
return { return {
resource: result.resources, resource: result.resources,
pincode: result.resourcePolicyPincode ?? result.resourcePincode, pincode: effectivePolicyPincode ?? result.resourcePincode,
password: result.resourcePolicyPassword ?? result.resourcePassword, password: effectivePolicyPassword ?? result.resourcePassword,
headerAuth: headerAuth: effectivePolicyHeaderAuth ?? result.resourceHeaderAuth,
result.resourcePolicyHeaderAuth ?? result.resourceHeaderAuth, headerAuthExtendedCompatibility: effectivePolicyHeaderAuth
headerAuthExtendedCompatibility: result.resourcePolicyHeaderAuth
? ({ ? ({
headerAuthExtendedCompatibilityId: 0, headerAuthExtendedCompatibilityId: 0,
resourceId: result.resources.resourceId, resourceId: result.resources.resourceId,
extendedCompatibilityIsActivated: extendedCompatibilityIsActivated:
result.resourcePolicyHeaderAuth.extendedCompatibility effectivePolicyHeaderAuth.extendedCompatibility
} as ResourceHeaderAuthExtendedCompatibility) } as ResourceHeaderAuthExtendedCompatibility)
: result.resourceHeaderAuthExtendedCompatibility, : result.resourceHeaderAuthExtendedCompatibility,
org: result.orgs org: result.orgs
+85 -19
View File
@@ -61,6 +61,7 @@ import {
roles roles
} from "@server/db"; } from "@server/db";
import { eq, and, inArray, isNotNull, ne, or, sql } from "drizzle-orm"; import { eq, and, inArray, isNotNull, ne, or, sql } from "drizzle-orm";
import { alias } from "drizzle-orm/sqlite-core";
import { response } from "@server/lib/response"; import { response } from "@server/lib/response";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import { NextFunction, Request, Response } from "express"; import { NextFunction, Request, Response } from "express";
@@ -514,6 +515,33 @@ hybridRouter.get(
wildcardCandidates.push(`*.${domainParts.slice(i).join(".")}`); wildcardCandidates.push(`*.${domainParts.slice(i).join(".")}`);
} }
const sharedPolicy = alias(resourcePolicies, "sharedPolicy");
const defaultPolicy = alias(resourcePolicies, "defaultPolicy");
const sharedPolicyPincode = alias(
resourcePolicyPincode,
"sharedPolicyPincode"
);
const defaultPolicyPincode = alias(
resourcePolicyPincode,
"defaultPolicyPincode"
);
const sharedPolicyPassword = alias(
resourcePolicyPassword,
"sharedPolicyPassword"
);
const defaultPolicyPassword = alias(
resourcePolicyPassword,
"defaultPolicyPassword"
);
const sharedPolicyHeaderAuth = alias(
resourcePolicyHeaderAuth,
"sharedPolicyHeaderAuth"
);
const defaultPolicyHeaderAuth = alias(
resourcePolicyHeaderAuth,
"defaultPolicyHeaderAuth"
);
const potentialResults = await db const potentialResults = await db
.select() .select()
.from(resources) .from(resources)
@@ -537,31 +565,59 @@ hybridRouter.get(
) )
) )
.leftJoin( .leftJoin(
resourcePolicies, sharedPolicy,
eq( eq(
resourcePolicies.resourcePolicyId, sharedPolicy.resourcePolicyId,
resources.resourcePolicyId resources.resourcePolicyId
) )
) )
.leftJoin( .leftJoin(
resourcePolicyPincode, sharedPolicyPincode,
eq( eq(
resourcePolicyPincode.resourcePolicyId, sharedPolicyPincode.resourcePolicyId,
resourcePolicies.resourcePolicyId sharedPolicy.resourcePolicyId
) )
) )
.leftJoin( .leftJoin(
resourcePolicyPassword, sharedPolicyPassword,
eq( eq(
resourcePolicyPassword.resourcePolicyId, sharedPolicyPassword.resourcePolicyId,
resourcePolicies.resourcePolicyId sharedPolicy.resourcePolicyId
) )
) )
.leftJoin( .leftJoin(
resourcePolicyHeaderAuth, sharedPolicyHeaderAuth,
eq( eq(
resourcePolicyHeaderAuth.resourcePolicyId, sharedPolicyHeaderAuth.resourcePolicyId,
resourcePolicies.resourcePolicyId sharedPolicy.resourcePolicyId
)
)
.leftJoin(
defaultPolicy,
eq(
defaultPolicy.resourcePolicyId,
resources.defaultResourcePolicyId
)
)
.leftJoin(
defaultPolicyPincode,
eq(
defaultPolicyPincode.resourcePolicyId,
defaultPolicy.resourcePolicyId
)
)
.leftJoin(
defaultPolicyPassword,
eq(
defaultPolicyPassword.resourcePolicyId,
defaultPolicy.resourcePolicyId
)
)
.leftJoin(
defaultPolicyHeaderAuth,
eq(
defaultPolicyHeaderAuth.resourcePolicyId,
defaultPolicy.resourcePolicyId
) )
) )
.innerJoin(orgs, eq(orgs.orgId, resources.orgId)) .innerJoin(orgs, eq(orgs.orgId, resources.orgId))
@@ -614,21 +670,31 @@ hybridRouter.get(
}); });
} }
const effectivePolicyPincode =
result.sharedPolicyPincode ??
result.defaultPolicyPincode ??
null;
const effectivePolicyPassword =
result.sharedPolicyPassword ??
result.defaultPolicyPassword ??
null;
const effectivePolicyHeaderAuth =
result.sharedPolicyHeaderAuth ??
result.defaultPolicyHeaderAuth ??
null;
const resourceWithAuth: ResourceWithAuth = { const resourceWithAuth: ResourceWithAuth = {
resource: result.resources, resource: result.resources,
pincode: result.resourcePolicyPincode ?? result.resourcePincode, pincode: effectivePolicyPincode ?? result.resourcePincode,
password: password: effectivePolicyPassword ?? result.resourcePassword,
result.resourcePolicyPassword ?? result.resourcePassword,
headerAuth: headerAuth:
result.resourcePolicyHeaderAuth ?? effectivePolicyHeaderAuth ?? result.resourceHeaderAuth,
result.resourceHeaderAuth, headerAuthExtendedCompatibility: effectivePolicyHeaderAuth
headerAuthExtendedCompatibility: result.resourcePolicyHeaderAuth
? ({ ? ({
headerAuthExtendedCompatibilityId: 0, headerAuthExtendedCompatibilityId: 0,
resourceId: result.resources.resourceId, resourceId: result.resources.resourceId,
extendedCompatibilityIsActivated: extendedCompatibilityIsActivated:
result.resourcePolicyHeaderAuth effectivePolicyHeaderAuth.extendedCompatibility
.extendedCompatibility
} as ResourceHeaderAuthExtendedCompatibility) } as ResourceHeaderAuthExtendedCompatibility)
: result.resourceHeaderAuthExtendedCompatibility, : result.resourceHeaderAuthExtendedCompatibility,
org: result.orgs org: result.orgs
+3
View File
@@ -188,6 +188,9 @@ export async function exchangeSession(
userSessionId: requestSession.userSessionId, userSessionId: requestSession.userSessionId,
whitelistId: requestSession.whitelistId, whitelistId: requestSession.whitelistId,
accessTokenId: requestSession.accessTokenId, accessTokenId: requestSession.accessTokenId,
policyPasswordId: requestSession.policyPasswordId,
policyPincodeId: requestSession.policyPincodeId,
policyWhitelistId: requestSession.policyWhitelistId,
doNotExtend: false, doNotExtend: false,
expiresAt: expires, expiresAt: expires,
sessionLength: RESOURCE_SESSION_COOKIE_EXPIRES sessionLength: RESOURCE_SESSION_COOKIE_EXPIRES
+4
View File
@@ -876,6 +876,10 @@ function allowed(
message: "Access allowed", message: "Access allowed",
status: HttpCode.OK status: HttpCode.OK
}; };
logger.debug(
"++++++++++++++++++++++++++++++++++Access allowed, response data:",
data
);
return response<VerifyUserResponse>(res, data); return response<VerifyUserResponse>(res, data);
} }
+50 -9
View File
@@ -1,10 +1,17 @@
import { verify } from "@node-rs/argon2"; import { verify } from "@node-rs/argon2";
import { generateSessionToken } from "@server/auth/sessions/app"; import { generateSessionToken } from "@server/auth/sessions/app";
import { db } from "@server/db"; import { db } from "@server/db";
import { orgs, resourcePassword, resourcePolicies, resourcePolicyPassword, resources } from "@server/db"; import {
orgs,
resourcePassword,
resourcePolicies,
resourcePolicyPassword,
resources
} from "@server/db";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import response from "@server/lib/response"; import response from "@server/lib/response";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
import { alias } from "drizzle-orm/sqlite-core";
import { NextFunction, Request, Response } from "express"; import { NextFunction, Request, Response } from "express";
import createHttpError from "http-errors"; import createHttpError from "http-errors";
import { z } from "zod"; import { z } from "zod";
@@ -58,17 +65,45 @@ export async function authWithPassword(
const { password } = parsedBody.data; const { password } = parsedBody.data;
try { try {
const sharedPolicy = alias(resourcePolicies, "sharedPolicy");
const defaultPolicy = alias(resourcePolicies, "defaultPolicy");
const sharedPolicyPassword = alias(
resourcePolicyPassword,
"sharedPolicyPassword"
);
const defaultPolicyPassword = alias(
resourcePolicyPassword,
"defaultPolicyPassword"
);
const [result] = await db const [result] = await db
.select() .select()
.from(resources) .from(resources)
.leftJoin(orgs, eq(orgs.orgId, resources.orgId)) .leftJoin(orgs, eq(orgs.orgId, resources.orgId))
.leftJoin( .leftJoin(
resourcePolicies, sharedPolicy,
eq(resourcePolicies.resourcePolicyId, resources.resourcePolicyId) eq(sharedPolicy.resourcePolicyId, resources.resourcePolicyId)
) )
.leftJoin( .leftJoin(
resourcePolicyPassword, sharedPolicyPassword,
eq(resourcePolicyPassword.resourcePolicyId, resourcePolicies.resourcePolicyId) eq(
sharedPolicyPassword.resourcePolicyId,
sharedPolicy.resourcePolicyId
)
)
.leftJoin(
defaultPolicy,
eq(
defaultPolicy.resourcePolicyId,
resources.defaultResourcePolicyId
)
)
.leftJoin(
defaultPolicyPassword,
eq(
defaultPolicyPassword.resourcePolicyId,
defaultPolicy.resourcePolicyId
)
) )
.leftJoin( .leftJoin(
resourcePassword, resourcePassword,
@@ -80,9 +115,13 @@ export async function authWithPassword(
const resource = result?.resources; const resource = result?.resources;
const org = result?.orgs; const org = result?.orgs;
// Policy password takes precedence over resource-level password // Shared policy takes precedence, then default (inline) policy, then resource-level
const policyPassword = result?.resourcePolicyPassword ?? null; const policyPassword =
const definedPassword = policyPassword ?? result?.resourcePassword ?? null; result?.sharedPolicyPassword ??
result?.defaultPolicyPassword ??
null;
const definedPassword =
policyPassword ?? result?.resourcePassword ?? null;
const isPolicyPassword = !!policyPassword; const isPolicyPassword = !!policyPassword;
if (!org) { if (!org) {
@@ -136,7 +175,9 @@ export async function authWithPassword(
resourceId, resourceId,
token, token,
passwordId: isPolicyPassword ? null : definedPassword.passwordId, passwordId: isPolicyPassword ? null : definedPassword.passwordId,
policyPasswordId: isPolicyPassword ? definedPassword.passwordId : null, policyPasswordId: isPolicyPassword
? definedPassword.passwordId
: null,
isRequestToken: true, isRequestToken: true,
expiresAt: Date.now() + 1000 * 30, // 30 seconds expiresAt: Date.now() + 1000 * 30, // 30 seconds
sessionLength: 1000 * 30, sessionLength: 1000 * 30,
+43 -7
View File
@@ -1,9 +1,16 @@
import { generateSessionToken } from "@server/auth/sessions/app"; import { generateSessionToken } from "@server/auth/sessions/app";
import { db } from "@server/db"; import { db } from "@server/db";
import { orgs, resourcePincode, resourcePolicies, resourcePolicyPincode, resources } from "@server/db"; import {
orgs,
resourcePincode,
resourcePolicies,
resourcePolicyPincode,
resources
} from "@server/db";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import response from "@server/lib/response"; import response from "@server/lib/response";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
import { alias } from "drizzle-orm/sqlite-core";
import { NextFunction, Request, Response } from "express"; import { NextFunction, Request, Response } from "express";
import createHttpError from "http-errors"; import createHttpError from "http-errors";
import { z } from "zod"; import { z } from "zod";
@@ -57,17 +64,45 @@ export async function authWithPincode(
const { pincode } = parsedBody.data; const { pincode } = parsedBody.data;
try { try {
const sharedPolicy = alias(resourcePolicies, "sharedPolicy");
const defaultPolicy = alias(resourcePolicies, "defaultPolicy");
const sharedPolicyPincode = alias(
resourcePolicyPincode,
"sharedPolicyPincode"
);
const defaultPolicyPincode = alias(
resourcePolicyPincode,
"defaultPolicyPincode"
);
const [result] = await db const [result] = await db
.select() .select()
.from(resources) .from(resources)
.leftJoin(orgs, eq(orgs.orgId, resources.orgId)) .leftJoin(orgs, eq(orgs.orgId, resources.orgId))
.leftJoin( .leftJoin(
resourcePolicies, sharedPolicy,
eq(resourcePolicies.resourcePolicyId, resources.resourcePolicyId) eq(sharedPolicy.resourcePolicyId, resources.resourcePolicyId)
) )
.leftJoin( .leftJoin(
resourcePolicyPincode, sharedPolicyPincode,
eq(resourcePolicyPincode.resourcePolicyId, resourcePolicies.resourcePolicyId) eq(
sharedPolicyPincode.resourcePolicyId,
sharedPolicy.resourcePolicyId
)
)
.leftJoin(
defaultPolicy,
eq(
defaultPolicy.resourcePolicyId,
resources.defaultResourcePolicyId
)
)
.leftJoin(
defaultPolicyPincode,
eq(
defaultPolicyPincode.resourcePolicyId,
defaultPolicy.resourcePolicyId
)
) )
.leftJoin( .leftJoin(
resourcePincode, resourcePincode,
@@ -79,8 +114,9 @@ export async function authWithPincode(
const resource = result?.resources; const resource = result?.resources;
const org = result?.orgs; const org = result?.orgs;
// Policy pincode takes precedence over resource-level pincode // Shared policy takes precedence, then default (inline) policy, then resource-level
const policyPincode = result?.resourcePolicyPincode ?? null; const policyPincode =
result?.sharedPolicyPincode ?? result?.defaultPolicyPincode ?? null;
const definedPincode = policyPincode ?? result?.resourcePincode ?? null; const definedPincode = policyPincode ?? result?.resourcePincode ?? null;
const isPolicyPincode = !!policyPincode; const isPolicyPincode = !!policyPincode;
+72 -9
View File
@@ -1,6 +1,12 @@
import { generateSessionToken } from "@server/auth/sessions/app"; import { generateSessionToken } from "@server/auth/sessions/app";
import { db } from "@server/db"; import { db } from "@server/db";
import { orgs, resourceOtp, resources, resourceWhitelist, resourcePolicyWhiteList } from "@server/db"; import {
orgs,
resourceOtp,
resources,
resourceWhitelist,
resourcePolicyWhiteList
} from "@server/db";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import response from "@server/lib/response"; import response from "@server/lib/response";
import { eq, and } from "drizzle-orm"; import { eq, and } from "drizzle-orm";
@@ -84,15 +90,21 @@ export async function authWithWhitelist(
const wildcard = "*@" + email.split("@")[1]; const wildcard = "*@" + email.split("@")[1];
// Check policy whitelist first (policy takes precedence over resource whitelist) // Check shared policy whitelist first, then default (inline) policy whitelist
let policyWhitelistEntry: { whitelistId: number; email: string } | null = null; let policyWhitelistEntry: {
whitelistId: number;
email: string;
} | null = null;
if (resource.resourcePolicyId) { if (resource.resourcePolicyId) {
const [exact] = await db const [exact] = await db
.select() .select()
.from(resourcePolicyWhiteList) .from(resourcePolicyWhiteList)
.where( .where(
and( and(
eq(resourcePolicyWhiteList.resourcePolicyId, resource.resourcePolicyId), eq(
resourcePolicyWhiteList.resourcePolicyId,
resource.resourcePolicyId
),
eq(resourcePolicyWhiteList.email, email) eq(resourcePolicyWhiteList.email, email)
) )
) )
@@ -101,13 +113,57 @@ export async function authWithWhitelist(
if (exact) { if (exact) {
policyWhitelistEntry = exact; policyWhitelistEntry = exact;
} else { } else {
logger.debug("Checking for wildcard email in policy: " + wildcard); logger.debug(
"Checking for wildcard email in shared policy: " + wildcard
);
const [wildcardMatch] = await db const [wildcardMatch] = await db
.select() .select()
.from(resourcePolicyWhiteList) .from(resourcePolicyWhiteList)
.where( .where(
and( and(
eq(resourcePolicyWhiteList.resourcePolicyId, resource.resourcePolicyId), eq(
resourcePolicyWhiteList.resourcePolicyId,
resource.resourcePolicyId
),
eq(resourcePolicyWhiteList.email, wildcard)
)
)
.limit(1);
if (wildcardMatch) policyWhitelistEntry = wildcardMatch;
}
}
// Fall back to default (inline) policy whitelist if shared policy didn't match
if (!policyWhitelistEntry && resource.defaultResourcePolicyId) {
const [exact] = await db
.select()
.from(resourcePolicyWhiteList)
.where(
and(
eq(
resourcePolicyWhiteList.resourcePolicyId,
resource.defaultResourcePolicyId
),
eq(resourcePolicyWhiteList.email, email)
)
)
.limit(1);
if (exact) {
policyWhitelistEntry = exact;
} else {
logger.debug(
"Checking for wildcard email in default policy: " + wildcard
);
const [wildcardMatch] = await db
.select()
.from(resourcePolicyWhiteList)
.where(
and(
eq(
resourcePolicyWhiteList.resourcePolicyId,
resource.defaultResourcePolicyId
),
eq(resourcePolicyWhiteList.email, wildcard) eq(resourcePolicyWhiteList.email, wildcard)
) )
) )
@@ -117,7 +173,10 @@ export async function authWithWhitelist(
} }
// Fall back to resource whitelist if not found in policy // Fall back to resource whitelist if not found in policy
let resourceWhitelistEntry: { whitelistId: number; email: string } | null = null; let resourceWhitelistEntry: {
whitelistId: number;
email: string;
} | null = null;
if (!policyWhitelistEntry) { if (!policyWhitelistEntry) {
const [exact] = await db const [exact] = await db
.select() .select()
@@ -241,8 +300,12 @@ export async function authWithWhitelist(
await createResourceSession({ await createResourceSession({
resourceId, resourceId,
token, token,
whitelistId: isPolicyWhitelist ? null : whitelistedEmail.whitelistId, whitelistId: isPolicyWhitelist
policyWhitelistId: isPolicyWhitelist ? whitelistedEmail.whitelistId : null, ? null
: whitelistedEmail.whitelistId,
policyWhitelistId: isPolicyWhitelist
? whitelistedEmail.whitelistId
: null,
isRequestToken: true, isRequestToken: true,
expiresAt: Date.now() + 1000 * 30, // 30 seconds expiresAt: Date.now() + 1000 * 30, // 30 seconds
sessionLength: 1000 * 30, sessionLength: 1000 * 30,
+106 -27
View File
@@ -6,9 +6,13 @@ import {
resourcePolicyHeaderAuth, resourcePolicyHeaderAuth,
resourcePolicyPassword, resourcePolicyPassword,
resourcePolicyPincode, resourcePolicyPincode,
resourcePincode,
resourcePassword,
resourceHeaderAuth,
resources resources
} from "@server/db"; } from "@server/db";
import { eq, or } from "drizzle-orm"; import { eq } from "drizzle-orm";
import { alias } from "drizzle-orm/sqlite-core";
import response from "@server/lib/response"; import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode"; import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors"; import createHttpError from "http-errors";
@@ -60,42 +64,103 @@ export async function getResourceAuthInfo(
const isGuidInteger = /^\d+$/.test(resourceGuid); const isGuidInteger = /^\d+$/.test(resourceGuid);
const sharedPolicy = alias(resourcePolicies, "sharedPolicy");
const defaultPolicy = alias(resourcePolicies, "defaultPolicy");
const sharedPolicyPincode = alias(
resourcePolicyPincode,
"sharedPolicyPincode"
);
const defaultPolicyPincode = alias(
resourcePolicyPincode,
"defaultPolicyPincode"
);
const sharedPolicyPassword = alias(
resourcePolicyPassword,
"sharedPolicyPassword"
);
const defaultPolicyPassword = alias(
resourcePolicyPassword,
"defaultPolicyPassword"
);
const sharedPolicyHeaderAuth = alias(
resourcePolicyHeaderAuth,
"sharedPolicyHeaderAuth"
);
const defaultPolicyHeaderAuth = alias(
resourcePolicyHeaderAuth,
"defaultPolicyHeaderAuth"
);
const buildQuery = (whereClause: ReturnType<typeof eq>) => const buildQuery = (whereClause: ReturnType<typeof eq>) =>
db db
.select() .select()
.from(resources) .from(resources)
.leftJoin( .leftJoin(
resourcePolicies, resourcePincode,
or( eq(resourcePincode.resourceId, resources.resourceId)
eq( )
resourcePolicies.resourcePolicyId, .leftJoin(
resources.resourcePolicyId resourcePassword,
), eq(resourcePassword.resourceId, resources.resourceId)
eq( )
resourcePolicies.resourcePolicyId, .leftJoin(
resources.defaultResourcePolicyId resourceHeaderAuth,
) eq(resourceHeaderAuth.resourceId, resources.resourceId)
)
.leftJoin(
sharedPolicy,
eq(
sharedPolicy.resourcePolicyId,
resources.resourcePolicyId
) )
) )
.leftJoin( .leftJoin(
resourcePolicyPincode, sharedPolicyPincode,
eq( eq(
resourcePolicyPincode.resourcePolicyId, sharedPolicyPincode.resourcePolicyId,
resourcePolicies.resourcePolicyId sharedPolicy.resourcePolicyId
) )
) )
.leftJoin( .leftJoin(
resourcePolicyPassword, sharedPolicyPassword,
eq( eq(
resourcePolicyPassword.resourcePolicyId, sharedPolicyPassword.resourcePolicyId,
resourcePolicies.resourcePolicyId sharedPolicy.resourcePolicyId
) )
) )
.leftJoin( .leftJoin(
resourcePolicyHeaderAuth, sharedPolicyHeaderAuth,
eq( eq(
resourcePolicyHeaderAuth.resourcePolicyId, sharedPolicyHeaderAuth.resourcePolicyId,
resourcePolicies.resourcePolicyId sharedPolicy.resourcePolicyId
)
)
.leftJoin(
defaultPolicy,
eq(
defaultPolicy.resourcePolicyId,
resources.defaultResourcePolicyId
)
)
.leftJoin(
defaultPolicyPincode,
eq(
defaultPolicyPincode.resourcePolicyId,
defaultPolicy.resourcePolicyId
)
)
.leftJoin(
defaultPolicyPassword,
eq(
defaultPolicyPassword.resourcePolicyId,
defaultPolicy.resourcePolicyId
)
)
.leftJoin(
defaultPolicyHeaderAuth,
eq(
defaultPolicyHeaderAuth.resourcePolicyId,
defaultPolicy.resourcePolicyId
) )
) )
.where(whereClause) .where(whereClause)
@@ -115,10 +180,24 @@ export async function getResourceAuthInfo(
); );
} }
const policy = result?.resourcePolicies; // Shared (custom) policy takes precedence over the default policy.
const pincode = result?.resourcePolicyPincode; // For boolean fields (sso, whitelist), only fall back to defaultPolicy
const password = result?.resourcePolicyPassword; // when there is no shared policy at all.
const headerAuth = result?.resourcePolicyHeaderAuth; const effectivePolicyPincode =
result.sharedPolicyPincode ?? result.defaultPolicyPincode ?? null;
const effectivePolicyPassword =
result.sharedPolicyPassword ?? result.defaultPolicyPassword ?? null;
const effectivePolicyHeaderAuth =
result.sharedPolicyHeaderAuth ??
result.defaultPolicyHeaderAuth ??
null;
const effectivePolicy = result.sharedPolicy ?? result.defaultPolicy;
const pincode = effectivePolicyPincode ?? result.resourcePincode;
const password = effectivePolicyPassword ?? result.resourcePassword;
const headerAuth =
effectivePolicyHeaderAuth ?? result.resourceHeaderAuth;
const url = resource.fullDomain const url = resource.fullDomain
? `${resource.ssl ? "https" : "http"}://${resource.fullDomain}` ? `${resource.ssl ? "https" : "http"}://${resource.fullDomain}`
@@ -134,13 +213,13 @@ export async function getResourceAuthInfo(
pincode: pincode !== null, pincode: pincode !== null,
headerAuth: headerAuth !== null, headerAuth: headerAuth !== null,
headerAuthExtendedCompatibility: headerAuthExtendedCompatibility:
headerAuth?.extendedCompatibility ?? false, effectivePolicyHeaderAuth?.extendedCompatibility ?? false,
sso: policy?.sso ?? false, sso: effectivePolicy?.sso ?? false,
blockAccess: resource.blockAccess, blockAccess: resource.blockAccess,
url: url ?? "", url: url ?? "",
wildcard: resource.wildcard ?? false, wildcard: resource.wildcard ?? false,
fullDomain: resource.fullDomain, fullDomain: resource.fullDomain,
whitelist: policy?.emailWhitelistEnabled ?? false, whitelist: effectivePolicy?.emailWhitelistEnabled ?? false,
skipToIdpId: resource.skipToIdpId, skipToIdpId: resource.skipToIdpId,
orgId: resource.orgId, orgId: resource.orgId,
postAuthPath: resource.postAuthPath ?? null postAuthPath: resource.postAuthPath ?? null
+2 -1
View File
@@ -150,7 +150,8 @@ export default async function SshPage() {
await waitForRoundTripCompletion(messageIds, cookieHeader); await waitForRoundTripCompletion(messageIds, cookieHeader);
} catch (err) { } catch (err) {
console.error("Error signing SSH key:", err); console.error("Error signing SSH key:", err);
error = "Failed to sign SSH key for PAM push authentication."; error =
"Failed to sign SSH key for PAM push authentication. Did you sign in as a user?";
} }
} }
} catch (err) { } catch (err) {