mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 14:27:30 +00:00
feat: ui for aws dynamic secret
This commit is contained in:
@@ -44,6 +44,11 @@ export enum SqlProviders {
|
|||||||
MsSQL = "mssql"
|
MsSQL = "mssql"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum DynamicSecretAwsIamAuth {
|
||||||
|
AssumeRole = "assume-role",
|
||||||
|
AccessKey = "access-key"
|
||||||
|
}
|
||||||
|
|
||||||
export type TDynamicSecretProvider =
|
export type TDynamicSecretProvider =
|
||||||
| {
|
| {
|
||||||
type: DynamicSecretProviders.SqlDatabase;
|
type: DynamicSecretProviders.SqlDatabase;
|
||||||
@@ -78,7 +83,9 @@ export type TDynamicSecretProvider =
|
|||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: DynamicSecretProviders.AwsIam;
|
type: DynamicSecretProviders.AwsIam;
|
||||||
inputs: {
|
inputs:
|
||||||
|
| {
|
||||||
|
method: DynamicSecretAwsIamAuth.AccessKey;
|
||||||
accessKey: string;
|
accessKey: string;
|
||||||
secretAccessKey: string;
|
secretAccessKey: string;
|
||||||
region: string;
|
region: string;
|
||||||
@@ -86,6 +93,15 @@ export type TDynamicSecretProvider =
|
|||||||
policyDocument?: string;
|
policyDocument?: string;
|
||||||
userGroups?: string;
|
userGroups?: string;
|
||||||
policyArns?: string;
|
policyArns?: string;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
method: DynamicSecretAwsIamAuth.AssumeRole;
|
||||||
|
roleArn: string;
|
||||||
|
region: string;
|
||||||
|
awsPath?: string;
|
||||||
|
policyDocument?: string;
|
||||||
|
userGroups?: string;
|
||||||
|
policyArns?: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
|
|||||||
+79
-6
@@ -5,13 +5,26 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { TtlFormLabel } from "@app/components/features";
|
import { TtlFormLabel } from "@app/components/features";
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { Button, FilterableSelect, FormControl, Input, TextArea } from "@app/components/v2";
|
import {
|
||||||
|
Button,
|
||||||
|
FilterableSelect,
|
||||||
|
FormControl,
|
||||||
|
Input,
|
||||||
|
Select,
|
||||||
|
SelectItem,
|
||||||
|
TextArea
|
||||||
|
} from "@app/components/v2";
|
||||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
import { useCreateDynamicSecret } from "@app/hooks/api";
|
||||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
import {
|
||||||
|
DynamicSecretAwsIamAuth,
|
||||||
|
DynamicSecretProviders
|
||||||
|
} from "@app/hooks/api/dynamicSecret/types";
|
||||||
import { WorkspaceEnv } from "@app/hooks/api/types";
|
import { WorkspaceEnv } from "@app/hooks/api/types";
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
provider: z.object({
|
provider: z.discriminatedUnion("method", [
|
||||||
|
z.object({
|
||||||
|
method: z.literal(DynamicSecretAwsIamAuth.AccessKey),
|
||||||
accessKey: z.string().trim().min(1),
|
accessKey: z.string().trim().min(1),
|
||||||
secretAccessKey: z.string().trim().min(1),
|
secretAccessKey: z.string().trim().min(1),
|
||||||
region: z.string().trim().min(1),
|
region: z.string().trim().min(1),
|
||||||
@@ -21,6 +34,17 @@ const formSchema = z.object({
|
|||||||
userGroups: z.string().trim().optional(),
|
userGroups: z.string().trim().optional(),
|
||||||
policyArns: z.string().trim().optional()
|
policyArns: z.string().trim().optional()
|
||||||
}),
|
}),
|
||||||
|
z.object({
|
||||||
|
method: z.literal(DynamicSecretAwsIamAuth.AssumeRole),
|
||||||
|
roleArn: z.string().trim().min(1),
|
||||||
|
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()
|
||||||
|
})
|
||||||
|
]),
|
||||||
defaultTTL: z.string().superRefine((val, ctx) => {
|
defaultTTL: z.string().superRefine((val, ctx) => {
|
||||||
const valMs = ms(val);
|
const valMs = ms(val);
|
||||||
if (valMs < 60 * 1000)
|
if (valMs < 60 * 1000)
|
||||||
@@ -67,16 +91,21 @@ export const AwsIamInputForm = ({
|
|||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
formState: { isSubmitting },
|
formState: { isSubmitting },
|
||||||
handleSubmit
|
handleSubmit,
|
||||||
|
watch
|
||||||
} = useForm<TForm>({
|
} = useForm<TForm>({
|
||||||
resolver: zodResolver(formSchema),
|
resolver: zodResolver(formSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
environment: isSingleEnvironmentMode ? environments[0] : undefined,
|
environment: isSingleEnvironmentMode ? environments[0] : undefined,
|
||||||
usernameTemplate: "{{randomUsername}}"
|
usernameTemplate: "{{randomUsername}}",
|
||||||
|
provider: {
|
||||||
|
method: DynamicSecretAwsIamAuth.AssumeRole
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const createDynamicSecret = useCreateDynamicSecret();
|
const createDynamicSecret = useCreateDynamicSecret();
|
||||||
|
const isAccessKeyMethod = watch("provider.method") === DynamicSecretAwsIamAuth.AccessKey;
|
||||||
|
|
||||||
const handleCreateDynamicSecret = async ({
|
const handleCreateDynamicSecret = async ({
|
||||||
name,
|
name,
|
||||||
@@ -127,7 +156,7 @@ export const AwsIamInputForm = ({
|
|||||||
isError={Boolean(error)}
|
isError={Boolean(error)}
|
||||||
errorText={error?.message}
|
errorText={error?.message}
|
||||||
>
|
>
|
||||||
<Input {...field} placeholder="dynamic-postgres" />
|
<Input {...field} placeholder="dynamic-aws-iam" />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -170,6 +199,31 @@ export const AwsIamInputForm = ({
|
|||||||
Configuration
|
Configuration
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
|
<Controller
|
||||||
|
name="provider.method"
|
||||||
|
control={control}
|
||||||
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
errorText={error?.message}
|
||||||
|
isError={Boolean(error?.message)}
|
||||||
|
label="Method"
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
value={value}
|
||||||
|
onValueChange={(val) => onChange(val)}
|
||||||
|
className="w-full border border-mineshaft-500"
|
||||||
|
position="popper"
|
||||||
|
dropdownContainerClassName="max-w-none"
|
||||||
|
>
|
||||||
|
<SelectItem value={DynamicSecretAwsIamAuth.AssumeRole}>
|
||||||
|
Assume Role (Recommended)
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value={DynamicSecretAwsIamAuth.AccessKey}>Access Key</SelectItem>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{isAccessKeyMethod ? (
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
@@ -202,6 +256,25 @@ export const AwsIamInputForm = ({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="provider.roleArn"
|
||||||
|
defaultValue=""
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Assume Role ARN"
|
||||||
|
className="flex-grow"
|
||||||
|
isError={Boolean(error?.message)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|||||||
+62
-5
@@ -5,14 +5,15 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { TtlFormLabel } from "@app/components/features";
|
import { TtlFormLabel } from "@app/components/features";
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { Button, FormControl, Input, TextArea } from "@app/components/v2";
|
import { Button, FormControl, Input, Select, SelectItem, TextArea } from "@app/components/v2";
|
||||||
import { useUpdateDynamicSecret } from "@app/hooks/api";
|
import { useUpdateDynamicSecret } from "@app/hooks/api";
|
||||||
import { TDynamicSecret } from "@app/hooks/api/dynamicSecret/types";
|
import { DynamicSecretAwsIamAuth, TDynamicSecret } from "@app/hooks/api/dynamicSecret/types";
|
||||||
import { slugSchema } from "@app/lib/schemas";
|
import { slugSchema } from "@app/lib/schemas";
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
inputs: z
|
inputs: z.discriminatedUnion("method", [
|
||||||
.object({
|
z.object({
|
||||||
|
method: z.literal(DynamicSecretAwsIamAuth.AccessKey),
|
||||||
accessKey: z.string().trim().min(1),
|
accessKey: z.string().trim().min(1),
|
||||||
secretAccessKey: z.string().trim().min(1),
|
secretAccessKey: z.string().trim().min(1),
|
||||||
region: z.string().trim().min(1),
|
region: z.string().trim().min(1),
|
||||||
@@ -21,8 +22,18 @@ const formSchema = z.object({
|
|||||||
policyDocument: z.string().trim().optional(),
|
policyDocument: z.string().trim().optional(),
|
||||||
userGroups: z.string().trim().optional(),
|
userGroups: z.string().trim().optional(),
|
||||||
policyArns: z.string().trim().optional()
|
policyArns: z.string().trim().optional()
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
method: z.literal(DynamicSecretAwsIamAuth.AssumeRole),
|
||||||
|
roleArn: z.string().trim().min(1),
|
||||||
|
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()
|
||||||
})
|
})
|
||||||
.partial(),
|
]),
|
||||||
defaultTTL: z.string().superRefine((val, ctx) => {
|
defaultTTL: z.string().superRefine((val, ctx) => {
|
||||||
const valMs = ms(val);
|
const valMs = ms(val);
|
||||||
if (valMs < 60 * 1000)
|
if (valMs < 60 * 1000)
|
||||||
@@ -66,6 +77,7 @@ export const EditDynamicSecretAwsIamForm = ({
|
|||||||
}: Props) => {
|
}: Props) => {
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
|
watch,
|
||||||
formState: { isSubmitting },
|
formState: { isSubmitting },
|
||||||
handleSubmit
|
handleSubmit
|
||||||
} = useForm<TForm>({
|
} = useForm<TForm>({
|
||||||
@@ -80,6 +92,7 @@ export const EditDynamicSecretAwsIamForm = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const isAccessKeyMethod = watch("inputs.method") === DynamicSecretAwsIamAuth.AccessKey;
|
||||||
|
|
||||||
const updateDynamicSecret = useUpdateDynamicSecret();
|
const updateDynamicSecret = useUpdateDynamicSecret();
|
||||||
|
|
||||||
@@ -173,6 +186,31 @@ export const EditDynamicSecretAwsIamForm = ({
|
|||||||
<div>
|
<div>
|
||||||
<div className="mb-4 border-b border-b-mineshaft-600 pb-2">Configuration</div>
|
<div className="mb-4 border-b border-b-mineshaft-600 pb-2">Configuration</div>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
|
<Controller
|
||||||
|
name="inputs.method"
|
||||||
|
control={control}
|
||||||
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
errorText={error?.message}
|
||||||
|
isError={Boolean(error?.message)}
|
||||||
|
label="Method"
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
value={value}
|
||||||
|
onValueChange={(val) => onChange(val)}
|
||||||
|
className="w-full border border-mineshaft-500"
|
||||||
|
position="popper"
|
||||||
|
dropdownContainerClassName="max-w-none"
|
||||||
|
>
|
||||||
|
<SelectItem value={DynamicSecretAwsIamAuth.AssumeRole}>
|
||||||
|
Assume Role (Recommended)
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value={DynamicSecretAwsIamAuth.AccessKey}>Access Key</SelectItem>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{isAccessKeyMethod ? (
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
@@ -205,6 +243,25 @@ export const EditDynamicSecretAwsIamForm = ({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="inputs.roleArn"
|
||||||
|
defaultValue=""
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Assume Role ARN"
|
||||||
|
className="flex-grow"
|
||||||
|
isError={Boolean(error?.message)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|||||||
Reference in New Issue
Block a user