From e0199084ad0eda88871db81ed886fc00a3fcec6e Mon Sep 17 00:00:00 2001 From: Meet Date: Wed, 2 Oct 2024 20:51:02 +0530 Subject: [PATCH] fix: refactor and handle modify --- .../services/dynamic-secret/providers/ldap.ts | 50 +- .../dynamic-secret/providers/models.ts | 6 +- frontend/src/hooks/api/dynamicSecret/types.ts | 2 +- .../CreateDynamicSecretForm/LdapInputForm.tsx | 502 +++++++++--------- .../EditDynamicSecretLdapForm.tsx | 318 ++++++----- 5 files changed, 434 insertions(+), 444 deletions(-) diff --git a/backend/src/ee/services/dynamic-secret/providers/ldap.ts b/backend/src/ee/services/dynamic-secret/providers/ldap.ts index 026849ff4..9d41a6d7f 100644 --- a/backend/src/ee/services/dynamic-secret/providers/ldap.ts +++ b/backend/src/ee/services/dynamic-secret/providers/ldap.ts @@ -5,11 +5,10 @@ import { render } from "mustache"; import { customAlphabet } from "nanoid"; import { z } from "zod"; -import { logger } from "@app/lib/logger"; import { alphaNumericNanoId } from "@app/lib/nanoid"; import { LdapSchema, TDynamicProviderFns } from "./models"; - +import { BadRequestError } from "@app/lib/errors"; const ldif = require("ldif"); const generatePassword = () => { @@ -17,8 +16,15 @@ const generatePassword = () => { 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 = () => { - return alphaNumericNanoId(32); + return alphaNumericNanoId(20); }; const generateLDIF = ({ @@ -30,9 +36,11 @@ const generateLDIF = ({ password?: string; ldifTemplate: string; }): string => { + const data = { Username: username, - Password: password + Password: password, + EncodedPassword: encodePassword(password) }; const ldif = render(ldifTemplate, data); @@ -47,7 +55,7 @@ export const LdapProvider = (): TDynamicProviderFns => { }; const getClient = async (providerInputs: z.infer): Promise => { - return new Promise((resolve) => { + return new Promise((resolve, reject) => { const client = ldapjs.createClient({ url: providerInputs.url, tlsOptions: { @@ -57,18 +65,17 @@ export const LdapProvider = (): TDynamicProviderFns => { reconnect: true, bindDN: providerInputs.binddn, bindCredentials: providerInputs.bindpass, - log: logger }); client.on("error", (err) => { client.unbind(); - throw new Error(err.message); + reject(new BadRequestError({ message: err.message })); }); client.bind(providerInputs.binddn, providerInputs.bindpass, (err) => { if (err) { client.unbind(); - throw new Error(err.message); + reject(new BadRequestError({ message: err.message })); } else { resolve(client); } @@ -100,10 +107,10 @@ export const LdapProvider = (): TDynamicProviderFns => { 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) => { if (err) { - throw new Error(err.message); + reject(new BadRequestError({ message: err.message })); } else { resolve(dn); } @@ -117,28 +124,27 @@ export const LdapProvider = (): TDynamicProviderFns => { new ldapjs.Change({ operation: change.operation || "replace", modification: { - [change.attribute.attribute]: Array.isArray(change.value.value) - ? change.value.value - : [change.value.value] + type: change.attribute.attribute, + values: change.values.map((value: any) => value.value) } }) ); }); - response_dn = await new Promise((resolve) => { + response_dn = await new Promise((resolve, reject) => { client.modify(dn, changes, (err) => { if (err) { - throw new Error(err.message); + reject(new BadRequestError({ message: err.message })); } else { resolve(dn); } }); }); } else if (entry.type === "delete") { - response_dn = await new Promise((resolve) => { + response_dn = await new Promise((resolve, reject) => { client.del(dn, (err) => { if (err) { - throw new Error(err.message); + reject(new BadRequestError({ message: err.message })); } else { resolve(dn); } @@ -168,11 +174,11 @@ export const LdapProvider = (): TDynamicProviderFns => { return { entityId: username, data: { DN_ARRAY: dnArray, USERNAME: username, PASSWORD: password } }; } catch (err) { - const rollbackLdif = generateLDIF({ username, password, ldifTemplate: providerInputs.rollbackLdif }); - - await executeLdif(client, rollbackLdif); - - throw new Error((err as Error).message); + if (providerInputs.rollbackLdif) { + const rollbackLdif = generateLDIF({ username, password, ldifTemplate: providerInputs.rollbackLdif }); + await executeLdif(client, rollbackLdif); + } + throw new BadRequestError({ message: (err as Error).message }); } }; diff --git a/backend/src/ee/services/dynamic-secret/providers/models.ts b/backend/src/ee/services/dynamic-secret/providers/models.ts index 2f6f82c04..c204333ce 100644 --- a/backend/src/ee/services/dynamic-secret/providers/models.ts +++ b/backend/src/ee/services/dynamic-secret/providers/models.ts @@ -180,9 +180,9 @@ export const LdapSchema = z.object({ bindpass: z.string().trim().min(1), ca: z.string().optional(), - creationLdif: z.string().trim().min(1), - revocationLdif: z.string().trim().min(1), - rollbackLdif: z.string().trim().min(1) + creationLdif: z.string().min(1), + revocationLdif: z.string().min(1), + rollbackLdif: z.string().optional() }); export enum DynamicSecretProviders { diff --git a/frontend/src/hooks/api/dynamicSecret/types.ts b/frontend/src/hooks/api/dynamicSecret/types.ts index f4bd05bfa..792fad8d3 100644 --- a/frontend/src/hooks/api/dynamicSecret/types.ts +++ b/frontend/src/hooks/api/dynamicSecret/types.ts @@ -199,7 +199,7 @@ export type TDynamicSecretProvider = ca?: string | undefined; creationLdif: string; revocationLdif: string; - rollbackLdif: string; + rollbackLdif?: string; }; }; ; diff --git a/frontend/src/views/SecretMainPage/components/ActionBar/CreateDynamicSecretForm/LdapInputForm.tsx b/frontend/src/views/SecretMainPage/components/ActionBar/CreateDynamicSecretForm/LdapInputForm.tsx index 818fee386..c59f914b5 100644 --- a/frontend/src/views/SecretMainPage/components/ActionBar/CreateDynamicSecretForm/LdapInputForm.tsx +++ b/frontend/src/views/SecretMainPage/components/ActionBar/CreateDynamicSecretForm/LdapInputForm.tsx @@ -10,274 +10,270 @@ import { useCreateDynamicSecret } from "@app/hooks/api"; import { DynamicSecretProviders } from "@app/hooks/api/dynamicSecret/types"; const formSchema = z.object({ - provider: z.object({ - url: z.string().trim().min(1), - binddn: z.string().trim().min(1), - bindpass: z.string().trim().min(1), - ca: z.string().optional(), + provider: z.object({ + url: z.string().trim().min(1), + binddn: z.string().trim().min(1), + bindpass: z.string().trim().min(1), + ca: z.string().optional(), - creationLdif: z.string().trim().min(1), - revocationLdif: z.string().trim().min(1), - rollbackLdif: z.string().trim().min(1), - }), + creationLdif: z.string().min(1), + revocationLdif: z.string().min(1), + rollbackLdif: z.string().optional() + }), - defaultTTL: z.string().superRefine((val, ctx) => { - 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" }); + defaultTTL: z.string().superRefine((val, ctx) => { + 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 + .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 - .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") + name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase") }); type TForm = z.infer; type Props = { - onCompleted: () => void; - onCancel: () => void; - secretPath: string; - projectSlug: string; - environment: string; + onCompleted: () => void; + onCancel: () => void; + secretPath: string; + projectSlug: string; + environment: string; }; -export const LdapInputForm = ( - { - onCompleted, - onCancel, - secretPath, +export const LdapInputForm = ({ + onCompleted, + onCancel, + secretPath, + projectSlug, + environment +}: Props) => { + const { + control, + formState: { isSubmitting }, + handleSubmit + } = useForm({ + 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, - environment, - }: Props -) => { - const { - control, - formState: { isSubmitting }, - handleSubmit - } = useForm({ - resolver: zodResolver(formSchema), - defaultValues: { - provider: { - url: "", - binddn: "", - bindpass: "", - ca: "", - creationLdif: "", - revocationLdif: "", - rollbackLdif: "" - }, - } - }); + environmentSlug: environment + }); + onCompleted(); + } catch (err) { + createNotification({ + type: "error", + text: "Failed to create dynamic secret" + }); + } + }; - const createDynamicSecret = useCreateDynamicSecret(); + return ( +
+
+
+
+ ( + + + + )} + /> +
+
+ ( + } + isError={Boolean(error?.message)} + errorText={error?.message} + > + + + )} + /> +
+
+ ( + } + isError={Boolean(error?.message)} + errorText={error?.message} + > + + + )} + /> +
+
+
+
+ Configuration +
+
+
+
+ ( + + + + )} + /> - 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, - environmentSlug: environment - }); - onCompleted(); - } catch (err) { - createNotification({ - type: "error", - text: "Failed to create dynamic secret" - }); - } - }; + ( + + + + )} + /> - return ( - -
-
-
- ( - - - - )} - /> -
-
- ( - } - isError={Boolean(error?.message)} - errorText={error?.message} - > - - - )} - /> -
-
- ( - } - isError={Boolean(error?.message)} - errorText={error?.message} - > - - - )} - /> -
-
-
-
- Configuration -
-
-
-
- ( - - - - )} - /> + ( + + + + )} + /> - ( - - - - )} - /> + ( + +