mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-20 13:43:37 +00:00
Merge branch 'main' into ENG-2932
This commit is contained in:
+14
@@ -0,0 +1,14 @@
|
||||
import { TRailwaySync } from "@app/hooks/api/secretSyncs/types/railway-sync";
|
||||
|
||||
import { getSecretSyncDestinationColValues } from "../helpers";
|
||||
import { SecretSyncTableCell } from "../SecretSyncTableCell";
|
||||
|
||||
type Props = {
|
||||
secretSync: TRailwaySync;
|
||||
};
|
||||
|
||||
export const RailwaySyncDestinationCol = ({ secretSync }: Props) => {
|
||||
const { primaryText, secondaryText } = getSecretSyncDestinationColValues(secretSync);
|
||||
|
||||
return <SecretSyncTableCell primaryText={primaryText} secondaryText={secondaryText} />;
|
||||
};
|
||||
+3
@@ -18,6 +18,7 @@ import { HCVaultSyncDestinationCol } from "./HCVaultSyncDestinationCol";
|
||||
import { HerokuSyncDestinationCol } from "./HerokuSyncDestinationCol";
|
||||
import { HumanitecSyncDestinationCol } from "./HumanitecSyncDestinationCol";
|
||||
import { OCIVaultSyncDestinationCol } from "./OCIVaultSyncDestinationCol";
|
||||
import { RailwaySyncDestinationCol } from "./RailwaySyncDestinationCol";
|
||||
import { RenderSyncDestinationCol } from "./RenderSyncDestinationCol";
|
||||
import { TeamCitySyncDestinationCol } from "./TeamCitySyncDestinationCol";
|
||||
import { TerraformCloudSyncDestinationCol } from "./TerraformCloudSyncDestinationCol";
|
||||
@@ -79,6 +80,8 @@ export const SecretSyncDestinationCol = ({ secretSync }: Props) => {
|
||||
return <CloudflareWorkersSyncDestinationCol secretSync={secretSync} />;
|
||||
case SecretSync.Zabbix:
|
||||
return <ZabbixSyncDestinationCol secretSync={secretSync} />;
|
||||
case SecretSync.Railway:
|
||||
return <RailwaySyncDestinationCol secretSync={secretSync} />;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unhandled Secret Sync Destination Col: ${(secretSync as TSecretSync).destination}`
|
||||
|
||||
+4
@@ -160,6 +160,10 @@ export const getSecretSyncDestinationColValues = (secretSync: TSecretSync) => {
|
||||
throw new Error(`Unhandled Zabbix Scope Destination Col Values ${destination}`);
|
||||
}
|
||||
break;
|
||||
case SecretSync.Railway:
|
||||
primaryText = "Railway Project";
|
||||
secondaryText = destinationConfig.projectName;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unhandled Destination Col Values ${destination}`);
|
||||
}
|
||||
|
||||
+21
-16
@@ -55,7 +55,7 @@ const formSchema = z
|
||||
.object({
|
||||
environment: z.object({ slug: z.string(), name: z.string() }),
|
||||
name: z.string().optional(),
|
||||
secretPath: z.string().optional(),
|
||||
secretPath: z.string().trim().min(1),
|
||||
approvals: z.number().min(1).default(1),
|
||||
userApprovers: z
|
||||
.object({ type: z.literal(ApproverType.User), id: z.string() })
|
||||
@@ -93,20 +93,19 @@ const formSchema = z
|
||||
.optional()
|
||||
})
|
||||
.superRefine((data, ctx) => {
|
||||
if (
|
||||
data.policyType === PolicyType.ChangePolicy &&
|
||||
!(data.groupApprovers.length || data.userApprovers.length)
|
||||
) {
|
||||
ctx.addIssue({
|
||||
path: ["userApprovers"],
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "At least one approver should be provided"
|
||||
});
|
||||
ctx.addIssue({
|
||||
path: ["groupApprovers"],
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "At least one approver should be provided"
|
||||
});
|
||||
if (data.policyType === PolicyType.ChangePolicy) {
|
||||
if (!(data.groupApprovers.length || data.userApprovers.length)) {
|
||||
ctx.addIssue({
|
||||
path: ["userApprovers"],
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "At least one approver should be provided"
|
||||
});
|
||||
ctx.addIssue({
|
||||
path: ["groupApprovers"],
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "At least one approver should be provided"
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -127,6 +126,7 @@ const Form = ({
|
||||
control,
|
||||
handleSubmit,
|
||||
watch,
|
||||
resetField,
|
||||
formState: { isSubmitting }
|
||||
} = useForm<TFormSchema>({
|
||||
resolver: zodResolver(formSchema),
|
||||
@@ -177,6 +177,7 @@ const Form = ({
|
||||
: undefined,
|
||||
defaultValues: !editValues
|
||||
? {
|
||||
secretPath: "/",
|
||||
sequenceApprovers: [{ approvals: 1 }]
|
||||
}
|
||||
: undefined
|
||||
@@ -405,7 +406,10 @@ const Form = ({
|
||||
<Select
|
||||
isDisabled={isEditMode}
|
||||
value={value}
|
||||
onValueChange={(val) => onChange(val as PolicyType)}
|
||||
onValueChange={(val) => {
|
||||
onChange(val as PolicyType);
|
||||
resetField("secretPath");
|
||||
}}
|
||||
className="w-full border border-mineshaft-500"
|
||||
>
|
||||
{Object.values(PolicyType).map((policyType) => {
|
||||
@@ -465,6 +469,7 @@ const Form = ({
|
||||
<FormControl
|
||||
tooltipText="Secret paths support glob patterns. For example, '/**' will match all paths."
|
||||
label="Secret Path"
|
||||
isRequired
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
className="flex-1"
|
||||
|
||||
+27
-3
@@ -15,6 +15,7 @@ import {
|
||||
TextArea
|
||||
} from "@app/components/v2";
|
||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
||||
import { useGetServerConfig } from "@app/hooks/api/admin";
|
||||
import {
|
||||
DynamicSecretAwsIamAuth,
|
||||
DynamicSecretProviders
|
||||
@@ -61,6 +62,23 @@ const formSchema = z.object({
|
||||
})
|
||||
)
|
||||
.optional()
|
||||
}),
|
||||
z.object({
|
||||
method: z.literal(DynamicSecretAwsIamAuth.IRSA),
|
||||
region: z.string().trim().min(1),
|
||||
awsPath: z.string().trim().optional(),
|
||||
permissionBoundaryPolicyArn: z.string().trim().optional(),
|
||||
policyDocument: z.string().trim().optional(),
|
||||
userGroups: z.string().trim().optional(),
|
||||
policyArns: z.string().trim().optional(),
|
||||
tags: z
|
||||
.array(
|
||||
z.object({
|
||||
key: z.string().trim().min(1).max(128),
|
||||
value: z.string().trim().min(1).max(256)
|
||||
})
|
||||
)
|
||||
.optional()
|
||||
})
|
||||
]),
|
||||
defaultTTL: z.string().superRefine((val, ctx) => {
|
||||
@@ -106,6 +124,8 @@ export const AwsIamInputForm = ({
|
||||
projectSlug,
|
||||
isSingleEnvironmentMode
|
||||
}: Props) => {
|
||||
const { data: serverConfig } = useGetServerConfig();
|
||||
|
||||
const {
|
||||
control,
|
||||
formState: { isSubmitting },
|
||||
@@ -123,7 +143,7 @@ export const AwsIamInputForm = ({
|
||||
});
|
||||
|
||||
const createDynamicSecret = useCreateDynamicSecret();
|
||||
const isAccessKeyMethod = watch("provider.method") === DynamicSecretAwsIamAuth.AccessKey;
|
||||
const method = watch("provider.method");
|
||||
|
||||
const handleCreateDynamicSecret = async ({
|
||||
name,
|
||||
@@ -237,11 +257,14 @@ export const AwsIamInputForm = ({
|
||||
Assume Role (Recommended)
|
||||
</SelectItem>
|
||||
<SelectItem value={DynamicSecretAwsIamAuth.AccessKey}>Access Key</SelectItem>
|
||||
{serverConfig?.kubernetesAutoFetchServiceAccountToken && (
|
||||
<SelectItem value={DynamicSecretAwsIamAuth.IRSA}>IRSA (EKS)</SelectItem>
|
||||
)}
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
{isAccessKeyMethod ? (
|
||||
{method === DynamicSecretAwsIamAuth.AccessKey && (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Controller
|
||||
control={control}
|
||||
@@ -274,7 +297,8 @@ export const AwsIamInputForm = ({
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
)}
|
||||
{method === DynamicSecretAwsIamAuth.AssumeRole && (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Controller
|
||||
control={control}
|
||||
|
||||
+22
-4
@@ -6,7 +6,7 @@ import { z } from "zod";
|
||||
import { TtlFormLabel } from "@app/components/features";
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { Button, FormControl, Input, Select, SelectItem, TextArea } from "@app/components/v2";
|
||||
import { useUpdateDynamicSecret } from "@app/hooks/api";
|
||||
import { useGetServerConfig, useUpdateDynamicSecret } from "@app/hooks/api";
|
||||
import { DynamicSecretAwsIamAuth, TDynamicSecret } from "@app/hooks/api/dynamicSecret/types";
|
||||
import { slugSchema } from "@app/lib/schemas";
|
||||
|
||||
@@ -40,6 +40,18 @@ const formSchema = z.object({
|
||||
tags: z
|
||||
.array(z.object({ key: z.string().trim().min(1), value: z.string().trim().min(1) }))
|
||||
.optional()
|
||||
}),
|
||||
z.object({
|
||||
method: z.literal(DynamicSecretAwsIamAuth.IRSA),
|
||||
region: z.string().trim().min(1),
|
||||
awsPath: z.string().trim().optional(),
|
||||
permissionBoundaryPolicyArn: z.string().trim().optional(),
|
||||
policyDocument: z.string().trim().optional(),
|
||||
userGroups: z.string().trim().optional(),
|
||||
policyArns: z.string().trim().optional(),
|
||||
tags: z
|
||||
.array(z.object({ key: z.string().trim().min(1), value: z.string().trim().min(1) }))
|
||||
.optional()
|
||||
})
|
||||
]),
|
||||
defaultTTL: z.string().superRefine((val, ctx) => {
|
||||
@@ -83,6 +95,8 @@ export const EditDynamicSecretAwsIamForm = ({
|
||||
secretPath,
|
||||
projectSlug
|
||||
}: Props) => {
|
||||
const { data: serverConfig } = useGetServerConfig();
|
||||
|
||||
const {
|
||||
control,
|
||||
watch,
|
||||
@@ -100,7 +114,7 @@ export const EditDynamicSecretAwsIamForm = ({
|
||||
}
|
||||
}
|
||||
});
|
||||
const isAccessKeyMethod = watch("inputs.method") === DynamicSecretAwsIamAuth.AccessKey;
|
||||
const method = watch("inputs.method");
|
||||
|
||||
const updateDynamicSecret = useUpdateDynamicSecret();
|
||||
|
||||
@@ -214,11 +228,14 @@ export const EditDynamicSecretAwsIamForm = ({
|
||||
Assume Role (Recommended)
|
||||
</SelectItem>
|
||||
<SelectItem value={DynamicSecretAwsIamAuth.AccessKey}>Access Key</SelectItem>
|
||||
{serverConfig?.kubernetesAutoFetchServiceAccountToken && (
|
||||
<SelectItem value={DynamicSecretAwsIamAuth.IRSA}>IRSA (EKS)</SelectItem>
|
||||
)}
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
{isAccessKeyMethod ? (
|
||||
{method === DynamicSecretAwsIamAuth.AccessKey && (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Controller
|
||||
control={control}
|
||||
@@ -251,7 +268,8 @@ export const EditDynamicSecretAwsIamForm = ({
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
)}
|
||||
{method === DynamicSecretAwsIamAuth.AssumeRole && (
|
||||
<div className="flex items-center space-x-2">
|
||||
<Controller
|
||||
control={control}
|
||||
|
||||
+23
-8
@@ -9,7 +9,7 @@ import { twMerge } from "tailwind-merge";
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
// TODO:(akhilmhdh) convert all the util functions like this into a lib folder grouped by functionality
|
||||
import { parseDotEnv, parseJson } from "@app/components/utilities/parseSecrets";
|
||||
import { parseDotEnv, parseJson, parseYaml } from "@app/components/utilities/parseSecrets";
|
||||
import { Button, Lottie, Modal, ModalContent } from "@app/components/v2";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
|
||||
import { usePopUp, useToggle } from "@app/hooks";
|
||||
@@ -127,7 +127,7 @@ export const SecretDropzone = ({
|
||||
}
|
||||
};
|
||||
|
||||
const parseFile = (file?: File, isJson?: boolean) => {
|
||||
const parseFile = (file?: File) => {
|
||||
const reader = new FileReader();
|
||||
if (!file) {
|
||||
createNotification({
|
||||
@@ -140,10 +140,25 @@ export const SecretDropzone = ({
|
||||
setIsLoading.on();
|
||||
reader.onload = (event) => {
|
||||
if (!event?.target?.result) return;
|
||||
// parse function's argument looks like to be ArrayBuffer
|
||||
const env = isJson
|
||||
? parseJson(event.target.result as ArrayBuffer)
|
||||
: parseDotEnv(event.target.result as ArrayBuffer);
|
||||
|
||||
let env: TParsedEnv;
|
||||
|
||||
const src = event.target.result as ArrayBuffer;
|
||||
|
||||
switch (file.type) {
|
||||
case "application/json":
|
||||
env = parseJson(src);
|
||||
break;
|
||||
case "text/yaml":
|
||||
case "application/x-yaml":
|
||||
case "application/yaml":
|
||||
env = parseYaml(src);
|
||||
break;
|
||||
|
||||
default:
|
||||
env = parseDotEnv(src);
|
||||
break;
|
||||
}
|
||||
setIsLoading.off();
|
||||
handleParsedEnv(env);
|
||||
};
|
||||
@@ -165,12 +180,12 @@ export const SecretDropzone = ({
|
||||
|
||||
e.dataTransfer.dropEffect = "copy";
|
||||
setDragActive.off();
|
||||
parseFile(e.dataTransfer.files[0], e.dataTransfer.files[0].type === "application/json");
|
||||
parseFile(e.dataTransfer.files[0]);
|
||||
};
|
||||
|
||||
const handleFileUpload = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
e.preventDefault();
|
||||
parseFile(e.target?.files?.[0], e.target?.files?.[0]?.type === "application/json");
|
||||
parseFile(e.target?.files?.[0]);
|
||||
};
|
||||
|
||||
const handleSaveSecrets = async () => {
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import { GenericFieldLabel } from "@app/components/secret-syncs";
|
||||
import { TRailwaySync } from "@app/hooks/api/secretSyncs/types/railway-sync";
|
||||
|
||||
type Props = {
|
||||
secretSync: TRailwaySync;
|
||||
};
|
||||
|
||||
export const RailwaySyncDestinationSection = ({ secretSync }: Props) => {
|
||||
const { destinationConfig } = secretSync;
|
||||
|
||||
return (
|
||||
<>
|
||||
<GenericFieldLabel label="Project">{destinationConfig.projectName}</GenericFieldLabel>
|
||||
<GenericFieldLabel label="Environment">{destinationConfig.environmentName}</GenericFieldLabel>
|
||||
</>
|
||||
);
|
||||
};
|
||||
+4
@@ -29,6 +29,7 @@ import { HCVaultSyncDestinationSection } from "./HCVaultSyncDestinationSection";
|
||||
import { HerokuSyncDestinationSection } from "./HerokuSyncDestinationSection";
|
||||
import { HumanitecSyncDestinationSection } from "./HumanitecSyncDestinationSection";
|
||||
import { OCIVaultSyncDestinationSection } from "./OCIVaultSyncDestinationSection";
|
||||
import { RailwaySyncDestinationSection } from "./RailwaySyncDestinationSection";
|
||||
import { RenderSyncDestinationSection } from "./RenderSyncDestinationSection";
|
||||
import { TeamCitySyncDestinationSection } from "./TeamCitySyncDestinationSection";
|
||||
import { TerraformCloudSyncDestinationSection } from "./TerraformCloudSyncDestinationSection";
|
||||
@@ -122,6 +123,9 @@ export const SecretSyncDestinationSection = ({ secretSync, onEditDestination }:
|
||||
case SecretSync.Zabbix:
|
||||
DestinationComponents = <ZabbixSyncDestinationSection secretSync={secretSync} />;
|
||||
break;
|
||||
case SecretSync.Railway:
|
||||
DestinationComponents = <RailwaySyncDestinationSection secretSync={secretSync} />;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unhandled Destination Section components: ${destination}`);
|
||||
}
|
||||
|
||||
+1
@@ -62,6 +62,7 @@ export const SecretSyncOptionsSection = ({ secretSync, onEditOptions }: Props) =
|
||||
case SecretSync.CloudflarePages:
|
||||
case SecretSync.CloudflareWorkers:
|
||||
case SecretSync.Zabbix:
|
||||
case SecretSync.Railway:
|
||||
AdditionalSyncOptionsComponent = null;
|
||||
break;
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user