diff --git a/backend/src/ee/services/dynamic-secret/providers/models.ts b/backend/src/ee/services/dynamic-secret/providers/models.ts index 8b72775e9..c57f14477 100644 --- a/backend/src/ee/services/dynamic-secret/providers/models.ts +++ b/backend/src/ee/services/dynamic-secret/providers/models.ts @@ -182,9 +182,9 @@ export const DynamicSecretSnowflakeSchema = z.object({ orgId: z.string().trim().min(1), username: z.string().trim().min(1), password: z.string().trim().min(1), - creationStatement: z.string().trim(), - revocationStatement: z.string().trim(), - renewStatement: z.string().trim() + creationStatement: z.string().trim().min(1), + revocationStatement: z.string().trim().min(1), + renewStatement: z.string().trim().optional() }); export const AzureEntraIDSchema = z.object({ diff --git a/backend/src/ee/services/dynamic-secret/providers/snowflake.ts b/backend/src/ee/services/dynamic-secret/providers/snowflake.ts index 6c505fa24..623e821e3 100644 --- a/backend/src/ee/services/dynamic-secret/providers/snowflake.ts +++ b/backend/src/ee/services/dynamic-secret/providers/snowflake.ts @@ -3,6 +3,7 @@ import { customAlphabet } from "nanoid"; import snowflake from "snowflake-sdk"; import { z } from "zod"; +import { BadRequestError } from "@app/lib/errors"; import { alphaNumericNanoId } from "@app/lib/nanoid"; import { DynamicSecretSnowflakeSchema, TDynamicProviderFns } from "./models"; @@ -41,15 +42,7 @@ export const SnowflakeProvider = (): TDynamicProviderFns => { application: "Infisical" }); - await new Promise((resolve, reject) => { - client.connect((err) => { - if (err) { - return reject(err); - } - - return resolve(true); - }); - }); + await client.connectAsync(noop); return client; }; @@ -58,9 +51,20 @@ export const SnowflakeProvider = (): TDynamicProviderFns => { const providerInputs = await validateProviderInputs(inputs); const client = await getClient(providerInputs); - const isValidConnection = await client.isValidAsync(); + let isValidConnection: boolean; - client.destroy(noop); + try { + isValidConnection = await Promise.race([ + client.isValidAsync(), + new Promise((resolve) => { + setTimeout(resolve, 10000); + }).then(() => { + throw new BadRequestError({ message: "Unable to establish connection - verify credentials" }); + }) + ]); + } finally { + client.destroy(noop); + } return isValidConnection; }; @@ -72,15 +76,15 @@ export const SnowflakeProvider = (): TDynamicProviderFns => { const username = generateUsername(); const password = generatePassword(); - const expiration = getDaysToExpiry(new Date(expireAt)); - - const creationStatement = handlebars.compile(providerInputs.creationStatement, { noEscape: true })({ - username, - password, - expiration - }); try { + const expiration = getDaysToExpiry(new Date(expireAt)); + const creationStatement = handlebars.compile(providerInputs.creationStatement, { noEscape: true })({ + username, + password, + expiration + }); + await new Promise((resolve, reject) => { client.execute({ sqlText: creationStatement, @@ -105,9 +109,9 @@ export const SnowflakeProvider = (): TDynamicProviderFns => { const client = await getClient(providerInputs); - const revokeStatement = handlebars.compile(providerInputs.revocationStatement)({ username }); - try { + const revokeStatement = handlebars.compile(providerInputs.revocationStatement)({ username }); + await new Promise((resolve, reject) => { client.execute({ sqlText: revokeStatement, @@ -132,14 +136,13 @@ export const SnowflakeProvider = (): TDynamicProviderFns => { const client = await getClient(providerInputs); - const expiration = getDaysToExpiry(new Date(expireAt)); - - const renewStatement = handlebars.compile(providerInputs.renewStatement)({ - username, - expiration - }); - try { + const expiration = getDaysToExpiry(new Date(expireAt)); + const renewStatement = handlebars.compile(providerInputs.renewStatement)({ + username, + expiration + }); + await new Promise((resolve, reject) => { client.execute({ sqlText: renewStatement, diff --git a/frontend/src/hooks/api/dynamicSecret/types.ts b/frontend/src/hooks/api/dynamicSecret/types.ts index 517beb39c..4f2a787b9 100644 --- a/frontend/src/hooks/api/dynamicSecret/types.ts +++ b/frontend/src/hooks/api/dynamicSecret/types.ts @@ -226,7 +226,7 @@ export type TDynamicSecretProvider = password: string; creationStatement: string; revocationStatement: string; - renewStatement: string; + renewStatement?: string; }; }; export type TCreateDynamicSecretDTO = { diff --git a/frontend/src/views/SecretMainPage/components/ActionBar/CreateDynamicSecretForm/SnowflakeInputForm.tsx b/frontend/src/views/SecretMainPage/components/ActionBar/CreateDynamicSecretForm/SnowflakeInputForm.tsx index b6ab3e8d2..deb765a50 100644 --- a/frontend/src/views/SecretMainPage/components/ActionBar/CreateDynamicSecretForm/SnowflakeInputForm.tsx +++ b/frontend/src/views/SecretMainPage/components/ActionBar/CreateDynamicSecretForm/SnowflakeInputForm.tsx @@ -29,7 +29,7 @@ const formSchema = z.object({ password: z.string().trim().min(1), creationStatement: z.string().trim().min(1), revocationStatement: z.string().trim().min(1), - renewStatement: z.string().trim().min(1) + renewStatement: z.string().trim().optional() }), defaultTTL: z.string().superRefine((val, ctx) => { const valMs = ms(val); @@ -51,7 +51,11 @@ const formSchema = z.object({ 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() + .trim() + .min(1) + .refine((val) => val.toLowerCase() === val, "Must be lowercase") }); type TForm = z.infer; @@ -105,7 +109,7 @@ export const SnowflakeInputForm = ({ } catch (err) { createNotification({ type: "error", - text: "Failed to create dynamic secret" + text: err instanceof Error ? err.message : "Failed to create dynamic secret" }); } }; diff --git a/frontend/src/views/SecretMainPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretSnowflakeForm.tsx b/frontend/src/views/SecretMainPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretSnowflakeForm.tsx index cd58828fc..83961c3b7 100644 --- a/frontend/src/views/SecretMainPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretSnowflakeForm.tsx +++ b/frontend/src/views/SecretMainPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretSnowflakeForm.tsx @@ -28,9 +28,9 @@ const formSchema = z.object({ orgId: z.string().min(1), username: z.string().min(1), password: z.string().min(1), - creationStatement: z.string().min(1), - revocationStatement: z.string().min(1), - renewStatement: z.string().optional() + creationStatement: z.string().trim().min(1), + revocationStatement: z.string().trim().min(1), + renewStatement: z.string().trim().optional() }) .partial(), defaultTTL: z.string().superRefine((val, ctx) => { @@ -53,7 +53,11 @@ const formSchema = z.object({ if (valMs > 24 * 60 * 60 * 1000) ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" }); }), - newName: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase") + newName: z + .string() + .trim() + .min(1) + .refine((val) => val.toLowerCase() === val, "Must be lowercase") }); type TForm = z.infer; @@ -114,7 +118,7 @@ export const EditDynamicSecretSnowflakeForm = ({ } catch (err) { createNotification({ type: "error", - text: "Failed to update dynamic secret" + text: err instanceof Error ? err.message : "Failed to create dynamic secret" }); } };