mirror of
https://github.com/fosrl/pangolin.git
synced 2026-08-07 13:08:03 +02:00
Converting to use both inline and shared policy
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user