mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-08 03:27:38 +00:00
requested changes
This commit is contained in:
@@ -367,7 +367,8 @@ export const identityTokenAuthServiceFactory = ({
|
|||||||
|
|
||||||
const tokens = await identityAccessTokenDAL.find(
|
const tokens = await identityAccessTokenDAL.find(
|
||||||
{
|
{
|
||||||
identityId
|
identityId,
|
||||||
|
authMethod: IdentityAuthMethod.TOKEN_AUTH
|
||||||
},
|
},
|
||||||
{ offset, limit, sort: [["updatedAt", "desc"]] }
|
{ offset, limit, sort: [["updatedAt", "desc"]] }
|
||||||
);
|
);
|
||||||
@@ -383,8 +384,12 @@ export const identityTokenAuthServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
}: TUpdateTokenAuthTokenDTO) => {
|
}: TUpdateTokenAuthTokenDTO) => {
|
||||||
const foundToken = await identityAccessTokenDAL.findById(tokenId);
|
const foundToken = await identityAccessTokenDAL.findOne({
|
||||||
|
id: tokenId,
|
||||||
|
authMethod: IdentityAuthMethod.TOKEN_AUTH
|
||||||
|
});
|
||||||
if (!foundToken) throw new NotFoundError({ message: `Token with ID ${tokenId} not found` });
|
if (!foundToken) throw new NotFoundError({ message: `Token with ID ${tokenId} not found` });
|
||||||
|
|
||||||
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId: foundToken.identityId });
|
const identityMembershipOrg = await identityOrgMembershipDAL.findOne({ identityId: foundToken.identityId });
|
||||||
if (!identityMembershipOrg) {
|
if (!identityMembershipOrg) {
|
||||||
throw new NotFoundError({ message: `Failed to find identity with ID ${foundToken.identityId}` });
|
throw new NotFoundError({ message: `Failed to find identity with ID ${foundToken.identityId}` });
|
||||||
@@ -418,6 +423,7 @@ export const identityTokenAuthServiceFactory = ({
|
|||||||
|
|
||||||
const [token] = await identityAccessTokenDAL.update(
|
const [token] = await identityAccessTokenDAL.update(
|
||||||
{
|
{
|
||||||
|
authMethod: IdentityAuthMethod.TOKEN_AUTH,
|
||||||
identityId: foundToken.identityId,
|
identityId: foundToken.identityId,
|
||||||
id: tokenId
|
id: tokenId
|
||||||
},
|
},
|
||||||
@@ -438,7 +444,8 @@ export const identityTokenAuthServiceFactory = ({
|
|||||||
}: TRevokeTokenAuthTokenDTO) => {
|
}: TRevokeTokenAuthTokenDTO) => {
|
||||||
const identityAccessToken = await identityAccessTokenDAL.findOne({
|
const identityAccessToken = await identityAccessTokenDAL.findOne({
|
||||||
[`${TableName.IdentityAccessToken}.id` as "id"]: tokenId,
|
[`${TableName.IdentityAccessToken}.id` as "id"]: tokenId,
|
||||||
isAccessTokenRevoked: false
|
isAccessTokenRevoked: false,
|
||||||
|
authMethod: IdentityAuthMethod.TOKEN_AUTH
|
||||||
});
|
});
|
||||||
if (!identityAccessToken)
|
if (!identityAccessToken)
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
@@ -462,9 +469,15 @@ export const identityTokenAuthServiceFactory = ({
|
|||||||
);
|
);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Identity);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Identity);
|
||||||
|
|
||||||
const revokedToken = await identityAccessTokenDAL.updateById(identityAccessToken.id, {
|
const [revokedToken] = await identityAccessTokenDAL.update(
|
||||||
isAccessTokenRevoked: true
|
{
|
||||||
});
|
id: identityAccessToken.id,
|
||||||
|
authMethod: IdentityAuthMethod.TOKEN_AUTH
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isAccessTokenRevoked: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return { revokedToken };
|
return { revokedToken };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,12 +18,12 @@ export const buildAuthMethods = ({
|
|||||||
tokenId?: string;
|
tokenId?: string;
|
||||||
}) => {
|
}) => {
|
||||||
return [
|
return [
|
||||||
...(uaId ? [IdentityAuthMethod.UNIVERSAL_AUTH] : []),
|
...[uaId ? IdentityAuthMethod.UNIVERSAL_AUTH : null],
|
||||||
...(gcpId ? [IdentityAuthMethod.GCP_AUTH] : []),
|
...[gcpId ? IdentityAuthMethod.GCP_AUTH : null],
|
||||||
...(awsId ? [IdentityAuthMethod.AWS_AUTH] : []),
|
...[awsId ? IdentityAuthMethod.AWS_AUTH : null],
|
||||||
...(kubernetesId ? [IdentityAuthMethod.KUBERNETES_AUTH] : []),
|
...[kubernetesId ? IdentityAuthMethod.KUBERNETES_AUTH : null],
|
||||||
...(oidcId ? [IdentityAuthMethod.OIDC_AUTH] : []),
|
...[oidcId ? IdentityAuthMethod.OIDC_AUTH : null],
|
||||||
...(azureId ? [IdentityAuthMethod.AZURE_AUTH] : []),
|
...[azureId ? IdentityAuthMethod.AZURE_AUTH : null],
|
||||||
...(tokenId ? [IdentityAuthMethod.TOKEN_AUTH] : [])
|
...[tokenId ? IdentityAuthMethod.TOKEN_AUTH : null]
|
||||||
].filter((authMethod) => authMethod);
|
].filter((authMethod) => authMethod) as IdentityAuthMethod[];
|
||||||
};
|
};
|
||||||
|
|||||||
+17
-285
@@ -1,40 +1,10 @@
|
|||||||
import { useEffect } from "react";
|
import { useState } from "react";
|
||||||
import { Controller, useForm } from "react-hook-form";
|
|
||||||
import { yupResolver } from "@hookform/resolvers/yup";
|
|
||||||
import * as yup from "yup";
|
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { Modal, ModalContent } from "@app/components/v2";
|
||||||
import {
|
|
||||||
Badge,
|
|
||||||
DeleteActionModal,
|
|
||||||
FormControl,
|
|
||||||
Modal,
|
|
||||||
ModalContent,
|
|
||||||
Select,
|
|
||||||
SelectItem,
|
|
||||||
Tooltip,
|
|
||||||
UpgradePlanModal
|
|
||||||
} from "@app/components/v2";
|
|
||||||
import { useOrganization } from "@app/context";
|
|
||||||
import {
|
|
||||||
useDeleteIdentityAwsAuth,
|
|
||||||
useDeleteIdentityAzureAuth,
|
|
||||||
useDeleteIdentityGcpAuth,
|
|
||||||
useDeleteIdentityKubernetesAuth,
|
|
||||||
useDeleteIdentityOidcAuth,
|
|
||||||
useDeleteIdentityTokenAuth,
|
|
||||||
useDeleteIdentityUniversalAuth
|
|
||||||
} from "@app/hooks/api";
|
|
||||||
import { IdentityAuthMethod, identityAuthToNameMap } from "@app/hooks/api/identities";
|
import { IdentityAuthMethod, identityAuthToNameMap } from "@app/hooks/api/identities";
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
import { IdentityAwsAuthForm } from "./IdentityAwsAuthForm";
|
import { IdentityAuthMethodModalContent } from "./IdentityAuthMethodModalContent";
|
||||||
import { IdentityAzureAuthForm } from "./IdentityAzureAuthForm";
|
|
||||||
import { IdentityGcpAuthForm } from "./IdentityGcpAuthForm";
|
|
||||||
import { IdentityKubernetesAuthForm } from "./IdentityKubernetesAuthForm";
|
|
||||||
import { IdentityOidcAuthForm } from "./IdentityOidcAuthForm";
|
|
||||||
import { IdentityTokenAuthForm } from "./IdentityTokenAuthForm";
|
|
||||||
import { IdentityUniversalAuthForm } from "./IdentityUniversalAuthForm";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
popUp: UsePopUpState<["identityAuthMethod", "upgradePlan", "revokeAuthMethod"]>;
|
popUp: UsePopUpState<["identityAuthMethod", "upgradePlan", "revokeAuthMethod"]>;
|
||||||
@@ -45,175 +15,13 @@ type Props = {
|
|||||||
) => void;
|
) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type TRevokeOptions = {
|
|
||||||
identityId: string;
|
|
||||||
organizationId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type TRevokeMethods = {
|
|
||||||
revokeMethod: (revokeOptions: TRevokeOptions) => Promise<any>;
|
|
||||||
render: () => JSX.Element;
|
|
||||||
};
|
|
||||||
|
|
||||||
const identityAuthMethods = [
|
|
||||||
{ label: "Token Auth", value: IdentityAuthMethod.TOKEN_AUTH },
|
|
||||||
{ label: "Universal Auth", value: IdentityAuthMethod.UNIVERSAL_AUTH },
|
|
||||||
{ label: "Kubernetes Auth", value: IdentityAuthMethod.KUBERNETES_AUTH },
|
|
||||||
{ label: "GCP Auth", value: IdentityAuthMethod.GCP_AUTH },
|
|
||||||
{ label: "AWS Auth", value: IdentityAuthMethod.AWS_AUTH },
|
|
||||||
{ label: "Azure Auth", value: IdentityAuthMethod.AZURE_AUTH },
|
|
||||||
{ label: "OIDC Auth", value: IdentityAuthMethod.OIDC_AUTH }
|
|
||||||
];
|
|
||||||
|
|
||||||
const schema = yup
|
|
||||||
.object({
|
|
||||||
authMethod: yup
|
|
||||||
.mixed<IdentityAuthMethod>()
|
|
||||||
.oneOf(Object.values(IdentityAuthMethod))
|
|
||||||
.required("Auth method is required")
|
|
||||||
})
|
|
||||||
.required();
|
|
||||||
|
|
||||||
export type FormData = yup.InferType<typeof schema>;
|
|
||||||
|
|
||||||
export const IdentityAuthMethodModal = ({ popUp, handlePopUpOpen, handlePopUpToggle }: Props) => {
|
export const IdentityAuthMethodModal = ({ popUp, handlePopUpOpen, handlePopUpToggle }: Props) => {
|
||||||
const { currentOrg } = useOrganization();
|
const [selectedAuthMethod, setSelectedAuthMethod] = useState<IdentityAuthMethod | null>(null);
|
||||||
const orgId = currentOrg?.id || "";
|
|
||||||
|
|
||||||
const { mutateAsync: revokeUniversalAuth } = useDeleteIdentityUniversalAuth();
|
|
||||||
const { mutateAsync: revokeTokenAuth } = useDeleteIdentityTokenAuth();
|
|
||||||
const { mutateAsync: revokeKubernetesAuth } = useDeleteIdentityKubernetesAuth();
|
|
||||||
const { mutateAsync: revokeGcpAuth } = useDeleteIdentityGcpAuth();
|
|
||||||
const { mutateAsync: revokeAwsAuth } = useDeleteIdentityAwsAuth();
|
|
||||||
const { mutateAsync: revokeAzureAuth } = useDeleteIdentityAzureAuth();
|
|
||||||
const { mutateAsync: revokeOidcAuth } = useDeleteIdentityOidcAuth();
|
|
||||||
|
|
||||||
const initialAuthMethod = popUp?.identityAuthMethod?.data?.authMethod;
|
const initialAuthMethod = popUp?.identityAuthMethod?.data?.authMethod;
|
||||||
|
|
||||||
const { control, watch, setValue, reset } = useForm<FormData>({
|
|
||||||
resolver: yupResolver(schema),
|
|
||||||
defaultValues: {
|
|
||||||
authMethod: initialAuthMethod
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (popUp.identityAuthMethod.isOpen) {
|
|
||||||
reset({ authMethod: popUp?.identityAuthMethod?.data?.authMethod });
|
|
||||||
}
|
|
||||||
}, [popUp.identityAuthMethod.isOpen]);
|
|
||||||
|
|
||||||
const watchedAuthMethod = watch("authMethod");
|
|
||||||
|
|
||||||
const identityAuthMethodData = {
|
|
||||||
identityId: popUp?.identityAuthMethod.data?.identityId,
|
|
||||||
name: popUp?.identityAuthMethod?.data?.name,
|
|
||||||
authMethod: watch("authMethod"),
|
|
||||||
configuredAuthMethods: popUp?.identityAuthMethod?.data?.allAuthMethods
|
|
||||||
} as {
|
|
||||||
identityId: string;
|
|
||||||
name: string;
|
|
||||||
authMethod?: IdentityAuthMethod;
|
|
||||||
configuredAuthMethods?: IdentityAuthMethod[];
|
|
||||||
};
|
|
||||||
|
|
||||||
const isSelectedAuthAlreadyConfigured =
|
const isSelectedAuthAlreadyConfigured =
|
||||||
identityAuthMethodData?.configuredAuthMethods?.includes(watchedAuthMethod);
|
popUp?.identityAuthMethod?.data?.allAuthMethods?.includes(selectedAuthMethod);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (popUp?.identityAuthMethod?.data?.authMethod) {
|
|
||||||
setValue("authMethod", popUp?.identityAuthMethod?.data?.authMethod);
|
|
||||||
} else {
|
|
||||||
const firstAuthMethodNotConfiguredAuthMethod = identityAuthMethods.find(
|
|
||||||
({ value }) => !identityAuthMethodData?.configuredAuthMethods?.includes(value)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (firstAuthMethodNotConfiguredAuthMethod) {
|
|
||||||
setValue("authMethod", firstAuthMethodNotConfiguredAuthMethod.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [popUp.identityAuthMethod.isOpen]);
|
|
||||||
|
|
||||||
const methodMap: Record<IdentityAuthMethod, TRevokeMethods | undefined> = {
|
|
||||||
[IdentityAuthMethod.UNIVERSAL_AUTH]: {
|
|
||||||
revokeMethod: revokeUniversalAuth,
|
|
||||||
render: () => (
|
|
||||||
<IdentityUniversalAuthForm
|
|
||||||
identityAuthMethodData={identityAuthMethodData}
|
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
|
||||||
handlePopUpToggle={handlePopUpToggle}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
[IdentityAuthMethod.OIDC_AUTH]: {
|
|
||||||
revokeMethod: revokeOidcAuth,
|
|
||||||
render: () => (
|
|
||||||
<IdentityOidcAuthForm
|
|
||||||
identityAuthMethodData={identityAuthMethodData}
|
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
|
||||||
handlePopUpToggle={handlePopUpToggle}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
[IdentityAuthMethod.TOKEN_AUTH]: {
|
|
||||||
revokeMethod: revokeTokenAuth,
|
|
||||||
render: () => (
|
|
||||||
<IdentityTokenAuthForm
|
|
||||||
identityAuthMethodData={identityAuthMethodData}
|
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
|
||||||
handlePopUpToggle={handlePopUpToggle}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
[IdentityAuthMethod.AZURE_AUTH]: {
|
|
||||||
revokeMethod: revokeAzureAuth,
|
|
||||||
render: () => (
|
|
||||||
<IdentityAzureAuthForm
|
|
||||||
identityAuthMethodData={identityAuthMethodData}
|
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
|
||||||
handlePopUpToggle={handlePopUpToggle}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
[IdentityAuthMethod.GCP_AUTH]: {
|
|
||||||
revokeMethod: revokeGcpAuth,
|
|
||||||
render: () => (
|
|
||||||
<IdentityGcpAuthForm
|
|
||||||
identityAuthMethodData={identityAuthMethodData}
|
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
|
||||||
handlePopUpToggle={handlePopUpToggle}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
[IdentityAuthMethod.KUBERNETES_AUTH]: {
|
|
||||||
revokeMethod: revokeKubernetesAuth,
|
|
||||||
render: () => (
|
|
||||||
<IdentityKubernetesAuthForm
|
|
||||||
identityAuthMethodData={identityAuthMethodData}
|
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
|
||||||
handlePopUpToggle={handlePopUpToggle}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|
|
||||||
[IdentityAuthMethod.AWS_AUTH]: {
|
|
||||||
revokeMethod: revokeAwsAuth,
|
|
||||||
render: () => (
|
|
||||||
<IdentityAwsAuthForm
|
|
||||||
identityAuthMethodData={identityAuthMethodData}
|
|
||||||
handlePopUpOpen={handlePopUpOpen}
|
|
||||||
handlePopUpToggle={handlePopUpToggle}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const selectedMethodItem = methodMap[identityAuthMethodData.authMethod!];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
@@ -225,97 +33,21 @@ export const IdentityAuthMethodModal = ({ popUp, handlePopUpOpen, handlePopUpTog
|
|||||||
<ModalContent
|
<ModalContent
|
||||||
title={
|
title={
|
||||||
isSelectedAuthAlreadyConfigured
|
isSelectedAuthAlreadyConfigured
|
||||||
? `Edit ${identityAuthToNameMap[identityAuthMethodData.authMethod!] ?? ""}`
|
? `Edit ${identityAuthToNameMap[selectedAuthMethod!] ?? ""}`
|
||||||
: `Create new ${identityAuthToNameMap[identityAuthMethodData.authMethod!] ?? ""}`
|
: `Create new ${identityAuthToNameMap[selectedAuthMethod!] ?? ""}`
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Controller
|
<IdentityAuthMethodModalContent
|
||||||
control={control}
|
popUp={popUp}
|
||||||
name="authMethod"
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
defaultValue={IdentityAuthMethod.UNIVERSAL_AUTH}
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
identity={{
|
||||||
<FormControl label="Auth Method" errorText={error?.message} isError={Boolean(error)}>
|
name: popUp?.identityAuthMethod?.data?.name,
|
||||||
<Select
|
authMethods: popUp?.identityAuthMethod?.data?.allAuthMethods,
|
||||||
isDisabled={isSelectedAuthAlreadyConfigured}
|
id: popUp?.identityAuthMethod.data?.identityId
|
||||||
defaultValue={field.value}
|
|
||||||
{...field}
|
|
||||||
onValueChange={(e) => {
|
|
||||||
const alreadyConfigured =
|
|
||||||
popUp?.identityAuthMethod?.data?.allAuthMethods?.includes(e);
|
|
||||||
|
|
||||||
if (!alreadyConfigured) {
|
|
||||||
onChange(e);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
className="w-full"
|
|
||||||
>
|
|
||||||
{identityAuthMethods.map(({ label, value }) => {
|
|
||||||
const alreadyConfigured =
|
|
||||||
popUp?.identityAuthMethod?.data?.allAuthMethods?.includes(value);
|
|
||||||
return (
|
|
||||||
<Tooltip
|
|
||||||
key={`auth-method-${value}`}
|
|
||||||
content="Authentication method already configured"
|
|
||||||
isDisabled={!alreadyConfigured}
|
|
||||||
>
|
|
||||||
<SelectItem
|
|
||||||
isDisabled={alreadyConfigured}
|
|
||||||
value={String(value || "")}
|
|
||||||
key={label}
|
|
||||||
>
|
|
||||||
{label}{" "}
|
|
||||||
{alreadyConfigured && !isSelectedAuthAlreadyConfigured && (
|
|
||||||
<Badge>Configured</Badge>
|
|
||||||
)}
|
|
||||||
</SelectItem>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{selectedMethodItem?.render ? selectedMethodItem.render() : <div />}
|
|
||||||
<UpgradePlanModal
|
|
||||||
isOpen={popUp?.upgradePlan?.isOpen}
|
|
||||||
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
|
||||||
text="You can use IP allowlisting if you switch to Infisical's Pro plan."
|
|
||||||
/>
|
|
||||||
<DeleteActionModal
|
|
||||||
isOpen={popUp?.revokeAuthMethod?.isOpen}
|
|
||||||
title={`Are you sure want to remove ${
|
|
||||||
identityAuthMethodData?.authMethod
|
|
||||||
? identityAuthToNameMap[identityAuthMethodData.authMethod]
|
|
||||||
: "the auth method"
|
|
||||||
} on ${identityAuthMethodData?.name ?? ""}?`}
|
|
||||||
onChange={(isOpen) => handlePopUpToggle("revokeAuthMethod", isOpen)}
|
|
||||||
deleteKey="confirm"
|
|
||||||
buttonText="Remove"
|
|
||||||
onDeleteApproved={async () => {
|
|
||||||
if (!identityAuthMethodData.authMethod || !orgId || !selectedMethodItem) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await selectedMethodItem.revokeMethod({
|
|
||||||
identityId: identityAuthMethodData.identityId,
|
|
||||||
organizationId: orgId
|
|
||||||
});
|
|
||||||
|
|
||||||
createNotification({
|
|
||||||
text: "Successfully removed auth method",
|
|
||||||
type: "success"
|
|
||||||
});
|
|
||||||
|
|
||||||
handlePopUpToggle("revokeAuthMethod", false);
|
|
||||||
handlePopUpToggle("identityAuthMethod", false);
|
|
||||||
} catch (err) {
|
|
||||||
createNotification({
|
|
||||||
text: "Failed to remove auth method",
|
|
||||||
type: "error"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
|
initialAuthMethod={initialAuthMethod}
|
||||||
|
setSelectedAuthMethod={setSelectedAuthMethod}
|
||||||
/>
|
/>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
+317
@@ -0,0 +1,317 @@
|
|||||||
|
import { useCallback } from "react";
|
||||||
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
import { yupResolver } from "@hookform/resolvers/yup";
|
||||||
|
import * as yup from "yup";
|
||||||
|
|
||||||
|
import { createNotification } from "@app/components/notifications";
|
||||||
|
import {
|
||||||
|
Badge,
|
||||||
|
DeleteActionModal,
|
||||||
|
FormControl,
|
||||||
|
Select,
|
||||||
|
SelectItem,
|
||||||
|
Tooltip,
|
||||||
|
UpgradePlanModal
|
||||||
|
} from "@app/components/v2";
|
||||||
|
import { useOrganization } from "@app/context";
|
||||||
|
import {
|
||||||
|
useDeleteIdentityAwsAuth,
|
||||||
|
useDeleteIdentityAzureAuth,
|
||||||
|
useDeleteIdentityGcpAuth,
|
||||||
|
useDeleteIdentityKubernetesAuth,
|
||||||
|
useDeleteIdentityOidcAuth,
|
||||||
|
useDeleteIdentityTokenAuth,
|
||||||
|
useDeleteIdentityUniversalAuth
|
||||||
|
} from "@app/hooks/api";
|
||||||
|
import { IdentityAuthMethod, identityAuthToNameMap } from "@app/hooks/api/identities";
|
||||||
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
|
import { IdentityAwsAuthForm } from "./IdentityAwsAuthForm";
|
||||||
|
import { IdentityAzureAuthForm } from "./IdentityAzureAuthForm";
|
||||||
|
import { IdentityGcpAuthForm } from "./IdentityGcpAuthForm";
|
||||||
|
import { IdentityKubernetesAuthForm } from "./IdentityKubernetesAuthForm";
|
||||||
|
import { IdentityOidcAuthForm } from "./IdentityOidcAuthForm";
|
||||||
|
import { IdentityTokenAuthForm } from "./IdentityTokenAuthForm";
|
||||||
|
import { IdentityUniversalAuthForm } from "./IdentityUniversalAuthForm";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
popUp: UsePopUpState<["identityAuthMethod", "upgradePlan", "revokeAuthMethod"]>;
|
||||||
|
handlePopUpOpen: (popUpName: keyof UsePopUpState<["upgradePlan"]>) => void;
|
||||||
|
handlePopUpToggle: (
|
||||||
|
popUpName: keyof UsePopUpState<["identityAuthMethod", "upgradePlan", "revokeAuthMethod"]>,
|
||||||
|
state?: boolean
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
identity: {
|
||||||
|
name: string;
|
||||||
|
id: string;
|
||||||
|
authMethods: IdentityAuthMethod[];
|
||||||
|
};
|
||||||
|
initialAuthMethod: IdentityAuthMethod;
|
||||||
|
setSelectedAuthMethod: (authMethod: IdentityAuthMethod) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type TRevokeOptions = {
|
||||||
|
identityId: string;
|
||||||
|
organizationId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type TRevokeMethods = {
|
||||||
|
revokeMethod: (revokeOptions: TRevokeOptions) => Promise<any>;
|
||||||
|
render: () => JSX.Element;
|
||||||
|
};
|
||||||
|
|
||||||
|
const identityAuthMethods = [
|
||||||
|
{ label: "Token Auth", value: IdentityAuthMethod.TOKEN_AUTH },
|
||||||
|
{ label: "Universal Auth", value: IdentityAuthMethod.UNIVERSAL_AUTH },
|
||||||
|
{ label: "Kubernetes Auth", value: IdentityAuthMethod.KUBERNETES_AUTH },
|
||||||
|
{ label: "GCP Auth", value: IdentityAuthMethod.GCP_AUTH },
|
||||||
|
{ label: "AWS Auth", value: IdentityAuthMethod.AWS_AUTH },
|
||||||
|
{ label: "Azure Auth", value: IdentityAuthMethod.AZURE_AUTH },
|
||||||
|
{ label: "OIDC Auth", value: IdentityAuthMethod.OIDC_AUTH }
|
||||||
|
];
|
||||||
|
|
||||||
|
const schema = yup
|
||||||
|
.object({
|
||||||
|
authMethod: yup
|
||||||
|
.mixed<IdentityAuthMethod>()
|
||||||
|
.oneOf(Object.values(IdentityAuthMethod))
|
||||||
|
.required("Auth method is required")
|
||||||
|
})
|
||||||
|
.required();
|
||||||
|
|
||||||
|
export type FormData = yup.InferType<typeof schema>;
|
||||||
|
|
||||||
|
export const IdentityAuthMethodModalContent = ({
|
||||||
|
popUp,
|
||||||
|
handlePopUpOpen,
|
||||||
|
handlePopUpToggle,
|
||||||
|
identity,
|
||||||
|
initialAuthMethod,
|
||||||
|
setSelectedAuthMethod
|
||||||
|
}: Props) => {
|
||||||
|
const { currentOrg } = useOrganization();
|
||||||
|
const orgId = currentOrg?.id || "";
|
||||||
|
|
||||||
|
const { mutateAsync: revokeUniversalAuth } = useDeleteIdentityUniversalAuth();
|
||||||
|
const { mutateAsync: revokeTokenAuth } = useDeleteIdentityTokenAuth();
|
||||||
|
const { mutateAsync: revokeKubernetesAuth } = useDeleteIdentityKubernetesAuth();
|
||||||
|
const { mutateAsync: revokeGcpAuth } = useDeleteIdentityGcpAuth();
|
||||||
|
const { mutateAsync: revokeAwsAuth } = useDeleteIdentityAwsAuth();
|
||||||
|
const { mutateAsync: revokeAzureAuth } = useDeleteIdentityAzureAuth();
|
||||||
|
const { mutateAsync: revokeOidcAuth } = useDeleteIdentityOidcAuth();
|
||||||
|
|
||||||
|
const { control, watch } = useForm<FormData>({
|
||||||
|
resolver: yupResolver(schema),
|
||||||
|
defaultValues: async () => {
|
||||||
|
let authMethod = initialAuthMethod;
|
||||||
|
|
||||||
|
if (!authMethod) {
|
||||||
|
const firstAuthMethodNotConfiguredAuthMethod = identityAuthMethods.find(
|
||||||
|
({ value }) => !identity?.authMethods?.includes(value)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (firstAuthMethodNotConfiguredAuthMethod) {
|
||||||
|
authMethod = firstAuthMethodNotConfiguredAuthMethod.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelectedAuthMethod(authMethod);
|
||||||
|
return {
|
||||||
|
authMethod
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const watchedAuthMethod = watch("authMethod");
|
||||||
|
|
||||||
|
const identityAuthMethodData = {
|
||||||
|
identityId: identity.id,
|
||||||
|
name: identity.name,
|
||||||
|
authMethod: watch("authMethod"),
|
||||||
|
configuredAuthMethods: identity.authMethods
|
||||||
|
} as {
|
||||||
|
identityId: string;
|
||||||
|
name: string;
|
||||||
|
authMethod?: IdentityAuthMethod;
|
||||||
|
configuredAuthMethods?: IdentityAuthMethod[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const isSelectedAuthAlreadyConfigured =
|
||||||
|
identityAuthMethodData?.configuredAuthMethods?.includes(watchedAuthMethod);
|
||||||
|
|
||||||
|
const methodMap: Record<IdentityAuthMethod, TRevokeMethods | undefined> = {
|
||||||
|
[IdentityAuthMethod.UNIVERSAL_AUTH]: {
|
||||||
|
revokeMethod: revokeUniversalAuth,
|
||||||
|
render: () => (
|
||||||
|
<IdentityUniversalAuthForm
|
||||||
|
identityAuthMethodData={identityAuthMethodData}
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
[IdentityAuthMethod.OIDC_AUTH]: {
|
||||||
|
revokeMethod: revokeOidcAuth,
|
||||||
|
render: () => (
|
||||||
|
<IdentityOidcAuthForm
|
||||||
|
identityAuthMethodData={identityAuthMethodData}
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
[IdentityAuthMethod.TOKEN_AUTH]: {
|
||||||
|
revokeMethod: revokeTokenAuth,
|
||||||
|
render: () => (
|
||||||
|
<IdentityTokenAuthForm
|
||||||
|
identityAuthMethodData={identityAuthMethodData}
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
[IdentityAuthMethod.AZURE_AUTH]: {
|
||||||
|
revokeMethod: revokeAzureAuth,
|
||||||
|
render: () => (
|
||||||
|
<IdentityAzureAuthForm
|
||||||
|
identityAuthMethodData={identityAuthMethodData}
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
[IdentityAuthMethod.GCP_AUTH]: {
|
||||||
|
revokeMethod: revokeGcpAuth,
|
||||||
|
render: () => (
|
||||||
|
<IdentityGcpAuthForm
|
||||||
|
identityAuthMethodData={identityAuthMethodData}
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
[IdentityAuthMethod.KUBERNETES_AUTH]: {
|
||||||
|
revokeMethod: revokeKubernetesAuth,
|
||||||
|
render: () => (
|
||||||
|
<IdentityKubernetesAuthForm
|
||||||
|
identityAuthMethodData={identityAuthMethodData}
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
[IdentityAuthMethod.AWS_AUTH]: {
|
||||||
|
revokeMethod: revokeAwsAuth,
|
||||||
|
render: () => (
|
||||||
|
<IdentityAwsAuthForm
|
||||||
|
identityAuthMethodData={identityAuthMethodData}
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const isAlreadyConfigured = useCallback((method: IdentityAuthMethod) => {
|
||||||
|
return identityAuthMethodData?.configuredAuthMethods?.includes(method);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const selectedMethodItem = methodMap[identityAuthMethodData.authMethod!];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="authMethod"
|
||||||
|
defaultValue={IdentityAuthMethod.UNIVERSAL_AUTH}
|
||||||
|
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
||||||
|
<FormControl label="Auth Method" errorText={error?.message} isError={Boolean(error)}>
|
||||||
|
<Select
|
||||||
|
isDisabled={isSelectedAuthAlreadyConfigured}
|
||||||
|
defaultValue={field.value}
|
||||||
|
{...field}
|
||||||
|
onValueChange={(e) => {
|
||||||
|
if (!isAlreadyConfigured(e as IdentityAuthMethod)) {
|
||||||
|
setSelectedAuthMethod(e as IdentityAuthMethod);
|
||||||
|
onChange(e);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
{identityAuthMethods.map(({ label, value }) => {
|
||||||
|
const alreadyConfigured = isAlreadyConfigured(value);
|
||||||
|
return (
|
||||||
|
<Tooltip
|
||||||
|
key={`auth-method-${value}`}
|
||||||
|
content="Authentication method already configured"
|
||||||
|
isDisabled={!alreadyConfigured}
|
||||||
|
>
|
||||||
|
<SelectItem
|
||||||
|
isDisabled={alreadyConfigured}
|
||||||
|
value={String(value || "")}
|
||||||
|
key={label}
|
||||||
|
>
|
||||||
|
{label}{" "}
|
||||||
|
{alreadyConfigured && !isSelectedAuthAlreadyConfigured && (
|
||||||
|
<Badge>Configured</Badge>
|
||||||
|
)}
|
||||||
|
</SelectItem>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{selectedMethodItem?.render ? selectedMethodItem.render() : <div />}
|
||||||
|
<UpgradePlanModal
|
||||||
|
isOpen={popUp?.upgradePlan?.isOpen}
|
||||||
|
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
||||||
|
text="You can use IP allowlisting if you switch to Infisical's Pro plan."
|
||||||
|
/>
|
||||||
|
<DeleteActionModal
|
||||||
|
isOpen={popUp?.revokeAuthMethod?.isOpen}
|
||||||
|
title={`Are you sure want to remove ${
|
||||||
|
identityAuthMethodData?.authMethod
|
||||||
|
? identityAuthToNameMap[identityAuthMethodData.authMethod]
|
||||||
|
: "the auth method"
|
||||||
|
} on ${identityAuthMethodData?.name ?? ""}?`}
|
||||||
|
onChange={(isOpen) => handlePopUpToggle("revokeAuthMethod", isOpen)}
|
||||||
|
deleteKey="confirm"
|
||||||
|
buttonText="Remove"
|
||||||
|
onDeleteApproved={async () => {
|
||||||
|
if (!identityAuthMethodData.authMethod || !orgId || !selectedMethodItem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await selectedMethodItem.revokeMethod({
|
||||||
|
identityId: identityAuthMethodData.identityId,
|
||||||
|
organizationId: orgId
|
||||||
|
});
|
||||||
|
|
||||||
|
createNotification({
|
||||||
|
text: "Successfully removed auth method",
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
|
||||||
|
handlePopUpToggle("revokeAuthMethod", false);
|
||||||
|
handlePopUpToggle("identityAuthMethod", false);
|
||||||
|
} catch (err) {
|
||||||
|
createNotification({
|
||||||
|
text: "Failed to remove auth method",
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user