This commit is contained in:
Scott Wilson
2025-04-11 12:25:26 -07:00
parent 27fdf68e42
commit 581e4b35f9
120 changed files with 1963 additions and 423 deletions
@@ -1,4 +1,6 @@
import { useState } from "react";
import { faArrowUpRightFromSquare, faBookOpen } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { SecretRotationV2Form } from "@app/components/secret-rotations-v2/forms";
import { SecretRotationV2ModalHeader } from "@app/components/secret-rotations-v2/SecretRotationV2ModalHeader";
@@ -54,7 +56,24 @@ export const CreateSecretRotationV2Modal = ({ onOpenChange, isOpen, ...props }:
selectedRotation ? (
<SecretRotationV2ModalHeader isConfigured={false} type={selectedRotation} />
) : (
"Add Secret Rotation"
<div className="flex items-center text-mineshaft-300">
Add Secret Rotation
<a
target="_blank"
href="https://infisical.com/docs/documentation/platform/secret-rotation/overview"
className="mb-1 ml-1"
rel="noopener noreferrer"
>
<div className="inline-block rounded-md bg-yellow/20 px-1.5 text-sm text-yellow opacity-80 hover:opacity-100">
<FontAwesomeIcon icon={faBookOpen} className="mb-[0.03rem] mr-1 text-[12px]" />
<span>Docs</span>
<FontAwesomeIcon
icon={faArrowUpRightFromSquare}
className="mb-[0.07rem] ml-1 text-[10px]"
/>
</div>
</a>
</div>
)
}
onPointerDownOutside={(e) => e.preventDefault()}
@@ -24,7 +24,7 @@ export const SecretRotationV2ModalHeader = ({ type, isConfigured }: Props) => {
{destinationDetails.name} Rotation
<a
target="_blank"
href={`https://infisical.com/docs/platform/secret-rotations/${type}`}
href={`https://infisical.com/docs/documentation/platform/secret-rotation/${type}`}
className="mb-1 ml-1"
rel="noopener noreferrer"
>
@@ -24,17 +24,7 @@ export const SecretRotationV2Select = ({ onSelect }: Props) => {
return (
<div className="grid grid-cols-3 gap-2">
{secretRotationOptions?.map(({ type }) => {
const { image, name } = SECRET_ROTATION_MAP[type];
let size: number;
switch (type) {
case SecretRotation.MsSqlCredentials:
size = 50;
break;
default:
size = 45;
}
const { image, name, size } = SECRET_ROTATION_MAP[type];
return (
<button
@@ -0,0 +1,56 @@
import { CredentialDisplay } from "@app/components/secret-rotations-v2/ViewSecretRotationV2GeneratedCredentials/shared/CredentialDisplay";
import { NoticeBannerV2 } from "@app/components/v2/NoticeBannerV2/NoticeBannerV2";
import { TAuth0ClientSecretRotationGeneratedCredentialsResponse } from "@app/hooks/api/secretRotationsV2/types/auth0-client-secret-rotation";
import { ViewRotationGeneratedCredentialsDisplay } from "./shared";
type Props = {
generatedCredentialsResponse: TAuth0ClientSecretRotationGeneratedCredentialsResponse;
};
export const ViewAuth0ClientSecretRotationGeneratedCredentials = ({
generatedCredentialsResponse: { generatedCredentials, activeIndex }
}: Props) => {
const inactiveIndex = activeIndex === 0 ? 1 : 0;
const activeCredentials = generatedCredentials[activeIndex];
const inactiveCredentials = generatedCredentials[inactiveIndex];
return (
<>
<ViewRotationGeneratedCredentialsDisplay
activeCredentials={
<>
<CredentialDisplay label="Client ID">{activeCredentials?.clientId}</CredentialDisplay>
<CredentialDisplay isSensitive label="Client Secret">
{activeCredentials?.clientSecret}
</CredentialDisplay>
</>
}
inactiveCredentials={
<>
<CredentialDisplay label="Client ID">{inactiveCredentials?.clientId}</CredentialDisplay>
<CredentialDisplay isSensitive label="Client Secret">
{inactiveCredentials?.clientSecret}
</CredentialDisplay>
</>
}
/>
<NoticeBannerV2 title="Auth0 Retired Credentials Behavior">
<p className="text-sm text-mineshaft-300">
Due to how Auth0 client secrets are rotated, retired credentials will not be able to
authenticate with Auth0 during their{" "}
<a
target="_blank"
href="https://infisical.com/docs/documentation/platform/secret-rotation/overview#how-rotation-works"
rel="noopener noreferrer"
className="underline decoration-primary underline-offset-2 hover:text-mineshaft-200"
>
inactive period
</a>
. This is a limitation of the Auth0 platform and cannot be rectified by Infisical.
</p>
</NoticeBannerV2>
</>
);
};
@@ -3,6 +3,7 @@ import { faRotate } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { format } from "date-fns";
import { ViewAuth0ClientSecretRotationGeneratedCredentials } from "@app/components/secret-rotations-v2/ViewSecretRotationV2GeneratedCredentials/ViewAuth0ClientSecretRotationGeneratedCredentials";
import { Modal, ModalContent, Spinner } from "@app/components/v2";
import { SECRET_ROTATION_MAP } from "@app/helpers/secretRotationsV2";
import {
@@ -11,7 +12,7 @@ import {
useViewSecretRotationV2GeneratedCredentials
} from "@app/hooks/api/secretRotationsV2";
import { ViewSqlRotationGeneratedCredentials } from "./shared";
import { ViewSqlCredentialsRotationGeneratedCredentials } from "./shared";
type Props = {
secretRotation?: TSecretRotationV2;
@@ -54,7 +55,14 @@ const Content = ({ secretRotation }: ContentProps) => {
case SecretRotation.PostgresCredentials:
case SecretRotation.MsSqlCredentials:
Component = (
<ViewSqlRotationGeneratedCredentials
<ViewSqlCredentialsRotationGeneratedCredentials
generatedCredentialsResponse={generatedCredentialsResponse}
/>
);
break;
case SecretRotation.Auth0ClientSecret:
Component = (
<ViewAuth0ClientSecretRotationGeneratedCredentials
generatedCredentialsResponse={generatedCredentialsResponse}
/>
);
@@ -0,0 +1,48 @@
import { ReactNode } from "react";
import { faCheck, faClockRotateLeft } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
type Props = {
activeCredentials: ReactNode;
inactiveCredentials?: ReactNode;
};
export const ViewRotationGeneratedCredentialsDisplay = ({
activeCredentials,
inactiveCredentials
}: Props) => {
return (
<>
<div className="flex flex-col gap-2">
<div className="w-full border-b border-mineshaft-600">
<span className="text-sm text-mineshaft-100">
<FontAwesomeIcon icon={faCheck} className="mr-1.5 text-green" />
Current Credentials
</span>
</div>
<p className="text-sm text-mineshaft-300">
The active credential set currently mapped to the rotation secrets.
</p>
<div className="flex flex-col gap-x-8 gap-y-2 rounded border border-mineshaft-600 bg-mineshaft-700 p-2">
{activeCredentials}
</div>
</div>
{inactiveCredentials && (
<div className="flex flex-col gap-2">
<div className="w-full border-b border-mineshaft-600">
<span className="text-sm text-mineshaft-100">
<FontAwesomeIcon icon={faClockRotateLeft} className="mr-1.5 text-yellow" />
Retired Credentials
</span>
</div>
<p className="text-sm text-mineshaft-300">
The retired credential set that will be revoked during the next rotation cycle.
</p>
<div className="flex flex-col gap-x-8 gap-y-2 rounded border border-mineshaft-600 bg-mineshaft-700 p-2">
{inactiveCredentials}
</div>
</div>
)}
</>
);
};
@@ -0,0 +1,40 @@
import { CredentialDisplay } from "@app/components/secret-rotations-v2/ViewSecretRotationV2GeneratedCredentials/shared/CredentialDisplay";
import { ViewRotationGeneratedCredentialsDisplay } from "@app/components/secret-rotations-v2/ViewSecretRotationV2GeneratedCredentials/shared/ViewRotationGeneratedCredentialsDisplay";
import { TMsSqlCredentialsRotationGeneratedCredentialsResponse } from "@app/hooks/api/secretRotationsV2/types/mssql-credentials-rotation";
import { TPostgresCredentialsRotationGeneratedCredentialsResponse } from "@app/hooks/api/secretRotationsV2/types/postgres-credentials-rotation";
type Props = {
generatedCredentialsResponse:
| TMsSqlCredentialsRotationGeneratedCredentialsResponse
| TPostgresCredentialsRotationGeneratedCredentialsResponse;
};
export const ViewSqlCredentialsRotationGeneratedCredentials = ({
generatedCredentialsResponse: { generatedCredentials, activeIndex }
}: Props) => {
const inactiveIndex = activeIndex === 0 ? 1 : 0;
const activeCredentials = generatedCredentials[activeIndex];
const inactiveCredentials = generatedCredentials[inactiveIndex];
return (
<ViewRotationGeneratedCredentialsDisplay
activeCredentials={
<>
<CredentialDisplay label="Username">{activeCredentials?.username}</CredentialDisplay>
<CredentialDisplay isSensitive label="Password">
{activeCredentials?.password}
</CredentialDisplay>
</>
}
inactiveCredentials={
<>
<CredentialDisplay label="Username">{inactiveCredentials?.username}</CredentialDisplay>
<CredentialDisplay isSensitive label="Password">
{inactiveCredentials?.password}
</CredentialDisplay>
</>
}
/>
);
};
@@ -1,57 +0,0 @@
import { faCheck, faClockRotateLeft } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { CredentialDisplay } from "@app/components/secret-rotations-v2/ViewSecretRotationV2GeneratedCredentials/shared/CredentialDisplay";
import { TViewSecretRotationGeneratedCredentialsResponse } from "@app/hooks/api/secretRotationsV2";
type Props = {
generatedCredentialsResponse: TViewSecretRotationGeneratedCredentialsResponse;
};
export const ViewSqlRotationGeneratedCredentials = ({
generatedCredentialsResponse: { generatedCredentials, activeIndex }
}: Props) => {
const inactiveIndex = activeIndex === 0 ? 1 : 0;
const activeCredentials = generatedCredentials[activeIndex];
const inactiveCredentials = generatedCredentials[inactiveIndex];
return (
<>
<div className="flex flex-col gap-2">
<div className="w-full border-b border-mineshaft-600">
<span className="text-sm text-mineshaft-100">
<FontAwesomeIcon icon={faCheck} className="mr-1.5 text-green" />
Current Credentials
</span>
</div>
<p className="text-sm text-mineshaft-300">
The active credential set currently mapped to the rotation secrets.
</p>
<div className="flex flex-col gap-x-8 gap-y-2 rounded border border-mineshaft-600 bg-mineshaft-700 p-2">
<CredentialDisplay label="Username">{activeCredentials?.username}</CredentialDisplay>
<CredentialDisplay isSensitive label="Password">
{activeCredentials?.password}
</CredentialDisplay>
</div>
</div>
<div className="flex flex-col gap-2">
<div className="w-full border-b border-mineshaft-600">
<span className="text-sm text-mineshaft-100">
<FontAwesomeIcon icon={faClockRotateLeft} className="mr-1.5 text-yellow" />
Retired Credentials
</span>
</div>
<p className="text-sm text-mineshaft-300">
The retired credential set that will be revoked during the next rotation cycle.
</p>
<div className="flex flex-col gap-x-8 gap-y-2 rounded border border-mineshaft-600 bg-mineshaft-700 p-2">
<CredentialDisplay label="Username">{inactiveCredentials?.username}</CredentialDisplay>
<CredentialDisplay isSensitive label="Password">
{inactiveCredentials?.password}
</CredentialDisplay>
</div>
</div>
</>
);
};
@@ -1 +1,3 @@
export * from "./ViewSqlRotationGeneratedCredentials";
export * from "./CredentialDisplay";
export * from "./ViewRotationGeneratedCredentialsDisplay";
export * from "./ViewSqlCredentialsRotationGeneratedCredentials";
@@ -92,7 +92,7 @@ export const SecretRotationV2Form = ({
},
environment: currentWorkspace?.environments.find((env) => env.slug === envSlug),
secretPath,
...rotationOption!.template
...(rotationOption!.template as object) // can't infer type since we don't know which specific type it is
},
reValidateMode: "onChange"
});
@@ -142,7 +142,11 @@ export const SecretRotationV2Form = ({
setSelectedTabIndex((prev) => prev - 1);
};
const { handleSubmit, trigger } = formMethods;
const {
handleSubmit,
trigger,
formState: { isSubmitting }
} = formMethods;
const isStepValid = async (index: number) => trigger(FORM_TABS[index].fields);
@@ -219,7 +223,12 @@ export const SecretRotationV2Form = ({
</Tab.Group>
</FormProvider>
<div className="flex w-full flex-row-reverse justify-between gap-4 pt-4">
<Button onClick={handleNext} colorSchema="secondary">
<Button
onClick={handleNext}
isLoading={isSubmitting}
isDisabled={isSubmitting}
colorSchema="secondary"
>
{isFinalStep ? `${secretRotation ? "Update" : "Create"} Secret Rotation` : "Next"}
</Button>
<Button onClick={handlePrev} colorSchema="secondary">
@@ -0,0 +1,30 @@
import { Controller, useFormContext } from "react-hook-form";
import { TSecretRotationV2Form } from "@app/components/secret-rotations-v2/forms/schemas";
import { FormControl, Input } from "@app/components/v2";
import { SecretRotation } from "@app/hooks/api/secretRotationsV2";
export const Auth0ClientSecretRotationParametersFields = () => {
const { control } = useFormContext<
TSecretRotationV2Form & {
type: SecretRotation.Auth0ClientSecret;
}
>();
return (
<Controller
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
tooltipText="The Client ID of the Auth0 Application you want to rotate the client secret for."
isError={Boolean(error)}
errorText={error?.message}
label="Client ID"
>
<Input value={value} onChange={onChange} />
</FormControl>
)}
control={control}
name="parameters.clientId"
/>
);
};
@@ -3,11 +3,13 @@ import { useFormContext } from "react-hook-form";
import { SecretRotation } from "@app/hooks/api/secretRotationsV2";
import { TSecretRotationV2Form } from "../schemas";
import { SqlRotationParametersFields } from "./shared";
import { Auth0ClientSecretRotationParametersFields } from "./Auth0ClientSecretRotationParametersFields";
import { SqlCredentialsRotationParametersFields } from "./shared";
const COMPONENT_MAP: Record<SecretRotation, React.FC> = {
[SecretRotation.PostgresCredentials]: SqlRotationParametersFields,
[SecretRotation.MsSqlCredentials]: SqlRotationParametersFields
[SecretRotation.PostgresCredentials]: SqlCredentialsRotationParametersFields,
[SecretRotation.MsSqlCredentials]: SqlCredentialsRotationParametersFields,
[SecretRotation.Auth0ClientSecret]: Auth0ClientSecretRotationParametersFields
};
export const SecretRotationV2ParametersFields = () => {
@@ -5,7 +5,7 @@ import { FormControl, Input } from "@app/components/v2";
import { NoticeBannerV2 } from "@app/components/v2/NoticeBannerV2/NoticeBannerV2";
import { SecretRotation, useSecretRotationV2Option } from "@app/hooks/api/secretRotationsV2";
export const SqlRotationParametersFields = () => {
export const SqlCredentialsRotationParametersFields = () => {
const { control, watch } = useFormContext<
TSecretRotationV2Form & {
type: SecretRotation.PostgresCredentials; // all sql rotations share these fields
@@ -1 +1 @@
export * from "./SqlRotationParametersFields";
export * from "./SqlCredentialsRotationParametersFields";
@@ -0,0 +1,29 @@
import { useFormContext } from "react-hook-form";
import { TSecretRotationV2Form } from "@app/components/secret-rotations-v2/forms/schemas";
import { GenericFieldLabel } from "@app/components/v2";
import { SecretRotation } from "@app/hooks/api/secretRotationsV2";
import { SecretRotationReviewSection } from "./shared";
export const Auth0ClientSecretRotationReviewFields = () => {
const { watch } = useFormContext<
TSecretRotationV2Form & {
type: SecretRotation.Auth0ClientSecret;
}
>();
const [parameters, { clientId, clientSecret }] = watch(["parameters", "secretsMapping"]);
return (
<>
<SecretRotationReviewSection label="Parameters">
<GenericFieldLabel label="Client ID">{parameters.clientId}</GenericFieldLabel>
</SecretRotationReviewSection>
<SecretRotationReviewSection label="Secrets Mapping">
<GenericFieldLabel label="Client ID">{clientId}</GenericFieldLabel>
<GenericFieldLabel label="Client Secret">{clientSecret}</GenericFieldLabel>
</SecretRotationReviewSection>
</>
);
};
@@ -2,14 +2,17 @@ import { useFormContext } from "react-hook-form";
import { format } from "date-fns";
import { TSecretRotationV2Form } from "@app/components/secret-rotations-v2/forms/schemas";
import { SqlRotationReviewFields } from "@app/components/secret-rotations-v2/forms/SecretRotationV2ReviewFields/shared";
import { GenericFieldLabel } from "@app/components/v2";
import { getRotateAtLocal } from "@app/helpers/secretRotationsV2";
import { SecretRotation } from "@app/hooks/api/secretRotationsV2";
import { Auth0ClientSecretRotationReviewFields } from "./Auth0ClientSecretRotationReviewFields";
import { SqlCredentialsRotationReviewFields } from "./shared";
const COMPONENT_MAP: Record<SecretRotation, React.FC> = {
[SecretRotation.PostgresCredentials]: SqlRotationReviewFields,
[SecretRotation.MsSqlCredentials]: SqlRotationReviewFields
[SecretRotation.PostgresCredentials]: SqlCredentialsRotationReviewFields,
[SecretRotation.MsSqlCredentials]: SqlCredentialsRotationReviewFields,
[SecretRotation.Auth0ClientSecret]: Auth0ClientSecretRotationReviewFields
};
export const SecretRotationV2ReviewFields = () => {
@@ -0,0 +1,17 @@
import { ReactNode } from "react";
type Props = {
label: "Parameters" | "Secrets Mapping";
children: ReactNode;
};
export const SecretRotationReviewSection = ({ label, children }: Props) => {
return (
<div className="flex flex-col gap-3">
<div className="w-full border-b border-mineshaft-600">
<span className="text-sm text-mineshaft-300">{label}</span>
</div>
<div className="flex flex-wrap gap-x-8 gap-y-2">{children}</div>
</div>
);
};
@@ -0,0 +1,33 @@
import { useFormContext } from "react-hook-form";
import { TSecretRotationV2Form } from "@app/components/secret-rotations-v2/forms/schemas";
import { GenericFieldLabel } from "@app/components/v2";
import { SecretRotation } from "@app/hooks/api/secretRotationsV2";
import { SecretRotationReviewSection } from "./SecretRotationReviewSection";
export const SqlCredentialsRotationReviewFields = () => {
const { watch } = useFormContext<
TSecretRotationV2Form & {
type: SecretRotation.PostgresCredentials; // all sql rotations share these fields
}
>();
const [{ username1, username2 }, { username, password }] = watch([
"parameters",
"secretsMapping"
]);
return (
<>
<SecretRotationReviewSection label="Parameters">
<GenericFieldLabel label="Database Username 1">{username1}</GenericFieldLabel>
<GenericFieldLabel label="Database Username 2">{username2}</GenericFieldLabel>
</SecretRotationReviewSection>
<SecretRotationReviewSection label="Secrets Mapping">
<GenericFieldLabel label="Username">{username}</GenericFieldLabel>
<GenericFieldLabel label="Password">{password}</GenericFieldLabel>
</SecretRotationReviewSection>
</>
);
};
@@ -1,41 +0,0 @@
import { useFormContext } from "react-hook-form";
import { TSecretRotationV2Form } from "@app/components/secret-rotations-v2/forms/schemas";
import { GenericFieldLabel } from "@app/components/v2";
import { SecretRotation } from "@app/hooks/api/secretRotationsV2";
export const SqlRotationReviewFields = () => {
const { watch } = useFormContext<
TSecretRotationV2Form & {
type: SecretRotation.PostgresCredentials; // all sql rotations share these fields
}
>();
const [{ username1, username2 }, { username, password }] = watch([
"parameters",
"secretsMapping"
]);
return (
<>
<div className="flex flex-col gap-3">
<div className="w-full border-b border-mineshaft-600">
<span className="text-sm text-mineshaft-300">Parameters</span>
</div>
<div className="flex flex-wrap gap-x-8 gap-y-2">
<GenericFieldLabel label="Database Username 1">{username1}</GenericFieldLabel>
<GenericFieldLabel label="Database Username 2">{username2}</GenericFieldLabel>
</div>
</div>
<div className="flex flex-col gap-3">
<div className="w-full border-b border-mineshaft-600">
<span className="text-sm text-mineshaft-300">Secrets Mapping</span>
</div>
<div className="flex flex-wrap gap-x-8 gap-y-2">
<GenericFieldLabel label="Username">{username}</GenericFieldLabel>
<GenericFieldLabel label="Password">{password}</GenericFieldLabel>
</div>
</div>
</>
);
};
@@ -1 +1,2 @@
export * from "./SqlRotationReviewFields";
export * from "./SecretRotationReviewSection";
export * from "./SqlCredentialsRotationReviewFields";
@@ -0,0 +1,58 @@
import { Controller, useFormContext } from "react-hook-form";
import { TSecretRotationV2Form } from "@app/components/secret-rotations-v2/forms/schemas";
import { FormControl, Input } from "@app/components/v2";
import { SecretRotation, useSecretRotationV2Option } from "@app/hooks/api/secretRotationsV2";
import { SecretsMappingTable } from "./shared";
export const Auth0ClientSecretRotationSecretsMappingFields = () => {
const { control } = useFormContext<
TSecretRotationV2Form & {
type: SecretRotation.Auth0ClientSecret;
}
>();
const { rotationOption } = useSecretRotationV2Option(SecretRotation.Auth0ClientSecret);
const items = [
{
name: "Client ID",
input: (
<Controller
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl isError={Boolean(error)} errorText={error?.message}>
<Input
value={value}
onChange={onChange}
placeholder={rotationOption?.template.secretsMapping.clientId}
/>
</FormControl>
)}
control={control}
name="secretsMapping.clientId"
/>
)
},
{
name: "Client Secret",
input: (
<Controller
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl isError={Boolean(error)} errorText={error?.message}>
<Input
value={value}
onChange={onChange}
placeholder={rotationOption?.template.secretsMapping.clientSecret}
/>
</FormControl>
)}
control={control}
name="secretsMapping.clientSecret"
/>
)
}
];
return <SecretsMappingTable items={items} />;
};
@@ -3,11 +3,13 @@ import { useFormContext } from "react-hook-form";
import { SecretRotation } from "@app/hooks/api/secretRotationsV2";
import { TSecretRotationV2Form } from "../schemas";
import { SqlRotationSecretsMappingFields } from "./shared";
import { Auth0ClientSecretRotationSecretsMappingFields } from "./Auth0ClientSecretRotationSecretsMappingFields";
import { SqlCredentialsRotationSecretsMappingFields } from "./shared";
const COMPONENT_MAP: Record<SecretRotation, React.FC> = {
[SecretRotation.PostgresCredentials]: SqlRotationSecretsMappingFields,
[SecretRotation.MsSqlCredentials]: SqlRotationSecretsMappingFields
[SecretRotation.PostgresCredentials]: SqlCredentialsRotationSecretsMappingFields,
[SecretRotation.MsSqlCredentials]: SqlCredentialsRotationSecretsMappingFields,
[SecretRotation.Auth0ClientSecret]: Auth0ClientSecretRotationSecretsMappingFields
};
export const SecretRotationV2SecretsMappingFields = () => {
@@ -1,49 +1,14 @@
import { Controller, useFormContext } from "react-hook-form";
import { ReactNode } from "react";
import { faArrowRight, faKey } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { TSecretRotationV2Form } from "@app/components/secret-rotations-v2/forms/schemas";
import { Badge, FormControl, FormLabel, Input } from "@app/components/v2";
import { SecretRotation } from "@app/hooks/api/secretRotationsV2";
import { Badge, FormLabel } from "@app/components/v2";
export const SqlRotationSecretsMappingFields = () => {
const { control } = useFormContext<
TSecretRotationV2Form & {
type: SecretRotation.PostgresCredentials; // all sql rotations share these fields
}
>();
const items = [
{
name: "Username",
input: (
<Controller
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl isError={Boolean(error)} errorText={error?.message}>
<Input value={value} onChange={onChange} placeholder="DB_USERNAME" />
</FormControl>
)}
control={control}
name="secretsMapping.username"
/>
)
},
{
name: "Password",
input: (
<Controller
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl isError={Boolean(error)} errorText={error?.message}>
<Input value={value} onChange={onChange} placeholder="DB_PASSWORD" />
</FormControl>
)}
control={control}
name="secretsMapping.password"
/>
)
}
];
type Props = {
items: { name: string; input: ReactNode }[];
};
export const SecretsMappingTable = ({ items }: Props) => {
return (
<div className="w-full overflow-hidden">
<table className="w-full table-auto">
@@ -0,0 +1,60 @@
import { Controller, useFormContext } from "react-hook-form";
import { TSecretRotationV2Form } from "@app/components/secret-rotations-v2/forms/schemas";
import { FormControl, Input } from "@app/components/v2";
import { SecretRotation, useSecretRotationV2Option } from "@app/hooks/api/secretRotationsV2";
import { SecretsMappingTable } from "./SecretsMappingTable";
export const SqlCredentialsRotationSecretsMappingFields = () => {
const { control, watch } = useFormContext<
TSecretRotationV2Form & {
type: SecretRotation.PostgresCredentials; // all sql rotations share these fields
}
>();
const type = watch("type");
const { rotationOption } = useSecretRotationV2Option(type);
const items = [
{
name: "Username",
input: (
<Controller
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl isError={Boolean(error)} errorText={error?.message}>
<Input
value={value}
onChange={onChange}
placeholder={rotationOption?.template.secretsMapping.username}
/>
</FormControl>
)}
control={control}
name="secretsMapping.username"
/>
)
},
{
name: "Password",
input: (
<Controller
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl isError={Boolean(error)} errorText={error?.message}>
<Input
value={value}
onChange={onChange}
placeholder={rotationOption?.template.secretsMapping.password}
/>
</FormControl>
)}
control={control}
name="secretsMapping.password"
/>
)
}
];
return <SecretsMappingTable items={items} />;
};
@@ -1 +1,2 @@
export * from "./SqlRotationSecretsMappingFields";
export * from "./SecretsMappingTable";
export * from "./SqlCredentialsRotationSecretsMappingFields";
@@ -0,0 +1,17 @@
import { z } from "zod";
import { BaseSecretRotationSchema } from "@app/components/secret-rotations-v2/forms/schemas/base-secret-rotation-v2-schema";
import { SecretRotation } from "@app/hooks/api/secretRotationsV2";
export const Auth0ClientSecretRotationSchema = z
.object({
type: z.literal(SecretRotation.Auth0ClientSecret),
parameters: z.object({
clientId: z.string().trim().min(1, "Client ID required")
}),
secretsMapping: z.object({
clientId: z.string().trim().min(1, "Client ID required"),
clientSecret: z.string().trim().min(1, "Client Secret required")
})
})
.merge(BaseSecretRotationSchema);
@@ -1,11 +1,13 @@
import { z } from "zod";
import { Auth0ClientSecretRotationSchema } from "@app/components/secret-rotations-v2/forms/schemas/auth0-client-secret-rotation-schema";
import { MsSqlCredentialsRotationSchema } from "@app/components/secret-rotations-v2/forms/schemas/mssql-credentials-rotation-schema";
import { PostgresCredentialsRotationSchema } from "@app/components/secret-rotations-v2/forms/schemas/postgres-credentials-rotation-schema";
const SecretRotationUnionSchema = z.discriminatedUnion("type", [
PostgresCredentialsRotationSchema,
MsSqlCredentialsRotationSchema
MsSqlCredentialsRotationSchema,
Auth0ClientSecretRotationSchema
]);
export const SecretRotationV2FormSchema = SecretRotationUnionSchema;