Merge remote-tracking branch 'origin/main' into misc/privilege-management-v2-transition

This commit is contained in:
Sheen Capadngan
2025-03-19 19:51:41 +08:00
264 changed files with 6747 additions and 890 deletions
@@ -15,6 +15,7 @@ import { TPermissionServiceFactory } from "@app/ee/services/permission/permissio
import { getConfig } from "@app/lib/config/env";
import { BadRequestError, ForbiddenRequestError, NotFoundError, UnauthorizedError } from "@app/lib/errors";
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
import { getStringValueByDot } from "@app/lib/template/dot-access";
import { ActorType, AuthTokenType } from "../auth/auth-type";
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
@@ -80,7 +81,7 @@ export const identityOidcAuthServiceFactory = ({
const { data: discoveryDoc } = await axios.get<{ jwks_uri: string }>(
`${identityOidcAuth.oidcDiscoveryUrl}/.well-known/openid-configuration`,
{
httpsAgent: requestAgent
httpsAgent: identityOidcAuth.oidcDiscoveryUrl.includes("https") ? requestAgent : undefined
}
);
const jwksUri = discoveryDoc.jwks_uri;
@@ -94,7 +95,7 @@ export const identityOidcAuthServiceFactory = ({
const client = new JwksClient({
jwksUri,
requestAgent
requestAgent: identityOidcAuth.oidcDiscoveryUrl.includes("https") ? requestAgent : undefined
});
const { kid } = decodedToken.header;
@@ -111,7 +112,6 @@ export const identityOidcAuthServiceFactory = ({
message: `Access denied: ${error.message}`
});
}
throw error;
}
@@ -138,10 +138,16 @@ export const identityOidcAuthServiceFactory = ({
if (identityOidcAuth.boundClaims) {
Object.keys(identityOidcAuth.boundClaims).forEach((claimKey) => {
const claimValue = (identityOidcAuth.boundClaims as Record<string, string>)[claimKey];
const value = getStringValueByDot(tokenData, claimKey) || "";
if (!value) {
throw new UnauthorizedError({
message: `Access denied: token has no ${claimKey} field`
});
}
// handle both single and multi-valued claims
if (
!claimValue.split(", ").some((claimEntry) => doesFieldValueMatchOidcPolicy(tokenData[claimKey], claimEntry))
) {
if (!claimValue.split(", ").some((claimEntry) => doesFieldValueMatchOidcPolicy(value, claimEntry))) {
throw new UnauthorizedError({
message: "Access denied: OIDC claim not allowed."
});
@@ -149,6 +155,20 @@ export const identityOidcAuthServiceFactory = ({
});
}
const filteredClaims: Record<string, string> = {};
if (identityOidcAuth.claimMetadataMapping) {
Object.keys(identityOidcAuth.claimMetadataMapping).forEach((permissionKey) => {
const claimKey = (identityOidcAuth.claimMetadataMapping as Record<string, string>)[permissionKey];
const value = getStringValueByDot(tokenData, claimKey) || "";
if (!value) {
throw new UnauthorizedError({
message: `Access denied: token has no ${claimKey} field`
});
}
filteredClaims[permissionKey] = value;
});
}
const identityAccessToken = await identityOidcAuthDAL.transaction(async (tx) => {
const newToken = await identityAccessTokenDAL.create(
{
@@ -170,7 +190,12 @@ export const identityOidcAuthServiceFactory = ({
{
identityId: identityOidcAuth.identityId,
identityAccessTokenId: identityAccessToken.id,
authTokenType: AuthTokenType.IDENTITY_ACCESS_TOKEN
authTokenType: AuthTokenType.IDENTITY_ACCESS_TOKEN,
identityAuth: {
oidc: {
claims: filteredClaims
}
}
} as TIdentityAccessTokenJwtPayload,
appCfg.AUTH_SECRET,
// akhilmhdh: for non-expiry tokens you should not even set the value, including undefined. Even for undefined jsonwebtoken throws error
@@ -191,6 +216,7 @@ export const identityOidcAuthServiceFactory = ({
boundIssuer,
boundAudiences,
boundClaims,
claimMetadataMapping,
boundSubject,
accessTokenTTL,
accessTokenMaxTTL,
@@ -257,6 +283,7 @@ export const identityOidcAuthServiceFactory = ({
boundIssuer,
boundAudiences,
boundClaims,
claimMetadataMapping,
boundSubject,
accessTokenMaxTTL,
accessTokenTTL,
@@ -277,6 +304,7 @@ export const identityOidcAuthServiceFactory = ({
boundIssuer,
boundAudiences,
boundClaims,
claimMetadataMapping,
boundSubject,
accessTokenTTL,
accessTokenMaxTTL,
@@ -338,6 +366,7 @@ export const identityOidcAuthServiceFactory = ({
boundIssuer,
boundAudiences,
boundClaims,
claimMetadataMapping,
boundSubject,
accessTokenMaxTTL,
accessTokenTTL,