Merge pull request #3598 from Infisical/ENG-2755

feat(secret-sync): Secret Key Schema
This commit is contained in:
x032205
2025-05-14 23:57:18 -04:00
committed by GitHub
43 changed files with 327 additions and 156 deletions
@@ -44,7 +44,9 @@ const Content = ({ secretSync, onComplete }: ContentProps) => {
handleSubmit,
control,
formState: { isSubmitting, isDirty }
} = useForm<TFormData>({ resolver: zodResolver(FormSchema) });
} = useForm<TFormData>({
resolver: zodResolver(FormSchema)
});
const triggerImportSecrets = useTriggerSecretSyncImportSecrets();
@@ -1,9 +1,13 @@
import { ReactNode } from "react";
import { Controller, useFormContext } from "react-hook-form";
import { faQuestionCircle, faTriangleExclamation } from "@fortawesome/free-solid-svg-icons";
import {
faCircleInfo,
faQuestionCircle,
faTriangleExclamation
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { FormControl, Select, SelectItem, Switch, Tooltip } from "@app/components/v2";
import { FormControl, Input, Select, SelectItem, Switch, Tooltip } from "@app/components/v2";
import { SECRET_SYNC_INITIAL_SYNC_BEHAVIOR_MAP, SECRET_SYNC_MAP } from "@app/helpers/secretSyncs";
import { SecretSync, useSecretSyncOption } from "@app/hooks/api/secretSyncs";
@@ -122,6 +126,45 @@ export const SecretSyncOptionsFields = ({ hideInitialSync }: Props) => {
)}
</>
)}
<Controller
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
tooltipClassName="max-w-md"
tooltipText="When a secret is synced, its key will be injected into the key schema before it reaches the destination. This is useful for organization."
isError={Boolean(error)}
isOptional
errorText={error?.message}
label="Key Schema"
helperText={
<Tooltip
className="max-w-md"
content={
<span>
We highly recommend using a{" "}
<a
href="https://infisical.com/docs/integrations/secret-syncs/overview#key-schemas"
target="_blank"
>
Key Schema
</a>{" "}
to ensure that Infisical only manages the specific keys you intend, keeping
everything else untouched.
</span>
}
>
<div>
<span>Infisical strongly advises setting a Key Schema</span>{" "}
<FontAwesomeIcon icon={faCircleInfo} className="text-mineshaft-400" />
</div>
</Tooltip>
}
>
<Input value={value} onChange={onChange} placeholder="INFISICAL_{{secretKey}}" />
</FormControl>
)}
control={control}
name="syncOptions.keySchema"
/>
{AdditionalSyncOptionsFieldsComponent}
<Controller
control={control}
@@ -161,34 +204,6 @@ export const SecretSyncOptionsFields = ({ hideInitialSync }: Props) => {
);
}}
/>
{/* <Controller
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
isError={Boolean(error)}
isOptional
errorText={error?.message}
label="Prepend Prefix"
>
<Input className="uppercase" value={value} onChange={onChange} placeholder="INF_" />
</FormControl>
)}
control={control}
name="syncOptions.prependPrefix"
/>
<Controller
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
isError={Boolean(error)}
isOptional
errorText={error?.message}
label="Append Suffix"
>
<Input className="uppercase" value={value} onChange={onChange} placeholder="_INF" />
</FormControl>
)}
control={control}
name="syncOptions.appendSuffix"
/> */}
</>
);
};
@@ -41,11 +41,7 @@ export const SecretSyncReviewFields = () => {
connection,
environment,
secretPath,
syncOptions: {
// appendSuffix, prependPrefix,
disableSecretDeletion,
initialSyncBehavior
},
syncOptions: { disableSecretDeletion, initialSyncBehavior, keySchema },
destination,
isAutoSyncEnabled
} = watch();
@@ -137,8 +133,7 @@ export const SecretSyncReviewFields = () => {
<GenericFieldLabel label="Initial Sync Behavior">
{SECRET_SYNC_INITIAL_SYNC_BEHAVIOR_MAP[initialSyncBehavior](destinationName).name}
</GenericFieldLabel>
{/* <SecretSyncLabel label="Prepend Prefix">{prependPrefix}</SecretSyncLabel>
<SecretSyncLabel label="Append Suffix">{appendSuffix}</SecretSyncLabel> */}
<GenericFieldLabel label="Key Schema">{keySchema}</GenericFieldLabel>
{AdditionalSyncOptionsFieldsComponent}
{disableSecretDeletion && (
<GenericFieldLabel label="Secret Deletion">
@@ -8,18 +8,17 @@ export const BaseSecretSyncSchema = <T extends AnyZodObject | undefined = undefi
) => {
const baseSyncOptionsSchema = z.object({
initialSyncBehavior: z.nativeEnum(SecretSyncInitialSyncBehavior),
disableSecretDeletion: z.boolean().optional().default(false)
// scott: removed temporarily for evaluation of template formatting
// prependPrefix: z
// .string()
// .trim()
// .transform((str) => str.toUpperCase())
// .optional(),
// appendSuffix: z
// .string()
// .trim()
// .transform((str) => str.toUpperCase())
// .optional()
disableSecretDeletion: z.boolean().optional().default(false),
keySchema: z
.string()
.optional()
.refine(
(val) => !val || /^(?:[a-zA-Z0-9\-/]*)(?:\{\{secretKey\}\})(?:[a-zA-Z0-9\-/]*)$/.test(val),
{
message:
"Key schema must include one {{secretKey}} and only contain letters, numbers, dashes, slashes, and the {{secretKey}} placeholder."
}
)
});
const syncOptionsSchema = additionalSyncOptions
@@ -4,8 +4,7 @@ import { SecretSyncInitialSyncBehavior, SecretSyncStatus } from "@app/hooks/api/
export type RootSyncOptions = {
initialSyncBehavior: SecretSyncInitialSyncBehavior;
disableSecretDeletion?: boolean;
// prependPrefix?: string;
// appendSuffix?: string;
keySchema?: string;
};
export type TRootSecretSync = {
@@ -21,12 +21,7 @@ type Props = {
export const SecretSyncOptionsSection = ({ secretSync, onEditOptions }: Props) => {
const {
destination,
syncOptions: {
// appendSuffix,
// prependPrefix,
initialSyncBehavior,
disableSecretDeletion
}
syncOptions: { initialSyncBehavior, disableSecretDeletion, keySchema }
} = secretSync;
let AdditionalSyncOptionsComponent: ReactNode;
@@ -88,8 +83,7 @@ export const SecretSyncOptionsSection = ({ secretSync, onEditOptions }: Props) =
<GenericFieldLabel label="Initial Sync Behavior">
{SECRET_SYNC_INITIAL_SYNC_BEHAVIOR_MAP[initialSyncBehavior](destination).name}
</GenericFieldLabel>
{/* <SecretSyncLabel label="Prefix">{prependPrefix}</SecretSyncLabel>
<SecretSyncLabel label="Suffix">{appendSuffix}</SecretSyncLabel> */}
<GenericFieldLabel label="Key Schema">{keySchema}</GenericFieldLabel>
{AdditionalSyncOptionsComponent}
{disableSecretDeletion && (
<GenericFieldLabel label="Secret Deletion">