mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-05 03:26:27 +00:00
improvements: address feedback
This commit is contained in:
@@ -923,7 +923,7 @@ export const useAddIdentityTokenAuth = () => {
|
|||||||
});
|
});
|
||||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: identitiesKeys.getIdentityUniversalAuth(identityId)
|
queryKey: identitiesKeys.getIdentityTokenAuth(identityId)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -959,7 +959,7 @@ export const useUpdateIdentityTokenAuth = () => {
|
|||||||
});
|
});
|
||||||
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
queryClient.invalidateQueries({ queryKey: identitiesKeys.getIdentityById(identityId) });
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: identitiesKeys.getIdentityUniversalAuth(identityId)
|
queryKey: identitiesKeys.getIdentityTokenAuth(identityId)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
+1
-1
@@ -34,7 +34,7 @@ export const IdentityAuthMethodModal = ({ popUp, handlePopUpOpen, handlePopUpTog
|
|||||||
title={
|
title={
|
||||||
isSelectedAuthAlreadyConfigured
|
isSelectedAuthAlreadyConfigured
|
||||||
? `Edit ${identityAuthToNameMap[selectedAuthMethod!] ?? ""}`
|
? `Edit ${identityAuthToNameMap[selectedAuthMethod!] ?? ""}`
|
||||||
: `Create new ${identityAuthToNameMap[selectedAuthMethod!] ?? ""}`
|
: `Add ${identityAuthToNameMap[selectedAuthMethod!] ?? ""}`
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<IdentityAuthMethodModalContent
|
<IdentityAuthMethodModalContent
|
||||||
|
|||||||
+8
-8
@@ -111,7 +111,7 @@ export const IdentityAuthMethodModalContent = ({
|
|||||||
[IdentityAuthMethod.UNIVERSAL_AUTH]: {
|
[IdentityAuthMethod.UNIVERSAL_AUTH]: {
|
||||||
render: () => (
|
render: () => (
|
||||||
<IdentityUniversalAuthForm
|
<IdentityUniversalAuthForm
|
||||||
identityAuthMethodData={identityAuthMethodData}
|
identityId={identityAuthMethodData.identityId}
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
handlePopUpToggle={handlePopUpToggle}
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
/>
|
/>
|
||||||
@@ -121,7 +121,7 @@ export const IdentityAuthMethodModalContent = ({
|
|||||||
[IdentityAuthMethod.OIDC_AUTH]: {
|
[IdentityAuthMethod.OIDC_AUTH]: {
|
||||||
render: () => (
|
render: () => (
|
||||||
<IdentityOidcAuthForm
|
<IdentityOidcAuthForm
|
||||||
identityAuthMethodData={identityAuthMethodData}
|
identityId={identityAuthMethodData.identityId}
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
handlePopUpToggle={handlePopUpToggle}
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
/>
|
/>
|
||||||
@@ -131,7 +131,7 @@ export const IdentityAuthMethodModalContent = ({
|
|||||||
[IdentityAuthMethod.TOKEN_AUTH]: {
|
[IdentityAuthMethod.TOKEN_AUTH]: {
|
||||||
render: () => (
|
render: () => (
|
||||||
<IdentityTokenAuthForm
|
<IdentityTokenAuthForm
|
||||||
identityAuthMethodData={identityAuthMethodData}
|
identityId={identityAuthMethodData.identityId}
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
handlePopUpToggle={handlePopUpToggle}
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
/>
|
/>
|
||||||
@@ -141,7 +141,7 @@ export const IdentityAuthMethodModalContent = ({
|
|||||||
[IdentityAuthMethod.AZURE_AUTH]: {
|
[IdentityAuthMethod.AZURE_AUTH]: {
|
||||||
render: () => (
|
render: () => (
|
||||||
<IdentityAzureAuthForm
|
<IdentityAzureAuthForm
|
||||||
identityAuthMethodData={identityAuthMethodData}
|
identityId={identityAuthMethodData.identityId}
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
handlePopUpToggle={handlePopUpToggle}
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
/>
|
/>
|
||||||
@@ -151,7 +151,7 @@ export const IdentityAuthMethodModalContent = ({
|
|||||||
[IdentityAuthMethod.GCP_AUTH]: {
|
[IdentityAuthMethod.GCP_AUTH]: {
|
||||||
render: () => (
|
render: () => (
|
||||||
<IdentityGcpAuthForm
|
<IdentityGcpAuthForm
|
||||||
identityAuthMethodData={identityAuthMethodData}
|
identityId={identityAuthMethodData.identityId}
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
handlePopUpToggle={handlePopUpToggle}
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
/>
|
/>
|
||||||
@@ -161,7 +161,7 @@ export const IdentityAuthMethodModalContent = ({
|
|||||||
[IdentityAuthMethod.KUBERNETES_AUTH]: {
|
[IdentityAuthMethod.KUBERNETES_AUTH]: {
|
||||||
render: () => (
|
render: () => (
|
||||||
<IdentityKubernetesAuthForm
|
<IdentityKubernetesAuthForm
|
||||||
identityAuthMethodData={identityAuthMethodData}
|
identityId={identityAuthMethodData.identityId}
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
handlePopUpToggle={handlePopUpToggle}
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
/>
|
/>
|
||||||
@@ -171,7 +171,7 @@ export const IdentityAuthMethodModalContent = ({
|
|||||||
[IdentityAuthMethod.AWS_AUTH]: {
|
[IdentityAuthMethod.AWS_AUTH]: {
|
||||||
render: () => (
|
render: () => (
|
||||||
<IdentityAwsAuthForm
|
<IdentityAwsAuthForm
|
||||||
identityAuthMethodData={identityAuthMethodData}
|
identityId={identityAuthMethodData.identityId}
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
handlePopUpToggle={handlePopUpToggle}
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
/>
|
/>
|
||||||
@@ -181,7 +181,7 @@ export const IdentityAuthMethodModalContent = ({
|
|||||||
[IdentityAuthMethod.JWT_AUTH]: {
|
[IdentityAuthMethod.JWT_AUTH]: {
|
||||||
render: () => (
|
render: () => (
|
||||||
<IdentityJwtAuthForm
|
<IdentityJwtAuthForm
|
||||||
identityAuthMethodData={identityAuthMethodData}
|
identityId={identityAuthMethodData.identityId}
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
handlePopUpToggle={handlePopUpToggle}
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
/>
|
/>
|
||||||
|
|||||||
+202
-181
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Controller, useFieldArray, useForm } from "react-hook-form";
|
import { Controller, useFieldArray, useForm } from "react-hook-form";
|
||||||
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";
|
||||||
@@ -6,17 +6,27 @@ 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 } from "@app/components/v2";
|
import {
|
||||||
|
Button,
|
||||||
|
FormControl,
|
||||||
|
IconButton,
|
||||||
|
Input,
|
||||||
|
Tab,
|
||||||
|
TabList,
|
||||||
|
TabPanel,
|
||||||
|
Tabs
|
||||||
|
} from "@app/components/v2";
|
||||||
import { useOrganization, useSubscription } from "@app/context";
|
import { useOrganization, useSubscription } from "@app/context";
|
||||||
import {
|
import {
|
||||||
useAddIdentityAwsAuth,
|
useAddIdentityAwsAuth,
|
||||||
useGetIdentityAwsAuth,
|
useGetIdentityAwsAuth,
|
||||||
useUpdateIdentityAwsAuth
|
useUpdateIdentityAwsAuth
|
||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
|
||||||
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
|
import { IdentityFormTab } from "./types";
|
||||||
|
|
||||||
const schema = z
|
const schema = z
|
||||||
.object({
|
.object({
|
||||||
stsEndpoint: z.string(),
|
stsEndpoint: z.string(),
|
||||||
@@ -52,18 +62,15 @@ type Props = {
|
|||||||
popUpName: keyof UsePopUpState<["identityAuthMethod"]>,
|
popUpName: keyof UsePopUpState<["identityAuthMethod"]>,
|
||||||
state?: boolean
|
state?: boolean
|
||||||
) => void;
|
) => void;
|
||||||
identityAuthMethodData: {
|
identityId?: string;
|
||||||
identityId: string;
|
isUpdate?: boolean;
|
||||||
name: string;
|
|
||||||
configuredAuthMethods?: IdentityAuthMethod[];
|
|
||||||
authMethod?: IdentityAuthMethod;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IdentityAwsAuthForm = ({
|
export const IdentityAwsAuthForm = ({
|
||||||
handlePopUpOpen,
|
handlePopUpOpen,
|
||||||
handlePopUpToggle,
|
handlePopUpToggle,
|
||||||
identityAuthMethodData
|
identityId,
|
||||||
|
isUpdate
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const orgId = currentOrg?.id || "";
|
const orgId = currentOrg?.id || "";
|
||||||
@@ -71,11 +78,9 @@ export const IdentityAwsAuthForm = ({
|
|||||||
|
|
||||||
const { mutateAsync: addMutateAsync } = useAddIdentityAwsAuth();
|
const { mutateAsync: addMutateAsync } = useAddIdentityAwsAuth();
|
||||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityAwsAuth();
|
const { mutateAsync: updateMutateAsync } = useUpdateIdentityAwsAuth();
|
||||||
|
const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration);
|
||||||
|
|
||||||
const isUpdate = identityAuthMethodData?.configuredAuthMethods?.includes(
|
const { data } = useGetIdentityAwsAuth(identityId ?? "", {
|
||||||
identityAuthMethodData.authMethod! || ""
|
|
||||||
);
|
|
||||||
const { data } = useGetIdentityAwsAuth(identityAuthMethodData?.identityId ?? "", {
|
|
||||||
enabled: isUpdate
|
enabled: isUpdate
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -143,7 +148,7 @@ export const IdentityAwsAuthForm = ({
|
|||||||
accessTokenTrustedIps
|
accessTokenTrustedIps
|
||||||
}: FormData) => {
|
}: FormData) => {
|
||||||
try {
|
try {
|
||||||
if (!identityAuthMethodData) return;
|
if (!identityId) return;
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
await updateMutateAsync({
|
await updateMutateAsync({
|
||||||
@@ -151,7 +156,7 @@ export const IdentityAwsAuthForm = ({
|
|||||||
stsEndpoint,
|
stsEndpoint,
|
||||||
allowedPrincipalArns,
|
allowedPrincipalArns,
|
||||||
allowedAccountIds,
|
allowedAccountIds,
|
||||||
identityId: identityAuthMethodData.identityId,
|
identityId,
|
||||||
accessTokenTTL: Number(accessTokenTTL),
|
accessTokenTTL: Number(accessTokenTTL),
|
||||||
accessTokenMaxTTL: Number(accessTokenMaxTTL),
|
accessTokenMaxTTL: Number(accessTokenMaxTTL),
|
||||||
accessTokenNumUsesLimit: Number(accessTokenNumUsesLimit),
|
accessTokenNumUsesLimit: Number(accessTokenNumUsesLimit),
|
||||||
@@ -160,7 +165,7 @@ export const IdentityAwsAuthForm = ({
|
|||||||
} else {
|
} else {
|
||||||
await addMutateAsync({
|
await addMutateAsync({
|
||||||
organizationId: orgId,
|
organizationId: orgId,
|
||||||
identityId: identityAuthMethodData.identityId,
|
identityId,
|
||||||
stsEndpoint: stsEndpoint || "",
|
stsEndpoint: stsEndpoint || "",
|
||||||
allowedPrincipalArns: allowedPrincipalArns || "",
|
allowedPrincipalArns: allowedPrincipalArns || "",
|
||||||
allowedAccountIds: allowedAccountIds || "",
|
allowedAccountIds: allowedAccountIds || "",
|
||||||
@@ -188,178 +193,194 @@ export const IdentityAwsAuthForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
<form
|
||||||
<Controller
|
onSubmit={handleSubmit(onFormSubmit, (fields) => {
|
||||||
control={control}
|
setTabValue(
|
||||||
defaultValue="2592000"
|
["accessTokenTrustedIps"].includes(Object.keys(fields)[0])
|
||||||
name="allowedPrincipalArns"
|
? IdentityFormTab.Advanced
|
||||||
render={({ field, fieldState: { error } }) => (
|
: IdentityFormTab.Configuration
|
||||||
<FormControl
|
);
|
||||||
label="Allowed Principal ARNs"
|
})}
|
||||||
isError={Boolean(error)}
|
>
|
||||||
errorText={error?.message}
|
<Tabs value={tabValue} onValueChange={(value) => setTabValue(value as IdentityFormTab)}>
|
||||||
>
|
<TabList>
|
||||||
<Input
|
<Tab value={IdentityFormTab.Configuration}>Configuration</Tab>
|
||||||
{...field}
|
<Tab value={IdentityFormTab.Advanced}>Advanced</Tab>
|
||||||
placeholder="arn:aws:iam::123456789012:role/MyRoleName, arn:aws:iam::123456789012:user/MyUserName..."
|
</TabList>
|
||||||
type="text"
|
<TabPanel value={IdentityFormTab.Configuration}>
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="allowedAccountIds"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Allowed Account IDs"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="123456789012, ..." />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue="https://sts.amazonaws.com/"
|
|
||||||
name="stsEndpoint"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl label="STS Endpoint" isError={Boolean(error)} errorText={error?.message}>
|
|
||||||
<Input {...field} placeholder="https://sts.amazonaws.com/" type="text" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue="2592000"
|
|
||||||
name="accessTokenTTL"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Access Token TTL (seconds)"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue="2592000"
|
|
||||||
name="accessTokenMaxTTL"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Access Token Max TTL (seconds)"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue="0"
|
|
||||||
name="accessTokenNumUsesLimit"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Access Token Max Number of Uses"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="0" type="number" min="0" step="1" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{accessTokenTrustedIpsFields.map(({ id }, index) => (
|
|
||||||
<div className="mb-3 flex items-end space-x-2" key={id}>
|
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name={`accessTokenTrustedIps.${index}.ipAddress`}
|
defaultValue="2592000"
|
||||||
defaultValue="0.0.0.0/0"
|
name="allowedPrincipalArns"
|
||||||
render={({ field, fieldState: { error } }) => {
|
render={({ field, fieldState: { error } }) => (
|
||||||
return (
|
<FormControl
|
||||||
<FormControl
|
label="Allowed Principal ARNs"
|
||||||
className="mb-0 flex-grow"
|
isError={Boolean(error)}
|
||||||
label={index === 0 ? "Access Token Trusted IPs" : undefined}
|
errorText={error?.message}
|
||||||
isError={Boolean(error)}
|
>
|
||||||
errorText={error?.message}
|
<Input
|
||||||
>
|
{...field}
|
||||||
<Input
|
placeholder="arn:aws:iam::123456789012:role/MyRoleName, arn:aws:iam::123456789012:user/MyUserName..."
|
||||||
value={field.value}
|
type="text"
|
||||||
onChange={(e) => {
|
/>
|
||||||
if (subscription?.ipAllowlisting) {
|
</FormControl>
|
||||||
field.onChange(e);
|
)}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
|
||||||
}}
|
|
||||||
placeholder="123.456.789.0"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<Controller
|
||||||
onClick={() => {
|
control={control}
|
||||||
if (subscription?.ipAllowlisting) {
|
name="allowedAccountIds"
|
||||||
removeAccessTokenTrustedIp(index);
|
render={({ field, fieldState: { error } }) => (
|
||||||
return;
|
<FormControl
|
||||||
}
|
label="Allowed Account IDs"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="123456789012, ..." />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue="https://sts.amazonaws.com/"
|
||||||
|
name="stsEndpoint"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl label="STS Endpoint" isError={Boolean(error)} errorText={error?.message}>
|
||||||
|
<Input {...field} placeholder="https://sts.amazonaws.com/" type="text" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue="2592000"
|
||||||
|
name="accessTokenTTL"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Access Token TTL (seconds)"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue="2592000"
|
||||||
|
name="accessTokenMaxTTL"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Access Token Max TTL (seconds)"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue="0"
|
||||||
|
name="accessTokenNumUsesLimit"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Access Token Max Number of Uses"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="0" type="number" min="0" step="1" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel value={IdentityFormTab.Advanced}>
|
||||||
|
{accessTokenTrustedIpsFields.map(({ id }, index) => (
|
||||||
|
<div className="mb-3 flex items-end space-x-2" key={id}>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name={`accessTokenTrustedIps.${index}.ipAddress`}
|
||||||
|
defaultValue="0.0.0.0/0"
|
||||||
|
render={({ field, fieldState: { error } }) => {
|
||||||
|
return (
|
||||||
|
<FormControl
|
||||||
|
className="mb-0 flex-grow"
|
||||||
|
label={index === 0 ? "Access Token Trusted IPs" : undefined}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
value={field.value}
|
||||||
|
onChange={(e) => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
|
field.onChange(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
handlePopUpOpen("upgradePlan");
|
||||||
}}
|
}}
|
||||||
size="lg"
|
placeholder="123.456.789.0"
|
||||||
colorSchema="danger"
|
/>
|
||||||
variant="plain"
|
</FormControl>
|
||||||
ariaLabel="update"
|
);
|
||||||
className="p-3"
|
}}
|
||||||
>
|
/>
|
||||||
<FontAwesomeIcon icon={faXmark} />
|
<IconButton
|
||||||
</IconButton>
|
onClick={() => {
|
||||||
</div>
|
if (subscription?.ipAllowlisting) {
|
||||||
))}
|
removeAccessTokenTrustedIp(index);
|
||||||
<div className="my-4 ml-1">
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
}}
|
||||||
|
size="lg"
|
||||||
|
colorSchema="danger"
|
||||||
|
variant="plain"
|
||||||
|
ariaLabel="update"
|
||||||
|
className="p-3"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faXmark} />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div className="my-4 ml-1">
|
||||||
|
<Button
|
||||||
|
variant="outline_bg"
|
||||||
|
onClick={() => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
|
appendAccessTokenTrustedIp({
|
||||||
|
ipAddress: "0.0.0.0/0"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
}}
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
|
size="xs"
|
||||||
|
>
|
||||||
|
Add IP Address
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</TabPanel>
|
||||||
|
</Tabs>
|
||||||
|
<div className="flex items-center">
|
||||||
<Button
|
<Button
|
||||||
variant="outline_bg"
|
className="mr-4"
|
||||||
onClick={() => {
|
size="sm"
|
||||||
if (subscription?.ipAllowlisting) {
|
type="submit"
|
||||||
appendAccessTokenTrustedIp({
|
isLoading={isSubmitting}
|
||||||
ipAddress: "0.0.0.0/0"
|
isDisabled={isSubmitting}
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
|
||||||
}}
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
|
||||||
size="xs"
|
|
||||||
>
|
>
|
||||||
Add IP Address
|
{isUpdate ? "Update" : "Add"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Button
|
|
||||||
className="mr-4"
|
|
||||||
size="sm"
|
|
||||||
type="submit"
|
|
||||||
isLoading={isSubmitting}
|
|
||||||
isDisabled={isSubmitting}
|
|
||||||
>
|
|
||||||
{!isUpdate ? "Create" : "Edit"}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
colorSchema="secondary"
|
colorSchema="secondary"
|
||||||
variant="plain"
|
variant="plain"
|
||||||
onClick={() => handlePopUpToggle("identityAuthMethod", false)}
|
onClick={() => handlePopUpToggle("identityAuthMethod", false)}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
|||||||
+202
-181
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Controller, useFieldArray, useForm } from "react-hook-form";
|
import { Controller, useFieldArray, useForm } from "react-hook-form";
|
||||||
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";
|
||||||
@@ -6,17 +6,27 @@ 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 } from "@app/components/v2";
|
import {
|
||||||
|
Button,
|
||||||
|
FormControl,
|
||||||
|
IconButton,
|
||||||
|
Input,
|
||||||
|
Tab,
|
||||||
|
TabList,
|
||||||
|
TabPanel,
|
||||||
|
Tabs
|
||||||
|
} from "@app/components/v2";
|
||||||
import { useOrganization, useSubscription } from "@app/context";
|
import { useOrganization, useSubscription } from "@app/context";
|
||||||
import {
|
import {
|
||||||
useAddIdentityAzureAuth,
|
useAddIdentityAzureAuth,
|
||||||
useGetIdentityAzureAuth,
|
useGetIdentityAzureAuth,
|
||||||
useUpdateIdentityAzureAuth
|
useUpdateIdentityAzureAuth
|
||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
|
||||||
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
|
import { IdentityFormTab } from "./types";
|
||||||
|
|
||||||
const schema = z
|
const schema = z
|
||||||
.object({
|
.object({
|
||||||
tenantId: z.string().min(1),
|
tenantId: z.string().min(1),
|
||||||
@@ -47,18 +57,15 @@ type Props = {
|
|||||||
popUpName: keyof UsePopUpState<["identityAuthMethod"]>,
|
popUpName: keyof UsePopUpState<["identityAuthMethod"]>,
|
||||||
state?: boolean
|
state?: boolean
|
||||||
) => void;
|
) => void;
|
||||||
identityAuthMethodData: {
|
identityId?: string;
|
||||||
identityId: string;
|
isUpdate?: boolean;
|
||||||
name: string;
|
|
||||||
configuredAuthMethods?: IdentityAuthMethod[];
|
|
||||||
authMethod?: IdentityAuthMethod;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IdentityAzureAuthForm = ({
|
export const IdentityAzureAuthForm = ({
|
||||||
handlePopUpOpen,
|
handlePopUpOpen,
|
||||||
handlePopUpToggle,
|
handlePopUpToggle,
|
||||||
identityAuthMethodData
|
identityId,
|
||||||
|
isUpdate
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const orgId = currentOrg?.id || "";
|
const orgId = currentOrg?.id || "";
|
||||||
@@ -66,11 +73,9 @@ export const IdentityAzureAuthForm = ({
|
|||||||
|
|
||||||
const { mutateAsync: addMutateAsync } = useAddIdentityAzureAuth();
|
const { mutateAsync: addMutateAsync } = useAddIdentityAzureAuth();
|
||||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityAzureAuth();
|
const { mutateAsync: updateMutateAsync } = useUpdateIdentityAzureAuth();
|
||||||
|
const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration);
|
||||||
|
|
||||||
const isUpdate = identityAuthMethodData?.configuredAuthMethods?.includes(
|
const { data } = useGetIdentityAzureAuth(identityId ?? "", {
|
||||||
identityAuthMethodData.authMethod! || ""
|
|
||||||
);
|
|
||||||
const { data } = useGetIdentityAzureAuth(identityAuthMethodData?.identityId ?? "", {
|
|
||||||
enabled: isUpdate
|
enabled: isUpdate
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -139,12 +144,12 @@ export const IdentityAzureAuthForm = ({
|
|||||||
accessTokenTrustedIps
|
accessTokenTrustedIps
|
||||||
}: FormData) => {
|
}: FormData) => {
|
||||||
try {
|
try {
|
||||||
if (!identityAuthMethodData) return;
|
if (!identityId) return;
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
await updateMutateAsync({
|
await updateMutateAsync({
|
||||||
organizationId: orgId,
|
organizationId: orgId,
|
||||||
identityId: identityAuthMethodData.identityId,
|
identityId,
|
||||||
tenantId,
|
tenantId,
|
||||||
resource,
|
resource,
|
||||||
allowedServicePrincipalIds,
|
allowedServicePrincipalIds,
|
||||||
@@ -156,7 +161,7 @@ export const IdentityAzureAuthForm = ({
|
|||||||
} else {
|
} else {
|
||||||
await addMutateAsync({
|
await addMutateAsync({
|
||||||
organizationId: orgId,
|
organizationId: orgId,
|
||||||
identityId: identityAuthMethodData.identityId,
|
identityId,
|
||||||
tenantId: tenantId || "",
|
tenantId: tenantId || "",
|
||||||
resource: resource || "",
|
resource: resource || "",
|
||||||
allowedServicePrincipalIds: allowedServicePrincipalIds || "",
|
allowedServicePrincipalIds: allowedServicePrincipalIds || "",
|
||||||
@@ -184,178 +189,194 @@ export const IdentityAzureAuthForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
<form
|
||||||
<Controller
|
onSubmit={handleSubmit(onFormSubmit, (fields) => {
|
||||||
control={control}
|
setTabValue(
|
||||||
defaultValue="2592000"
|
["accessTokenTrustedIps"].includes(Object.keys(fields)[0])
|
||||||
name="tenantId"
|
? IdentityFormTab.Advanced
|
||||||
render={({ field, fieldState: { error } }) => (
|
: IdentityFormTab.Configuration
|
||||||
<FormControl
|
);
|
||||||
label="Tenant ID"
|
})}
|
||||||
isError={Boolean(error)}
|
>
|
||||||
errorText={error?.message}
|
<Tabs value={tabValue} onValueChange={(value) => setTabValue(value as IdentityFormTab)}>
|
||||||
isRequired
|
<TabList>
|
||||||
>
|
<Tab value={IdentityFormTab.Configuration}>Configuration</Tab>
|
||||||
<Input {...field} placeholder="00000000-0000-0000-0000-000000000000" type="text" />
|
<Tab value={IdentityFormTab.Advanced}>Advanced</Tab>
|
||||||
</FormControl>
|
</TabList>
|
||||||
)}
|
<TabPanel value={IdentityFormTab.Configuration}>
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="resource"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Resource / Audience"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="https://management.azure.com/" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="allowedServicePrincipalIds"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Allowed Service Principal IDs"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="00000000-0000-0000-0000-000000000000, ..." />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue="2592000"
|
|
||||||
name="accessTokenTTL"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Access Token TTL (seconds)"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue="2592000"
|
|
||||||
name="accessTokenMaxTTL"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Access Token Max TTL (seconds)"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue="0"
|
|
||||||
name="accessTokenNumUsesLimit"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Access Token Max Number of Uses"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="0" type="number" min="0" step="1" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{accessTokenTrustedIpsFields.map(({ id }, index) => (
|
|
||||||
<div className="mb-3 flex items-end space-x-2" key={id}>
|
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name={`accessTokenTrustedIps.${index}.ipAddress`}
|
defaultValue="2592000"
|
||||||
defaultValue="0.0.0.0/0"
|
name="tenantId"
|
||||||
render={({ field, fieldState: { error } }) => {
|
render={({ field, fieldState: { error } }) => (
|
||||||
return (
|
<FormControl
|
||||||
<FormControl
|
label="Tenant ID"
|
||||||
className="mb-0 flex-grow"
|
isError={Boolean(error)}
|
||||||
label={index === 0 ? "Access Token Trusted IPs" : undefined}
|
errorText={error?.message}
|
||||||
isError={Boolean(error)}
|
isRequired
|
||||||
errorText={error?.message}
|
>
|
||||||
>
|
<Input {...field} placeholder="00000000-0000-0000-0000-000000000000" type="text" />
|
||||||
<Input
|
</FormControl>
|
||||||
value={field.value}
|
)}
|
||||||
onChange={(e) => {
|
|
||||||
if (subscription?.ipAllowlisting) {
|
|
||||||
field.onChange(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
|
||||||
}}
|
|
||||||
placeholder="123.456.789.0"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<Controller
|
||||||
onClick={() => {
|
control={control}
|
||||||
if (subscription?.ipAllowlisting) {
|
name="resource"
|
||||||
removeAccessTokenTrustedIp(index);
|
render={({ field, fieldState: { error } }) => (
|
||||||
return;
|
<FormControl
|
||||||
}
|
label="Resource / Audience"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="https://management.azure.com/" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="allowedServicePrincipalIds"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Allowed Service Principal IDs"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="00000000-0000-0000-0000-000000000000, ..." />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue="2592000"
|
||||||
|
name="accessTokenTTL"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Access Token TTL (seconds)"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue="2592000"
|
||||||
|
name="accessTokenMaxTTL"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Access Token Max TTL (seconds)"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue="0"
|
||||||
|
name="accessTokenNumUsesLimit"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Access Token Max Number of Uses"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="0" type="number" min="0" step="1" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel value={IdentityFormTab.Advanced}>
|
||||||
|
{accessTokenTrustedIpsFields.map(({ id }, index) => (
|
||||||
|
<div className="mb-3 flex items-end space-x-2" key={id}>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name={`accessTokenTrustedIps.${index}.ipAddress`}
|
||||||
|
defaultValue="0.0.0.0/0"
|
||||||
|
render={({ field, fieldState: { error } }) => {
|
||||||
|
return (
|
||||||
|
<FormControl
|
||||||
|
className="mb-0 flex-grow"
|
||||||
|
label={index === 0 ? "Access Token Trusted IPs" : undefined}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
value={field.value}
|
||||||
|
onChange={(e) => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
|
field.onChange(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
handlePopUpOpen("upgradePlan");
|
||||||
}}
|
}}
|
||||||
size="lg"
|
placeholder="123.456.789.0"
|
||||||
colorSchema="danger"
|
/>
|
||||||
variant="plain"
|
</FormControl>
|
||||||
ariaLabel="update"
|
);
|
||||||
className="p-3"
|
}}
|
||||||
>
|
/>
|
||||||
<FontAwesomeIcon icon={faXmark} />
|
<IconButton
|
||||||
</IconButton>
|
onClick={() => {
|
||||||
</div>
|
if (subscription?.ipAllowlisting) {
|
||||||
))}
|
removeAccessTokenTrustedIp(index);
|
||||||
<div className="my-4 ml-1">
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
}}
|
||||||
|
size="lg"
|
||||||
|
colorSchema="danger"
|
||||||
|
variant="plain"
|
||||||
|
ariaLabel="update"
|
||||||
|
className="p-3"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faXmark} />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div className="my-4 ml-1">
|
||||||
|
<Button
|
||||||
|
variant="outline_bg"
|
||||||
|
onClick={() => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
|
appendAccessTokenTrustedIp({
|
||||||
|
ipAddress: "0.0.0.0/0"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
}}
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
|
size="xs"
|
||||||
|
>
|
||||||
|
Add IP Address
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</TabPanel>
|
||||||
|
</Tabs>
|
||||||
|
<div className="flex items-center">
|
||||||
<Button
|
<Button
|
||||||
variant="outline_bg"
|
className="mr-4"
|
||||||
onClick={() => {
|
size="sm"
|
||||||
if (subscription?.ipAllowlisting) {
|
type="submit"
|
||||||
appendAccessTokenTrustedIp({
|
isLoading={isSubmitting}
|
||||||
ipAddress: "0.0.0.0/0"
|
isDisabled={isSubmitting}
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
|
||||||
}}
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
|
||||||
size="xs"
|
|
||||||
>
|
>
|
||||||
Add IP Address
|
{isUpdate ? "Update" : "Add"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Button
|
|
||||||
className="mr-4"
|
|
||||||
size="sm"
|
|
||||||
type="submit"
|
|
||||||
isLoading={isSubmitting}
|
|
||||||
isDisabled={isSubmitting}
|
|
||||||
>
|
|
||||||
{!isUpdate ? "Create" : "Edit"}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
colorSchema="secondary"
|
colorSchema="secondary"
|
||||||
variant="plain"
|
variant="plain"
|
||||||
onClick={() => handlePopUpToggle("identityAuthMethod", false)}
|
onClick={() => handlePopUpToggle("identityAuthMethod", false)}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
|||||||
+227
-200
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Controller, useFieldArray, useForm } from "react-hook-form";
|
import { Controller, useFieldArray, useForm } from "react-hook-form";
|
||||||
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";
|
||||||
@@ -6,17 +6,29 @@ 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, Select, SelectItem } from "@app/components/v2";
|
import {
|
||||||
|
Button,
|
||||||
|
FormControl,
|
||||||
|
IconButton,
|
||||||
|
Input,
|
||||||
|
Select,
|
||||||
|
SelectItem,
|
||||||
|
Tab,
|
||||||
|
TabList,
|
||||||
|
TabPanel,
|
||||||
|
Tabs
|
||||||
|
} from "@app/components/v2";
|
||||||
import { useOrganization, useSubscription } from "@app/context";
|
import { useOrganization, useSubscription } from "@app/context";
|
||||||
import {
|
import {
|
||||||
useAddIdentityGcpAuth,
|
useAddIdentityGcpAuth,
|
||||||
useGetIdentityGcpAuth,
|
useGetIdentityGcpAuth,
|
||||||
useUpdateIdentityGcpAuth
|
useUpdateIdentityGcpAuth
|
||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
|
||||||
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
|
import { IdentityFormTab } from "./types";
|
||||||
|
|
||||||
const schema = z
|
const schema = z
|
||||||
.object({
|
.object({
|
||||||
type: z.enum(["iam", "gce"]),
|
type: z.enum(["iam", "gce"]),
|
||||||
@@ -48,18 +60,15 @@ type Props = {
|
|||||||
popUpName: keyof UsePopUpState<["identityAuthMethod"]>,
|
popUpName: keyof UsePopUpState<["identityAuthMethod"]>,
|
||||||
state?: boolean
|
state?: boolean
|
||||||
) => void;
|
) => void;
|
||||||
identityAuthMethodData: {
|
identityId?: string;
|
||||||
identityId: string;
|
isUpdate?: boolean;
|
||||||
name: string;
|
|
||||||
configuredAuthMethods?: IdentityAuthMethod[];
|
|
||||||
authMethod?: IdentityAuthMethod;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IdentityGcpAuthForm = ({
|
export const IdentityGcpAuthForm = ({
|
||||||
handlePopUpOpen,
|
handlePopUpOpen,
|
||||||
handlePopUpToggle,
|
handlePopUpToggle,
|
||||||
identityAuthMethodData
|
identityId,
|
||||||
|
isUpdate
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const orgId = currentOrg?.id || "";
|
const orgId = currentOrg?.id || "";
|
||||||
@@ -67,11 +76,9 @@ export const IdentityGcpAuthForm = ({
|
|||||||
|
|
||||||
const { mutateAsync: addMutateAsync } = useAddIdentityGcpAuth();
|
const { mutateAsync: addMutateAsync } = useAddIdentityGcpAuth();
|
||||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityGcpAuth();
|
const { mutateAsync: updateMutateAsync } = useUpdateIdentityGcpAuth();
|
||||||
|
const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration);
|
||||||
|
|
||||||
const isUpdate = identityAuthMethodData?.configuredAuthMethods?.includes(
|
const { data } = useGetIdentityGcpAuth(identityId ?? "", {
|
||||||
identityAuthMethodData.authMethod! || ""
|
|
||||||
);
|
|
||||||
const { data } = useGetIdentityGcpAuth(identityAuthMethodData?.identityId ?? "", {
|
|
||||||
enabled: isUpdate
|
enabled: isUpdate
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -146,11 +153,11 @@ export const IdentityGcpAuthForm = ({
|
|||||||
accessTokenTrustedIps
|
accessTokenTrustedIps
|
||||||
}: FormData) => {
|
}: FormData) => {
|
||||||
try {
|
try {
|
||||||
if (!identityAuthMethodData) return;
|
if (!identityId) return;
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
await updateMutateAsync({
|
await updateMutateAsync({
|
||||||
identityId: identityAuthMethodData.identityId,
|
identityId,
|
||||||
organizationId: orgId,
|
organizationId: orgId,
|
||||||
type,
|
type,
|
||||||
allowedServiceAccounts,
|
allowedServiceAccounts,
|
||||||
@@ -163,7 +170,7 @@ export const IdentityGcpAuthForm = ({
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await addMutateAsync({
|
await addMutateAsync({
|
||||||
identityId: identityAuthMethodData.identityId,
|
identityId,
|
||||||
organizationId: orgId,
|
organizationId: orgId,
|
||||||
type,
|
type,
|
||||||
allowedServiceAccounts: allowedServiceAccounts || "",
|
allowedServiceAccounts: allowedServiceAccounts || "",
|
||||||
@@ -193,202 +200,222 @@ export const IdentityGcpAuthForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
<form
|
||||||
<Controller
|
onSubmit={handleSubmit(onFormSubmit, (fields) => {
|
||||||
control={control}
|
setTabValue(
|
||||||
name="type"
|
["accessTokenTrustedIps"].includes(Object.keys(fields)[0])
|
||||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
? IdentityFormTab.Advanced
|
||||||
<FormControl label="Type" isError={Boolean(error)} errorText={error?.message}>
|
: IdentityFormTab.Configuration
|
||||||
<Select
|
);
|
||||||
defaultValue={field.value}
|
})}
|
||||||
{...field}
|
>
|
||||||
onValueChange={(e) => onChange(e)}
|
<Tabs value={tabValue} onValueChange={(value) => setTabValue(value as IdentityFormTab)}>
|
||||||
className="w-full"
|
<TabList>
|
||||||
>
|
<Tab value={IdentityFormTab.Configuration}>Configuration</Tab>
|
||||||
<SelectItem value="gce" key="gce">
|
<Tab value={IdentityFormTab.Advanced}>Advanced</Tab>
|
||||||
GCP ID Token Auth (Recommended)
|
</TabList>
|
||||||
</SelectItem>
|
<TabPanel value={IdentityFormTab.Configuration}>
|
||||||
<SelectItem value="iam" key="iam">
|
|
||||||
GCP IAM Auth
|
|
||||||
</SelectItem>
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue="2592000"
|
|
||||||
name="allowedServiceAccounts"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Allowed Service Account Emails"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
placeholder="[email protected], [email protected]"
|
|
||||||
type="text"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{watchedType === "gce" && (
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="allowedProjects"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Allowed Projects"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="my-gcp-project, ..." />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{watchedType === "gce" && (
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="allowedZones"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl label="Allowed Zones" isError={Boolean(error)} errorText={error?.message}>
|
|
||||||
<Input {...field} placeholder="us-west2-a, us-central1-a, ..." />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue="2592000"
|
|
||||||
name="accessTokenTTL"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Access Token TTL (seconds)"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue="2592000"
|
|
||||||
name="accessTokenMaxTTL"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Access Token Max TTL (seconds)"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue="0"
|
|
||||||
name="accessTokenNumUsesLimit"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Access Token Max Number of Uses"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="0" type="number" min="0" step="1" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{accessTokenTrustedIpsFields.map(({ id }, index) => (
|
|
||||||
<div className="mb-3 flex items-end space-x-2" key={id}>
|
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name={`accessTokenTrustedIps.${index}.ipAddress`}
|
name="type"
|
||||||
defaultValue="0.0.0.0/0"
|
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
||||||
render={({ field, fieldState: { error } }) => {
|
<FormControl label="Type" isError={Boolean(error)} errorText={error?.message}>
|
||||||
return (
|
<Select
|
||||||
|
defaultValue={field.value}
|
||||||
|
{...field}
|
||||||
|
onValueChange={(e) => onChange(e)}
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
<SelectItem value="gce" key="gce">
|
||||||
|
GCP ID Token Auth (Recommended)
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="iam" key="iam">
|
||||||
|
GCP IAM Auth
|
||||||
|
</SelectItem>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue="2592000"
|
||||||
|
name="allowedServiceAccounts"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Allowed Service Account Emails"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
{...field}
|
||||||
|
placeholder="[email protected], [email protected]"
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{watchedType === "gce" && (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="allowedProjects"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
<FormControl
|
<FormControl
|
||||||
className="mb-0 flex-grow"
|
label="Allowed Projects"
|
||||||
label={index === 0 ? "Access Token Trusted IPs" : undefined}
|
|
||||||
isError={Boolean(error)}
|
isError={Boolean(error)}
|
||||||
errorText={error?.message}
|
errorText={error?.message}
|
||||||
>
|
>
|
||||||
<Input
|
<Input {...field} placeholder="my-gcp-project, ..." />
|
||||||
value={field.value}
|
|
||||||
onChange={(e) => {
|
|
||||||
if (subscription?.ipAllowlisting) {
|
|
||||||
field.onChange(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
|
||||||
}}
|
|
||||||
placeholder="123.456.789.0"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
</FormControl>
|
||||||
);
|
)}
|
||||||
}}
|
/>
|
||||||
|
)}
|
||||||
|
{watchedType === "gce" && (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="allowedZones"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Allowed Zones"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="us-west2-a, us-central1-a, ..." />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue="2592000"
|
||||||
|
name="accessTokenTTL"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Access Token TTL (seconds)"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<Controller
|
||||||
onClick={() => {
|
control={control}
|
||||||
if (subscription?.ipAllowlisting) {
|
defaultValue="2592000"
|
||||||
removeAccessTokenTrustedIp(index);
|
name="accessTokenMaxTTL"
|
||||||
return;
|
render={({ field, fieldState: { error } }) => (
|
||||||
}
|
<FormControl
|
||||||
|
label="Access Token Max TTL (seconds)"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue="0"
|
||||||
|
name="accessTokenNumUsesLimit"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Access Token Max Number of Uses"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="0" type="number" min="0" step="1" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel value={IdentityFormTab.Advanced}>
|
||||||
|
{accessTokenTrustedIpsFields.map(({ id }, index) => (
|
||||||
|
<div className="mb-3 flex items-end space-x-2" key={id}>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name={`accessTokenTrustedIps.${index}.ipAddress`}
|
||||||
|
defaultValue="0.0.0.0/0"
|
||||||
|
render={({ field, fieldState: { error } }) => {
|
||||||
|
return (
|
||||||
|
<FormControl
|
||||||
|
className="mb-0 flex-grow"
|
||||||
|
label={index === 0 ? "Access Token Trusted IPs" : undefined}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
value={field.value}
|
||||||
|
onChange={(e) => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
|
field.onChange(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
handlePopUpOpen("upgradePlan");
|
||||||
}}
|
}}
|
||||||
size="lg"
|
placeholder="123.456.789.0"
|
||||||
colorSchema="danger"
|
/>
|
||||||
variant="plain"
|
</FormControl>
|
||||||
ariaLabel="update"
|
);
|
||||||
className="p-3"
|
}}
|
||||||
>
|
/>
|
||||||
<FontAwesomeIcon icon={faXmark} />
|
<IconButton
|
||||||
</IconButton>
|
onClick={() => {
|
||||||
</div>
|
if (subscription?.ipAllowlisting) {
|
||||||
))}
|
removeAccessTokenTrustedIp(index);
|
||||||
<div className="my-4 ml-1">
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
}}
|
||||||
|
size="lg"
|
||||||
|
colorSchema="danger"
|
||||||
|
variant="plain"
|
||||||
|
ariaLabel="update"
|
||||||
|
className="p-3"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faXmark} />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div className="my-4 ml-1">
|
||||||
|
<Button
|
||||||
|
variant="outline_bg"
|
||||||
|
onClick={() => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
|
appendAccessTokenTrustedIp({
|
||||||
|
ipAddress: "0.0.0.0/0"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
}}
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
|
size="xs"
|
||||||
|
>
|
||||||
|
Add IP Address
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</TabPanel>
|
||||||
|
</Tabs>
|
||||||
|
<div className="flex items-center">
|
||||||
<Button
|
<Button
|
||||||
variant="outline_bg"
|
className="mr-4"
|
||||||
onClick={() => {
|
size="sm"
|
||||||
if (subscription?.ipAllowlisting) {
|
type="submit"
|
||||||
appendAccessTokenTrustedIp({
|
isLoading={isSubmitting}
|
||||||
ipAddress: "0.0.0.0/0"
|
isDisabled={isSubmitting}
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
|
||||||
}}
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
|
||||||
size="xs"
|
|
||||||
>
|
>
|
||||||
Add IP Address
|
{isUpdate ? "Update" : "Add"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Button
|
|
||||||
className="mr-4"
|
|
||||||
size="sm"
|
|
||||||
type="submit"
|
|
||||||
isLoading={isSubmitting}
|
|
||||||
isDisabled={isSubmitting}
|
|
||||||
>
|
|
||||||
{!isUpdate ? "Create" : "Edit"}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
colorSchema="secondary"
|
colorSchema="secondary"
|
||||||
variant="plain"
|
variant="plain"
|
||||||
onClick={() => handlePopUpToggle("identityAuthMethod", false)}
|
onClick={() => handlePopUpToggle("identityAuthMethod", false)}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
|||||||
+379
-358
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect, useState } 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 { 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";
|
||||||
@@ -14,17 +14,22 @@ import {
|
|||||||
Input,
|
Input,
|
||||||
Select,
|
Select,
|
||||||
SelectItem,
|
SelectItem,
|
||||||
|
Tab,
|
||||||
|
TabList,
|
||||||
|
TabPanel,
|
||||||
|
Tabs,
|
||||||
TextArea,
|
TextArea,
|
||||||
Tooltip
|
Tooltip
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { useOrganization, useSubscription } from "@app/context";
|
import { useOrganization, useSubscription } from "@app/context";
|
||||||
import { useAddIdentityJwtAuth, useUpdateIdentityJwtAuth } from "@app/hooks/api";
|
import { useAddIdentityJwtAuth, useUpdateIdentityJwtAuth } from "@app/hooks/api";
|
||||||
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
|
||||||
import { IdentityJwtConfigurationType } from "@app/hooks/api/identities/enums";
|
import { IdentityJwtConfigurationType } from "@app/hooks/api/identities/enums";
|
||||||
import { useGetIdentityJwtAuth } from "@app/hooks/api/identities/queries";
|
import { useGetIdentityJwtAuth } from "@app/hooks/api/identities/queries";
|
||||||
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
|
import { IdentityFormTab } from "./types";
|
||||||
|
|
||||||
const commonSchema = z.object({
|
const commonSchema = z.object({
|
||||||
accessTokenTrustedIps: z
|
accessTokenTrustedIps: z
|
||||||
.array(
|
.array(
|
||||||
@@ -88,18 +93,15 @@ type Props = {
|
|||||||
popUpName: keyof UsePopUpState<["identityAuthMethod"]>,
|
popUpName: keyof UsePopUpState<["identityAuthMethod"]>,
|
||||||
state?: boolean
|
state?: boolean
|
||||||
) => void;
|
) => void;
|
||||||
identityAuthMethodData: {
|
identityId?: string;
|
||||||
identityId: string;
|
isUpdate?: boolean;
|
||||||
name: string;
|
|
||||||
configuredAuthMethods?: IdentityAuthMethod[];
|
|
||||||
authMethod?: IdentityAuthMethod;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IdentityJwtAuthForm = ({
|
export const IdentityJwtAuthForm = ({
|
||||||
handlePopUpOpen,
|
handlePopUpOpen,
|
||||||
handlePopUpToggle,
|
handlePopUpToggle,
|
||||||
identityAuthMethodData
|
identityId,
|
||||||
|
isUpdate
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const orgId = currentOrg?.id || "";
|
const orgId = currentOrg?.id || "";
|
||||||
@@ -107,11 +109,9 @@ export const IdentityJwtAuthForm = ({
|
|||||||
|
|
||||||
const { mutateAsync: addMutateAsync } = useAddIdentityJwtAuth();
|
const { mutateAsync: addMutateAsync } = useAddIdentityJwtAuth();
|
||||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityJwtAuth();
|
const { mutateAsync: updateMutateAsync } = useUpdateIdentityJwtAuth();
|
||||||
|
const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration);
|
||||||
|
|
||||||
const isUpdate = identityAuthMethodData?.configuredAuthMethods?.includes(
|
const { data } = useGetIdentityJwtAuth(identityId ?? "", {
|
||||||
identityAuthMethodData.authMethod! || ""
|
|
||||||
);
|
|
||||||
const { data } = useGetIdentityJwtAuth(identityAuthMethodData?.identityId ?? "", {
|
|
||||||
enabled: isUpdate
|
enabled: isUpdate
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -218,13 +218,13 @@ export const IdentityJwtAuthForm = ({
|
|||||||
boundSubject
|
boundSubject
|
||||||
}: FormData) => {
|
}: FormData) => {
|
||||||
try {
|
try {
|
||||||
if (!identityAuthMethodData) {
|
if (!identityId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
await updateMutateAsync({
|
await updateMutateAsync({
|
||||||
identityId: identityAuthMethodData.identityId,
|
identityId,
|
||||||
organizationId: orgId,
|
organizationId: orgId,
|
||||||
configurationType,
|
configurationType,
|
||||||
jwksUrl,
|
jwksUrl,
|
||||||
@@ -241,7 +241,7 @@ export const IdentityJwtAuthForm = ({
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await addMutateAsync({
|
await addMutateAsync({
|
||||||
identityId: identityAuthMethodData.identityId,
|
identityId,
|
||||||
configurationType,
|
configurationType,
|
||||||
jwksUrl,
|
jwksUrl,
|
||||||
jwksCaCert,
|
jwksCaCert,
|
||||||
@@ -275,56 +275,230 @@ export const IdentityJwtAuthForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
<form
|
||||||
<Controller
|
onSubmit={handleSubmit(onFormSubmit, (fields) => {
|
||||||
control={control}
|
setTabValue(
|
||||||
name="configurationType"
|
[
|
||||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
"accessTokenTrustedIps",
|
||||||
<FormControl
|
"boundIssuer",
|
||||||
label="Configuration Type"
|
"boundAudiences",
|
||||||
isError={Boolean(error)}
|
"boundSubject",
|
||||||
errorText={error?.message}
|
"boundClaims"
|
||||||
>
|
].includes(Object.keys(fields)[0])
|
||||||
<Select
|
? IdentityFormTab.Advanced
|
||||||
defaultValue={field.value}
|
: IdentityFormTab.Configuration
|
||||||
{...field}
|
);
|
||||||
onValueChange={(e) => {
|
})}
|
||||||
if (e === IdentityJwtConfigurationType.JWKS) {
|
>
|
||||||
setValue("publicKeys", []);
|
<Tabs value={tabValue} onValueChange={(value) => setTabValue(value as IdentityFormTab)}>
|
||||||
} else {
|
<TabList>
|
||||||
setValue("publicKeys", [
|
<Tab value={IdentityFormTab.Configuration}>Configuration</Tab>
|
||||||
{
|
<Tab value={IdentityFormTab.Advanced}>Advanced</Tab>
|
||||||
value: ""
|
</TabList>
|
||||||
}
|
<TabPanel value={IdentityFormTab.Configuration}>
|
||||||
]);
|
|
||||||
setValue("jwksUrl", "");
|
|
||||||
setValue("jwksCaCert", "");
|
|
||||||
}
|
|
||||||
onChange(e);
|
|
||||||
}}
|
|
||||||
className="w-full"
|
|
||||||
>
|
|
||||||
<SelectItem value={IdentityJwtConfigurationType.JWKS} key="jwks">
|
|
||||||
JWKS
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value={IdentityJwtConfigurationType.STATIC} key="static">
|
|
||||||
Static
|
|
||||||
</SelectItem>
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{selectedConfigurationType === IdentityJwtConfigurationType.JWKS && (
|
|
||||||
<>
|
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="jwksUrl"
|
name="configurationType"
|
||||||
render={({ field, fieldState: { error } }) => (
|
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
||||||
<FormControl
|
<FormControl
|
||||||
isRequired
|
label="Configuration Type"
|
||||||
label="JWKS URL"
|
|
||||||
isError={Boolean(error)}
|
isError={Boolean(error)}
|
||||||
errorText={error?.message}
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
defaultValue={field.value}
|
||||||
|
{...field}
|
||||||
|
onValueChange={(e) => {
|
||||||
|
if (e === IdentityJwtConfigurationType.JWKS) {
|
||||||
|
setValue("publicKeys", []);
|
||||||
|
} else {
|
||||||
|
setValue("publicKeys", [
|
||||||
|
{
|
||||||
|
value: ""
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
setValue("jwksUrl", "");
|
||||||
|
setValue("jwksCaCert", "");
|
||||||
|
}
|
||||||
|
onChange(e);
|
||||||
|
}}
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
<SelectItem value={IdentityJwtConfigurationType.JWKS} key="jwks">
|
||||||
|
JWKS
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value={IdentityJwtConfigurationType.STATIC} key="static">
|
||||||
|
Static
|
||||||
|
</SelectItem>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{selectedConfigurationType === IdentityJwtConfigurationType.JWKS && (
|
||||||
|
<>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="jwksUrl"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
isRequired
|
||||||
|
label="JWKS URL"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} type="text" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="jwksCaCert"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="JWKS CA Certificate"
|
||||||
|
errorText={error?.message}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
>
|
||||||
|
<TextArea {...field} placeholder="-----BEGIN CERTIFICATE----- ..." />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{selectedConfigurationType === IdentityJwtConfigurationType.STATIC && (
|
||||||
|
<>
|
||||||
|
{publicKeyFields.map(({ id }, index) => (
|
||||||
|
<div key={id} className="flex gap-2">
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name={`publicKeys.${index}.value`}
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
className="flex-grow"
|
||||||
|
label={`Public Key ${index + 1}`}
|
||||||
|
errorText={error?.message}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
icon={
|
||||||
|
<Tooltip
|
||||||
|
className="text-center"
|
||||||
|
content={<span>This field only accepts PEM-formatted public keys</span>}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faQuestionCircle} size="sm" />
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<TextArea {...field} placeholder="-----BEGIN PUBLIC KEY----- ..." />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
onClick={() => {
|
||||||
|
if (publicKeyFields.length === 1) {
|
||||||
|
createNotification({
|
||||||
|
type: "error",
|
||||||
|
text: "A public key is required for static configurations"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
removePublicKeyFields(index);
|
||||||
|
}}
|
||||||
|
size="lg"
|
||||||
|
colorSchema="danger"
|
||||||
|
variant="plain"
|
||||||
|
ariaLabel="update"
|
||||||
|
className="p-3"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faXmark} />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div className="my-4 ml-1">
|
||||||
|
<Button
|
||||||
|
variant="outline_bg"
|
||||||
|
onClick={() =>
|
||||||
|
appendPublicKeyFields({
|
||||||
|
value: ""
|
||||||
|
})
|
||||||
|
}
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
|
size="xs"
|
||||||
|
>
|
||||||
|
Add Public Key
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue="2592000"
|
||||||
|
name="accessTokenTTL"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Access Token TTL (seconds)"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue="2592000"
|
||||||
|
name="accessTokenMaxTTL"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Access Token Max TTL (seconds)"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue="0"
|
||||||
|
name="accessTokenNumUsesLimit"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Access Token Max Number of Uses"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="0" type="number" min="0" step="1" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel value={IdentityFormTab.Advanced}>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="boundIssuer"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl label="Issuer" isError={Boolean(error)} errorText={error?.message}>
|
||||||
|
<Input {...field} type="text" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="boundSubject"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<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>
|
||||||
@@ -332,58 +506,79 @@ export const IdentityJwtAuthForm = ({
|
|||||||
/>
|
/>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="jwksCaCert"
|
name="boundAudiences"
|
||||||
render={({ field, fieldState: { error } }) => (
|
render={({ field, fieldState: { error } }) => (
|
||||||
<FormControl
|
<FormControl
|
||||||
label="JWKS CA Certificate"
|
label="Audiences"
|
||||||
errorText={error?.message}
|
|
||||||
isError={Boolean(error)}
|
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>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<TextArea {...field} placeholder="-----BEGIN CERTIFICATE----- ..." />
|
<Input {...field} type="text" placeholder="service1, service2" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</>
|
{boundClaimsFields.map(({ id }, index) => (
|
||||||
)}
|
<div className="mb-3 flex items-end space-x-2" key={id}>
|
||||||
|
|
||||||
{selectedConfigurationType === IdentityJwtConfigurationType.STATIC && (
|
|
||||||
<>
|
|
||||||
{publicKeyFields.map(({ id }, index) => (
|
|
||||||
<div key={id} className="flex gap-2">
|
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name={`publicKeys.${index}.value`}
|
name={`boundClaims.${index}.key`}
|
||||||
render={({ field, fieldState: { error } }) => (
|
render={({ field, fieldState: { error } }) => {
|
||||||
<FormControl
|
return (
|
||||||
className="flex-grow"
|
<FormControl
|
||||||
label={`Public Key ${index + 1}`}
|
className="mb-0 flex-grow"
|
||||||
errorText={error?.message}
|
label={index === 0 ? "Claims" : undefined}
|
||||||
isError={Boolean(error)}
|
icon={
|
||||||
icon={
|
index === 0 ? (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
className="text-center"
|
className="text-center"
|
||||||
content={<span>This field only accepts PEM-formatted public keys</span>}
|
content={<span>This field supports glob patterns</span>}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faQuestionCircle} size="sm" />
|
<FontAwesomeIcon icon={faQuestionCircle} size="sm" />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
) : undefined
|
||||||
>
|
}
|
||||||
<TextArea {...field} placeholder="-----BEGIN PUBLIC KEY----- ..." />
|
isError={Boolean(error)}
|
||||||
</FormControl>
|
errorText={error?.message}
|
||||||
)}
|
>
|
||||||
/>
|
<Input
|
||||||
<IconButton
|
value={field.value}
|
||||||
onClick={() => {
|
onChange={(e) => field.onChange(e)}
|
||||||
if (publicKeyFields.length === 1) {
|
placeholder="property"
|
||||||
createNotification({
|
/>
|
||||||
type: "error",
|
</FormControl>
|
||||||
text: "A public key is required for static configurations"
|
);
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
removePublicKeyFields(index);
|
|
||||||
}}
|
}}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name={`boundClaims.${index}.value`}
|
||||||
|
render={({ field, fieldState: { error } }) => {
|
||||||
|
return (
|
||||||
|
<FormControl
|
||||||
|
className="mb-0 flex-grow"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
value={field.value}
|
||||||
|
onChange={(e) => field.onChange(e)}
|
||||||
|
placeholder="value1, value2"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<IconButton
|
||||||
|
onClick={() => removeBoundClaimField(index)}
|
||||||
size="lg"
|
size="lg"
|
||||||
colorSchema="danger"
|
colorSchema="danger"
|
||||||
variant="plain"
|
variant="plain"
|
||||||
@@ -398,280 +593,106 @@ export const IdentityJwtAuthForm = ({
|
|||||||
<Button
|
<Button
|
||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
appendPublicKeyFields({
|
appendBoundClaimField({
|
||||||
|
key: "",
|
||||||
value: ""
|
value: ""
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
size="xs"
|
size="xs"
|
||||||
>
|
>
|
||||||
Add Public Key
|
Add Claims
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</>
|
{accessTokenTrustedIpsFields.map(({ id }, index) => (
|
||||||
)}
|
<div className="mb-3 flex items-end space-x-2" key={id}>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name={`accessTokenTrustedIps.${index}.ipAddress`}
|
||||||
|
defaultValue="0.0.0.0/0"
|
||||||
|
render={({ field, fieldState: { error } }) => {
|
||||||
|
return (
|
||||||
|
<FormControl
|
||||||
|
className="mb-0 flex-grow"
|
||||||
|
label={index === 0 ? "Access Token Trusted IPs" : undefined}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
value={field.value}
|
||||||
|
onChange={(e) => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
|
field.onChange(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
<Controller
|
handlePopUpOpen("upgradePlan");
|
||||||
control={control}
|
}}
|
||||||
name="boundIssuer"
|
placeholder="123.456.789.0"
|
||||||
render={({ field, fieldState: { error } }) => (
|
/>
|
||||||
<FormControl label="Issuer" isError={Boolean(error)} errorText={error?.message}>
|
</FormControl>
|
||||||
<Input {...field} type="text" />
|
);
|
||||||
</FormControl>
|
}}
|
||||||
)}
|
/>
|
||||||
/>
|
<IconButton
|
||||||
<Controller
|
onClick={() => {
|
||||||
control={control}
|
if (subscription?.ipAllowlisting) {
|
||||||
name="boundSubject"
|
removeAccessTokenTrustedIp(index);
|
||||||
render={({ field, fieldState: { error } }) => (
|
return;
|
||||||
<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" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="boundAudiences"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<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" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{boundClaimsFields.map(({ id }, index) => (
|
|
||||||
<div className="mb-3 flex items-end space-x-2" key={id}>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name={`boundClaims.${index}.key`}
|
|
||||||
render={({ field, fieldState: { error } }) => {
|
|
||||||
return (
|
|
||||||
<FormControl
|
|
||||||
className="mb-0 flex-grow"
|
|
||||||
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)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
value={field.value}
|
|
||||||
onChange={(e) => field.onChange(e)}
|
|
||||||
placeholder="property"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name={`boundClaims.${index}.value`}
|
|
||||||
render={({ field, fieldState: { error } }) => {
|
|
||||||
return (
|
|
||||||
<FormControl
|
|
||||||
className="mb-0 flex-grow"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
value={field.value}
|
|
||||||
onChange={(e) => field.onChange(e)}
|
|
||||||
placeholder="value1, value2"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<IconButton
|
handlePopUpOpen("upgradePlan");
|
||||||
onClick={() => removeBoundClaimField(index)}
|
}}
|
||||||
size="lg"
|
size="lg"
|
||||||
colorSchema="danger"
|
colorSchema="danger"
|
||||||
variant="plain"
|
variant="plain"
|
||||||
ariaLabel="update"
|
ariaLabel="update"
|
||||||
className="p-3"
|
className="p-3"
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faXmark} />
|
<FontAwesomeIcon icon={faXmark} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
<div className="my-4 ml-1">
|
<div className="my-4 ml-1">
|
||||||
|
<Button
|
||||||
|
variant="outline_bg"
|
||||||
|
onClick={() => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
|
appendAccessTokenTrustedIp({
|
||||||
|
ipAddress: "0.0.0.0/0"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
}}
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
|
size="xs"
|
||||||
|
>
|
||||||
|
Add IP Address
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</TabPanel>
|
||||||
|
</Tabs>
|
||||||
|
<div className="flex items-center">
|
||||||
<Button
|
<Button
|
||||||
variant="outline_bg"
|
className="mr-4"
|
||||||
onClick={() =>
|
size="sm"
|
||||||
appendBoundClaimField({
|
type="submit"
|
||||||
key: "",
|
isLoading={isSubmitting}
|
||||||
value: ""
|
isDisabled={isSubmitting}
|
||||||
})
|
|
||||||
}
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
|
||||||
size="xs"
|
|
||||||
>
|
>
|
||||||
Add Claims
|
{isUpdate ? "Update" : "Create"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue="2592000"
|
|
||||||
name="accessTokenTTL"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Access Token TTL (seconds)"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue="2592000"
|
|
||||||
name="accessTokenMaxTTL"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Access Token Max TTL (seconds)"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue="0"
|
|
||||||
name="accessTokenNumUsesLimit"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Access Token Max Number of Uses"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="0" type="number" min="0" step="1" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{accessTokenTrustedIpsFields.map(({ id }, index) => (
|
|
||||||
<div className="mb-3 flex items-end space-x-2" key={id}>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name={`accessTokenTrustedIps.${index}.ipAddress`}
|
|
||||||
defaultValue="0.0.0.0/0"
|
|
||||||
render={({ field, fieldState: { error } }) => {
|
|
||||||
return (
|
|
||||||
<FormControl
|
|
||||||
className="mb-0 flex-grow"
|
|
||||||
label={index === 0 ? "Access Token Trusted IPs" : undefined}
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
value={field.value}
|
|
||||||
onChange={(e) => {
|
|
||||||
if (subscription?.ipAllowlisting) {
|
|
||||||
field.onChange(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
|
||||||
}}
|
|
||||||
placeholder="123.456.789.0"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<IconButton
|
|
||||||
onClick={() => {
|
|
||||||
if (subscription?.ipAllowlisting) {
|
|
||||||
removeAccessTokenTrustedIp(index);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
|
||||||
}}
|
|
||||||
size="lg"
|
|
||||||
colorSchema="danger"
|
|
||||||
variant="plain"
|
|
||||||
ariaLabel="update"
|
|
||||||
className="p-3"
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon icon={faXmark} />
|
|
||||||
</IconButton>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<div className="my-4 ml-1">
|
|
||||||
<Button
|
<Button
|
||||||
variant="outline_bg"
|
colorSchema="secondary"
|
||||||
onClick={() => {
|
variant="plain"
|
||||||
if (subscription?.ipAllowlisting) {
|
onClick={() => handlePopUpToggle("identityAuthMethod", false)}
|
||||||
appendAccessTokenTrustedIp({
|
|
||||||
ipAddress: "0.0.0.0/0"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
|
||||||
}}
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
|
||||||
size="xs"
|
|
||||||
>
|
>
|
||||||
Add IP Address
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Button
|
|
||||||
className="mr-4"
|
|
||||||
size="sm"
|
|
||||||
type="submit"
|
|
||||||
isLoading={isSubmitting}
|
|
||||||
isDisabled={isSubmitting}
|
|
||||||
>
|
|
||||||
{isUpdate ? "Update" : "Create"}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
colorSchema="secondary"
|
|
||||||
variant="plain"
|
|
||||||
onClick={() => handlePopUpToggle("identityAuthMethod", false)}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+261
-233
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Controller, useFieldArray, useForm } from "react-hook-form";
|
import { Controller, useFieldArray, useForm } from "react-hook-form";
|
||||||
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";
|
||||||
@@ -6,17 +6,28 @@ 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,
|
||||||
|
Tab,
|
||||||
|
TabList,
|
||||||
|
TabPanel,
|
||||||
|
Tabs,
|
||||||
|
TextArea
|
||||||
|
} from "@app/components/v2";
|
||||||
import { useOrganization, useSubscription } from "@app/context";
|
import { useOrganization, useSubscription } from "@app/context";
|
||||||
import {
|
import {
|
||||||
useAddIdentityKubernetesAuth,
|
useAddIdentityKubernetesAuth,
|
||||||
useGetIdentityKubernetesAuth,
|
useGetIdentityKubernetesAuth,
|
||||||
useUpdateIdentityKubernetesAuth
|
useUpdateIdentityKubernetesAuth
|
||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
|
||||||
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
|
import { IdentityFormTab } from "./types";
|
||||||
|
|
||||||
const schema = z
|
const schema = z
|
||||||
.object({
|
.object({
|
||||||
kubernetesHost: z.string().min(1),
|
kubernetesHost: z.string().min(1),
|
||||||
@@ -50,18 +61,15 @@ type Props = {
|
|||||||
popUpName: keyof UsePopUpState<["identityAuthMethod"]>,
|
popUpName: keyof UsePopUpState<["identityAuthMethod"]>,
|
||||||
state?: boolean
|
state?: boolean
|
||||||
) => void;
|
) => void;
|
||||||
identityAuthMethodData: {
|
identityId?: string;
|
||||||
identityId: string;
|
isUpdate?: boolean;
|
||||||
name: string;
|
|
||||||
configuredAuthMethods?: IdentityAuthMethod[];
|
|
||||||
authMethod?: IdentityAuthMethod;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IdentityKubernetesAuthForm = ({
|
export const IdentityKubernetesAuthForm = ({
|
||||||
handlePopUpOpen,
|
handlePopUpOpen,
|
||||||
handlePopUpToggle,
|
handlePopUpToggle,
|
||||||
identityAuthMethodData
|
identityId,
|
||||||
|
isUpdate
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const orgId = currentOrg?.id || "";
|
const orgId = currentOrg?.id || "";
|
||||||
@@ -69,11 +77,9 @@ export const IdentityKubernetesAuthForm = ({
|
|||||||
|
|
||||||
const { mutateAsync: addMutateAsync } = useAddIdentityKubernetesAuth();
|
const { mutateAsync: addMutateAsync } = useAddIdentityKubernetesAuth();
|
||||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityKubernetesAuth();
|
const { mutateAsync: updateMutateAsync } = useUpdateIdentityKubernetesAuth();
|
||||||
|
const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration);
|
||||||
|
|
||||||
const isUpdate = identityAuthMethodData?.configuredAuthMethods?.includes(
|
const { data } = useGetIdentityKubernetesAuth(identityId ?? "", {
|
||||||
identityAuthMethodData.authMethod! || ""
|
|
||||||
);
|
|
||||||
const { data } = useGetIdentityKubernetesAuth(identityAuthMethodData?.identityId ?? "", {
|
|
||||||
enabled: isUpdate
|
enabled: isUpdate
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -154,7 +160,7 @@ export const IdentityKubernetesAuthForm = ({
|
|||||||
accessTokenTrustedIps
|
accessTokenTrustedIps
|
||||||
}: FormData) => {
|
}: FormData) => {
|
||||||
try {
|
try {
|
||||||
if (!identityAuthMethodData) return;
|
if (!identityId) return;
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
await updateMutateAsync({
|
await updateMutateAsync({
|
||||||
@@ -165,7 +171,7 @@ export const IdentityKubernetesAuthForm = ({
|
|||||||
allowedNamespaces,
|
allowedNamespaces,
|
||||||
allowedAudience,
|
allowedAudience,
|
||||||
caCert,
|
caCert,
|
||||||
identityId: identityAuthMethodData.identityId,
|
identityId,
|
||||||
accessTokenTTL: Number(accessTokenTTL),
|
accessTokenTTL: Number(accessTokenTTL),
|
||||||
accessTokenMaxTTL: Number(accessTokenMaxTTL),
|
accessTokenMaxTTL: Number(accessTokenMaxTTL),
|
||||||
accessTokenNumUsesLimit: Number(accessTokenNumUsesLimit),
|
accessTokenNumUsesLimit: Number(accessTokenNumUsesLimit),
|
||||||
@@ -174,7 +180,7 @@ export const IdentityKubernetesAuthForm = ({
|
|||||||
} else {
|
} else {
|
||||||
await addMutateAsync({
|
await addMutateAsync({
|
||||||
organizationId: orgId,
|
organizationId: orgId,
|
||||||
identityId: identityAuthMethodData.identityId,
|
identityId,
|
||||||
kubernetesHost: kubernetesHost || "",
|
kubernetesHost: kubernetesHost || "",
|
||||||
tokenReviewerJwt,
|
tokenReviewerJwt,
|
||||||
allowedNames: allowedNames || "",
|
allowedNames: allowedNames || "",
|
||||||
@@ -205,230 +211,252 @@ export const IdentityKubernetesAuthForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
<form
|
||||||
<Controller
|
onSubmit={handleSubmit(onFormSubmit, (fields) => {
|
||||||
control={control}
|
setTabValue(
|
||||||
defaultValue="2592000"
|
[
|
||||||
name="kubernetesHost"
|
"kubernetesHost",
|
||||||
render={({ field, fieldState: { error } }) => (
|
"tokenReviewerJwt",
|
||||||
<FormControl
|
"accessTokenTTL",
|
||||||
label="Kubernetes Host / Base Kubernetes API URL "
|
"accessTokenMaxTTL",
|
||||||
isError={Boolean(error)}
|
"accessTokenNumUsesLimit"
|
||||||
errorText={error?.message}
|
].includes(Object.keys(fields)[0])
|
||||||
tooltipText="The host string, host:port pair, or URL to the base of the Kubernetes API server. This can usually be obtained by running 'kubectl cluster-info'"
|
? IdentityFormTab.Configuration
|
||||||
isRequired
|
: IdentityFormTab.Advanced
|
||||||
>
|
);
|
||||||
<Input {...field} placeholder="https://my-example-k8s-api-host.com" type="text" />
|
})}
|
||||||
</FormControl>
|
>
|
||||||
)}
|
<Tabs value={tabValue} onValueChange={(value) => setTabValue(value as IdentityFormTab)}>
|
||||||
/>
|
<TabList>
|
||||||
<Controller
|
<Tab value={IdentityFormTab.Configuration}>Configuration</Tab>
|
||||||
control={control}
|
<Tab value={IdentityFormTab.Advanced}>Advanced</Tab>
|
||||||
name="tokenReviewerJwt"
|
</TabList>
|
||||||
render={({ field, fieldState: { error } }) => (
|
<TabPanel value={IdentityFormTab.Configuration}>
|
||||||
<FormControl
|
|
||||||
label="Token Reviewer JWT"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
tooltipText="A long-lived service account JWT token for Infisical to access the TokenReview API to validate other service account JWT tokens submitted by applications/pods."
|
|
||||||
isRequired
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="" type="password" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="allowedNames"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Allowed Service Account Names"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
tooltipText="An optional comma-separated list of trusted service account names that are allowed to authenticate with Infisical. Leave empty to allow any service account."
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="service-account-1-name, service-account-1-name" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue=""
|
|
||||||
name="allowedNamespaces"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Allowed Namespaces"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
tooltipText="A comma-separated list of trusted namespaces that service accounts must belong to authenticate with Infisical."
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="namespaceA, namespaceB" type="text" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue=""
|
|
||||||
name="allowedAudience"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Allowed Audience"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
tooltipText="An optional audience claim that the service account JWT token must have to authenticate with Infisical. Leave empty to allow any audience claim."
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="" type="text" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="caCert"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="CA Certificate"
|
|
||||||
errorText={error?.message}
|
|
||||||
isError={Boolean(error)}
|
|
||||||
tooltipText="An optional PEM-encoded CA cert for the Kubernetes API server. This is used by the TLS client for secure communication with the Kubernetes API server."
|
|
||||||
>
|
|
||||||
<TextArea {...field} placeholder="-----BEGIN CERTIFICATE----- ..." />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue="2592000"
|
|
||||||
name="accessTokenTTL"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Access Token TTL (seconds)"
|
|
||||||
tooltipText="The lifetime for an acccess token in seconds. This value will be referenced at renewal time."
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue="2592000"
|
|
||||||
name="accessTokenMaxTTL"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Access Token Max TTL (seconds)"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
tooltipText="The maximum lifetime for an access token in seconds. This value will be referenced at renewal time."
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue="0"
|
|
||||||
name="accessTokenNumUsesLimit"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Access Token Max Number of Uses"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
tooltipText="The maximum number of times that an access token can be used; a value of 0 implies infinite number of uses."
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="0" type="number" min="0" step="1" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{accessTokenTrustedIpsFields.map(({ id }, index) => (
|
|
||||||
<div className="mb-3 flex items-end space-x-2" key={id}>
|
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name={`accessTokenTrustedIps.${index}.ipAddress`}
|
defaultValue="2592000"
|
||||||
defaultValue="0.0.0.0/0"
|
name="kubernetesHost"
|
||||||
render={({ field, fieldState: { error } }) => {
|
render={({ field, fieldState: { error } }) => (
|
||||||
return (
|
<FormControl
|
||||||
<FormControl
|
label="Kubernetes Host / Base Kubernetes API URL "
|
||||||
className="mb-0 flex-grow"
|
isError={Boolean(error)}
|
||||||
label={index === 0 ? "Access Token Trusted IPs" : undefined}
|
errorText={error?.message}
|
||||||
isError={Boolean(error)}
|
tooltipText="The host string, host:port pair, or URL to the base of the Kubernetes API server. This can usually be obtained by running 'kubectl cluster-info'"
|
||||||
errorText={error?.message}
|
isRequired
|
||||||
tooltipText="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."
|
>
|
||||||
>
|
<Input {...field} placeholder="https://my-example-k8s-api-host.com" type="text" />
|
||||||
<Input
|
</FormControl>
|
||||||
value={field.value}
|
)}
|
||||||
onChange={(e) => {
|
|
||||||
if (subscription?.ipAllowlisting) {
|
|
||||||
field.onChange(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
|
||||||
}}
|
|
||||||
placeholder="123.456.789.0"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<Controller
|
||||||
onClick={() => {
|
control={control}
|
||||||
if (subscription?.ipAllowlisting) {
|
name="tokenReviewerJwt"
|
||||||
removeAccessTokenTrustedIp(index);
|
render={({ field, fieldState: { error } }) => (
|
||||||
return;
|
<FormControl
|
||||||
}
|
label="Token Reviewer JWT"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
tooltipText="A long-lived service account JWT token for Infisical to access the TokenReview API to validate other service account JWT tokens submitted by applications/pods."
|
||||||
|
isRequired
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="" type="password" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue="2592000"
|
||||||
|
name="accessTokenTTL"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Access Token TTL (seconds)"
|
||||||
|
tooltipText="The lifetime for an acccess token in seconds. This value will be referenced at renewal time."
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue="2592000"
|
||||||
|
name="accessTokenMaxTTL"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Access Token Max TTL (seconds)"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
tooltipText="The maximum lifetime for an access token in seconds. This value will be referenced at renewal time."
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue="0"
|
||||||
|
name="accessTokenNumUsesLimit"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Access Token Max Number of Uses"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
tooltipText="The maximum number of times that an access token can be used; a value of 0 implies infinite number of uses."
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="0" type="number" min="0" step="1" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel value={IdentityFormTab.Advanced}>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="allowedNames"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Allowed Service Account Names"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
tooltipText="An optional comma-separated list of trusted service account names that are allowed to authenticate with Infisical. Leave empty to allow any service account."
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="service-account-1-name, service-account-1-name" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue=""
|
||||||
|
name="allowedNamespaces"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Allowed Namespaces"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
tooltipText="A comma-separated list of trusted namespaces that service accounts must belong to authenticate with Infisical."
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="namespaceA, namespaceB" type="text" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue=""
|
||||||
|
name="allowedAudience"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Allowed Audience"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
tooltipText="An optional audience claim that the service account JWT token must have to authenticate with Infisical. Leave empty to allow any audience claim."
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="" type="text" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="caCert"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="CA Certificate"
|
||||||
|
errorText={error?.message}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
tooltipText="An optional PEM-encoded CA cert for the Kubernetes API server. This is used by the TLS client for secure communication with the Kubernetes API server."
|
||||||
|
>
|
||||||
|
<TextArea {...field} placeholder="-----BEGIN CERTIFICATE----- ..." />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{accessTokenTrustedIpsFields.map(({ id }, index) => (
|
||||||
|
<div className="mb-3 flex items-end space-x-2" key={id}>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name={`accessTokenTrustedIps.${index}.ipAddress`}
|
||||||
|
defaultValue="0.0.0.0/0"
|
||||||
|
render={({ field, fieldState: { error } }) => {
|
||||||
|
return (
|
||||||
|
<FormControl
|
||||||
|
className="mb-0 flex-grow"
|
||||||
|
label={index === 0 ? "Access Token Trusted IPs" : undefined}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
tooltipText="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."
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
value={field.value}
|
||||||
|
onChange={(e) => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
|
field.onChange(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
handlePopUpOpen("upgradePlan");
|
||||||
}}
|
}}
|
||||||
size="lg"
|
placeholder="123.456.789.0"
|
||||||
colorSchema="danger"
|
/>
|
||||||
variant="plain"
|
</FormControl>
|
||||||
ariaLabel="update"
|
);
|
||||||
className="p-3"
|
}}
|
||||||
>
|
/>
|
||||||
<FontAwesomeIcon icon={faXmark} />
|
<IconButton
|
||||||
</IconButton>
|
onClick={() => {
|
||||||
</div>
|
if (subscription?.ipAllowlisting) {
|
||||||
))}
|
removeAccessTokenTrustedIp(index);
|
||||||
<div className="my-4 ml-1">
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
}}
|
||||||
|
size="lg"
|
||||||
|
colorSchema="danger"
|
||||||
|
variant="plain"
|
||||||
|
ariaLabel="update"
|
||||||
|
className="p-3"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faXmark} />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div className="my-4 ml-1">
|
||||||
|
<Button
|
||||||
|
variant="outline_bg"
|
||||||
|
onClick={() => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
|
appendAccessTokenTrustedIp({
|
||||||
|
ipAddress: "0.0.0.0/0"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
}}
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
|
size="xs"
|
||||||
|
>
|
||||||
|
Add IP Address
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</TabPanel>
|
||||||
|
</Tabs>
|
||||||
|
<div className="flex items-center">
|
||||||
<Button
|
<Button
|
||||||
variant="outline_bg"
|
className="mr-4"
|
||||||
onClick={() => {
|
size="sm"
|
||||||
if (subscription?.ipAllowlisting) {
|
type="submit"
|
||||||
appendAccessTokenTrustedIp({
|
isLoading={isSubmitting}
|
||||||
ipAddress: "0.0.0.0/0"
|
isDisabled={isSubmitting}
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
|
||||||
}}
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
|
||||||
size="xs"
|
|
||||||
>
|
>
|
||||||
Add IP Address
|
{isUpdate ? "Update" : "Add"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Button
|
|
||||||
className="mr-4"
|
|
||||||
size="sm"
|
|
||||||
type="submit"
|
|
||||||
isLoading={isSubmitting}
|
|
||||||
isDisabled={isSubmitting}
|
|
||||||
>
|
|
||||||
{isUpdate ? "Update" : "Create"}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
colorSchema="secondary"
|
colorSchema="secondary"
|
||||||
variant="plain"
|
variant="plain"
|
||||||
onClick={() => handlePopUpToggle("identityAuthMethod", false)}
|
onClick={() => handlePopUpToggle("identityAuthMethod", false)}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
|||||||
+331
-298
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect, useState } 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 { 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";
|
||||||
@@ -7,14 +7,26 @@ 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, Tooltip } from "@app/components/v2";
|
import {
|
||||||
|
Button,
|
||||||
|
FormControl,
|
||||||
|
IconButton,
|
||||||
|
Input,
|
||||||
|
Tab,
|
||||||
|
TabList,
|
||||||
|
TabPanel,
|
||||||
|
Tabs,
|
||||||
|
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 { useGetIdentityOidcAuth } from "@app/hooks/api/identities/queries";
|
import { useGetIdentityOidcAuth } from "@app/hooks/api/identities/queries";
|
||||||
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
|
import { IdentityFormTab } from "./types";
|
||||||
|
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
accessTokenTrustedIps: z
|
accessTokenTrustedIps: z
|
||||||
.array(
|
.array(
|
||||||
@@ -51,18 +63,15 @@ type Props = {
|
|||||||
popUpName: keyof UsePopUpState<["identityAuthMethod"]>,
|
popUpName: keyof UsePopUpState<["identityAuthMethod"]>,
|
||||||
state?: boolean
|
state?: boolean
|
||||||
) => void;
|
) => void;
|
||||||
identityAuthMethodData: {
|
identityId?: string;
|
||||||
identityId: string;
|
isUpdate?: boolean;
|
||||||
name: string;
|
|
||||||
configuredAuthMethods?: IdentityAuthMethod[];
|
|
||||||
authMethod?: IdentityAuthMethod;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IdentityOidcAuthForm = ({
|
export const IdentityOidcAuthForm = ({
|
||||||
handlePopUpOpen,
|
handlePopUpOpen,
|
||||||
handlePopUpToggle,
|
handlePopUpToggle,
|
||||||
identityAuthMethodData
|
identityId,
|
||||||
|
isUpdate
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const orgId = currentOrg?.id || "";
|
const orgId = currentOrg?.id || "";
|
||||||
@@ -70,11 +79,9 @@ export const IdentityOidcAuthForm = ({
|
|||||||
|
|
||||||
const { mutateAsync: addMutateAsync } = useAddIdentityOidcAuth();
|
const { mutateAsync: addMutateAsync } = useAddIdentityOidcAuth();
|
||||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityOidcAuth();
|
const { mutateAsync: updateMutateAsync } = useUpdateIdentityOidcAuth();
|
||||||
|
const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration);
|
||||||
|
|
||||||
const isUpdate = identityAuthMethodData?.configuredAuthMethods?.includes(
|
const { data } = useGetIdentityOidcAuth(identityId ?? "", {
|
||||||
identityAuthMethodData.authMethod! || ""
|
|
||||||
);
|
|
||||||
const { data } = useGetIdentityOidcAuth(identityAuthMethodData?.identityId ?? "", {
|
|
||||||
enabled: isUpdate
|
enabled: isUpdate
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -160,13 +167,13 @@ export const IdentityOidcAuthForm = ({
|
|||||||
boundSubject
|
boundSubject
|
||||||
}: FormData) => {
|
}: FormData) => {
|
||||||
try {
|
try {
|
||||||
if (!identityAuthMethodData) {
|
if (!identityId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
await updateMutateAsync({
|
await updateMutateAsync({
|
||||||
identityId: identityAuthMethodData.identityId,
|
identityId,
|
||||||
organizationId: orgId,
|
organizationId: orgId,
|
||||||
oidcDiscoveryUrl,
|
oidcDiscoveryUrl,
|
||||||
caCert,
|
caCert,
|
||||||
@@ -181,7 +188,7 @@ export const IdentityOidcAuthForm = ({
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await addMutateAsync({
|
await addMutateAsync({
|
||||||
identityId: identityAuthMethodData.identityId,
|
identityId,
|
||||||
oidcDiscoveryUrl,
|
oidcDiscoveryUrl,
|
||||||
caCert,
|
caCert,
|
||||||
boundIssuer,
|
boundIssuer,
|
||||||
@@ -213,303 +220,329 @@ export const IdentityOidcAuthForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
<form
|
||||||
<Controller
|
onSubmit={handleSubmit(onFormSubmit, (fields) => {
|
||||||
control={control}
|
setTabValue(
|
||||||
name="oidcDiscoveryUrl"
|
[
|
||||||
render={({ field, fieldState: { error } }) => (
|
"accessTokenTrustedIps",
|
||||||
<FormControl
|
"caCert",
|
||||||
isRequired
|
"boundAudiences",
|
||||||
label="OIDC Discovery URL"
|
"boundSubject",
|
||||||
isError={Boolean(error)}
|
"boundClaims"
|
||||||
errorText={error?.message}
|
].includes(Object.keys(fields)[0])
|
||||||
>
|
? IdentityFormTab.Advanced
|
||||||
<Input
|
: IdentityFormTab.Configuration
|
||||||
{...field}
|
);
|
||||||
placeholder="https://token.actions.githubusercontent.com"
|
})}
|
||||||
type="text"
|
>
|
||||||
/>
|
<Tabs value={tabValue} onValueChange={(value) => setTabValue(value as IdentityFormTab)}>
|
||||||
</FormControl>
|
<TabList>
|
||||||
)}
|
<Tab value={IdentityFormTab.Configuration}>Configuration</Tab>
|
||||||
/>
|
<Tab value={IdentityFormTab.Advanced}>Advanced</Tab>
|
||||||
<Controller
|
</TabList>
|
||||||
control={control}
|
<TabPanel value={IdentityFormTab.Configuration}>
|
||||||
name="boundIssuer"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
isRequired
|
|
||||||
label="Issuer"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
type="text"
|
|
||||||
placeholder="https://token.actions.githubusercontent.com"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="caCert"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl label="CA Certificate" errorText={error?.message} isError={Boolean(error)}>
|
|
||||||
<TextArea {...field} placeholder="-----BEGIN CERTIFICATE----- ..." />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="boundSubject"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<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" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="boundAudiences"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<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" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{boundClaimsFields.map(({ id }, index) => (
|
|
||||||
<div className="mb-3 flex items-end space-x-2" key={id}>
|
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name={`boundClaims.${index}.key`}
|
name="oidcDiscoveryUrl"
|
||||||
render={({ field, fieldState: { error } }) => {
|
render={({ field, fieldState: { error } }) => (
|
||||||
return (
|
<FormControl
|
||||||
<FormControl
|
isRequired
|
||||||
className="mb-0 flex-grow"
|
label="OIDC Discovery URL"
|
||||||
label={index === 0 ? "Claims" : undefined}
|
isError={Boolean(error)}
|
||||||
icon={
|
errorText={error?.message}
|
||||||
index === 0 ? (
|
>
|
||||||
<Tooltip
|
<Input
|
||||||
className="text-center"
|
{...field}
|
||||||
content={<span>This field supports glob patterns</span>}
|
placeholder="https://token.actions.githubusercontent.com"
|
||||||
>
|
type="text"
|
||||||
<FontAwesomeIcon icon={faQuestionCircle} size="sm" />
|
/>
|
||||||
</Tooltip>
|
</FormControl>
|
||||||
) : undefined
|
)}
|
||||||
}
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
value={field.value}
|
|
||||||
onChange={(e) => field.onChange(e)}
|
|
||||||
placeholder="property"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name={`boundClaims.${index}.value`}
|
name="boundIssuer"
|
||||||
render={({ field, fieldState: { error } }) => {
|
render={({ field, fieldState: { error } }) => (
|
||||||
return (
|
<FormControl
|
||||||
<FormControl
|
isRequired
|
||||||
className="mb-0 flex-grow"
|
label="Issuer"
|
||||||
isError={Boolean(error)}
|
isError={Boolean(error)}
|
||||||
errorText={error?.message}
|
errorText={error?.message}
|
||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
value={field.value}
|
{...field}
|
||||||
onChange={(e) => field.onChange(e)}
|
type="text"
|
||||||
placeholder="value1, value2"
|
placeholder="https://token.actions.githubusercontent.com"
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
);
|
)}
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<IconButton
|
|
||||||
onClick={() => removeBoundClaimField(index)}
|
|
||||||
size="lg"
|
|
||||||
colorSchema="danger"
|
|
||||||
variant="plain"
|
|
||||||
ariaLabel="update"
|
|
||||||
className="p-3"
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon icon={faXmark} />
|
|
||||||
</IconButton>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<div className="my-4 ml-1">
|
|
||||||
<Button
|
|
||||||
variant="outline_bg"
|
|
||||||
onClick={() =>
|
|
||||||
appendBoundClaimField({
|
|
||||||
key: "",
|
|
||||||
value: ""
|
|
||||||
})
|
|
||||||
}
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
|
||||||
size="xs"
|
|
||||||
>
|
|
||||||
Add Claims
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue="2592000"
|
|
||||||
name="accessTokenTTL"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Access Token TTL (seconds)"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue="2592000"
|
|
||||||
name="accessTokenMaxTTL"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Access Token Max TTL (seconds)"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue="0"
|
|
||||||
name="accessTokenNumUsesLimit"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Access Token Max Number of Uses"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="0" type="number" min="0" step="1" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{accessTokenTrustedIpsFields.map(({ id }, index) => (
|
|
||||||
<div className="mb-3 flex items-end space-x-2" key={id}>
|
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name={`accessTokenTrustedIps.${index}.ipAddress`}
|
defaultValue="2592000"
|
||||||
defaultValue="0.0.0.0/0"
|
name="accessTokenTTL"
|
||||||
render={({ field, fieldState: { error } }) => {
|
render={({ field, fieldState: { error } }) => (
|
||||||
return (
|
<FormControl
|
||||||
<FormControl
|
label="Access Token TTL (seconds)"
|
||||||
className="mb-0 flex-grow"
|
isError={Boolean(error)}
|
||||||
label={index === 0 ? "Access Token Trusted IPs" : undefined}
|
errorText={error?.message}
|
||||||
isError={Boolean(error)}
|
>
|
||||||
errorText={error?.message}
|
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
||||||
>
|
</FormControl>
|
||||||
<Input
|
)}
|
||||||
value={field.value}
|
/>
|
||||||
onChange={(e) => {
|
<Controller
|
||||||
if (subscription?.ipAllowlisting) {
|
control={control}
|
||||||
field.onChange(e);
|
defaultValue="2592000"
|
||||||
return;
|
name="accessTokenMaxTTL"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Access Token Max TTL (seconds)"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue="0"
|
||||||
|
name="accessTokenNumUsesLimit"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Access Token Max Number of Uses"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="0" type="number" min="0" step="1" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel value={IdentityFormTab.Advanced}>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="caCert"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="CA Certificate"
|
||||||
|
errorText={error?.message}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
>
|
||||||
|
<TextArea {...field} placeholder="-----BEGIN CERTIFICATE----- ..." />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="boundSubject"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<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" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="boundAudiences"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<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" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{boundClaimsFields.map(({ id }, index) => (
|
||||||
|
<div className="mb-3 flex items-end space-x-2" key={id}>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name={`boundClaims.${index}.key`}
|
||||||
|
render={({ field, fieldState: { error } }) => {
|
||||||
|
return (
|
||||||
|
<FormControl
|
||||||
|
className="mb-0 flex-grow"
|
||||||
|
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)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
value={field.value}
|
||||||
|
onChange={(e) => field.onChange(e)}
|
||||||
|
placeholder="property"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name={`boundClaims.${index}.value`}
|
||||||
|
render={({ field, fieldState: { error } }) => {
|
||||||
|
return (
|
||||||
|
<FormControl
|
||||||
|
className="mb-0 flex-grow"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
value={field.value}
|
||||||
|
onChange={(e) => field.onChange(e)}
|
||||||
|
placeholder="value1, value2"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
<IconButton
|
||||||
}}
|
onClick={() => removeBoundClaimField(index)}
|
||||||
placeholder="123.456.789.0"
|
size="lg"
|
||||||
/>
|
colorSchema="danger"
|
||||||
</FormControl>
|
variant="plain"
|
||||||
);
|
ariaLabel="update"
|
||||||
}}
|
className="p-3"
|
||||||
/>
|
>
|
||||||
<IconButton
|
<FontAwesomeIcon icon={faXmark} />
|
||||||
onClick={() => {
|
</IconButton>
|
||||||
if (subscription?.ipAllowlisting) {
|
</div>
|
||||||
removeAccessTokenTrustedIp(index);
|
))}
|
||||||
return;
|
<div className="my-4 ml-1">
|
||||||
|
<Button
|
||||||
|
variant="outline_bg"
|
||||||
|
onClick={() =>
|
||||||
|
appendBoundClaimField({
|
||||||
|
key: "",
|
||||||
|
value: ""
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
|
size="xs"
|
||||||
|
>
|
||||||
|
Add Claims
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
{accessTokenTrustedIpsFields.map(({ id }, index) => (
|
||||||
|
<div className="mb-3 flex items-end space-x-2" key={id}>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name={`accessTokenTrustedIps.${index}.ipAddress`}
|
||||||
|
defaultValue="0.0.0.0/0"
|
||||||
|
render={({ field, fieldState: { error } }) => {
|
||||||
|
return (
|
||||||
|
<FormControl
|
||||||
|
className="mb-0 flex-grow"
|
||||||
|
label={index === 0 ? "Access Token Trusted IPs" : undefined}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
value={field.value}
|
||||||
|
onChange={(e) => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
|
field.onChange(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
handlePopUpOpen("upgradePlan");
|
||||||
}}
|
}}
|
||||||
size="lg"
|
placeholder="123.456.789.0"
|
||||||
colorSchema="danger"
|
/>
|
||||||
variant="plain"
|
</FormControl>
|
||||||
ariaLabel="update"
|
);
|
||||||
className="p-3"
|
}}
|
||||||
>
|
/>
|
||||||
<FontAwesomeIcon icon={faXmark} />
|
<IconButton
|
||||||
</IconButton>
|
onClick={() => {
|
||||||
</div>
|
if (subscription?.ipAllowlisting) {
|
||||||
))}
|
removeAccessTokenTrustedIp(index);
|
||||||
<div className="my-4 ml-1">
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
}}
|
||||||
|
size="lg"
|
||||||
|
colorSchema="danger"
|
||||||
|
variant="plain"
|
||||||
|
ariaLabel="update"
|
||||||
|
className="p-3"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faXmark} />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div className="my-4 ml-1">
|
||||||
|
<Button
|
||||||
|
variant="outline_bg"
|
||||||
|
onClick={() => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
|
appendAccessTokenTrustedIp({
|
||||||
|
ipAddress: "0.0.0.0/0"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
}}
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
|
size="xs"
|
||||||
|
>
|
||||||
|
Add IP Address
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</TabPanel>
|
||||||
|
</Tabs>
|
||||||
|
<div className="flex items-center">
|
||||||
<Button
|
<Button
|
||||||
variant="outline_bg"
|
className="mr-4"
|
||||||
onClick={() => {
|
size="sm"
|
||||||
if (subscription?.ipAllowlisting) {
|
type="submit"
|
||||||
appendAccessTokenTrustedIp({
|
isLoading={isSubmitting}
|
||||||
ipAddress: "0.0.0.0/0"
|
isDisabled={isSubmitting}
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
|
||||||
}}
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
|
||||||
size="xs"
|
|
||||||
>
|
>
|
||||||
Add IP Address
|
{isUpdate ? "Update" : "Add"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Button
|
|
||||||
className="mr-4"
|
|
||||||
size="sm"
|
|
||||||
type="submit"
|
|
||||||
isLoading={isSubmitting}
|
|
||||||
isDisabled={isSubmitting}
|
|
||||||
>
|
|
||||||
{isUpdate ? "Update" : "Create"}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
colorSchema="secondary"
|
colorSchema="secondary"
|
||||||
variant="plain"
|
variant="plain"
|
||||||
onClick={() => handlePopUpToggle("identityAuthMethod", false)}
|
onClick={() => handlePopUpToggle("identityAuthMethod", false)}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
|||||||
+186
-140
@@ -1,3 +1,4 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
import { Controller, useFieldArray, useForm } from "react-hook-form";
|
import { Controller, useFieldArray, useForm } from "react-hook-form";
|
||||||
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";
|
||||||
@@ -5,16 +6,27 @@ 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 } from "@app/components/v2";
|
import {
|
||||||
|
Button,
|
||||||
|
FormControl,
|
||||||
|
IconButton,
|
||||||
|
Input,
|
||||||
|
Tab,
|
||||||
|
TabList,
|
||||||
|
TabPanel,
|
||||||
|
Tabs
|
||||||
|
} from "@app/components/v2";
|
||||||
import { useOrganization, useSubscription } from "@app/context";
|
import { useOrganization, useSubscription } from "@app/context";
|
||||||
import {
|
import {
|
||||||
useAddIdentityTokenAuth,
|
useAddIdentityTokenAuth,
|
||||||
useGetIdentityTokenAuth,
|
useGetIdentityTokenAuth,
|
||||||
useUpdateIdentityTokenAuth
|
useUpdateIdentityTokenAuth
|
||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
|
import { IdentityFormTab } from "./types";
|
||||||
|
|
||||||
const schema = z
|
const schema = z
|
||||||
.object({
|
.object({
|
||||||
accessTokenTTL: z.string().refine((val) => Number(val) <= 315360000, {
|
accessTokenTTL: z.string().refine((val) => Number(val) <= 315360000, {
|
||||||
@@ -42,18 +54,15 @@ type Props = {
|
|||||||
popUpName: keyof UsePopUpState<["identityAuthMethod"]>,
|
popUpName: keyof UsePopUpState<["identityAuthMethod"]>,
|
||||||
state?: boolean
|
state?: boolean
|
||||||
) => void;
|
) => void;
|
||||||
identityAuthMethodData?: {
|
identityId?: string;
|
||||||
identityId: string;
|
isUpdate?: boolean;
|
||||||
name: string;
|
|
||||||
configuredAuthMethods?: IdentityAuthMethod[];
|
|
||||||
authMethod?: IdentityAuthMethod;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IdentityTokenAuthForm = ({
|
export const IdentityTokenAuthForm = ({
|
||||||
handlePopUpOpen,
|
handlePopUpOpen,
|
||||||
handlePopUpToggle,
|
handlePopUpToggle,
|
||||||
identityAuthMethodData
|
identityId,
|
||||||
|
isUpdate
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const orgId = currentOrg?.id || "";
|
const orgId = currentOrg?.id || "";
|
||||||
@@ -61,12 +70,9 @@ export const IdentityTokenAuthForm = ({
|
|||||||
|
|
||||||
const { mutateAsync: addMutateAsync } = useAddIdentityTokenAuth();
|
const { mutateAsync: addMutateAsync } = useAddIdentityTokenAuth();
|
||||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityTokenAuth();
|
const { mutateAsync: updateMutateAsync } = useUpdateIdentityTokenAuth();
|
||||||
|
const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration);
|
||||||
|
|
||||||
const isUpdate = identityAuthMethodData?.configuredAuthMethods?.includes(
|
const { data } = useGetIdentityTokenAuth(identityId ?? "", {
|
||||||
identityAuthMethodData.authMethod! || ""
|
|
||||||
);
|
|
||||||
|
|
||||||
const { data } = useGetIdentityTokenAuth(identityAuthMethodData?.identityId ?? "", {
|
|
||||||
enabled: isUpdate
|
enabled: isUpdate
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -91,6 +97,30 @@ export const IdentityTokenAuthForm = ({
|
|||||||
remove: removeAccessTokenTrustedIp
|
remove: removeAccessTokenTrustedIp
|
||||||
} = useFieldArray({ control, name: "accessTokenTrustedIps" });
|
} = useFieldArray({ control, name: "accessTokenTrustedIps" });
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (data) {
|
||||||
|
reset({
|
||||||
|
accessTokenTTL: String(data.accessTokenTTL),
|
||||||
|
accessTokenMaxTTL: String(data.accessTokenMaxTTL),
|
||||||
|
accessTokenNumUsesLimit: String(data.accessTokenNumUsesLimit),
|
||||||
|
accessTokenTrustedIps: data.accessTokenTrustedIps.map(
|
||||||
|
({ ipAddress, prefix }: IdentityTrustedIp) => {
|
||||||
|
return {
|
||||||
|
ipAddress: `${ipAddress}${prefix !== undefined ? `/${prefix}` : ""}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
reset({
|
||||||
|
accessTokenTTL: "2592000",
|
||||||
|
accessTokenMaxTTL: "2592000",
|
||||||
|
accessTokenNumUsesLimit: "0",
|
||||||
|
accessTokenTrustedIps: [{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
const onFormSubmit = async ({
|
const onFormSubmit = async ({
|
||||||
accessTokenTTL,
|
accessTokenTTL,
|
||||||
accessTokenMaxTTL,
|
accessTokenMaxTTL,
|
||||||
@@ -98,12 +128,12 @@ export const IdentityTokenAuthForm = ({
|
|||||||
accessTokenTrustedIps
|
accessTokenTrustedIps
|
||||||
}: FormData) => {
|
}: FormData) => {
|
||||||
try {
|
try {
|
||||||
if (!identityAuthMethodData) return;
|
if (!identityId) return;
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
await updateMutateAsync({
|
await updateMutateAsync({
|
||||||
organizationId: orgId,
|
organizationId: orgId,
|
||||||
identityId: identityAuthMethodData.identityId,
|
identityId,
|
||||||
accessTokenTTL: Number(accessTokenTTL),
|
accessTokenTTL: Number(accessTokenTTL),
|
||||||
accessTokenMaxTTL: Number(accessTokenMaxTTL),
|
accessTokenMaxTTL: Number(accessTokenMaxTTL),
|
||||||
accessTokenNumUsesLimit: Number(accessTokenNumUsesLimit),
|
accessTokenNumUsesLimit: Number(accessTokenNumUsesLimit),
|
||||||
@@ -112,7 +142,7 @@ export const IdentityTokenAuthForm = ({
|
|||||||
} else {
|
} else {
|
||||||
await addMutateAsync({
|
await addMutateAsync({
|
||||||
organizationId: orgId,
|
organizationId: orgId,
|
||||||
identityId: identityAuthMethodData.identityId,
|
identityId,
|
||||||
accessTokenTTL: Number(accessTokenTTL),
|
accessTokenTTL: Number(accessTokenTTL),
|
||||||
accessTokenMaxTTL: Number(accessTokenMaxTTL),
|
accessTokenMaxTTL: Number(accessTokenMaxTTL),
|
||||||
accessTokenNumUsesLimit: Number(accessTokenNumUsesLimit),
|
accessTokenNumUsesLimit: Number(accessTokenNumUsesLimit),
|
||||||
@@ -137,137 +167,153 @@ export const IdentityTokenAuthForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
<form
|
||||||
<Controller
|
onSubmit={handleSubmit(onFormSubmit, (fields) => {
|
||||||
control={control}
|
setTabValue(
|
||||||
defaultValue="2592000"
|
["accessTokenTrustedIps"].includes(Object.keys(fields)[0])
|
||||||
name="accessTokenTTL"
|
? IdentityFormTab.Advanced
|
||||||
render={({ field, fieldState: { error } }) => (
|
: IdentityFormTab.Configuration
|
||||||
<FormControl
|
);
|
||||||
label="Access Token TTL (seconds)"
|
})}
|
||||||
isError={Boolean(error)}
|
>
|
||||||
errorText={error?.message}
|
<Tabs value={tabValue} onValueChange={(value) => setTabValue(value as IdentityFormTab)}>
|
||||||
>
|
<TabList>
|
||||||
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
<Tab value={IdentityFormTab.Configuration}>Configuration</Tab>
|
||||||
</FormControl>
|
<Tab value={IdentityFormTab.Advanced}>Advanced</Tab>
|
||||||
)}
|
</TabList>
|
||||||
/>
|
<TabPanel value={IdentityFormTab.Configuration}>
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue="2592000"
|
|
||||||
name="accessTokenMaxTTL"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Access Token Max TTL (seconds)"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue="0"
|
|
||||||
name="accessTokenNumUsesLimit"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Access Token Max Number of Uses"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="0" type="number" min="0" step="1" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{accessTokenTrustedIpsFields.map(({ id }, index) => (
|
|
||||||
<div className="mb-3 flex items-end space-x-2" key={id}>
|
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name={`accessTokenTrustedIps.${index}.ipAddress`}
|
defaultValue="2592000"
|
||||||
defaultValue="0.0.0.0/0"
|
name="accessTokenTTL"
|
||||||
render={({ field, fieldState: { error } }) => {
|
render={({ field, fieldState: { error } }) => (
|
||||||
return (
|
<FormControl
|
||||||
<FormControl
|
label="Access Token TTL (seconds)"
|
||||||
className="mb-0 flex-grow"
|
isError={Boolean(error)}
|
||||||
label={index === 0 ? "Access Token Trusted IPs" : undefined}
|
errorText={error?.message}
|
||||||
isError={Boolean(error)}
|
>
|
||||||
errorText={error?.message}
|
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
||||||
>
|
</FormControl>
|
||||||
<Input
|
)}
|
||||||
value={field.value}
|
|
||||||
onChange={(e) => {
|
|
||||||
if (subscription?.ipAllowlisting) {
|
|
||||||
field.onChange(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
|
||||||
}}
|
|
||||||
placeholder="123.456.789.0"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<Controller
|
||||||
onClick={() => {
|
control={control}
|
||||||
if (subscription?.ipAllowlisting) {
|
defaultValue="2592000"
|
||||||
removeAccessTokenTrustedIp(index);
|
name="accessTokenMaxTTL"
|
||||||
return;
|
render={({ field, fieldState: { error } }) => (
|
||||||
}
|
<FormControl
|
||||||
|
label="Access Token Max TTL (seconds)"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue="0"
|
||||||
|
name="accessTokenNumUsesLimit"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Access Token Max Number of Uses"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="0" type="number" min="0" step="1" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel value={IdentityFormTab.Advanced}>
|
||||||
|
{accessTokenTrustedIpsFields.map(({ id }, index) => (
|
||||||
|
<div className="mb-3 flex items-end space-x-2" key={id}>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name={`accessTokenTrustedIps.${index}.ipAddress`}
|
||||||
|
defaultValue="0.0.0.0/0"
|
||||||
|
render={({ field, fieldState: { error } }) => {
|
||||||
|
return (
|
||||||
|
<FormControl
|
||||||
|
className="mb-0 flex-grow"
|
||||||
|
label={index === 0 ? "Access Token Trusted IPs" : undefined}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
value={field.value}
|
||||||
|
onChange={(e) => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
|
field.onChange(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
handlePopUpOpen("upgradePlan");
|
||||||
}}
|
}}
|
||||||
size="lg"
|
placeholder="123.456.789.0"
|
||||||
colorSchema="danger"
|
/>
|
||||||
variant="plain"
|
</FormControl>
|
||||||
ariaLabel="update"
|
);
|
||||||
className="p-3"
|
}}
|
||||||
>
|
/>
|
||||||
<FontAwesomeIcon icon={faXmark} />
|
<IconButton
|
||||||
</IconButton>
|
onClick={() => {
|
||||||
</div>
|
if (subscription?.ipAllowlisting) {
|
||||||
))}
|
removeAccessTokenTrustedIp(index);
|
||||||
<div className="my-4 ml-1">
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
}}
|
||||||
|
size="lg"
|
||||||
|
colorSchema="danger"
|
||||||
|
variant="plain"
|
||||||
|
ariaLabel="update"
|
||||||
|
className="p-3"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faXmark} />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div className="my-4 ml-1">
|
||||||
|
<Button
|
||||||
|
variant="outline_bg"
|
||||||
|
onClick={() => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
|
appendAccessTokenTrustedIp({
|
||||||
|
ipAddress: "0.0.0.0/0"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
}}
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
|
size="xs"
|
||||||
|
>
|
||||||
|
Add IP Address
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</TabPanel>
|
||||||
|
</Tabs>
|
||||||
|
<div className="flex items-center">
|
||||||
<Button
|
<Button
|
||||||
variant="outline_bg"
|
className="mr-4"
|
||||||
onClick={() => {
|
size="sm"
|
||||||
if (subscription?.ipAllowlisting) {
|
type="submit"
|
||||||
appendAccessTokenTrustedIp({
|
isLoading={isSubmitting}
|
||||||
ipAddress: "0.0.0.0/0"
|
isDisabled={isSubmitting}
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
|
||||||
}}
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
|
||||||
size="xs"
|
|
||||||
>
|
>
|
||||||
Add IP Address
|
{isUpdate ? "Update" : "Add"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Button
|
|
||||||
className="mr-4"
|
|
||||||
size="sm"
|
|
||||||
type="submit"
|
|
||||||
isLoading={isSubmitting}
|
|
||||||
isDisabled={isSubmitting}
|
|
||||||
>
|
|
||||||
{isUpdate ? "Update" : "Create"}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
colorSchema="secondary"
|
colorSchema="secondary"
|
||||||
variant="plain"
|
variant="plain"
|
||||||
onClick={() => handlePopUpToggle("identityAuthMethod", false)}
|
onClick={() => handlePopUpToggle("identityAuthMethod", false)}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
|||||||
+226
-206
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Controller, useFieldArray, useForm } from "react-hook-form";
|
import { Controller, useFieldArray, useForm } from "react-hook-form";
|
||||||
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";
|
||||||
@@ -6,17 +6,27 @@ 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 } from "@app/components/v2";
|
import {
|
||||||
|
Button,
|
||||||
|
FormControl,
|
||||||
|
IconButton,
|
||||||
|
Input,
|
||||||
|
Tab,
|
||||||
|
TabList,
|
||||||
|
TabPanel,
|
||||||
|
Tabs
|
||||||
|
} from "@app/components/v2";
|
||||||
import { useOrganization, useSubscription } from "@app/context";
|
import { useOrganization, useSubscription } from "@app/context";
|
||||||
import {
|
import {
|
||||||
useAddIdentityUniversalAuth,
|
useAddIdentityUniversalAuth,
|
||||||
useGetIdentityUniversalAuth,
|
useGetIdentityUniversalAuth,
|
||||||
useUpdateIdentityUniversalAuth
|
useUpdateIdentityUniversalAuth
|
||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
import { IdentityAuthMethod } from "@app/hooks/api/identities";
|
|
||||||
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
import { IdentityTrustedIp } from "@app/hooks/api/identities/types";
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
|
import { IdentityFormTab } from "./types";
|
||||||
|
|
||||||
const schema = z
|
const schema = z
|
||||||
.object({
|
.object({
|
||||||
accessTokenTTL: z
|
accessTokenTTL: z
|
||||||
@@ -55,30 +65,24 @@ type Props = {
|
|||||||
popUpName: keyof UsePopUpState<["identityAuthMethod"]>,
|
popUpName: keyof UsePopUpState<["identityAuthMethod"]>,
|
||||||
state?: boolean
|
state?: boolean
|
||||||
) => void;
|
) => void;
|
||||||
identityAuthMethodData?: {
|
identityId?: string;
|
||||||
identityId: string;
|
isUpdate?: boolean;
|
||||||
name: string;
|
|
||||||
configuredAuthMethods?: IdentityAuthMethod[];
|
|
||||||
authMethod?: IdentityAuthMethod;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IdentityUniversalAuthForm = ({
|
export const IdentityUniversalAuthForm = ({
|
||||||
handlePopUpOpen,
|
handlePopUpOpen,
|
||||||
handlePopUpToggle,
|
handlePopUpToggle,
|
||||||
identityAuthMethodData
|
identityId,
|
||||||
|
isUpdate
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const orgId = currentOrg?.id || "";
|
const orgId = currentOrg?.id || "";
|
||||||
const { subscription } = useSubscription();
|
const { subscription } = useSubscription();
|
||||||
const { mutateAsync: addMutateAsync } = useAddIdentityUniversalAuth();
|
const { mutateAsync: addMutateAsync } = useAddIdentityUniversalAuth();
|
||||||
const { mutateAsync: updateMutateAsync } = useUpdateIdentityUniversalAuth();
|
const { mutateAsync: updateMutateAsync } = useUpdateIdentityUniversalAuth();
|
||||||
|
const [tabValue, setTabValue] = useState<IdentityFormTab>(IdentityFormTab.Configuration);
|
||||||
|
|
||||||
const isUpdate = identityAuthMethodData?.configuredAuthMethods?.includes(
|
const { data } = useGetIdentityUniversalAuth(identityId ?? "", {
|
||||||
identityAuthMethodData.authMethod! || ""
|
|
||||||
);
|
|
||||||
|
|
||||||
const { data } = useGetIdentityUniversalAuth(identityAuthMethodData?.identityId ?? "", {
|
|
||||||
enabled: isUpdate
|
enabled: isUpdate
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -149,13 +153,13 @@ export const IdentityUniversalAuthForm = ({
|
|||||||
accessTokenTrustedIps
|
accessTokenTrustedIps
|
||||||
}: FormData) => {
|
}: FormData) => {
|
||||||
try {
|
try {
|
||||||
if (!identityAuthMethodData) return;
|
if (!identityId) return;
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
// update universal auth configuration
|
// update universal auth configuration
|
||||||
await updateMutateAsync({
|
await updateMutateAsync({
|
||||||
organizationId: orgId,
|
organizationId: orgId,
|
||||||
identityId: identityAuthMethodData.identityId,
|
identityId,
|
||||||
clientSecretTrustedIps,
|
clientSecretTrustedIps,
|
||||||
accessTokenTTL: Number(accessTokenTTL),
|
accessTokenTTL: Number(accessTokenTTL),
|
||||||
accessTokenMaxTTL: Number(accessTokenMaxTTL),
|
accessTokenMaxTTL: Number(accessTokenMaxTTL),
|
||||||
@@ -167,7 +171,7 @@ export const IdentityUniversalAuthForm = ({
|
|||||||
|
|
||||||
await addMutateAsync({
|
await addMutateAsync({
|
||||||
organizationId: orgId,
|
organizationId: orgId,
|
||||||
identityId: identityAuthMethodData.identityId,
|
identityId,
|
||||||
clientSecretTrustedIps,
|
clientSecretTrustedIps,
|
||||||
accessTokenTTL: Number(accessTokenTTL),
|
accessTokenTTL: Number(accessTokenTTL),
|
||||||
accessTokenMaxTTL: Number(accessTokenMaxTTL),
|
accessTokenMaxTTL: Number(accessTokenMaxTTL),
|
||||||
@@ -195,204 +199,220 @@ export const IdentityUniversalAuthForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
<form
|
||||||
<Controller
|
onSubmit={handleSubmit(onFormSubmit, (fields) => {
|
||||||
control={control}
|
setTabValue(
|
||||||
defaultValue="2592000"
|
["accessTokenTrustedIps", "clientSecretTrustedIps"].includes(Object.keys(fields)[0])
|
||||||
name="accessTokenTTL"
|
? IdentityFormTab.Advanced
|
||||||
render={({ field, fieldState: { error } }) => (
|
: IdentityFormTab.Configuration
|
||||||
<FormControl
|
);
|
||||||
label="Access Token TTL (seconds)"
|
})}
|
||||||
isError={Boolean(error)}
|
>
|
||||||
errorText={error?.message}
|
<Tabs value={tabValue} onValueChange={(value) => setTabValue(value as IdentityFormTab)}>
|
||||||
>
|
<TabList>
|
||||||
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
<Tab value={IdentityFormTab.Configuration}>Configuration</Tab>
|
||||||
</FormControl>
|
<Tab value={IdentityFormTab.Advanced}>Advanced</Tab>
|
||||||
)}
|
</TabList>
|
||||||
/>
|
<TabPanel value={IdentityFormTab.Configuration}>
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue="2592000"
|
|
||||||
name="accessTokenMaxTTL"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Access Token Max TTL (seconds)"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="2592000" type="number" min="0" step="1" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue="0"
|
|
||||||
name="accessTokenNumUsesLimit"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Access Token Max Number of Uses"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="0" type="number" min="0" step="1" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{clientSecretTrustedIpsFields.map(({ id }, index) => (
|
|
||||||
<div className="mb-3 flex items-end space-x-2" key={id}>
|
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name={`clientSecretTrustedIps.${index}.ipAddress`}
|
defaultValue="2592000"
|
||||||
defaultValue="0.0.0.0/0"
|
name="accessTokenTTL"
|
||||||
render={({ field, fieldState: { error } }) => {
|
render={({ field, fieldState: { error } }) => (
|
||||||
return (
|
<FormControl
|
||||||
<FormControl
|
label="Access Token TTL (seconds)"
|
||||||
className="mb-0 flex-grow"
|
isError={Boolean(error)}
|
||||||
label={index === 0 ? "Client Secret Trusted IPs" : undefined}
|
errorText={error?.message}
|
||||||
isError={Boolean(error)}
|
>
|
||||||
errorText={error?.message}
|
<Input {...field} placeholder="2592000" type="number" min="1" step="1" />
|
||||||
>
|
</FormControl>
|
||||||
<Input
|
)}
|
||||||
value={field.value}
|
|
||||||
onChange={(e) => {
|
|
||||||
if (subscription?.ipAllowlisting) {
|
|
||||||
field.onChange(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
|
||||||
}}
|
|
||||||
placeholder="123.456.789.0"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<IconButton
|
|
||||||
onClick={() => {
|
|
||||||
if (subscription?.ipAllowlisting) {
|
|
||||||
removeClientSecretTrustedIp(index);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
|
||||||
}}
|
|
||||||
size="lg"
|
|
||||||
colorSchema="danger"
|
|
||||||
variant="plain"
|
|
||||||
ariaLabel="update"
|
|
||||||
className="p-3"
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon icon={faXmark} />
|
|
||||||
</IconButton>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<div className="my-4 ml-1">
|
|
||||||
<Button
|
|
||||||
variant="outline_bg"
|
|
||||||
onClick={() => {
|
|
||||||
if (subscription?.ipAllowlisting) {
|
|
||||||
appendClientSecretTrustedIp({
|
|
||||||
ipAddress: "0.0.0.0/0"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
|
||||||
}}
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
|
||||||
size="xs"
|
|
||||||
>
|
|
||||||
Add IP Address
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
{accessTokenTrustedIpsFields.map(({ id }, index) => (
|
|
||||||
<div className="mb-3 flex items-end space-x-2" key={id}>
|
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name={`accessTokenTrustedIps.${index}.ipAddress`}
|
defaultValue="2592000"
|
||||||
defaultValue="0.0.0.0/0"
|
name="accessTokenMaxTTL"
|
||||||
render={({ field, fieldState: { error } }) => {
|
render={({ field, fieldState: { error } }) => (
|
||||||
return (
|
<FormControl
|
||||||
<FormControl
|
label="Access Token Max TTL (seconds)"
|
||||||
className="mb-0 flex-grow"
|
isError={Boolean(error)}
|
||||||
label={index === 0 ? "Access Token Trusted IPs" : undefined}
|
errorText={error?.message}
|
||||||
isError={Boolean(error)}
|
>
|
||||||
errorText={error?.message}
|
<Input {...field} placeholder="2592000" type="number" min="0" step="1" />
|
||||||
>
|
</FormControl>
|
||||||
<Input
|
)}
|
||||||
value={field.value}
|
|
||||||
onChange={(e) => {
|
|
||||||
if (subscription?.ipAllowlisting) {
|
|
||||||
field.onChange(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
|
||||||
}}
|
|
||||||
placeholder="123.456.789.0"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<Controller
|
||||||
onClick={() => {
|
control={control}
|
||||||
if (subscription?.ipAllowlisting) {
|
defaultValue="0"
|
||||||
removeAccessTokenTrustedIp(index);
|
name="accessTokenNumUsesLimit"
|
||||||
return;
|
render={({ field, fieldState: { error } }) => (
|
||||||
}
|
<FormControl
|
||||||
|
label="Access Token Max Number of Uses"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="0" type="number" min="0" step="1" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel value={IdentityFormTab.Advanced}>
|
||||||
|
{clientSecretTrustedIpsFields.map(({ id }, index) => (
|
||||||
|
<div className="mb-3 flex items-end space-x-2" key={id}>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name={`clientSecretTrustedIps.${index}.ipAddress`}
|
||||||
|
defaultValue="0.0.0.0/0"
|
||||||
|
render={({ field, fieldState: { error } }) => {
|
||||||
|
return (
|
||||||
|
<FormControl
|
||||||
|
className="mb-0 flex-grow"
|
||||||
|
label={index === 0 ? "Client Secret Trusted IPs" : undefined}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
value={field.value}
|
||||||
|
onChange={(e) => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
|
field.onChange(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
handlePopUpOpen("upgradePlan");
|
||||||
}}
|
}}
|
||||||
size="lg"
|
placeholder="123.456.789.0"
|
||||||
colorSchema="danger"
|
/>
|
||||||
variant="plain"
|
</FormControl>
|
||||||
ariaLabel="update"
|
);
|
||||||
className="p-3"
|
}}
|
||||||
>
|
/>
|
||||||
<FontAwesomeIcon icon={faXmark} />
|
<IconButton
|
||||||
</IconButton>
|
onClick={() => {
|
||||||
</div>
|
if (subscription?.ipAllowlisting) {
|
||||||
))}
|
removeClientSecretTrustedIp(index);
|
||||||
<div className="my-4 ml-1">
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
}}
|
||||||
|
size="lg"
|
||||||
|
colorSchema="danger"
|
||||||
|
variant="plain"
|
||||||
|
ariaLabel="update"
|
||||||
|
className="p-3"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faXmark} />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div className="my-4 ml-1">
|
||||||
|
<Button
|
||||||
|
variant="outline_bg"
|
||||||
|
onClick={() => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
|
appendClientSecretTrustedIp({
|
||||||
|
ipAddress: "0.0.0.0/0"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
}}
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
|
size="xs"
|
||||||
|
>
|
||||||
|
Add IP Address
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
{accessTokenTrustedIpsFields.map(({ id }, index) => (
|
||||||
|
<div className="mb-3 flex items-end space-x-2" key={id}>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name={`accessTokenTrustedIps.${index}.ipAddress`}
|
||||||
|
defaultValue="0.0.0.0/0"
|
||||||
|
render={({ field, fieldState: { error } }) => {
|
||||||
|
return (
|
||||||
|
<FormControl
|
||||||
|
className="mb-0 flex-grow"
|
||||||
|
label={index === 0 ? "Access Token Trusted IPs" : undefined}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
value={field.value}
|
||||||
|
onChange={(e) => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
|
field.onChange(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
}}
|
||||||
|
placeholder="123.456.789.0"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
onClick={() => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
|
removeAccessTokenTrustedIp(index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
}}
|
||||||
|
size="lg"
|
||||||
|
colorSchema="danger"
|
||||||
|
variant="plain"
|
||||||
|
ariaLabel="update"
|
||||||
|
className="p-3"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faXmark} />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div className="my-4 ml-1">
|
||||||
|
<Button
|
||||||
|
variant="outline_bg"
|
||||||
|
onClick={() => {
|
||||||
|
if (subscription?.ipAllowlisting) {
|
||||||
|
appendAccessTokenTrustedIp({
|
||||||
|
ipAddress: "0.0.0.0/0"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePopUpOpen("upgradePlan");
|
||||||
|
}}
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
|
size="xs"
|
||||||
|
>
|
||||||
|
Add IP Address
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</TabPanel>
|
||||||
|
</Tabs>
|
||||||
|
<div className="flex items-center">
|
||||||
<Button
|
<Button
|
||||||
variant="outline_bg"
|
className="mr-4"
|
||||||
onClick={() => {
|
size="sm"
|
||||||
if (subscription?.ipAllowlisting) {
|
type="submit"
|
||||||
appendAccessTokenTrustedIp({
|
isLoading={isSubmitting}
|
||||||
ipAddress: "0.0.0.0/0"
|
isDisabled={isSubmitting}
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlePopUpOpen("upgradePlan");
|
|
||||||
}}
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
|
||||||
size="xs"
|
|
||||||
>
|
>
|
||||||
Add IP Address
|
{isUpdate ? "Update" : "Add"}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
colorSchema="secondary"
|
||||||
|
variant="plain"
|
||||||
|
onClick={() => handlePopUpToggle("identityAuthMethod", false)}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Button
|
|
||||||
className="mr-4"
|
|
||||||
size="sm"
|
|
||||||
type="submit"
|
|
||||||
isLoading={isSubmitting}
|
|
||||||
isDisabled={isSubmitting}
|
|
||||||
>
|
|
||||||
{isUpdate ? "Edit" : "Create"}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
colorSchema="secondary"
|
|
||||||
variant="plain"
|
|
||||||
onClick={() => handlePopUpToggle("identityAuthMethod", false)}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
|||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
export enum IdentityFormTab {
|
||||||
|
Advanced = "advanced",
|
||||||
|
Configuration = "configuration"
|
||||||
|
}
|
||||||
@@ -201,14 +201,6 @@ const Page = () => {
|
|||||||
onOpenChange={(isOpen) => handlePopUpToggle("viewAuthMethod", isOpen)}
|
onOpenChange={(isOpen) => handlePopUpToggle("viewAuthMethod", isOpen)}
|
||||||
authMethod={popUp.viewAuthMethod.data}
|
authMethod={popUp.viewAuthMethod.data}
|
||||||
identityId={identityId}
|
identityId={identityId}
|
||||||
onEditAuthMethod={(authMethod) =>
|
|
||||||
handlePopUpOpen("identityAuthMethod", {
|
|
||||||
identityId,
|
|
||||||
name: data?.identity.name,
|
|
||||||
allAuthMethods: data?.identity.authMethods,
|
|
||||||
authMethod
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
+3
-3
@@ -9,11 +9,11 @@ type Props = {
|
|||||||
export const IdentityAuthFieldDisplay = ({ label, children, className }: Props) => {
|
export const IdentityAuthFieldDisplay = ({ label, children, className }: Props) => {
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<span className="text-xs font-medium text-mineshaft-400">{label}</span>
|
<span className="text-sm text-mineshaft-400">{label}</span>
|
||||||
{children ? (
|
{children ? (
|
||||||
<p className="break-words text-sm leading-4">{children}</p>
|
<p className="break-words text-base leading-4">{children}</p>
|
||||||
) : (
|
) : (
|
||||||
<p className="text-sm italic leading-4 text-bunker-400">Not set</p>
|
<p className="text-base italic leading-4 text-bunker-400">Not set</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
+2
-2
@@ -75,8 +75,8 @@ export const IdentityTokenAuthTokensTable = ({ tokens, identityId }: Props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="col-span-2 mt-3">
|
<div className="col-span-2 mt-3">
|
||||||
<div className="flex items-baseline justify-between border-b border-mineshaft-500 pb-1">
|
<div className="flex items-end justify-between border-b border-mineshaft-500 pb-2">
|
||||||
<span className="text-sm text-bunker-300">Access Tokens</span>
|
<span className="text-bunker-300">Access Tokens</span>
|
||||||
<Button
|
<Button
|
||||||
size="xs"
|
size="xs"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
+2
-2
@@ -64,8 +64,8 @@ export const IdentityUniversalAuthClientSecretsTable = ({ clientSecrets, identit
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="col-span-2">
|
<div className="col-span-2">
|
||||||
<div className="flex items-baseline justify-between border-b border-mineshaft-500 pb-1">
|
<div className="flex items-end justify-between border-b border-mineshaft-500 pb-2">
|
||||||
<span className="text-sm text-bunker-300">Client Secrets</span>
|
<span className="text-bunker-300">Client Secrets</span>
|
||||||
<Button
|
<Button
|
||||||
size="xs"
|
size="xs"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
+16
-8
@@ -1,3 +1,4 @@
|
|||||||
|
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { DeleteActionModal, Modal, ModalContent } from "@app/components/v2";
|
import { DeleteActionModal, Modal, ModalContent } from "@app/components/v2";
|
||||||
import { useOrganization } from "@app/context";
|
import { useOrganization } from "@app/context";
|
||||||
@@ -30,7 +31,6 @@ type Props = {
|
|||||||
authMethod?: IdentityAuthMethod;
|
authMethod?: IdentityAuthMethod;
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
onOpenChange: (isOpen: boolean) => void;
|
onOpenChange: (isOpen: boolean) => void;
|
||||||
onEditAuthMethod: (authMethod: IdentityAuthMethod) => void;
|
|
||||||
onDeleteAuthMethod: () => void;
|
onDeleteAuthMethod: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -42,13 +42,16 @@ type TRevokeOptions = {
|
|||||||
export const Content = ({
|
export const Content = ({
|
||||||
identityId,
|
identityId,
|
||||||
authMethod,
|
authMethod,
|
||||||
onEditAuthMethod,
|
|
||||||
onDeleteAuthMethod
|
onDeleteAuthMethod
|
||||||
}: Pick<Props, "authMethod" | "identityId" | "onEditAuthMethod" | "onDeleteAuthMethod">) => {
|
}: Pick<Props, "authMethod" | "identityId" | "onDeleteAuthMethod">) => {
|
||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const orgId = currentOrg?.id || "";
|
const orgId = currentOrg?.id || "";
|
||||||
|
|
||||||
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["revokeAuthMethod"] as const);
|
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp([
|
||||||
|
"revokeAuthMethod",
|
||||||
|
"upgradePlan",
|
||||||
|
"identityAuthMethod"
|
||||||
|
] as const);
|
||||||
|
|
||||||
const { mutateAsync: revokeUniversalAuth } = useDeleteIdentityUniversalAuth();
|
const { mutateAsync: revokeUniversalAuth } = useDeleteIdentityUniversalAuth();
|
||||||
const { mutateAsync: revokeTokenAuth } = useDeleteIdentityTokenAuth();
|
const { mutateAsync: revokeTokenAuth } = useDeleteIdentityTokenAuth();
|
||||||
@@ -128,7 +131,9 @@ export const Content = ({
|
|||||||
<Component
|
<Component
|
||||||
identityId={identityId}
|
identityId={identityId}
|
||||||
onDelete={handleDelete}
|
onDelete={handleDelete}
|
||||||
onEdit={() => onEditAuthMethod(authMethod)}
|
popUp={popUp}
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
/>
|
/>
|
||||||
<DeleteActionModal
|
<DeleteActionModal
|
||||||
isOpen={popUp?.revokeAuthMethod?.isOpen}
|
isOpen={popUp?.revokeAuthMethod?.isOpen}
|
||||||
@@ -138,6 +143,11 @@ export const Content = ({
|
|||||||
buttonText="Remove"
|
buttonText="Remove"
|
||||||
onDeleteApproved={handleDeleteAuthMethod}
|
onDeleteApproved={handleDeleteAuthMethod}
|
||||||
/>
|
/>
|
||||||
|
<UpgradePlanModal
|
||||||
|
isOpen={popUp.upgradePlan.isOpen}
|
||||||
|
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
||||||
|
text={(popUp.upgradePlan?.data as { description: string })?.description}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -146,8 +156,7 @@ export const ViewIdentityAuthModal = ({
|
|||||||
isOpen,
|
isOpen,
|
||||||
onOpenChange,
|
onOpenChange,
|
||||||
authMethod,
|
authMethod,
|
||||||
identityId,
|
identityId
|
||||||
onEditAuthMethod
|
|
||||||
}: Omit<Props, "onDeleteAuthMethod">) => {
|
}: Omit<Props, "onDeleteAuthMethod">) => {
|
||||||
if (!identityId || !authMethod) return null;
|
if (!identityId || !authMethod) return null;
|
||||||
|
|
||||||
@@ -157,7 +166,6 @@ export const ViewIdentityAuthModal = ({
|
|||||||
<Content
|
<Content
|
||||||
identityId={identityId}
|
identityId={identityId}
|
||||||
authMethod={authMethod}
|
authMethod={authMethod}
|
||||||
onEditAuthMethod={onEditAuthMethod}
|
|
||||||
onDeleteAuthMethod={() => onOpenChange(false)}
|
onDeleteAuthMethod={() => onOpenChange(false)}
|
||||||
/>
|
/>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|||||||
+32
-15
@@ -2,6 +2,7 @@ import { faBan } from "@fortawesome/free-solid-svg-icons";
|
|||||||
|
|
||||||
import { EmptyState, Spinner } from "@app/components/v2";
|
import { EmptyState, Spinner } from "@app/components/v2";
|
||||||
import { useGetIdentityAwsAuth } from "@app/hooks/api";
|
import { useGetIdentityAwsAuth } from "@app/hooks/api";
|
||||||
|
import { IdentityAwsAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityAwsAuthForm";
|
||||||
|
|
||||||
import { IdentityAuthFieldDisplay } from "./IdentityAuthFieldDisplay";
|
import { IdentityAuthFieldDisplay } from "./IdentityAuthFieldDisplay";
|
||||||
import { ViewAuthMethodProps } from "./types";
|
import { ViewAuthMethodProps } from "./types";
|
||||||
@@ -9,8 +10,10 @@ import { ViewIdentityContentWrapper } from "./ViewIdentityContentWrapper";
|
|||||||
|
|
||||||
export const ViewIdentityAwsAuthContent = ({
|
export const ViewIdentityAwsAuthContent = ({
|
||||||
identityId,
|
identityId,
|
||||||
onEdit,
|
handlePopUpToggle,
|
||||||
onDelete
|
handlePopUpOpen,
|
||||||
|
onDelete,
|
||||||
|
popUp
|
||||||
}: ViewAuthMethodProps) => {
|
}: ViewAuthMethodProps) => {
|
||||||
const { data, isPending } = useGetIdentityAwsAuth(identityId);
|
const { data, isPending } = useGetIdentityAwsAuth(identityId);
|
||||||
|
|
||||||
@@ -28,8 +31,34 @@ export const ViewIdentityAwsAuthContent = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (popUp.identityAuthMethod.isOpen) {
|
||||||
|
return (
|
||||||
|
<IdentityAwsAuthForm
|
||||||
|
identityId={identityId}
|
||||||
|
isUpdate
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewIdentityContentWrapper onEdit={onEdit} onDelete={onDelete}>
|
<ViewIdentityContentWrapper
|
||||||
|
onEdit={() => handlePopUpOpen("identityAuthMethod")}
|
||||||
|
onDelete={onDelete}
|
||||||
|
>
|
||||||
|
<IdentityAuthFieldDisplay label="Access Token TLL (seconds)">
|
||||||
|
{data.accessTokenTTL}
|
||||||
|
</IdentityAuthFieldDisplay>
|
||||||
|
<IdentityAuthFieldDisplay label="Access Token Max TLL (seconds)">
|
||||||
|
{data.accessTokenMaxTTL}
|
||||||
|
</IdentityAuthFieldDisplay>
|
||||||
|
<IdentityAuthFieldDisplay label="Access Token Max Number of Uses">
|
||||||
|
{data.accessTokenNumUsesLimit}
|
||||||
|
</IdentityAuthFieldDisplay>
|
||||||
|
<IdentityAuthFieldDisplay label="Access Token Trusted IPs">
|
||||||
|
{data.accessTokenTrustedIps.map((ip) => ip.ipAddress).join(", ")}
|
||||||
|
</IdentityAuthFieldDisplay>
|
||||||
<IdentityAuthFieldDisplay className="col-span-2" label="Allowed Principal ARNs">
|
<IdentityAuthFieldDisplay className="col-span-2" label="Allowed Principal ARNs">
|
||||||
{data.allowedPrincipalArns
|
{data.allowedPrincipalArns
|
||||||
?.split(",")
|
?.split(",")
|
||||||
@@ -45,18 +74,6 @@ export const ViewIdentityAwsAuthContent = ({
|
|||||||
<IdentityAuthFieldDisplay className="col-span-2" label="STS Endpoint">
|
<IdentityAuthFieldDisplay className="col-span-2" label="STS Endpoint">
|
||||||
{data.stsEndpoint}
|
{data.stsEndpoint}
|
||||||
</IdentityAuthFieldDisplay>
|
</IdentityAuthFieldDisplay>
|
||||||
<IdentityAuthFieldDisplay label="Access Token TLL (seconds)">
|
|
||||||
{data.accessTokenTTL}
|
|
||||||
</IdentityAuthFieldDisplay>
|
|
||||||
<IdentityAuthFieldDisplay label="Access Token Max TLL (seconds)">
|
|
||||||
{data.accessTokenMaxTTL}
|
|
||||||
</IdentityAuthFieldDisplay>
|
|
||||||
<IdentityAuthFieldDisplay label="Access Token Max Number of Uses">
|
|
||||||
{data.accessTokenNumUsesLimit}
|
|
||||||
</IdentityAuthFieldDisplay>
|
|
||||||
<IdentityAuthFieldDisplay label="Access Token Trusted IPs">
|
|
||||||
{data.accessTokenTrustedIps.map((ip) => ip.ipAddress).join(", ")}
|
|
||||||
</IdentityAuthFieldDisplay>
|
|
||||||
</ViewIdentityContentWrapper>
|
</ViewIdentityContentWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+32
-15
@@ -2,6 +2,7 @@ import { faBan } from "@fortawesome/free-solid-svg-icons";
|
|||||||
|
|
||||||
import { EmptyState, Spinner } from "@app/components/v2";
|
import { EmptyState, Spinner } from "@app/components/v2";
|
||||||
import { useGetIdentityAzureAuth } from "@app/hooks/api";
|
import { useGetIdentityAzureAuth } from "@app/hooks/api";
|
||||||
|
import { IdentityAzureAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityAzureAuthForm";
|
||||||
|
|
||||||
import { IdentityAuthFieldDisplay } from "./IdentityAuthFieldDisplay";
|
import { IdentityAuthFieldDisplay } from "./IdentityAuthFieldDisplay";
|
||||||
import { ViewAuthMethodProps } from "./types";
|
import { ViewAuthMethodProps } from "./types";
|
||||||
@@ -9,8 +10,10 @@ import { ViewIdentityContentWrapper } from "./ViewIdentityContentWrapper";
|
|||||||
|
|
||||||
export const ViewIdentityAzureAuthContent = ({
|
export const ViewIdentityAzureAuthContent = ({
|
||||||
identityId,
|
identityId,
|
||||||
onEdit,
|
handlePopUpToggle,
|
||||||
onDelete
|
handlePopUpOpen,
|
||||||
|
onDelete,
|
||||||
|
popUp
|
||||||
}: ViewAuthMethodProps) => {
|
}: ViewAuthMethodProps) => {
|
||||||
const { data, isPending } = useGetIdentityAzureAuth(identityId);
|
const { data, isPending } = useGetIdentityAzureAuth(identityId);
|
||||||
|
|
||||||
@@ -28,20 +31,22 @@ export const ViewIdentityAzureAuthContent = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (popUp.identityAuthMethod.isOpen) {
|
||||||
|
return (
|
||||||
|
<IdentityAzureAuthForm
|
||||||
|
identityId={identityId}
|
||||||
|
isUpdate
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewIdentityContentWrapper onEdit={onEdit} onDelete={onDelete}>
|
<ViewIdentityContentWrapper
|
||||||
<IdentityAuthFieldDisplay className="col-span-2" label="Tenant ID">
|
onEdit={() => handlePopUpOpen("identityAuthMethod")}
|
||||||
{data.tenantId}
|
onDelete={onDelete}
|
||||||
</IdentityAuthFieldDisplay>
|
>
|
||||||
<IdentityAuthFieldDisplay className="col-span-2" label="Resource / Audience">
|
|
||||||
{data.resource}
|
|
||||||
</IdentityAuthFieldDisplay>
|
|
||||||
<IdentityAuthFieldDisplay className="col-span-2" label="Allowed Service Principal IDs">
|
|
||||||
{data.allowedServicePrincipalIds
|
|
||||||
?.split(",")
|
|
||||||
.map((id) => id.trim())
|
|
||||||
.join(", ")}
|
|
||||||
</IdentityAuthFieldDisplay>
|
|
||||||
<IdentityAuthFieldDisplay label="Access Token TLL (seconds)">
|
<IdentityAuthFieldDisplay label="Access Token TLL (seconds)">
|
||||||
{data.accessTokenTTL}
|
{data.accessTokenTTL}
|
||||||
</IdentityAuthFieldDisplay>
|
</IdentityAuthFieldDisplay>
|
||||||
@@ -54,6 +59,18 @@ export const ViewIdentityAzureAuthContent = ({
|
|||||||
<IdentityAuthFieldDisplay label="Access Token Trusted IPs">
|
<IdentityAuthFieldDisplay label="Access Token Trusted IPs">
|
||||||
{data.accessTokenTrustedIps.map((ip) => ip.ipAddress).join(", ")}
|
{data.accessTokenTrustedIps.map((ip) => ip.ipAddress).join(", ")}
|
||||||
</IdentityAuthFieldDisplay>
|
</IdentityAuthFieldDisplay>
|
||||||
|
<IdentityAuthFieldDisplay className="col-span-2" label="Tenant ID">
|
||||||
|
{data.tenantId}
|
||||||
|
</IdentityAuthFieldDisplay>
|
||||||
|
<IdentityAuthFieldDisplay className="col-span-2" label="Resource / Audience">
|
||||||
|
{data.resource}
|
||||||
|
</IdentityAuthFieldDisplay>
|
||||||
|
<IdentityAuthFieldDisplay className="col-span-2" label="Allowed Service Principal IDs">
|
||||||
|
{data.allowedServicePrincipalIds
|
||||||
|
?.split(",")
|
||||||
|
.map((id) => id.trim())
|
||||||
|
.join(", ")}
|
||||||
|
</IdentityAuthFieldDisplay>
|
||||||
</ViewIdentityContentWrapper>
|
</ViewIdentityContentWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+29
-20
@@ -1,8 +1,14 @@
|
|||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
import { faEdit, faTrash } from "@fortawesome/free-solid-svg-icons";
|
import { faChevronDown, faEdit, faTrash } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
import { Button } from "@app/components/v2";
|
import {
|
||||||
|
Button,
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger
|
||||||
|
} from "@app/components/v2";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
@@ -14,25 +20,28 @@ export const ViewIdentityContentWrapper = ({ children, onDelete, onEdit }: Props
|
|||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-baseline justify-between border-b border-mineshaft-500 pb-1">
|
<div className="flex items-end justify-between border-b border-mineshaft-500 pb-2">
|
||||||
<span className="text-sm text-bunker-300">Details</span>
|
<span className="text-bunker-300">Details</span>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Button
|
<DropdownMenu>
|
||||||
size="xs"
|
<DropdownMenuTrigger asChild>
|
||||||
onClick={onEdit}
|
<Button
|
||||||
leftIcon={<FontAwesomeIcon icon={faEdit} />}
|
size="xs"
|
||||||
colorSchema="secondary"
|
rightIcon={<FontAwesomeIcon className="ml-1" icon={faChevronDown} />}
|
||||||
>
|
colorSchema="secondary"
|
||||||
Edit
|
>
|
||||||
</Button>
|
Options
|
||||||
<Button
|
</Button>
|
||||||
size="xs"
|
</DropdownMenuTrigger>
|
||||||
onClick={onDelete}
|
<DropdownMenuContent className="min-w-[120px]" align="end">
|
||||||
leftIcon={<FontAwesomeIcon icon={faTrash} />}
|
<DropdownMenuItem onClick={onEdit} icon={<FontAwesomeIcon icon={faEdit} />}>
|
||||||
colorSchema="danger"
|
Edit
|
||||||
>
|
</DropdownMenuItem>
|
||||||
Delete
|
<DropdownMenuItem onClick={onDelete} icon={<FontAwesomeIcon icon={faTrash} />}>
|
||||||
</Button>
|
Delete
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-3 grid grid-cols-2 gap-3">{children}</div>
|
<div className="mt-3 grid grid-cols-2 gap-3">{children}</div>
|
||||||
|
|||||||
+32
-15
@@ -2,6 +2,7 @@ import { faBan } from "@fortawesome/free-solid-svg-icons";
|
|||||||
|
|
||||||
import { EmptyState, Spinner } from "@app/components/v2";
|
import { EmptyState, Spinner } from "@app/components/v2";
|
||||||
import { useGetIdentityGcpAuth } from "@app/hooks/api";
|
import { useGetIdentityGcpAuth } from "@app/hooks/api";
|
||||||
|
import { IdentityGcpAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityGcpAuthForm";
|
||||||
|
|
||||||
import { IdentityAuthFieldDisplay } from "./IdentityAuthFieldDisplay";
|
import { IdentityAuthFieldDisplay } from "./IdentityAuthFieldDisplay";
|
||||||
import { ViewAuthMethodProps } from "./types";
|
import { ViewAuthMethodProps } from "./types";
|
||||||
@@ -9,8 +10,10 @@ import { ViewIdentityContentWrapper } from "./ViewIdentityContentWrapper";
|
|||||||
|
|
||||||
export const ViewIdentityGcpAuthContent = ({
|
export const ViewIdentityGcpAuthContent = ({
|
||||||
identityId,
|
identityId,
|
||||||
onEdit,
|
handlePopUpToggle,
|
||||||
onDelete
|
handlePopUpOpen,
|
||||||
|
onDelete,
|
||||||
|
popUp
|
||||||
}: ViewAuthMethodProps) => {
|
}: ViewAuthMethodProps) => {
|
||||||
const { data, isPending } = useGetIdentityGcpAuth(identityId);
|
const { data, isPending } = useGetIdentityGcpAuth(identityId);
|
||||||
|
|
||||||
@@ -28,8 +31,34 @@ export const ViewIdentityGcpAuthContent = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (popUp.identityAuthMethod.isOpen) {
|
||||||
|
return (
|
||||||
|
<IdentityGcpAuthForm
|
||||||
|
identityId={identityId}
|
||||||
|
isUpdate
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewIdentityContentWrapper onEdit={onEdit} onDelete={onDelete}>
|
<ViewIdentityContentWrapper
|
||||||
|
onEdit={() => handlePopUpOpen("identityAuthMethod")}
|
||||||
|
onDelete={onDelete}
|
||||||
|
>
|
||||||
|
<IdentityAuthFieldDisplay label="Access Token TLL (seconds)">
|
||||||
|
{data.accessTokenTTL}
|
||||||
|
</IdentityAuthFieldDisplay>
|
||||||
|
<IdentityAuthFieldDisplay label="Access Token Max TLL (seconds)">
|
||||||
|
{data.accessTokenMaxTTL}
|
||||||
|
</IdentityAuthFieldDisplay>
|
||||||
|
<IdentityAuthFieldDisplay label="Access Token Max Number of Uses">
|
||||||
|
{data.accessTokenNumUsesLimit}
|
||||||
|
</IdentityAuthFieldDisplay>
|
||||||
|
<IdentityAuthFieldDisplay label="Access Token Trusted IPs">
|
||||||
|
{data.accessTokenTrustedIps.map((ip) => ip.ipAddress).join(", ")}
|
||||||
|
</IdentityAuthFieldDisplay>
|
||||||
<IdentityAuthFieldDisplay label="Type">
|
<IdentityAuthFieldDisplay label="Type">
|
||||||
{data.type === "gce" ? "GCP ID Token Auth" : "GCP IAM Auth"}
|
{data.type === "gce" ? "GCP ID Token Auth" : "GCP IAM Auth"}
|
||||||
</IdentityAuthFieldDisplay>
|
</IdentityAuthFieldDisplay>
|
||||||
@@ -55,18 +84,6 @@ export const ViewIdentityGcpAuthContent = ({
|
|||||||
</IdentityAuthFieldDisplay>
|
</IdentityAuthFieldDisplay>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<IdentityAuthFieldDisplay label="Access Token TLL (seconds)">
|
|
||||||
{data.accessTokenTTL}
|
|
||||||
</IdentityAuthFieldDisplay>
|
|
||||||
<IdentityAuthFieldDisplay label="Access Token Max TLL (seconds)">
|
|
||||||
{data.accessTokenMaxTTL}
|
|
||||||
</IdentityAuthFieldDisplay>
|
|
||||||
<IdentityAuthFieldDisplay label="Access Token Max Number of Uses">
|
|
||||||
{data.accessTokenNumUsesLimit}
|
|
||||||
</IdentityAuthFieldDisplay>
|
|
||||||
<IdentityAuthFieldDisplay label="Access Token Trusted IPs">
|
|
||||||
{data.accessTokenTrustedIps.map((ip) => ip.ipAddress).join(", ")}
|
|
||||||
</IdentityAuthFieldDisplay>
|
|
||||||
</ViewIdentityContentWrapper>
|
</ViewIdentityContentWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+55
-35
@@ -4,6 +4,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|||||||
import { Badge, EmptyState, Spinner, Tooltip } from "@app/components/v2";
|
import { Badge, EmptyState, Spinner, Tooltip } from "@app/components/v2";
|
||||||
import { useGetIdentityJwtAuth } from "@app/hooks/api";
|
import { useGetIdentityJwtAuth } from "@app/hooks/api";
|
||||||
import { IdentityJwtConfigurationType } from "@app/hooks/api/identities/enums";
|
import { IdentityJwtConfigurationType } from "@app/hooks/api/identities/enums";
|
||||||
|
import { IdentityJwtAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityJwtAuthForm";
|
||||||
import { ViewIdentityContentWrapper } from "@app/pages/organization/IdentityDetailsByIDPage/components/ViewIdentityAuthModal/ViewIdentityContentWrapper";
|
import { ViewIdentityContentWrapper } from "@app/pages/organization/IdentityDetailsByIDPage/components/ViewIdentityAuthModal/ViewIdentityContentWrapper";
|
||||||
|
|
||||||
import { IdentityAuthFieldDisplay } from "./IdentityAuthFieldDisplay";
|
import { IdentityAuthFieldDisplay } from "./IdentityAuthFieldDisplay";
|
||||||
@@ -11,8 +12,10 @@ import { ViewAuthMethodProps } from "./types";
|
|||||||
|
|
||||||
export const ViewIdentityJwtAuthContent = ({
|
export const ViewIdentityJwtAuthContent = ({
|
||||||
identityId,
|
identityId,
|
||||||
onEdit,
|
handlePopUpToggle,
|
||||||
onDelete
|
handlePopUpOpen,
|
||||||
|
onDelete,
|
||||||
|
popUp
|
||||||
}: ViewAuthMethodProps) => {
|
}: ViewAuthMethodProps) => {
|
||||||
const { data, isPending } = useGetIdentityJwtAuth(identityId);
|
const { data, isPending } = useGetIdentityJwtAuth(identityId);
|
||||||
|
|
||||||
@@ -30,38 +33,66 @@ export const ViewIdentityJwtAuthContent = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (popUp.identityAuthMethod.isOpen) {
|
||||||
|
return (
|
||||||
|
<IdentityJwtAuthForm
|
||||||
|
identityId={identityId}
|
||||||
|
isUpdate
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewIdentityContentWrapper onEdit={onEdit} onDelete={onDelete}>
|
<ViewIdentityContentWrapper
|
||||||
|
onEdit={() => handlePopUpOpen("identityAuthMethod")}
|
||||||
|
onDelete={onDelete}
|
||||||
|
>
|
||||||
|
<IdentityAuthFieldDisplay label="Access Token TLL (seconds)">
|
||||||
|
{data.accessTokenTTL}
|
||||||
|
</IdentityAuthFieldDisplay>
|
||||||
|
<IdentityAuthFieldDisplay label="Access Token Max TLL (seconds)">
|
||||||
|
{data.accessTokenMaxTTL}
|
||||||
|
</IdentityAuthFieldDisplay>
|
||||||
|
<IdentityAuthFieldDisplay label="Access Token Max Number of Uses">
|
||||||
|
{data.accessTokenNumUsesLimit}
|
||||||
|
</IdentityAuthFieldDisplay>
|
||||||
|
<IdentityAuthFieldDisplay label="Access Token Trusted IPs">
|
||||||
|
{data.accessTokenTrustedIps.map((ip) => ip.ipAddress).join(", ")}
|
||||||
|
</IdentityAuthFieldDisplay>
|
||||||
<IdentityAuthFieldDisplay label="Configuration Type">
|
<IdentityAuthFieldDisplay label="Configuration Type">
|
||||||
{data.configurationType === IdentityJwtConfigurationType.JWKS ? "JWKS" : "Static"}
|
{data.configurationType === IdentityJwtConfigurationType.JWKS ? "JWKS" : "Static"}
|
||||||
</IdentityAuthFieldDisplay>
|
</IdentityAuthFieldDisplay>
|
||||||
{data.configurationType === IdentityJwtConfigurationType.JWKS ? (
|
{data.configurationType === IdentityJwtConfigurationType.JWKS ? (
|
||||||
<>
|
<>
|
||||||
<IdentityAuthFieldDisplay className="col-span-2" label="JWKS CA Certificate">
|
|
||||||
<Tooltip
|
|
||||||
side="right"
|
|
||||||
className="max-w-xl p-2"
|
|
||||||
content={
|
|
||||||
<p className="break-words rounded bg-mineshaft-600 p-2">{data.jwksCaCert}</p>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div className="w-min">
|
|
||||||
<Badge className="flex h-5 w-min items-center gap-1.5 whitespace-nowrap bg-mineshaft-400/50 text-bunker-300">
|
|
||||||
<FontAwesomeIcon icon={faEye} />
|
|
||||||
<span>Reveal</span>
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
</IdentityAuthFieldDisplay>
|
|
||||||
<IdentityAuthFieldDisplay className="col-span-2" label="JWKS URL">
|
<IdentityAuthFieldDisplay className="col-span-2" label="JWKS URL">
|
||||||
{data.jwksUrl}
|
{data.jwksUrl}
|
||||||
</IdentityAuthFieldDisplay>
|
</IdentityAuthFieldDisplay>
|
||||||
|
<IdentityAuthFieldDisplay className="col-span-2" label="JWKS CA Certificate">
|
||||||
|
{data.jwksCaCert && (
|
||||||
|
<Tooltip
|
||||||
|
side="right"
|
||||||
|
className="max-w-xl p-2"
|
||||||
|
content={
|
||||||
|
<p className="break-words rounded bg-mineshaft-600 p-2">{data.jwksCaCert}</p>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="w-min">
|
||||||
|
<Badge className="flex h-5 w-min items-center gap-1.5 whitespace-nowrap bg-mineshaft-400/50 text-bunker-300">
|
||||||
|
<FontAwesomeIcon icon={faEye} />
|
||||||
|
<span>Reveal</span>
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</IdentityAuthFieldDisplay>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<IdentityAuthFieldDisplay className="col-span-2" label="Public Keys">
|
<IdentityAuthFieldDisplay className="col-span-2" label="Public Keys">
|
||||||
<div className="flex flex-wrap gap-1">
|
{data.publicKeys.length && (
|
||||||
{data.publicKeys.length &&
|
<div className="flex flex-wrap gap-1">
|
||||||
data.publicKeys.map((key, index) => (
|
{data.publicKeys.map((key, index) => (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
side="right"
|
side="right"
|
||||||
className="max-w-xl p-2"
|
className="max-w-xl p-2"
|
||||||
@@ -80,7 +111,8 @@ export const ViewIdentityJwtAuthContent = ({
|
|||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</IdentityAuthFieldDisplay>
|
</IdentityAuthFieldDisplay>
|
||||||
)}
|
)}
|
||||||
<IdentityAuthFieldDisplay className="col-span-2" label="Issuer">
|
<IdentityAuthFieldDisplay className="col-span-2" label="Issuer">
|
||||||
@@ -115,18 +147,6 @@ export const ViewIdentityJwtAuthContent = ({
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
</IdentityAuthFieldDisplay>
|
</IdentityAuthFieldDisplay>
|
||||||
<IdentityAuthFieldDisplay label="Access Token TLL (seconds)">
|
|
||||||
{data.accessTokenTTL}
|
|
||||||
</IdentityAuthFieldDisplay>
|
|
||||||
<IdentityAuthFieldDisplay label="Access Token Max TLL (seconds)">
|
|
||||||
{data.accessTokenMaxTTL}
|
|
||||||
</IdentityAuthFieldDisplay>
|
|
||||||
<IdentityAuthFieldDisplay label="Access Token Max Number of Uses">
|
|
||||||
{data.accessTokenNumUsesLimit}
|
|
||||||
</IdentityAuthFieldDisplay>
|
|
||||||
<IdentityAuthFieldDisplay label="Access Token Trusted IPs">
|
|
||||||
{data.accessTokenTrustedIps.map((ip) => ip.ipAddress).join(", ")}
|
|
||||||
</IdentityAuthFieldDisplay>
|
|
||||||
</ViewIdentityContentWrapper>
|
</ViewIdentityContentWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+46
-27
@@ -3,6 +3,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|||||||
|
|
||||||
import { Badge, EmptyState, Spinner, Tooltip } from "@app/components/v2";
|
import { Badge, EmptyState, Spinner, Tooltip } from "@app/components/v2";
|
||||||
import { useGetIdentityKubernetesAuth } from "@app/hooks/api";
|
import { useGetIdentityKubernetesAuth } from "@app/hooks/api";
|
||||||
|
import { IdentityKubernetesAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityKubernetesAuthForm";
|
||||||
|
|
||||||
import { IdentityAuthFieldDisplay } from "./IdentityAuthFieldDisplay";
|
import { IdentityAuthFieldDisplay } from "./IdentityAuthFieldDisplay";
|
||||||
import { ViewAuthMethodProps } from "./types";
|
import { ViewAuthMethodProps } from "./types";
|
||||||
@@ -10,8 +11,10 @@ import { ViewIdentityContentWrapper } from "./ViewIdentityContentWrapper";
|
|||||||
|
|
||||||
export const ViewIdentityKubernetesAuthContent = ({
|
export const ViewIdentityKubernetesAuthContent = ({
|
||||||
identityId,
|
identityId,
|
||||||
onEdit,
|
handlePopUpToggle,
|
||||||
onDelete
|
handlePopUpOpen,
|
||||||
|
onDelete,
|
||||||
|
popUp
|
||||||
}: ViewAuthMethodProps) => {
|
}: ViewAuthMethodProps) => {
|
||||||
const { data, isPending } = useGetIdentityKubernetesAuth(identityId);
|
const { data, isPending } = useGetIdentityKubernetesAuth(identityId);
|
||||||
|
|
||||||
@@ -32,8 +35,34 @@ export const ViewIdentityKubernetesAuthContent = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (popUp.identityAuthMethod.isOpen) {
|
||||||
|
return (
|
||||||
|
<IdentityKubernetesAuthForm
|
||||||
|
identityId={identityId}
|
||||||
|
isUpdate
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewIdentityContentWrapper onEdit={onEdit} onDelete={onDelete}>
|
<ViewIdentityContentWrapper
|
||||||
|
onEdit={() => handlePopUpOpen("identityAuthMethod")}
|
||||||
|
onDelete={onDelete}
|
||||||
|
>
|
||||||
|
<IdentityAuthFieldDisplay label="Access Token TLL (seconds)">
|
||||||
|
{data.accessTokenTTL}
|
||||||
|
</IdentityAuthFieldDisplay>
|
||||||
|
<IdentityAuthFieldDisplay label="Access Token Max TLL (seconds)">
|
||||||
|
{data.accessTokenMaxTTL}
|
||||||
|
</IdentityAuthFieldDisplay>
|
||||||
|
<IdentityAuthFieldDisplay label="Access Token Max Number of Uses">
|
||||||
|
{data.accessTokenNumUsesLimit}
|
||||||
|
</IdentityAuthFieldDisplay>
|
||||||
|
<IdentityAuthFieldDisplay label="Access Token Trusted IPs">
|
||||||
|
{data.accessTokenTrustedIps.map((ip) => ip.ipAddress).join(", ")}
|
||||||
|
</IdentityAuthFieldDisplay>
|
||||||
<IdentityAuthFieldDisplay
|
<IdentityAuthFieldDisplay
|
||||||
className="col-span-2"
|
className="col-span-2"
|
||||||
label="Kubernetes Host / Base Kubernetes API URL"
|
label="Kubernetes Host / Base Kubernetes API URL"
|
||||||
@@ -72,30 +101,20 @@ export const ViewIdentityKubernetesAuthContent = ({
|
|||||||
{data.allowedAudience}
|
{data.allowedAudience}
|
||||||
</IdentityAuthFieldDisplay>
|
</IdentityAuthFieldDisplay>
|
||||||
<IdentityAuthFieldDisplay className="col-span-2" label="CA Certificate">
|
<IdentityAuthFieldDisplay className="col-span-2" label="CA Certificate">
|
||||||
<Tooltip
|
{data.caCert && (
|
||||||
side="right"
|
<Tooltip
|
||||||
className="max-w-xl p-2"
|
side="right"
|
||||||
content={<p className="break-words rounded bg-mineshaft-600 p-2">{data.caCert}</p>}
|
className="max-w-xl p-2"
|
||||||
>
|
content={<p className="break-words rounded bg-mineshaft-600 p-2">{data.caCert}</p>}
|
||||||
<div className="w-min">
|
>
|
||||||
<Badge className="flex h-5 w-min items-center gap-1.5 whitespace-nowrap bg-mineshaft-400/50 text-bunker-300">
|
<div className="w-min">
|
||||||
<FontAwesomeIcon icon={faEye} />
|
<Badge className="flex h-5 w-min items-center gap-1.5 whitespace-nowrap bg-mineshaft-400/50 text-bunker-300">
|
||||||
<span>Reveal</span>
|
<FontAwesomeIcon icon={faEye} />
|
||||||
</Badge>
|
<span>Reveal</span>
|
||||||
</div>
|
</Badge>
|
||||||
</Tooltip>
|
</div>
|
||||||
</IdentityAuthFieldDisplay>
|
</Tooltip>
|
||||||
<IdentityAuthFieldDisplay label="Access Token TLL (seconds)">
|
)}
|
||||||
{data.accessTokenTTL}
|
|
||||||
</IdentityAuthFieldDisplay>
|
|
||||||
<IdentityAuthFieldDisplay label="Access Token Max TLL (seconds)">
|
|
||||||
{data.accessTokenMaxTTL}
|
|
||||||
</IdentityAuthFieldDisplay>
|
|
||||||
<IdentityAuthFieldDisplay label="Access Token Max Number of Uses">
|
|
||||||
{data.accessTokenNumUsesLimit}
|
|
||||||
</IdentityAuthFieldDisplay>
|
|
||||||
<IdentityAuthFieldDisplay label="Access Token Trusted IPs">
|
|
||||||
{data.accessTokenTrustedIps.map((ip) => ip.ipAddress).join(", ")}
|
|
||||||
</IdentityAuthFieldDisplay>
|
</IdentityAuthFieldDisplay>
|
||||||
</ViewIdentityContentWrapper>
|
</ViewIdentityContentWrapper>
|
||||||
);
|
);
|
||||||
|
|||||||
+46
-27
@@ -3,6 +3,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|||||||
|
|
||||||
import { Badge, EmptyState, Spinner, Tooltip } from "@app/components/v2";
|
import { Badge, EmptyState, Spinner, Tooltip } from "@app/components/v2";
|
||||||
import { useGetIdentityOidcAuth } from "@app/hooks/api";
|
import { useGetIdentityOidcAuth } from "@app/hooks/api";
|
||||||
|
import { IdentityOidcAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityOidcAuthForm";
|
||||||
import { ViewIdentityContentWrapper } from "@app/pages/organization/IdentityDetailsByIDPage/components/ViewIdentityAuthModal/ViewIdentityContentWrapper";
|
import { ViewIdentityContentWrapper } from "@app/pages/organization/IdentityDetailsByIDPage/components/ViewIdentityAuthModal/ViewIdentityContentWrapper";
|
||||||
|
|
||||||
import { IdentityAuthFieldDisplay } from "./IdentityAuthFieldDisplay";
|
import { IdentityAuthFieldDisplay } from "./IdentityAuthFieldDisplay";
|
||||||
@@ -10,8 +11,10 @@ import { ViewAuthMethodProps } from "./types";
|
|||||||
|
|
||||||
export const ViewIdentityOidcAuthContent = ({
|
export const ViewIdentityOidcAuthContent = ({
|
||||||
identityId,
|
identityId,
|
||||||
onEdit,
|
handlePopUpToggle,
|
||||||
onDelete
|
handlePopUpOpen,
|
||||||
|
onDelete,
|
||||||
|
popUp
|
||||||
}: ViewAuthMethodProps) => {
|
}: ViewAuthMethodProps) => {
|
||||||
const { data, isPending } = useGetIdentityOidcAuth(identityId);
|
const { data, isPending } = useGetIdentityOidcAuth(identityId);
|
||||||
|
|
||||||
@@ -29,8 +32,34 @@ export const ViewIdentityOidcAuthContent = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (popUp.identityAuthMethod.isOpen) {
|
||||||
|
return (
|
||||||
|
<IdentityOidcAuthForm
|
||||||
|
identityId={identityId}
|
||||||
|
isUpdate
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewIdentityContentWrapper onEdit={onEdit} onDelete={onDelete}>
|
<ViewIdentityContentWrapper
|
||||||
|
onEdit={() => handlePopUpOpen("identityAuthMethod")}
|
||||||
|
onDelete={onDelete}
|
||||||
|
>
|
||||||
|
<IdentityAuthFieldDisplay label="Access Token TLL (seconds)">
|
||||||
|
{data.accessTokenTTL}
|
||||||
|
</IdentityAuthFieldDisplay>
|
||||||
|
<IdentityAuthFieldDisplay label="Access Token Max TLL (seconds)">
|
||||||
|
{data.accessTokenMaxTTL}
|
||||||
|
</IdentityAuthFieldDisplay>
|
||||||
|
<IdentityAuthFieldDisplay label="Access Token Max Number of Uses">
|
||||||
|
{data.accessTokenNumUsesLimit}
|
||||||
|
</IdentityAuthFieldDisplay>
|
||||||
|
<IdentityAuthFieldDisplay label="Access Token Trusted IPs">
|
||||||
|
{data.accessTokenTrustedIps.map((ip) => ip.ipAddress).join(", ")}
|
||||||
|
</IdentityAuthFieldDisplay>
|
||||||
<IdentityAuthFieldDisplay className="col-span-2" label="OIDC Discovery URL">
|
<IdentityAuthFieldDisplay className="col-span-2" label="OIDC Discovery URL">
|
||||||
{data.oidcDiscoveryUrl}
|
{data.oidcDiscoveryUrl}
|
||||||
</IdentityAuthFieldDisplay>
|
</IdentityAuthFieldDisplay>
|
||||||
@@ -38,18 +67,20 @@ export const ViewIdentityOidcAuthContent = ({
|
|||||||
{data.boundIssuer}
|
{data.boundIssuer}
|
||||||
</IdentityAuthFieldDisplay>
|
</IdentityAuthFieldDisplay>
|
||||||
<IdentityAuthFieldDisplay className="col-span-2" label="CA Certificate">
|
<IdentityAuthFieldDisplay className="col-span-2" label="CA Certificate">
|
||||||
<Tooltip
|
{data.caCert && (
|
||||||
side="right"
|
<Tooltip
|
||||||
className="max-w-xl p-2"
|
side="right"
|
||||||
content={<p className="break-words rounded bg-mineshaft-600 p-2">{data.caCert}</p>}
|
className="max-w-xl p-2"
|
||||||
>
|
content={<p className="break-words rounded bg-mineshaft-600 p-2">{data.caCert}</p>}
|
||||||
<div className="w-min">
|
>
|
||||||
<Badge className="flex h-5 w-min items-center gap-1.5 whitespace-nowrap bg-mineshaft-400/50 text-bunker-300">
|
<div className="w-min">
|
||||||
<FontAwesomeIcon icon={faEye} />
|
<Badge className="flex h-5 w-min items-center gap-1.5 whitespace-nowrap bg-mineshaft-400/50 text-bunker-300">
|
||||||
<span>Reveal</span>
|
<FontAwesomeIcon icon={faEye} />
|
||||||
</Badge>
|
<span>Reveal</span>
|
||||||
</div>
|
</Badge>
|
||||||
</Tooltip>
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
</IdentityAuthFieldDisplay>
|
</IdentityAuthFieldDisplay>
|
||||||
<IdentityAuthFieldDisplay className="col-span-2" label="Subject">
|
<IdentityAuthFieldDisplay className="col-span-2" label="Subject">
|
||||||
{data.boundSubject}
|
{data.boundSubject}
|
||||||
@@ -80,18 +111,6 @@ export const ViewIdentityOidcAuthContent = ({
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
</IdentityAuthFieldDisplay>
|
</IdentityAuthFieldDisplay>
|
||||||
<IdentityAuthFieldDisplay label="Access Token TLL (seconds)">
|
|
||||||
{data.accessTokenTTL}
|
|
||||||
</IdentityAuthFieldDisplay>
|
|
||||||
<IdentityAuthFieldDisplay label="Access Token Max TLL (seconds)">
|
|
||||||
{data.accessTokenMaxTTL}
|
|
||||||
</IdentityAuthFieldDisplay>
|
|
||||||
<IdentityAuthFieldDisplay label="Access Token Max Number of Uses">
|
|
||||||
{data.accessTokenNumUsesLimit}
|
|
||||||
</IdentityAuthFieldDisplay>
|
|
||||||
<IdentityAuthFieldDisplay label="Access Token Trusted IPs">
|
|
||||||
{data.accessTokenTrustedIps.map((ip) => ip.ipAddress).join(", ")}
|
|
||||||
</IdentityAuthFieldDisplay>
|
|
||||||
</ViewIdentityContentWrapper>
|
</ViewIdentityContentWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+20
-3
@@ -2,6 +2,7 @@ import { faBan } from "@fortawesome/free-solid-svg-icons";
|
|||||||
|
|
||||||
import { EmptyState, Spinner } from "@app/components/v2";
|
import { EmptyState, Spinner } from "@app/components/v2";
|
||||||
import { useGetIdentityTokenAuth, useGetIdentityTokensTokenAuth } from "@app/hooks/api";
|
import { useGetIdentityTokenAuth, useGetIdentityTokensTokenAuth } from "@app/hooks/api";
|
||||||
|
import { IdentityTokenAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityTokenAuthForm";
|
||||||
|
|
||||||
import { IdentityAuthFieldDisplay } from "./IdentityAuthFieldDisplay";
|
import { IdentityAuthFieldDisplay } from "./IdentityAuthFieldDisplay";
|
||||||
import { IdentityTokenAuthTokensTable } from "./IdentityTokenAuthTokensTable";
|
import { IdentityTokenAuthTokensTable } from "./IdentityTokenAuthTokensTable";
|
||||||
@@ -10,8 +11,10 @@ import { ViewIdentityContentWrapper } from "./ViewIdentityContentWrapper";
|
|||||||
|
|
||||||
export const ViewIdentityTokenAuthContent = ({
|
export const ViewIdentityTokenAuthContent = ({
|
||||||
identityId,
|
identityId,
|
||||||
onEdit,
|
handlePopUpToggle,
|
||||||
onDelete
|
handlePopUpOpen,
|
||||||
|
onDelete,
|
||||||
|
popUp
|
||||||
}: ViewAuthMethodProps) => {
|
}: ViewAuthMethodProps) => {
|
||||||
const { data, isPending } = useGetIdentityTokenAuth(identityId);
|
const { data, isPending } = useGetIdentityTokenAuth(identityId);
|
||||||
const { data: tokens = [], isPending: clientSecretsPending } =
|
const { data: tokens = [], isPending: clientSecretsPending } =
|
||||||
@@ -31,8 +34,22 @@ export const ViewIdentityTokenAuthContent = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (popUp.identityAuthMethod.isOpen) {
|
||||||
|
return (
|
||||||
|
<IdentityTokenAuthForm
|
||||||
|
identityId={identityId}
|
||||||
|
isUpdate
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewIdentityContentWrapper onEdit={onEdit} onDelete={onDelete}>
|
<ViewIdentityContentWrapper
|
||||||
|
onEdit={() => handlePopUpOpen("identityAuthMethod")}
|
||||||
|
onDelete={onDelete}
|
||||||
|
>
|
||||||
<IdentityAuthFieldDisplay label="Access Token TLL (seconds)">
|
<IdentityAuthFieldDisplay label="Access Token TLL (seconds)">
|
||||||
{data.accessTokenTTL}
|
{data.accessTokenTTL}
|
||||||
</IdentityAuthFieldDisplay>
|
</IdentityAuthFieldDisplay>
|
||||||
|
|||||||
+22
-5
@@ -7,6 +7,7 @@ import {
|
|||||||
useGetIdentityUniversalAuth,
|
useGetIdentityUniversalAuth,
|
||||||
useGetIdentityUniversalAuthClientSecrets
|
useGetIdentityUniversalAuthClientSecrets
|
||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
|
import { IdentityUniversalAuthForm } from "@app/pages/organization/AccessManagementPage/components/OrgIdentityTab/components/IdentitySection/IdentityUniversalAuthForm";
|
||||||
|
|
||||||
import { IdentityAuthFieldDisplay } from "./IdentityAuthFieldDisplay";
|
import { IdentityAuthFieldDisplay } from "./IdentityAuthFieldDisplay";
|
||||||
import { IdentityUniversalAuthClientSecretsTable } from "./IdentityUniversalAuthClientSecretsTable";
|
import { IdentityUniversalAuthClientSecretsTable } from "./IdentityUniversalAuthClientSecretsTable";
|
||||||
@@ -15,8 +16,10 @@ import { ViewIdentityContentWrapper } from "./ViewIdentityContentWrapper";
|
|||||||
|
|
||||||
export const ViewIdentityUniversalAuthContent = ({
|
export const ViewIdentityUniversalAuthContent = ({
|
||||||
identityId,
|
identityId,
|
||||||
onEdit,
|
handlePopUpToggle,
|
||||||
onDelete
|
handlePopUpOpen,
|
||||||
|
onDelete,
|
||||||
|
popUp
|
||||||
}: ViewAuthMethodProps) => {
|
}: ViewAuthMethodProps) => {
|
||||||
const { data, isPending } = useGetIdentityUniversalAuth(identityId);
|
const { data, isPending } = useGetIdentityUniversalAuth(identityId);
|
||||||
const { data: clientSecrets = [], isPending: clientSecretsPending } =
|
const { data: clientSecrets = [], isPending: clientSecretsPending } =
|
||||||
@@ -43,8 +46,22 @@ export const ViewIdentityUniversalAuthContent = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (popUp.identityAuthMethod.isOpen) {
|
||||||
|
return (
|
||||||
|
<IdentityUniversalAuthForm
|
||||||
|
identityId={identityId}
|
||||||
|
isUpdate
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewIdentityContentWrapper onEdit={onEdit} onDelete={onDelete}>
|
<ViewIdentityContentWrapper
|
||||||
|
onEdit={() => handlePopUpOpen("identityAuthMethod")}
|
||||||
|
onDelete={onDelete}
|
||||||
|
>
|
||||||
<IdentityAuthFieldDisplay label="Access Token TLL (seconds)">
|
<IdentityAuthFieldDisplay label="Access Token TLL (seconds)">
|
||||||
{data.accessTokenTTL}
|
{data.accessTokenTTL}
|
||||||
</IdentityAuthFieldDisplay>
|
</IdentityAuthFieldDisplay>
|
||||||
@@ -61,8 +78,8 @@ export const ViewIdentityUniversalAuthContent = ({
|
|||||||
{data.clientSecretTrustedIps.map((ip) => ip.ipAddress).join(", ")}
|
{data.clientSecretTrustedIps.map((ip) => ip.ipAddress).join(", ")}
|
||||||
</IdentityAuthFieldDisplay>
|
</IdentityAuthFieldDisplay>
|
||||||
<div className="col-span-2 my-3">
|
<div className="col-span-2 my-3">
|
||||||
<div className="mb-2 border-b border-mineshaft-500">
|
<div className="mb-3 border-b border-mineshaft-500 pb-2">
|
||||||
<span className="text-sm text-bunker-300">Client ID</span>
|
<span className="text-bunker-300">Client ID</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="text-sm">{data.clientId}</span>
|
<span className="text-sm">{data.clientId}</span>
|
||||||
|
|||||||
+8
-1
@@ -1,5 +1,12 @@
|
|||||||
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
export type ViewAuthMethodProps = {
|
export type ViewAuthMethodProps = {
|
||||||
identityId: string;
|
identityId: string;
|
||||||
onEdit: () => void;
|
|
||||||
onDelete: () => void;
|
onDelete: () => void;
|
||||||
|
handlePopUpOpen: (popUpName: keyof UsePopUpState<["upgradePlan", "identityAuthMethod"]>) => void;
|
||||||
|
handlePopUpToggle: (
|
||||||
|
popUpName: keyof UsePopUpState<["identityAuthMethod"]>,
|
||||||
|
state?: boolean
|
||||||
|
) => void;
|
||||||
|
popUp: UsePopUpState<["revokeAuthMethod", "upgradePlan", "identityAuthMethod"]>;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user