mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 17:27:40 +00:00
misc: add support for number matching in oidc and jwt
This commit is contained in:
@@ -1,11 +1,11 @@
|
|||||||
/**
|
/**
|
||||||
* Safely retrieves a value from a nested object using dot notation path
|
* Safely retrieves a value from a nested object using dot notation path
|
||||||
*/
|
*/
|
||||||
export const getStringValueByDot = (
|
export const getValueByDot = (
|
||||||
obj: Record<string, unknown> | null | undefined,
|
obj: Record<string, unknown> | null | undefined,
|
||||||
path: string,
|
path: string,
|
||||||
defaultValue?: string
|
defaultValue?: string | number | boolean
|
||||||
): string | undefined => {
|
): string | number | boolean | undefined => {
|
||||||
// Handle null or undefined input
|
// Handle null or undefined input
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
@@ -26,7 +26,7 @@ export const getStringValueByDot = (
|
|||||||
current = (current as Record<string, unknown>)[part];
|
current = (current as Record<string, unknown>)[part];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof current !== "string") {
|
if (typeof current !== "string" && typeof current !== "number" && typeof current !== "boolean") {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import {
|
|||||||
UnauthorizedError
|
UnauthorizedError
|
||||||
} from "@app/lib/errors";
|
} from "@app/lib/errors";
|
||||||
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||||
import { getStringValueByDot } from "@app/lib/template/dot-access";
|
import { getValueByDot } from "@app/lib/template/dot-access";
|
||||||
|
|
||||||
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||||
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
||||||
@@ -189,7 +189,7 @@ export const identityJwtAuthServiceFactory = ({
|
|||||||
if (identityJwtAuth.boundClaims) {
|
if (identityJwtAuth.boundClaims) {
|
||||||
Object.keys(identityJwtAuth.boundClaims).forEach((claimKey) => {
|
Object.keys(identityJwtAuth.boundClaims).forEach((claimKey) => {
|
||||||
const claimValue = (identityJwtAuth.boundClaims as Record<string, string>)[claimKey];
|
const claimValue = (identityJwtAuth.boundClaims as Record<string, string>)[claimKey];
|
||||||
const value = getStringValueByDot(tokenData, claimKey) || "";
|
const value = getValueByDot(tokenData, claimKey);
|
||||||
|
|
||||||
if (!value) {
|
if (!value) {
|
||||||
throw new UnauthorizedError({
|
throw new UnauthorizedError({
|
||||||
@@ -198,9 +198,7 @@ export const identityJwtAuthServiceFactory = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// handle both single and multi-valued claims
|
// handle both single and multi-valued claims
|
||||||
if (
|
if (!claimValue.split(", ").some((claimEntry) => doesFieldValueMatchJwtPolicy(value, claimEntry))) {
|
||||||
!claimValue.split(", ").some((claimEntry) => doesFieldValueMatchJwtPolicy(tokenData[claimKey], claimEntry))
|
|
||||||
) {
|
|
||||||
throw new UnauthorizedError({
|
throw new UnauthorizedError({
|
||||||
message: `Access denied: claim mismatch for field ${claimKey}`
|
message: `Access denied: claim mismatch for field ${claimKey}`
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,16 @@
|
|||||||
import picomatch from "picomatch";
|
import picomatch from "picomatch";
|
||||||
|
|
||||||
export const doesFieldValueMatchOidcPolicy = (fieldValue: string, policyValue: string) =>
|
export const doesFieldValueMatchOidcPolicy = (fieldValue: string | number | boolean, policyValue: string) => {
|
||||||
policyValue === fieldValue || picomatch.isMatch(fieldValue, policyValue);
|
if (typeof fieldValue === "boolean") {
|
||||||
|
return fieldValue === (policyValue === "true");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof fieldValue === "number") {
|
||||||
|
return fieldValue === parseInt(policyValue, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
return policyValue === fieldValue || picomatch.isMatch(fieldValue, policyValue);
|
||||||
|
};
|
||||||
|
|
||||||
export const doesAudValueMatchOidcPolicy = (fieldValue: string | string[], policyValue: string) => {
|
export const doesAudValueMatchOidcPolicy = (fieldValue: string | string[], policyValue: string) => {
|
||||||
if (Array.isArray(fieldValue)) {
|
if (Array.isArray(fieldValue)) {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import {
|
|||||||
UnauthorizedError
|
UnauthorizedError
|
||||||
} from "@app/lib/errors";
|
} from "@app/lib/errors";
|
||||||
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
|
||||||
import { getStringValueByDot } from "@app/lib/template/dot-access";
|
import { getValueByDot } from "@app/lib/template/dot-access";
|
||||||
|
|
||||||
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
import { ActorType, AuthTokenType } from "../auth/auth-type";
|
||||||
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
||||||
@@ -146,7 +146,7 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
if (identityOidcAuth.boundClaims) {
|
if (identityOidcAuth.boundClaims) {
|
||||||
Object.keys(identityOidcAuth.boundClaims).forEach((claimKey) => {
|
Object.keys(identityOidcAuth.boundClaims).forEach((claimKey) => {
|
||||||
const claimValue = (identityOidcAuth.boundClaims as Record<string, string>)[claimKey];
|
const claimValue = (identityOidcAuth.boundClaims as Record<string, string>)[claimKey];
|
||||||
const value = getStringValueByDot(tokenData, claimKey) || "";
|
const value = getValueByDot(tokenData, claimKey);
|
||||||
|
|
||||||
if (!value) {
|
if (!value) {
|
||||||
throw new UnauthorizedError({
|
throw new UnauthorizedError({
|
||||||
@@ -167,13 +167,13 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
if (identityOidcAuth.claimMetadataMapping) {
|
if (identityOidcAuth.claimMetadataMapping) {
|
||||||
Object.keys(identityOidcAuth.claimMetadataMapping).forEach((permissionKey) => {
|
Object.keys(identityOidcAuth.claimMetadataMapping).forEach((permissionKey) => {
|
||||||
const claimKey = (identityOidcAuth.claimMetadataMapping as Record<string, string>)[permissionKey];
|
const claimKey = (identityOidcAuth.claimMetadataMapping as Record<string, string>)[permissionKey];
|
||||||
const value = getStringValueByDot(tokenData, claimKey) || "";
|
const value = getValueByDot(tokenData, claimKey);
|
||||||
if (!value) {
|
if (!value) {
|
||||||
throw new UnauthorizedError({
|
throw new UnauthorizedError({
|
||||||
message: `Access denied: token has no ${claimKey} field`
|
message: `Access denied: token has no ${claimKey} field`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
filteredClaims[permissionKey] = value;
|
filteredClaims[permissionKey] = value.toString();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user