mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-05 11:26:33 +00:00
Merge pull request #2389 from Infisical/misc/support-glob-patterns-oidc
misc: support glob patterns for OIDC
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
import picomatch from "picomatch";
|
||||||
|
|
||||||
|
export const doesFieldValueMatchOidcPolicy = (fieldValue: string, policyValue: string) =>
|
||||||
|
policyValue === fieldValue || picomatch.isMatch(fieldValue, policyValue);
|
||||||
@@ -28,6 +28,7 @@ import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identit
|
|||||||
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
|
||||||
import { TOrgBotDALFactory } from "../org/org-bot-dal";
|
import { TOrgBotDALFactory } from "../org/org-bot-dal";
|
||||||
import { TIdentityOidcAuthDALFactory } from "./identity-oidc-auth-dal";
|
import { TIdentityOidcAuthDALFactory } from "./identity-oidc-auth-dal";
|
||||||
|
import { doesFieldValueMatchOidcPolicy } from "./identity-oidc-auth-fns";
|
||||||
import {
|
import {
|
||||||
TAttachOidcAuthDTO,
|
TAttachOidcAuthDTO,
|
||||||
TGetOidcAuthDTO,
|
TGetOidcAuthDTO,
|
||||||
@@ -123,7 +124,7 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
}) as Record<string, string>;
|
}) as Record<string, string>;
|
||||||
|
|
||||||
if (identityOidcAuth.boundSubject) {
|
if (identityOidcAuth.boundSubject) {
|
||||||
if (tokenData.sub !== identityOidcAuth.boundSubject) {
|
if (!doesFieldValueMatchOidcPolicy(tokenData.sub, identityOidcAuth.boundSubject)) {
|
||||||
throw new ForbiddenRequestError({
|
throw new ForbiddenRequestError({
|
||||||
message: "Access denied: OIDC subject not allowed."
|
message: "Access denied: OIDC subject not allowed."
|
||||||
});
|
});
|
||||||
@@ -131,7 +132,11 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (identityOidcAuth.boundAudiences) {
|
if (identityOidcAuth.boundAudiences) {
|
||||||
if (!identityOidcAuth.boundAudiences.split(", ").includes(tokenData.aud)) {
|
if (
|
||||||
|
!identityOidcAuth.boundAudiences
|
||||||
|
.split(", ")
|
||||||
|
.some((policyValue) => doesFieldValueMatchOidcPolicy(tokenData.aud, policyValue))
|
||||||
|
) {
|
||||||
throw new ForbiddenRequestError({
|
throw new ForbiddenRequestError({
|
||||||
message: "Access denied: OIDC audience not allowed."
|
message: "Access denied: OIDC audience not allowed."
|
||||||
});
|
});
|
||||||
@@ -142,7 +147,9 @@ export const identityOidcAuthServiceFactory = ({
|
|||||||
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];
|
||||||
// handle both single and multi-valued claims
|
// handle both single and multi-valued claims
|
||||||
if (!claimValue.split(", ").some((claimEntry) => tokenData[claimKey] === claimEntry)) {
|
if (
|
||||||
|
!claimValue.split(", ").some((claimEntry) => doesFieldValueMatchOidcPolicy(tokenData[claimKey], claimEntry))
|
||||||
|
) {
|
||||||
throw new ForbiddenRequestError({
|
throw new ForbiddenRequestError({
|
||||||
message: "Access denied: OIDC claim not allowed."
|
message: "Access denied: OIDC claim not allowed."
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -93,7 +93,12 @@ In the following steps, we explore how to create and use identities to access th
|
|||||||
- Access Token Max TTL (default is `2592000` equivalent to 30 days): The maximum lifetime for an acccess token in seconds. This value will be referenced at renewal time.
|
- Access Token Max TTL (default is `2592000` equivalent to 30 days): The maximum lifetime for an acccess token in seconds. This value will be referenced at renewal time.
|
||||||
- Access Token Max Number of Uses (default is `0`): The maximum number of times that an access token can be used; a value of `0` implies infinite number of uses.
|
- Access Token Max Number of Uses (default is `0`): The maximum number of times that an access token can be used; a value of `0` implies infinite number of uses.
|
||||||
- Access Token Trusted IPs: The IPs or CIDR ranges that access tokens can be used from. By default, each token is given the `0.0.0.0/0`, allowing usage from any network address.
|
- Access Token Trusted IPs: The IPs or CIDR ranges that access tokens can be used from. By default, each token is given the `0.0.0.0/0`, allowing usage from any network address.
|
||||||
|
<Info>
|
||||||
|
The `subject`, `audiences`, and `claims` fields support glob pattern matching; however, we highly recommend using hardcoded values whenever possible.
|
||||||
|
</Info>
|
||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
|
|
||||||
<Step title="Adding an identity to a project">
|
<Step title="Adding an identity to a project">
|
||||||
To enable the identity to access project-level resources such as secrets within a specific project, you should add it to that project.
|
To enable the identity to access project-level resources such as secrets within a specific project, you should add it to that project.
|
||||||
|
|
||||||
|
|||||||
@@ -92,8 +92,8 @@ In the following steps, we explore how to create and use identities to access th
|
|||||||
- Access Token Max TTL (default is `2592000` equivalent to 30 days): The maximum lifetime for an acccess token in seconds. This value will be referenced at renewal time.
|
- Access Token Max TTL (default is `2592000` equivalent to 30 days): The maximum lifetime for an acccess token in seconds. This value will be referenced at renewal time.
|
||||||
- Access Token Max Number of Uses (default is `0`): The maximum number of times that an access token can be used; a value of `0` implies infinite number of uses.
|
- Access Token Max Number of Uses (default is `0`): The maximum number of times that an access token can be used; a value of `0` implies infinite number of uses.
|
||||||
- Access Token Trusted IPs: The IPs or CIDR ranges that access tokens can be used from. By default, each token is given the `0.0.0.0/0`, allowing usage from any network address.
|
- Access Token Trusted IPs: The IPs or CIDR ranges that access tokens can be used from. By default, each token is given the `0.0.0.0/0`, allowing usage from any network address.
|
||||||
|
|
||||||
<Tip>If you are unsure about what to configure for the subject, audience, and claims fields you can use [github/actions-oidc-debugger](https://github.com/github/actions-oidc-debugger) to get the appropriate values. Alternatively, you can fetch the JWT from the workflow and inspect the fields manually.</Tip>
|
<Tip>If you are unsure about what to configure for the subject, audience, and claims fields you can use [github/actions-oidc-debugger](https://github.com/github/actions-oidc-debugger) to get the appropriate values. Alternatively, you can fetch the JWT from the workflow and inspect the fields manually.</Tip>
|
||||||
|
<Info>The `subject`, `audiences`, and `claims` fields support glob pattern matching; however, we highly recommend using hardcoded values whenever possible.</Info>
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Adding an identity to a project">
|
<Step title="Adding an identity to a project">
|
||||||
To enable the identity to access project-level resources such as secrets within a specific project, you should add it to that project.
|
To enable the identity to access project-level resources such as secrets within a specific project, you should add it to that project.
|
||||||
|
|||||||
+38
-3
@@ -1,12 +1,13 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { Controller, useFieldArray, useForm } from "react-hook-form";
|
import { Controller, useFieldArray, useForm } from "react-hook-form";
|
||||||
|
import { faQuestionCircle } from "@fortawesome/free-regular-svg-icons";
|
||||||
import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons";
|
import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { Button, FormControl, IconButton, Input, TextArea } from "@app/components/v2";
|
import { Button, FormControl, IconButton, Input, TextArea, Tooltip } from "@app/components/v2";
|
||||||
import { useOrganization, useSubscription } from "@app/context";
|
import { useOrganization, useSubscription } from "@app/context";
|
||||||
import { useAddIdentityOidcAuth, useUpdateIdentityOidcAuth } from "@app/hooks/api";
|
import { useAddIdentityOidcAuth, useUpdateIdentityOidcAuth } from "@app/hooks/api";
|
||||||
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
||||||
@@ -258,7 +259,19 @@ export const IdentityOidcAuthForm = ({
|
|||||||
control={control}
|
control={control}
|
||||||
name="boundSubject"
|
name="boundSubject"
|
||||||
render={({ field, fieldState: { error } }) => (
|
render={({ field, fieldState: { error } }) => (
|
||||||
<FormControl label="Subject" isError={Boolean(error)} errorText={error?.message}>
|
<FormControl
|
||||||
|
label="Subject"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
icon={
|
||||||
|
<Tooltip
|
||||||
|
className="text-center"
|
||||||
|
content={<span>This field supports glob patterns</span>}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faQuestionCircle} size="sm" />
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
>
|
||||||
<Input {...field} type="text" />
|
<Input {...field} type="text" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
@@ -267,7 +280,19 @@ export const IdentityOidcAuthForm = ({
|
|||||||
control={control}
|
control={control}
|
||||||
name="boundAudiences"
|
name="boundAudiences"
|
||||||
render={({ field, fieldState: { error } }) => (
|
render={({ field, fieldState: { error } }) => (
|
||||||
<FormControl label="Audiences" isError={Boolean(error)} errorText={error?.message}>
|
<FormControl
|
||||||
|
label="Audiences"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
icon={
|
||||||
|
<Tooltip
|
||||||
|
className="text-center"
|
||||||
|
content={<span>This field supports glob patterns</span>}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faQuestionCircle} size="sm" />
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
>
|
||||||
<Input {...field} type="text" placeholder="service1, service2" />
|
<Input {...field} type="text" placeholder="service1, service2" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
@@ -282,6 +307,16 @@ export const IdentityOidcAuthForm = ({
|
|||||||
<FormControl
|
<FormControl
|
||||||
className="mb-0 flex-grow"
|
className="mb-0 flex-grow"
|
||||||
label={index === 0 ? "Claims" : undefined}
|
label={index === 0 ? "Claims" : undefined}
|
||||||
|
icon={
|
||||||
|
index === 0 ? (
|
||||||
|
<Tooltip
|
||||||
|
className="text-center"
|
||||||
|
content={<span>This field supports glob patterns</span>}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faQuestionCircle} size="sm" />
|
||||||
|
</Tooltip>
|
||||||
|
) : undefined
|
||||||
|
}
|
||||||
isError={Boolean(error)}
|
isError={Boolean(error)}
|
||||||
errorText={error?.message}
|
errorText={error?.message}
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user