Merge pull request #3487 from Infisical/ENG-2633

feat(secret-sync): Hashicorp Vault App Connection & Secret Sync
This commit is contained in:
x032205
2025-05-01 20:31:18 -04:00
committed by GitHub
111 changed files with 1693 additions and 157 deletions
@@ -0,0 +1,86 @@
import { Controller, useFormContext, useWatch } from "react-hook-form";
import { SingleValue } from "react-select";
import { faCircleInfo } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { SecretSyncConnectionField } from "@app/components/secret-syncs/forms/SecretSyncConnectionField";
import { FilterableSelect, FormControl, Input, Tooltip } from "@app/components/v2";
import { useHCVaultConnectionListMounts } from "@app/hooks/api/appConnections/hc-vault";
import { SecretSync } from "@app/hooks/api/secretSyncs";
import { TSecretSyncForm } from "../schemas";
export const HCVaultSyncFields = () => {
const { control, setValue } = useFormContext<
TSecretSyncForm & { destination: SecretSync.HCVault }
>();
const connectionId = useWatch({ name: "connection.id", control });
const { data: mounts, isLoading: isMountsLoading } = useHCVaultConnectionListMounts(
connectionId,
{
enabled: Boolean(connectionId)
}
);
return (
<>
<SecretSyncConnectionField
onChange={() => {
setValue("destinationConfig.mount", "");
setValue("destinationConfig.path", "");
}}
/>
<Controller
name="destinationConfig.mount"
control={control}
render={({ field: { onChange }, fieldState: { error } }) => (
<FormControl
isError={Boolean(error)}
errorText={error?.message}
label="Secrets Engine Mount"
helperText={
<Tooltip
className="max-w-md"
content="Ensure the Secrets Engine mount exists and that your App Role / Access Token has permission to access it. Infisical only supports version 2 KV Secrets Engine mounts. If you're using Hashicorp Cloud Platform, ensure that you correctly defined your 'namespace' when creating the App Connection."
>
<div>
<span>Don&#39;t see the mount you&#39;re looking for?</span>{" "}
<FontAwesomeIcon icon={faCircleInfo} className="text-mineshaft-400" />
</div>
</Tooltip>
}
>
<FilterableSelect
menuPlacement="top"
isLoading={isMountsLoading && Boolean(connectionId)}
isDisabled={!connectionId}
onChange={(option) =>
onChange((option as SingleValue<{ value: string }>)?.value ?? null)
}
options={mounts?.map((v) => ({ label: v, value: v }))}
placeholder="Select a Secrets Engine Mount..."
/>
</FormControl>
)}
/>
<Controller
name="destinationConfig.path"
control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
tooltipClassName="max-w-sm"
tooltipText="The Secrets Engine mount path where secrets should be synced to. If the path does not exist, it will be created."
isError={Boolean(error)}
errorText={error?.message}
label="Path"
>
<Input value={value} onChange={onChange} placeholder="dev/example" />
</FormControl>
)}
/>
</>
);
};
@@ -11,6 +11,7 @@ import { CamundaSyncFields } from "./CamundaSyncFields";
import { DatabricksSyncFields } from "./DatabricksSyncFields";
import { GcpSyncFields } from "./GcpSyncFields";
import { GitHubSyncFields } from "./GitHubSyncFields";
import { HCVaultSyncFields } from "./HCVaultSyncFields";
import { HumanitecSyncFields } from "./HumanitecSyncFields";
import { TeamCitySyncFields } from "./TeamCitySyncFields";
import { TerraformCloudSyncFields } from "./TerraformCloudSyncFields";
@@ -47,6 +48,8 @@ export const SecretSyncDestinationFields = () => {
return <VercelSyncFields />;
case SecretSync.Windmill:
return <WindmillSyncFields />;
case SecretSync.HCVault:
return <HCVaultSyncFields />;
case SecretSync.TeamCity:
return <TeamCitySyncFields />;
default:
@@ -43,6 +43,7 @@ export const SecretSyncOptionsFields = ({ hideInitialSync }: Props) => {
case SecretSync.Camunda:
case SecretSync.Vercel:
case SecretSync.Windmill:
case SecretSync.HCVault:
case SecretSync.TeamCity:
AdditionalSyncOptionsFieldsComponent = null;
break;
@@ -0,0 +1,18 @@
import { useFormContext } from "react-hook-form";
import { TSecretSyncForm } from "@app/components/secret-syncs/forms/schemas";
import { GenericFieldLabel } from "@app/components/v2";
import { SecretSync } from "@app/hooks/api/secretSyncs";
export const HCVaultSyncReviewFields = () => {
const { watch } = useFormContext<TSecretSyncForm & { destination: SecretSync.HCVault }>();
const mount = watch("destinationConfig.mount");
const path = watch("destinationConfig.path");
return (
<>
<GenericFieldLabel label="Secrets Engine Mount">{mount}</GenericFieldLabel>
<GenericFieldLabel label="Path">{path}</GenericFieldLabel>
</>
);
};
@@ -21,6 +21,7 @@ import { CamundaSyncReviewFields } from "./CamundaSyncReviewFields";
import { DatabricksSyncReviewFields } from "./DatabricksSyncReviewFields";
import { GcpSyncReviewFields } from "./GcpSyncReviewFields";
import { GitHubSyncReviewFields } from "./GitHubSyncReviewFields";
import { HCVaultSyncReviewFields } from "./HCVaultSyncReviewFields";
import { HumanitecSyncReviewFields } from "./HumanitecSyncReviewFields";
import { TeamCitySyncReviewFields } from "./TeamCitySyncReviewFields";
import { TerraformCloudSyncReviewFields } from "./TerraformCloudSyncReviewFields";
@@ -89,6 +90,9 @@ export const SecretSyncReviewFields = () => {
case SecretSync.Windmill:
DestinationFieldsComponent = <WindmillSyncReviewFields />;
break;
case SecretSync.HCVault:
DestinationFieldsComponent = <HCVaultSyncReviewFields />;
break;
case SecretSync.TeamCity:
DestinationFieldsComponent = <TeamCitySyncReviewFields />;
break;
@@ -0,0 +1,22 @@
import { z } from "zod";
import { BaseSecretSyncSchema } from "@app/components/secret-syncs/forms/schemas/base-secret-sync-schema";
import { SecretSync } from "@app/hooks/api/secretSyncs";
export const HCVaultSyncDestinationSchema = BaseSecretSyncSchema().merge(
z.object({
destination: z.literal(SecretSync.HCVault),
destinationConfig: z.object({
mount: z.string().trim().min(1, "Secrets Engine Mount required"),
path: z
.string()
.trim()
.min(1, "Path required")
.transform((val) => val.trim().replace(/^\/+|\/+$/g, "")) // removes leading/trailing slashes
.refine((val) => /^([a-zA-Z0-9._-]+\/)*[a-zA-Z0-9._-]+$/.test(val), {
message:
"Invalid Vault path format. Use alphanumerics, dots, dashes, underscores, and single slashes between segments."
})
})
})
);
@@ -8,6 +8,7 @@ import { CamundaSyncDestinationSchema } from "./camunda-sync-destination-schema"
import { DatabricksSyncDestinationSchema } from "./databricks-sync-destination-schema";
import { GcpSyncDestinationSchema } from "./gcp-sync-destination-schema";
import { GitHubSyncDestinationSchema } from "./github-sync-destination-schema";
import { HCVaultSyncDestinationSchema } from "./hc-vault-sync-destination-schema";
import { HumanitecSyncDestinationSchema } from "./humanitec-sync-destination-schema";
import { TeamCitySyncDestinationSchema } from "./teamcity-sync-destination-schema";
import { TerraformCloudSyncDestinationSchema } from "./terraform-cloud-destination-schema";
@@ -27,6 +28,7 @@ const SecretSyncUnionSchema = z.discriminatedUnion("destination", [
CamundaSyncDestinationSchema,
VercelSyncDestinationSchema,
WindmillSyncDestinationSchema,
HCVaultSyncDestinationSchema,
TeamCitySyncDestinationSchema
]);
+5
View File
@@ -19,6 +19,7 @@ import {
DatabricksConnectionMethod,
GcpConnectionMethod,
GitHubConnectionMethod,
HCVaultConnectionMethod,
HumanitecConnectionMethod,
LdapConnectionMethod,
MsSqlConnectionMethod,
@@ -58,6 +59,7 @@ export const APP_CONNECTION_MAP: Record<
[AppConnection.Camunda]: { name: "Camunda", image: "Camunda.png" },
[AppConnection.Windmill]: { name: "Windmill", image: "Windmill.png" },
[AppConnection.Auth0]: { name: "Auth0", image: "Auth0.png", size: 40 },
[AppConnection.HCVault]: { name: "Hashicorp Vault", image: "Vault.png", size: 65 },
[AppConnection.LDAP]: { name: "LDAP", image: "LDAP.png", size: 65 },
[AppConnection.TeamCity]: { name: "TeamCity", image: "TeamCity.png" }
};
@@ -88,11 +90,14 @@ export const getAppConnectionMethodDetails = (method: TAppConnection["method"])
case PostgresConnectionMethod.UsernameAndPassword:
case MsSqlConnectionMethod.UsernameAndPassword:
return { name: "Username & Password", icon: faLock };
case HCVaultConnectionMethod.AccessToken:
case TeamCityConnectionMethod.AccessToken:
case WindmillConnectionMethod.AccessToken:
return { name: "Access Token", icon: faKey };
case Auth0ConnectionMethod.ClientCredentials:
return { name: "Client Credentials", icon: faServer };
case HCVaultConnectionMethod.AppRole:
return { name: "App Role", icon: faUser };
case LdapConnectionMethod.SimpleBind:
return { name: "Simple Bind", icon: faLink };
default:
+5
View File
@@ -40,6 +40,10 @@ export const SECRET_SYNC_MAP: Record<SecretSync, { name: string; image: string }
name: "Windmill",
image: "Windmill.png"
},
[SecretSync.HCVault]: {
name: "Hashicorp Vault",
image: "Vault.png"
},
[SecretSync.TeamCity]: {
name: "TeamCity",
image: "TeamCity.png"
@@ -59,6 +63,7 @@ export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
[SecretSync.Camunda]: AppConnection.Camunda,
[SecretSync.Vercel]: AppConnection.Vercel,
[SecretSync.Windmill]: AppConnection.Windmill,
[SecretSync.HCVault]: AppConnection.HCVault,
[SecretSync.TeamCity]: AppConnection.TeamCity
};
@@ -14,6 +14,7 @@ export enum AppConnection {
Camunda = "camunda",
Windmill = "windmill",
Auth0 = "auth0",
HCVault = "hashicorp-vault",
LDAP = "ldap",
TeamCity = "teamcity"
}
@@ -0,0 +1 @@
export * from "./queries";
@@ -0,0 +1,36 @@
import { useQuery, UseQueryOptions } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import { appConnectionKeys } from "../queries";
const hcVaultConnectionKeys = {
all: [...appConnectionKeys.all, "hcvault"] as const,
listMounts: (connectionId: string) =>
[...hcVaultConnectionKeys.all, "mounts", connectionId] as const
};
export const useHCVaultConnectionListMounts = (
connectionId: string,
options?: Omit<
UseQueryOptions<
string[],
unknown,
string[],
ReturnType<typeof hcVaultConnectionKeys.listMounts>
>,
"queryKey" | "queryFn"
>
) => {
return useQuery({
queryKey: hcVaultConnectionKeys.listMounts(connectionId),
queryFn: async () => {
const { data } = await apiRequest.get<string[]>(
`/api/v1/app-connections/hashicorp-vault/${connectionId}/mounts`
);
return data;
},
...options
});
};
@@ -72,6 +72,10 @@ export type TAuth0ConnectionOption = TAppConnectionOptionBase & {
app: AppConnection.Auth0;
};
export type THCVaultConnectionOption = TAppConnectionOptionBase & {
app: AppConnection.HCVault;
};
export type TLdapConnectionOption = TAppConnectionOptionBase & {
app: AppConnection.LDAP;
};
@@ -96,6 +100,7 @@ export type TAppConnectionOption =
| TCamundaConnectionOption
| TWindmillConnectionOption
| TAuth0ConnectionOption
| THCVaultConnectionOption
| TTeamCityConnectionOption;
export type TAppConnectionOptionMap = {
@@ -114,6 +119,7 @@ export type TAppConnectionOptionMap = {
[AppConnection.Camunda]: TCamundaConnectionOption;
[AppConnection.Windmill]: TWindmillConnectionOption;
[AppConnection.Auth0]: TAuth0ConnectionOption;
[AppConnection.HCVault]: THCVaultConnectionOption;
[AppConnection.LDAP]: TLdapConnectionOption;
[AppConnection.TeamCity]: TTeamCityConnectionOption;
};
@@ -0,0 +1,27 @@
import { AppConnection } from "@app/hooks/api/appConnections/enums";
import { TRootAppConnection } from "@app/hooks/api/appConnections/types/root-connection";
export enum HCVaultConnectionMethod {
AccessToken = "access-token",
AppRole = "app-role"
}
export type THCVaultConnection = TRootAppConnection & { app: AppConnection.HCVault } & (
| {
method: HCVaultConnectionMethod.AccessToken;
credentials: {
instanceUrl: string;
namespace?: string;
accessToken: string;
};
}
| {
method: HCVaultConnectionMethod.AppRole;
credentials: {
instanceUrl: string;
namespace?: string;
roleId: string;
secretId: string;
};
}
);
@@ -9,6 +9,7 @@ import { TCamundaConnection } from "./camunda-connection";
import { TDatabricksConnection } from "./databricks-connection";
import { TGcpConnection } from "./gcp-connection";
import { TGitHubConnection } from "./github-connection";
import { THCVaultConnection } from "./hc-vault-connection";
import { THumanitecConnection } from "./humanitec-connection";
import { TLdapConnection } from "./ldap-connection";
import { TMsSqlConnection } from "./mssql-connection";
@@ -27,6 +28,7 @@ export * from "./camunda-connection";
export * from "./databricks-connection";
export * from "./gcp-connection";
export * from "./github-connection";
export * from "./hc-vault-connection";
export * from "./humanitec-connection";
export * from "./ldap-connection";
export * from "./mssql-connection";
@@ -52,6 +54,7 @@ export type TAppConnection =
| TCamundaConnection
| TWindmillConnection
| TAuth0Connection
| THCVaultConnection
| TLdapConnection
| TTeamCityConnection;
@@ -96,6 +99,7 @@ export type TAppConnectionMap = {
[AppConnection.Camunda]: TCamundaConnection;
[AppConnection.Windmill]: TWindmillConnection;
[AppConnection.Auth0]: TAuth0Connection;
[AppConnection.HCVault]: THCVaultConnection;
[AppConnection.LDAP]: TLdapConnection;
[AppConnection.TeamCity]: TTeamCityConnection;
};
@@ -11,6 +11,7 @@ export enum SecretSync {
Camunda = "camunda",
Vercel = "vercel",
Windmill = "windmill",
HCVault = "hashicorp-vault",
TeamCity = "teamcity"
}
@@ -0,0 +1,16 @@
import { AppConnection } from "@app/hooks/api/appConnections/enums";
import { SecretSync } from "@app/hooks/api/secretSyncs";
import { TRootSecretSync } from "@app/hooks/api/secretSyncs/types/root-sync";
export type THCVaultSync = TRootSecretSync & {
destination: SecretSync.HCVault;
destinationConfig: {
mount: string;
path: string;
};
connection: {
app: AppConnection.HCVault;
name: string;
id: string;
};
};
@@ -9,6 +9,7 @@ import { TCamundaSync } from "./camunda-sync";
import { TDatabricksSync } from "./databricks-sync";
import { TGcpSync } from "./gcp-sync";
import { TGitHubSync } from "./github-sync";
import { THCVaultSync } from "./hc-vault-sync";
import { THumanitecSync } from "./humanitec-sync";
import { TTeamCitySync } from "./teamcity-sync";
import { TTerraformCloudSync } from "./terraform-cloud-sync";
@@ -34,6 +35,7 @@ export type TSecretSync =
| TCamundaSync
| TVercelSync
| TWindmillSync
| THCVaultSync
| TTeamCitySync;
export type TListSecretSyncs = { secretSyncs: TSecretSync[] };
@@ -13,9 +13,9 @@ import {
TBreadcrumbFormat
} from "@app/components/v2";
import {
useProjectPermission,
ProjectPermissionActions,
ProjectPermissionSub,
useProjectPermission,
useSubscription,
useWorkspace
} from "@app/context";
@@ -18,6 +18,7 @@ import { CamundaConnectionForm } from "./CamundaConnectionForm";
import { DatabricksConnectionForm } from "./DatabricksConnectionForm";
import { GcpConnectionForm } from "./GcpConnectionForm";
import { GitHubConnectionForm } from "./GitHubConnectionForm";
import { HCVaultConnectionForm } from "./HCVaultConnectionForm";
import { HumanitecConnectionForm } from "./HumanitecConnectionForm";
import { LdapConnectionForm } from "./LdapConnectionForm";
import { MsSqlConnectionForm } from "./MsSqlConnectionForm";
@@ -94,6 +95,8 @@ const CreateForm = ({ app, onComplete }: CreateFormProps) => {
return <WindmillConnectionForm onSubmit={onSubmit} />;
case AppConnection.Auth0:
return <Auth0ConnectionForm onSubmit={onSubmit} />;
case AppConnection.HCVault:
return <HCVaultConnectionForm onSubmit={onSubmit} />;
case AppConnection.LDAP:
return <LdapConnectionForm onSubmit={onSubmit} />;
case AppConnection.TeamCity:
@@ -164,6 +167,8 @@ const UpdateForm = ({ appConnection, onComplete }: UpdateFormProps) => {
return <WindmillConnectionForm onSubmit={onSubmit} appConnection={appConnection} />;
case AppConnection.Auth0:
return <Auth0ConnectionForm onSubmit={onSubmit} appConnection={appConnection} />;
case AppConnection.HCVault:
return <HCVaultConnectionForm onSubmit={onSubmit} appConnection={appConnection} />;
case AppConnection.LDAP:
return <LdapConnectionForm onSubmit={onSubmit} appConnection={appConnection} />;
case AppConnection.TeamCity:
@@ -0,0 +1,224 @@
import { Controller, FormProvider, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import {
Button,
FormControl,
Input,
ModalClose,
SecretInput,
Select,
SelectItem
} from "@app/components/v2";
import { APP_CONNECTION_MAP, getAppConnectionMethodDetails } from "@app/helpers/appConnections";
import { HCVaultConnectionMethod, THCVaultConnection } from "@app/hooks/api/appConnections";
import { AppConnection } from "@app/hooks/api/appConnections/enums";
import {
genericAppConnectionFieldsSchema,
GenericAppConnectionsFields
} from "./GenericAppConnectionFields";
type Props = {
appConnection?: THCVaultConnection;
onSubmit: (formData: FormData) => Promise<void>;
};
const rootSchema = genericAppConnectionFieldsSchema.extend({
app: z.literal(AppConnection.HCVault)
});
const InstanceUrlSchema = z
.string()
.trim()
.min(1, "Instance URL required")
.url("Invalid Instance URL");
const NamespaceSchema = z.string().trim().optional();
const formSchema = z.discriminatedUnion("method", [
rootSchema.extend({
method: z.literal(HCVaultConnectionMethod.AccessToken),
credentials: z.object({
instanceUrl: InstanceUrlSchema,
namespace: NamespaceSchema,
accessToken: z.string().trim().min(1, "Access Token required")
})
}),
rootSchema.extend({
method: z.literal(HCVaultConnectionMethod.AppRole),
credentials: z.object({
instanceUrl: InstanceUrlSchema,
namespace: NamespaceSchema,
roleId: z.string().trim().min(1, "Role ID required"),
secretId: z.string().trim().min(1, "Secret ID required")
})
})
]);
type FormData = z.infer<typeof formSchema>;
export const HCVaultConnectionForm = ({ appConnection, onSubmit }: Props) => {
const isUpdate = Boolean(appConnection);
const form = useForm<FormData>({
resolver: zodResolver(formSchema),
defaultValues: appConnection ?? {
app: AppConnection.HCVault,
method: HCVaultConnectionMethod.AppRole
}
});
const {
handleSubmit,
control,
watch,
formState: { isSubmitting, isDirty }
} = form;
const selectedMethod = watch("method");
return (
<FormProvider {...form}>
<form onSubmit={handleSubmit(onSubmit)}>
{!isUpdate && <GenericAppConnectionsFields />}
<Controller
name="method"
control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
tooltipText={`The method you would like to use to connect with ${
APP_CONNECTION_MAP[AppConnection.HCVault].name
}. This field cannot be changed after creation.`}
errorText={error?.message}
isError={Boolean(error?.message)}
label="Method"
>
<Select
isDisabled={isUpdate}
value={value}
onValueChange={(val) => onChange(val)}
className="w-full border border-mineshaft-500"
position="popper"
dropdownContainerClassName="max-w-none"
>
{Object.values(HCVaultConnectionMethod).map((method) => {
return (
<SelectItem value={method} key={method}>
{getAppConnectionMethodDetails(method).name}{" "}
{method === HCVaultConnectionMethod.AppRole ? " (Recommended)" : ""}
</SelectItem>
);
})}
</Select>
</FormControl>
)}
/>
<Controller
name="credentials.instanceUrl"
control={control}
shouldUnregister
render={({ field, fieldState: { error } }) => (
<FormControl
errorText={error?.message}
isError={Boolean(error?.message)}
label="Instance URL"
tooltipClassName="max-w-sm"
tooltipText="The URL at which your Hashicorp Vault instance is hosted."
>
<Input {...field} placeholder="https://vault.example.com" />
</FormControl>
)}
/>
<Controller
name="credentials.namespace"
control={control}
shouldUnregister
render={({ field, fieldState: { error } }) => (
<FormControl
errorText={error?.message}
isError={Boolean(error?.message)}
label="Namespace"
isOptional
tooltipClassName="max-w-sm"
tooltipText="On self-hosted and enterprise clusters there may not be namespaces."
>
<Input {...field} placeholder="admin" />
</FormControl>
)}
/>
{selectedMethod === HCVaultConnectionMethod.AccessToken ? (
<Controller
name="credentials.accessToken"
control={control}
shouldUnregister
render={({ field, fieldState: { error } }) => (
<FormControl
errorText={error?.message}
isError={Boolean(error?.message)}
label="Access Token"
>
<SecretInput
{...field}
containerClassName="text-gray-400 group-focus-within:!border-primary-400/50 border border-mineshaft-500 bg-mineshaft-900 px-2.5 py-1.5"
/>
</FormControl>
)}
/>
) : (
<>
<Controller
name="credentials.roleId"
control={control}
shouldUnregister
render={({ field, fieldState: { error } }) => (
<FormControl
errorText={error?.message}
isError={Boolean(error?.message)}
label="Role ID"
>
<Input {...field} placeholder="00000000-0000-0000-0000-000000000000" />
</FormControl>
)}
/>
<Controller
name="credentials.secretId"
control={control}
shouldUnregister
render={({ field, fieldState: { error } }) => (
<FormControl
errorText={error?.message}
isError={Boolean(error?.message)}
label="Secret ID"
>
<SecretInput
{...field}
containerClassName="text-gray-400 group-focus-within:!border-primary-400/50 border border-mineshaft-500 bg-mineshaft-900 px-2.5 py-1.5"
/>
</FormControl>
)}
/>
</>
)}
<div className="mt-8 flex items-center">
<Button
className="mr-4"
size="sm"
type="submit"
colorSchema="secondary"
isLoading={isSubmitting}
isDisabled={isSubmitting || !isDirty}
>
{isUpdate ? "Update Credentials" : "Connect to Hashicorp Vault"}
</Button>
<ModalClose asChild>
<Button colorSchema="secondary" variant="plain">
Cancel
</Button>
</ModalClose>
</div>
</form>
</FormProvider>
);
};
@@ -0,0 +1,14 @@
import { THCVaultSync } from "@app/hooks/api/secretSyncs/types/hc-vault-sync";
import { getSecretSyncDestinationColValues } from "../helpers";
import { SecretSyncTableCell } from "../SecretSyncTableCell";
type Props = {
secretSync: THCVaultSync;
};
export const HCVaultSyncDestinationCol = ({ secretSync }: Props) => {
const { primaryText, secondaryText } = getSecretSyncDestinationColValues(secretSync);
return <SecretSyncTableCell primaryText={primaryText} secondaryText={secondaryText} />;
};
@@ -8,6 +8,7 @@ import { CamundaSyncDestinationCol } from "./CamundaSyncDestinationCol";
import { DatabricksSyncDestinationCol } from "./DatabricksSyncDestinationCol";
import { GcpSyncDestinationCol } from "./GcpSyncDestinationCol";
import { GitHubSyncDestinationCol } from "./GitHubSyncDestinationCol";
import { HCVaultSyncDestinationCol } from "./HCVaultSyncDestinationCol";
import { HumanitecSyncDestinationCol } from "./HumanitecSyncDestinationCol";
import { TeamCitySyncDestinationCol } from "./TeamCitySyncDestinationCol";
import { TerraformCloudSyncDestinationCol } from "./TerraformCloudSyncDestinationCol";
@@ -44,6 +45,8 @@ export const SecretSyncDestinationCol = ({ secretSync }: Props) => {
return <VercelSyncDestinationCol secretSync={secretSync} />;
case SecretSync.Windmill:
return <WindmillSyncDestinationCol secretSync={secretSync} />;
case SecretSync.HCVault:
return <HCVaultSyncDestinationCol secretSync={secretSync} />;
case SecretSync.TeamCity:
return <TeamCitySyncDestinationCol secretSync={secretSync} />;
default:
@@ -94,6 +94,10 @@ export const getSecretSyncDestinationColValues = (secretSync: TSecretSync) => {
primaryText = destinationConfig.workspace;
secondaryText = destinationConfig.path;
break;
case SecretSync.HCVault:
primaryText = destinationConfig.mount;
secondaryText = destinationConfig.path;
break;
case SecretSync.TeamCity:
primaryText = destinationConfig.project;
secondaryText = destinationConfig.buildConfig;
@@ -0,0 +1,19 @@
import { GenericFieldLabel } from "@app/components/secret-syncs";
import { THCVaultSync } from "@app/hooks/api/secretSyncs/types/hc-vault-sync";
type Props = {
secretSync: THCVaultSync;
};
export const HCVaultSyncDestinationSection = ({ secretSync }: Props) => {
const {
destinationConfig: { path, mount }
} = secretSync;
return (
<>
<GenericFieldLabel label="Secrets Engine Mount">{mount}</GenericFieldLabel>
<GenericFieldLabel label="Path">{path}</GenericFieldLabel>
</>
);
};
@@ -18,6 +18,7 @@ import { CamundaSyncDestinationSection } from "./CamundaSyncDestinationSection";
import { DatabricksSyncDestinationSection } from "./DatabricksSyncDestinationSection";
import { GcpSyncDestinationSection } from "./GcpSyncDestinationSection";
import { GitHubSyncDestinationSection } from "./GitHubSyncDestinationSection";
import { HCVaultSyncDestinationSection } from "./HCVaultSyncDestinationSection";
import { HumanitecSyncDestinationSection } from "./HumanitecSyncDestinationSection";
import { TeamCitySyncDestinationSection } from "./TeamCitySyncDestinationSection";
import { TerraformCloudSyncDestinationSection } from "./TerraformCloudSyncDestinationSection";
@@ -35,7 +36,7 @@ export const SecretSyncDestinationSection = ({ secretSync, onEditDestination }:
const app = APP_CONNECTION_MAP[connection.app].name;
let DestinationComponents: ReactNode;
switch (secretSync.destination) {
switch (destination) {
case SecretSync.AWSParameterStore:
DestinationComponents = <AwsParameterStoreSyncDestinationSection secretSync={secretSync} />;
break;
@@ -74,6 +75,9 @@ export const SecretSyncDestinationSection = ({ secretSync, onEditDestination }:
case SecretSync.Windmill:
DestinationComponents = <WindmillSyncDestinationSection secretSync={secretSync} />;
break;
case SecretSync.HCVault:
DestinationComponents = <HCVaultSyncDestinationSection secretSync={secretSync} />;
break;
case SecretSync.TeamCity:
DestinationComponents = <TeamCitySyncDestinationSection secretSync={secretSync} />;
break;
@@ -52,6 +52,7 @@ export const SecretSyncOptionsSection = ({ secretSync, onEditOptions }: Props) =
case SecretSync.Camunda:
case SecretSync.Vercel:
case SecretSync.Windmill:
case SecretSync.HCVault:
case SecretSync.TeamCity:
AdditionalSyncOptionsComponent = null;
break;