mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
fix: refactor and handle modify
This commit is contained in:
@@ -5,11 +5,10 @@ import { render } from "mustache";
|
|||||||
import { customAlphabet } from "nanoid";
|
import { customAlphabet } from "nanoid";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { logger } from "@app/lib/logger";
|
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
|
|
||||||
import { LdapSchema, TDynamicProviderFns } from "./models";
|
import { LdapSchema, TDynamicProviderFns } from "./models";
|
||||||
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
const ldif = require("ldif");
|
const ldif = require("ldif");
|
||||||
|
|
||||||
const generatePassword = () => {
|
const generatePassword = () => {
|
||||||
@@ -17,8 +16,15 @@ const generatePassword = () => {
|
|||||||
return customAlphabet(charset, 64)();
|
return customAlphabet(charset, 64)();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const encodePassword = (password?: string) => {
|
||||||
|
const quotedPassword = `"${password}"`;
|
||||||
|
const utf16lePassword = Buffer.from(quotedPassword, "utf16le");
|
||||||
|
const base64Password = utf16lePassword.toString("base64");
|
||||||
|
return base64Password;
|
||||||
|
}
|
||||||
|
|
||||||
const generateUsername = () => {
|
const generateUsername = () => {
|
||||||
return alphaNumericNanoId(32);
|
return alphaNumericNanoId(20);
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateLDIF = ({
|
const generateLDIF = ({
|
||||||
@@ -30,9 +36,11 @@ const generateLDIF = ({
|
|||||||
password?: string;
|
password?: string;
|
||||||
ldifTemplate: string;
|
ldifTemplate: string;
|
||||||
}): string => {
|
}): string => {
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
Username: username,
|
Username: username,
|
||||||
Password: password
|
Password: password,
|
||||||
|
EncodedPassword: encodePassword(password)
|
||||||
};
|
};
|
||||||
|
|
||||||
const ldif = render(ldifTemplate, data);
|
const ldif = render(ldifTemplate, data);
|
||||||
@@ -47,7 +55,7 @@ export const LdapProvider = (): TDynamicProviderFns => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getClient = async (providerInputs: z.infer<typeof LdapSchema>): Promise<ldapjs.Client> => {
|
const getClient = async (providerInputs: z.infer<typeof LdapSchema>): Promise<ldapjs.Client> => {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve, reject) => {
|
||||||
const client = ldapjs.createClient({
|
const client = ldapjs.createClient({
|
||||||
url: providerInputs.url,
|
url: providerInputs.url,
|
||||||
tlsOptions: {
|
tlsOptions: {
|
||||||
@@ -57,18 +65,17 @@ export const LdapProvider = (): TDynamicProviderFns => {
|
|||||||
reconnect: true,
|
reconnect: true,
|
||||||
bindDN: providerInputs.binddn,
|
bindDN: providerInputs.binddn,
|
||||||
bindCredentials: providerInputs.bindpass,
|
bindCredentials: providerInputs.bindpass,
|
||||||
log: logger
|
|
||||||
});
|
});
|
||||||
|
|
||||||
client.on("error", (err) => {
|
client.on("error", (err) => {
|
||||||
client.unbind();
|
client.unbind();
|
||||||
throw new Error(err.message);
|
reject(new BadRequestError({ message: err.message }));
|
||||||
});
|
});
|
||||||
|
|
||||||
client.bind(providerInputs.binddn, providerInputs.bindpass, (err) => {
|
client.bind(providerInputs.binddn, providerInputs.bindpass, (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
client.unbind();
|
client.unbind();
|
||||||
throw new Error(err.message);
|
reject(new BadRequestError({ message: err.message }));
|
||||||
} else {
|
} else {
|
||||||
resolve(client);
|
resolve(client);
|
||||||
}
|
}
|
||||||
@@ -100,10 +107,10 @@ export const LdapProvider = (): TDynamicProviderFns => {
|
|||||||
attributes[attrName] = Array.isArray(attrValue) ? attrValue : [attrValue];
|
attributes[attrName] = Array.isArray(attrValue) ? attrValue : [attrValue];
|
||||||
});
|
});
|
||||||
|
|
||||||
response_dn = await new Promise((resolve) => {
|
response_dn = await new Promise((resolve, reject) => {
|
||||||
client.add(dn, attributes, (err) => {
|
client.add(dn, attributes, (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw new Error(err.message);
|
reject(new BadRequestError({ message: err.message }));
|
||||||
} else {
|
} else {
|
||||||
resolve(dn);
|
resolve(dn);
|
||||||
}
|
}
|
||||||
@@ -117,28 +124,27 @@ export const LdapProvider = (): TDynamicProviderFns => {
|
|||||||
new ldapjs.Change({
|
new ldapjs.Change({
|
||||||
operation: change.operation || "replace",
|
operation: change.operation || "replace",
|
||||||
modification: {
|
modification: {
|
||||||
[change.attribute.attribute]: Array.isArray(change.value.value)
|
type: change.attribute.attribute,
|
||||||
? change.value.value
|
values: change.values.map((value: any) => value.value)
|
||||||
: [change.value.value]
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
response_dn = await new Promise((resolve) => {
|
response_dn = await new Promise((resolve, reject) => {
|
||||||
client.modify(dn, changes, (err) => {
|
client.modify(dn, changes, (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw new Error(err.message);
|
reject(new BadRequestError({ message: err.message }));
|
||||||
} else {
|
} else {
|
||||||
resolve(dn);
|
resolve(dn);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else if (entry.type === "delete") {
|
} else if (entry.type === "delete") {
|
||||||
response_dn = await new Promise((resolve) => {
|
response_dn = await new Promise((resolve, reject) => {
|
||||||
client.del(dn, (err) => {
|
client.del(dn, (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw new Error(err.message);
|
reject(new BadRequestError({ message: err.message }));
|
||||||
} else {
|
} else {
|
||||||
resolve(dn);
|
resolve(dn);
|
||||||
}
|
}
|
||||||
@@ -168,11 +174,11 @@ export const LdapProvider = (): TDynamicProviderFns => {
|
|||||||
|
|
||||||
return { entityId: username, data: { DN_ARRAY: dnArray, USERNAME: username, PASSWORD: password } };
|
return { entityId: username, data: { DN_ARRAY: dnArray, USERNAME: username, PASSWORD: password } };
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const rollbackLdif = generateLDIF({ username, password, ldifTemplate: providerInputs.rollbackLdif });
|
if (providerInputs.rollbackLdif) {
|
||||||
|
const rollbackLdif = generateLDIF({ username, password, ldifTemplate: providerInputs.rollbackLdif });
|
||||||
await executeLdif(client, rollbackLdif);
|
await executeLdif(client, rollbackLdif);
|
||||||
|
}
|
||||||
throw new Error((err as Error).message);
|
throw new BadRequestError({ message: (err as Error).message });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -180,9 +180,9 @@ export const LdapSchema = z.object({
|
|||||||
bindpass: z.string().trim().min(1),
|
bindpass: z.string().trim().min(1),
|
||||||
ca: z.string().optional(),
|
ca: z.string().optional(),
|
||||||
|
|
||||||
creationLdif: z.string().trim().min(1),
|
creationLdif: z.string().min(1),
|
||||||
revocationLdif: z.string().trim().min(1),
|
revocationLdif: z.string().min(1),
|
||||||
rollbackLdif: z.string().trim().min(1)
|
rollbackLdif: z.string().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export enum DynamicSecretProviders {
|
export enum DynamicSecretProviders {
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ export type TDynamicSecretProvider =
|
|||||||
ca?: string | undefined;
|
ca?: string | undefined;
|
||||||
creationLdif: string;
|
creationLdif: string;
|
||||||
revocationLdif: string;
|
revocationLdif: string;
|
||||||
rollbackLdif: string;
|
rollbackLdif?: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
;
|
;
|
||||||
|
|||||||
@@ -10,274 +10,270 @@ import { useCreateDynamicSecret } from "@app/hooks/api";
|
|||||||
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
provider: z.object({
|
provider: z.object({
|
||||||
url: z.string().trim().min(1),
|
url: z.string().trim().min(1),
|
||||||
binddn: z.string().trim().min(1),
|
binddn: z.string().trim().min(1),
|
||||||
bindpass: z.string().trim().min(1),
|
bindpass: z.string().trim().min(1),
|
||||||
ca: z.string().optional(),
|
ca: z.string().optional(),
|
||||||
|
|
||||||
creationLdif: z.string().trim().min(1),
|
creationLdif: z.string().min(1),
|
||||||
revocationLdif: z.string().trim().min(1),
|
revocationLdif: z.string().min(1),
|
||||||
rollbackLdif: z.string().trim().min(1),
|
rollbackLdif: z.string().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)
|
||||||
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be a greater than 1min" });
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be a greater than 1min" });
|
||||||
// a day
|
// a day
|
||||||
if (valMs > 24 * 60 * 60 * 1000)
|
if (valMs > 24 * 60 * 60 * 1000)
|
||||||
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
||||||
|
}),
|
||||||
|
maxTTL: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.superRefine((val, ctx) => {
|
||||||
|
if (!val) return;
|
||||||
|
const valMs = ms(val);
|
||||||
|
if (valMs < 60 * 1000)
|
||||||
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be a greater than 1min" });
|
||||||
|
// a day
|
||||||
|
if (valMs > 24 * 60 * 60 * 1000)
|
||||||
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
||||||
}),
|
}),
|
||||||
maxTTL: z
|
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
||||||
.string()
|
|
||||||
.optional()
|
|
||||||
.superRefine((val, ctx) => {
|
|
||||||
if (!val) return;
|
|
||||||
const valMs = ms(val);
|
|
||||||
if (valMs < 60 * 1000)
|
|
||||||
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be a greater than 1min" });
|
|
||||||
// a day
|
|
||||||
if (valMs > 24 * 60 * 60 * 1000)
|
|
||||||
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" });
|
|
||||||
}),
|
|
||||||
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
|
||||||
});
|
});
|
||||||
|
|
||||||
type TForm = z.infer<typeof formSchema>;
|
type TForm = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
onCompleted: () => void;
|
onCompleted: () => void;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
projectSlug: string;
|
projectSlug: string;
|
||||||
environment: string;
|
environment: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LdapInputForm = (
|
export const LdapInputForm = ({
|
||||||
{
|
onCompleted,
|
||||||
onCompleted,
|
onCancel,
|
||||||
onCancel,
|
secretPath,
|
||||||
secretPath,
|
projectSlug,
|
||||||
|
environment
|
||||||
|
}: Props) => {
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
formState: { isSubmitting },
|
||||||
|
handleSubmit
|
||||||
|
} = useForm<TForm>({
|
||||||
|
resolver: zodResolver(formSchema),
|
||||||
|
defaultValues: {
|
||||||
|
provider: {
|
||||||
|
url: "",
|
||||||
|
binddn: "",
|
||||||
|
bindpass: "",
|
||||||
|
ca: "",
|
||||||
|
creationLdif: "",
|
||||||
|
revocationLdif: "",
|
||||||
|
rollbackLdif: ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const createDynamicSecret = useCreateDynamicSecret();
|
||||||
|
|
||||||
|
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
||||||
|
// wait till previous request is finished
|
||||||
|
if (createDynamicSecret.isLoading) return;
|
||||||
|
try {
|
||||||
|
await createDynamicSecret.mutateAsync({
|
||||||
|
provider: { type: DynamicSecretProviders.Ldap, inputs: provider },
|
||||||
|
maxTTL,
|
||||||
|
name,
|
||||||
|
path: secretPath,
|
||||||
|
defaultTTL,
|
||||||
projectSlug,
|
projectSlug,
|
||||||
environment,
|
environmentSlug: environment
|
||||||
}: Props
|
});
|
||||||
) => {
|
onCompleted();
|
||||||
const {
|
} catch (err) {
|
||||||
control,
|
createNotification({
|
||||||
formState: { isSubmitting },
|
type: "error",
|
||||||
handleSubmit
|
text: "Failed to create dynamic secret"
|
||||||
} = useForm<TForm>({
|
});
|
||||||
resolver: zodResolver(formSchema),
|
}
|
||||||
defaultValues: {
|
};
|
||||||
provider: {
|
|
||||||
url: "",
|
|
||||||
binddn: "",
|
|
||||||
bindpass: "",
|
|
||||||
ca: "",
|
|
||||||
creationLdif: "",
|
|
||||||
revocationLdif: "",
|
|
||||||
rollbackLdif: ""
|
|
||||||
},
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const createDynamicSecret = useCreateDynamicSecret();
|
return (
|
||||||
|
<form onSubmit={handleSubmit(handleCreateDynamicSecret)} autoComplete="off">
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<div className="flex-grow">
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue=""
|
||||||
|
name="name"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Secret Name"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} placeholder="dynamic-secret" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="w-32">
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="defaultTTL"
|
||||||
|
defaultValue="1h"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label={<TtlFormLabel label="Default TTL" />}
|
||||||
|
isError={Boolean(error?.message)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="w-32">
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="maxTTL"
|
||||||
|
defaultValue="24h"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label={<TtlFormLabel label="Max TTL" />}
|
||||||
|
isError={Boolean(error?.message)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="mb-4 mt-4 border-b border-mineshaft-500 pb-2 pl-1 font-medium text-mineshaft-200">
|
||||||
|
Configuration
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<div className="flex-grow">
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="provider.url"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="URL"
|
||||||
|
isError={Boolean(error?.message)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
<Controller
|
||||||
// wait till previous request is finished
|
control={control}
|
||||||
if (createDynamicSecret.isLoading) return;
|
name="provider.binddn"
|
||||||
try {
|
render={({ field, fieldState: { error } }) => (
|
||||||
await createDynamicSecret.mutateAsync({
|
<FormControl
|
||||||
provider: { type: DynamicSecretProviders.Ldap, inputs: provider },
|
label="Bind DN"
|
||||||
maxTTL,
|
isError={Boolean(error?.message)}
|
||||||
name,
|
errorText={error?.message}
|
||||||
path: secretPath,
|
>
|
||||||
defaultTTL,
|
<Input {...field} />
|
||||||
projectSlug,
|
</FormControl>
|
||||||
environmentSlug: environment
|
)}
|
||||||
});
|
/>
|
||||||
onCompleted();
|
|
||||||
} catch (err) {
|
|
||||||
createNotification({
|
|
||||||
type: "error",
|
|
||||||
text: "Failed to create dynamic secret"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
<Controller
|
||||||
<form onSubmit={handleSubmit(handleCreateDynamicSecret)} autoComplete="off">
|
control={control}
|
||||||
<div>
|
name="provider.bindpass"
|
||||||
<div className="flex items-center space-x-2">
|
render={({ field, fieldState: { error } }) => (
|
||||||
<div className="flex-grow">
|
<FormControl
|
||||||
<Controller
|
label="Bind Password"
|
||||||
control={control}
|
isError={Boolean(error?.message)}
|
||||||
defaultValue=""
|
errorText={error?.message}
|
||||||
name="name"
|
>
|
||||||
render={({ field, fieldState: { error } }) => (
|
<Input {...field} type="password" />
|
||||||
<FormControl
|
</FormControl>
|
||||||
label="Secret Name"
|
)}
|
||||||
isError={Boolean(error)}
|
/>
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="dynamic-secret" />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="w-32">
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="defaultTTL"
|
|
||||||
defaultValue="1h"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label={<TtlFormLabel label="Default TTL" />}
|
|
||||||
isError={Boolean(error?.message)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="w-32">
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="maxTTL"
|
|
||||||
defaultValue="24h"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label={<TtlFormLabel label="Max TTL" />}
|
|
||||||
isError={Boolean(error?.message)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="mb-4 mt-4 border-b border-mineshaft-500 pb-2 pl-1 font-medium text-mineshaft-200">
|
|
||||||
Configuration
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
<div className="flex-grow">
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="provider.url"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="URL"
|
|
||||||
isError={Boolean(error?.message)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="provider.binddn"
|
name="provider.ca"
|
||||||
render={({ field, fieldState: { error } }) => (
|
render={({ field, fieldState: { error } }) => (
|
||||||
<FormControl
|
<FormControl
|
||||||
label="Bind DN"
|
label="CA"
|
||||||
isError={Boolean(error?.message)}
|
isError={Boolean(error?.message)}
|
||||||
errorText={error?.message}
|
errorText={error?.message}
|
||||||
>
|
>
|
||||||
<Input {...field} />
|
<TextArea {...field} placeholder="-----BEGIN CERTIFICATE----- ..." />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="provider.bindpass"
|
name="provider.creationLdif"
|
||||||
render={({ field, fieldState: { error } }) => (
|
render={({ field, fieldState: { error } }) => (
|
||||||
<FormControl
|
<FormControl
|
||||||
label="Bind Password"
|
label="Creation LDIF"
|
||||||
isError={Boolean(error?.message)}
|
isError={Boolean(error?.message)}
|
||||||
errorText={error?.message}
|
errorText={error?.message}
|
||||||
>
|
>
|
||||||
<Input {...field} />
|
<TextArea {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="provider.ca"
|
name="provider.revocationLdif"
|
||||||
render={({ field, fieldState: { error } }) => (
|
render={({ field, fieldState: { error } }) => (
|
||||||
<FormControl
|
<FormControl
|
||||||
label="CA"
|
label="Revocation LDIF"
|
||||||
isError={Boolean(error?.message)}
|
isError={Boolean(error?.message)}
|
||||||
errorText={error?.message}
|
errorText={error?.message}
|
||||||
>
|
>
|
||||||
<TextArea {...field} placeholder="-----BEGIN CERTIFICATE----- ..." />
|
<TextArea {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="provider.creationLdif"
|
name="provider.rollbackLdif"
|
||||||
render={({ field, fieldState: { error } }) => (
|
render={({ field, fieldState: { error } }) => (
|
||||||
<FormControl
|
<FormControl
|
||||||
label="Creation LDIF"
|
label="Rollback LDIF"
|
||||||
isError={Boolean(error?.message)}
|
isError={Boolean(error?.message)}
|
||||||
errorText={error?.message}
|
errorText={error?.message}
|
||||||
>
|
>
|
||||||
<TextArea {...field} />
|
<TextArea {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
<Controller
|
</div>
|
||||||
control={control}
|
</div>
|
||||||
name="provider.revocationLdif"
|
</div>
|
||||||
render={({ field, fieldState: { error } }) => (
|
</div>
|
||||||
<FormControl
|
<div className="mt-4 flex items-center space-x-4">
|
||||||
label="Revocation LDIF"
|
<Button type="submit" isLoading={isSubmitting}>
|
||||||
isError={Boolean(error?.message)}
|
Submit
|
||||||
errorText={error?.message}
|
</Button>
|
||||||
>
|
<Button variant="outline_bg" onClick={onCancel}>
|
||||||
<TextArea {...field} />
|
Cancel
|
||||||
</FormControl>
|
</Button>
|
||||||
)}
|
</div>
|
||||||
/>
|
</form>
|
||||||
|
);
|
||||||
<Controller
|
};
|
||||||
control={control}
|
|
||||||
name="provider.rollbackLdif"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Rollback LDIF"
|
|
||||||
isError={Boolean(error?.message)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<TextArea {...field} />
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="mt-4 flex items-center space-x-4">
|
|
||||||
<Button type="submit" isLoading={isSubmitting}>
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
<Button variant="outline_bg" onClick={onCancel}>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -12,13 +12,13 @@ import { TDynamicSecret } from "@app/hooks/api/dynamicSecret/types";
|
|||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
inputs: z
|
inputs: z
|
||||||
.object({
|
.object({
|
||||||
url: z.string().trim().min(1),
|
url: z.string().trim().min(1),
|
||||||
binddn: z.string().trim().min(1),
|
binddn: z.string().trim().min(1),
|
||||||
bindpass: z.string().trim().min(1),
|
bindpass: z.string().trim().min(1),
|
||||||
ca: z.string().optional(),
|
ca: z.string().optional(),
|
||||||
creationLdif: z.string().trim().min(1),
|
creationLdif: z.string().min(1),
|
||||||
revocationLdif: z.string().trim().min(1),
|
revocationLdif: z.string().min(1),
|
||||||
rollbackLdif: z.string().trim().min(1),
|
rollbackLdif: z.string().optional()
|
||||||
})
|
})
|
||||||
.partial(),
|
.partial(),
|
||||||
defaultTTL: z.string().superRefine((val, ctx) => {
|
defaultTTL: z.string().superRefine((val, ctx) => {
|
||||||
@@ -58,62 +58,62 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const EditDynamicSecretLdapForm = ({
|
export const EditDynamicSecretLdapForm = ({
|
||||||
onClose,
|
onClose,
|
||||||
dynamicSecret,
|
dynamicSecret,
|
||||||
environment,
|
environment,
|
||||||
secretPath,
|
secretPath,
|
||||||
projectSlug
|
projectSlug
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
formState: { isSubmitting },
|
formState: { isSubmitting },
|
||||||
handleSubmit
|
handleSubmit
|
||||||
} = useForm<TForm>({
|
} = useForm<TForm>({
|
||||||
resolver: zodResolver(formSchema),
|
resolver: zodResolver(formSchema),
|
||||||
values: {
|
values: {
|
||||||
defaultTTL: dynamicSecret.defaultTTL,
|
defaultTTL: dynamicSecret.defaultTTL,
|
||||||
maxTTL: dynamicSecret.maxTTL,
|
maxTTL: dynamicSecret.maxTTL,
|
||||||
newName: dynamicSecret.name,
|
newName: dynamicSecret.name,
|
||||||
inputs: {
|
inputs: {
|
||||||
...(dynamicSecret.inputs as TForm["inputs"])
|
...(dynamicSecret.inputs as TForm["inputs"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateDynamicSecret = useUpdateDynamicSecret();
|
||||||
|
|
||||||
|
const handleUpdateDynamicSecret = async ({ inputs, maxTTL, defaultTTL, newName }: TForm) => {
|
||||||
|
// wait till previous request is finished
|
||||||
|
if (updateDynamicSecret.isLoading) return;
|
||||||
|
try {
|
||||||
|
await updateDynamicSecret.mutateAsync({
|
||||||
|
name: dynamicSecret.name,
|
||||||
|
path: secretPath,
|
||||||
|
projectSlug,
|
||||||
|
environmentSlug: environment,
|
||||||
|
data: {
|
||||||
|
maxTTL: maxTTL || undefined,
|
||||||
|
defaultTTL,
|
||||||
|
inputs,
|
||||||
|
newName: newName === dynamicSecret.name ? undefined : newName
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
onClose();
|
||||||
|
createNotification({
|
||||||
const updateDynamicSecret = useUpdateDynamicSecret();
|
type: "success",
|
||||||
|
text: "Successfully updated dynamic secret"
|
||||||
const handleUpdateDynamicSecret = async ({ inputs, maxTTL, defaultTTL, newName }: TForm) => {
|
});
|
||||||
// wait till previous request is finished
|
} catch (err) {
|
||||||
if (updateDynamicSecret.isLoading) return;
|
createNotification({
|
||||||
try {
|
type: "error",
|
||||||
await updateDynamicSecret.mutateAsync({
|
text: "Failed to update dynamic secret"
|
||||||
name: dynamicSecret.name,
|
});
|
||||||
path: secretPath,
|
}
|
||||||
projectSlug,
|
};
|
||||||
environmentSlug: environment,
|
|
||||||
data: {
|
return (
|
||||||
maxTTL: maxTTL || undefined,
|
<div>
|
||||||
defaultTTL,
|
<form onSubmit={handleSubmit(handleUpdateDynamicSecret)} autoComplete="off">
|
||||||
inputs,
|
|
||||||
newName: newName === dynamicSecret.name ? undefined : newName
|
|
||||||
}
|
|
||||||
});
|
|
||||||
onClose();
|
|
||||||
createNotification({
|
|
||||||
type: "success",
|
|
||||||
text: "Successfully updated dynamic secret"
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
createNotification({
|
|
||||||
type: "error",
|
|
||||||
text: "Failed to update dynamic secret"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<form onSubmit={handleSubmit(handleUpdateDynamicSecret)} autoComplete="off">
|
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<div className="flex-grow">
|
<div className="flex-grow">
|
||||||
<Controller
|
<Controller
|
||||||
@@ -162,107 +162,95 @@ export const EditDynamicSecretLdapForm = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="inputs.url"
|
name="inputs.url"
|
||||||
render={({ field, fieldState: { error } }) => (
|
render={({ field, fieldState: { error } }) => (
|
||||||
<FormControl
|
<FormControl label="URL" isError={Boolean(error)} errorText={error?.message}>
|
||||||
label="URL"
|
<Input {...field} />
|
||||||
isError={Boolean(error)}
|
</FormControl>
|
||||||
errorText={error?.message}
|
)}
|
||||||
>
|
/>
|
||||||
<Input {...field} />
|
<Controller
|
||||||
</FormControl>
|
control={control}
|
||||||
)}
|
name="inputs.binddn"
|
||||||
/>
|
render={({ field, fieldState: { error } }) => (
|
||||||
<Controller
|
<FormControl label="Bind DN" isError={Boolean(error)} errorText={error?.message}>
|
||||||
control={control}
|
<Input {...field} />
|
||||||
name="inputs.binddn"
|
</FormControl>
|
||||||
render={({ field, fieldState: { error } }) => (
|
)}
|
||||||
<FormControl
|
/>
|
||||||
label="Bind DN"
|
<Controller
|
||||||
isError={Boolean(error)}
|
control={control}
|
||||||
errorText={error?.message}
|
name="inputs.bindpass"
|
||||||
>
|
render={({ field, fieldState: { error } }) => (
|
||||||
<Input {...field} />
|
<FormControl
|
||||||
</FormControl>
|
label="Bind Password"
|
||||||
)}
|
isError={Boolean(error)}
|
||||||
/>
|
errorText={error?.message}
|
||||||
<Controller
|
>
|
||||||
control={control}
|
<Input {...field} type="password" />
|
||||||
name="inputs.bindpass"
|
</FormControl>
|
||||||
render={({ field, fieldState: { error } }) => (
|
)}
|
||||||
<FormControl
|
/>
|
||||||
label="Bind Password"
|
<Controller
|
||||||
isError={Boolean(error)}
|
control={control}
|
||||||
errorText={error?.message}
|
name="inputs.ca"
|
||||||
>
|
render={({ field, fieldState: { error } }) => (
|
||||||
<Input {...field} />
|
<FormControl label="CA" isError={Boolean(error)} errorText={error?.message}>
|
||||||
</FormControl>
|
<TextArea {...field} />
|
||||||
)}
|
</FormControl>
|
||||||
/>
|
)}
|
||||||
<Controller
|
/>
|
||||||
control={control}
|
<Controller
|
||||||
name="inputs.ca"
|
control={control}
|
||||||
render={({ field, fieldState: { error } }) => (
|
name="inputs.creationLdif"
|
||||||
<FormControl
|
render={({ field, fieldState: { error } }) => (
|
||||||
label="CA"
|
<FormControl
|
||||||
isError={Boolean(error)}
|
label="Creation LDIF"
|
||||||
errorText={error?.message}
|
isError={Boolean(error)}
|
||||||
>
|
errorText={error?.message}
|
||||||
<TextArea {...field} />
|
>
|
||||||
</FormControl>
|
<TextArea {...field} />
|
||||||
)}
|
</FormControl>
|
||||||
/>
|
)}
|
||||||
<Controller
|
/>
|
||||||
control={control}
|
<Controller
|
||||||
name="inputs.creationLdif"
|
control={control}
|
||||||
render={({ field, fieldState: { error } }) => (
|
name="inputs.revocationLdif"
|
||||||
<FormControl
|
render={({ field, fieldState: { error } }) => (
|
||||||
label="Creation LDIF"
|
<FormControl
|
||||||
isError={Boolean(error)}
|
label="Revocation LDIF"
|
||||||
errorText={error?.message}
|
isError={Boolean(error)}
|
||||||
>
|
errorText={error?.message}
|
||||||
<TextArea {...field} />
|
>
|
||||||
</FormControl>
|
<TextArea {...field} />
|
||||||
)}
|
</FormControl>
|
||||||
/>
|
)}
|
||||||
<Controller
|
/>
|
||||||
control={control}
|
<Controller
|
||||||
name="inputs.revocationLdif"
|
control={control}
|
||||||
render={({ field, fieldState: { error } }) => (
|
name="inputs.rollbackLdif"
|
||||||
<FormControl
|
render={({ field, fieldState: { error } }) => (
|
||||||
label="Revocation LDIF"
|
<FormControl
|
||||||
isError={Boolean(error)}
|
label="Rollback LDIF"
|
||||||
errorText={error?.message}
|
isError={Boolean(error)}
|
||||||
>
|
errorText={error?.message}
|
||||||
<TextArea {...field} />
|
>
|
||||||
</FormControl>
|
<TextArea {...field} />
|
||||||
)}
|
</FormControl>
|
||||||
/>
|
)}
|
||||||
<Controller
|
/>
|
||||||
control={control}
|
</div>
|
||||||
name="inputs.rollbackLdif"
|
<div className="mt-4 flex items-center space-x-4">
|
||||||
render={({ field, fieldState: { error } }) => (
|
<Button type="submit" isLoading={isSubmitting}>
|
||||||
<FormControl
|
Submit
|
||||||
label="Rollback LDIF"
|
</Button>
|
||||||
isError={Boolean(error)}
|
<Button variant="outline_bg" onClick={onClose}>
|
||||||
errorText={error?.message}
|
Cancel
|
||||||
>
|
</Button>
|
||||||
<TextArea {...field} />
|
</div>
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="mt-4 flex items-center space-x-4">
|
|
||||||
<Button type="submit" isLoading={isSubmitting}>
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
<Button variant="outline_bg" onClick={onClose}>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user