From 8e3559828f7745b4ff708867bb9ca2b7d85ac448 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 28 May 2025 16:09:12 +0530 Subject: [PATCH] feat: ui changes for input template --- frontend/src/hooks/api/dynamicSecret/types.ts | 3 ++ .../AwsElastiCacheInputForm.tsx | 34 +++++++++++++++-- .../AwsIamInputForm.tsx | 33 ++++++++++++++-- .../CassandraInputForm.tsx | 33 ++++++++++++++-- .../ElasticSearchInputForm.tsx | 33 ++++++++++++++-- .../CreateDynamicSecretForm/LdapInputForm.tsx | 32 ++++++++++++++-- .../MongoAtlasInputForm.tsx | 37 +++++++++++++++--- .../MongoDBInputForm.tsx | 36 +++++++++++++++--- .../RabbitMqInputForm.tsx | 34 +++++++++++++++-- .../RedisInputForm.tsx | 32 ++++++++++++++-- .../SapAseInputForm.tsx | 34 +++++++++++++++-- .../SapHanaInputForm.tsx | 30 ++++++++++++++- .../SnowflakeInputForm.tsx | 34 +++++++++++++++-- .../SqlDatabaseInputForm.tsx | 34 +++++++++++++++-- ...ynamicSecretAwsElastiCacheProviderForm.tsx | 34 +++++++++++++++-- .../EditDynamicSecretAwsIamForm.tsx | 34 +++++++++++++++-- .../EditDynamicSecretCassandraForm.tsx | 36 ++++++++++++++++-- .../EditDynamicSecretElasticSearchForm.tsx | 35 +++++++++++++++-- .../EditDynamicSecretLdapForm.tsx | 35 +++++++++++++++-- .../EditDynamicSecretMongoAtlasForm.tsx | 37 ++++++++++++++++-- .../EditDynamicSecretMongoDBForm.tsx | 38 +++++++++++++++++-- .../EditDynamicSecretRabbitMqForm.tsx | 37 ++++++++++++++++-- .../EditDynamicSecretRedisProviderForm.tsx | 33 +++++++++++++++- .../EditDynamicSecretSapAseForm.tsx | 33 +++++++++++++++- .../EditDynamicSecretSapHanaForm.tsx | 35 +++++++++++++++-- .../EditDynamicSecretSnowflakeForm.tsx | 34 +++++++++++++++-- .../EditDynamicSecretSqlProviderForm.tsx | 30 +++++++++++++-- 27 files changed, 800 insertions(+), 90 deletions(-) diff --git a/frontend/src/hooks/api/dynamicSecret/types.ts b/frontend/src/hooks/api/dynamicSecret/types.ts index 1aedf264f..7c61dc7a4 100644 --- a/frontend/src/hooks/api/dynamicSecret/types.ts +++ b/frontend/src/hooks/api/dynamicSecret/types.ts @@ -13,6 +13,7 @@ export type TDynamicSecret = { status?: DynamicSecretStatus; statusDetails?: string; maxTTL: string; + usernameTemplate?: string | null; metadata?: { key: string; value: string }[]; }; @@ -272,6 +273,7 @@ export type TCreateDynamicSecretDTO = { environmentSlug: string; name: string; metadata?: { key: string; value: string }[]; + usernameTemplate?: string; }; export type TUpdateDynamicSecretDTO = { @@ -285,6 +287,7 @@ export type TUpdateDynamicSecretDTO = { defaultTTL?: string; maxTTL?: string | null; inputs?: unknown; + usernameTemplate?: string | null; }; }; diff --git a/frontend/src/pages/secret-manager/SecretDashboardPage/components/ActionBar/CreateDynamicSecretForm/AwsElastiCacheInputForm.tsx b/frontend/src/pages/secret-manager/SecretDashboardPage/components/ActionBar/CreateDynamicSecretForm/AwsElastiCacheInputForm.tsx index 1c513d44a..84cf7f7b5 100644 --- a/frontend/src/pages/secret-manager/SecretDashboardPage/components/ActionBar/CreateDynamicSecretForm/AwsElastiCacheInputForm.tsx +++ b/frontend/src/pages/secret-manager/SecretDashboardPage/components/ActionBar/CreateDynamicSecretForm/AwsElastiCacheInputForm.tsx @@ -53,7 +53,8 @@ const formSchema = z.object({ 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"), - environment: z.object({ name: z.string(), slug: z.string() }) + environment: z.object({ name: z.string(), slug: z.string() }), + usernameTemplate: z.string().nullable().optional() }); type TForm = z.infer; @@ -93,7 +94,8 @@ export const AwsElastiCacheInputForm = ({ "UserId": "{{username}}" }` }, - environment: isSingleEnvironmentMode ? environments[0] : undefined + environment: isSingleEnvironmentMode ? environments[0] : undefined, + usernameTemplate: "{{randomUsername}}" } }); @@ -104,10 +106,13 @@ export const AwsElastiCacheInputForm = ({ maxTTL, provider, defaultTTL, - environment + environment, + usernameTemplate }: TForm) => { // wait till previous request is finished if (createDynamicSecret.isPending) return; + + const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}"; try { await createDynamicSecret.mutateAsync({ provider: { type: DynamicSecretProviders.AwsElastiCache, inputs: provider }, @@ -116,7 +121,9 @@ export const AwsElastiCacheInputForm = ({ path: secretPath, defaultTTL, projectSlug, - environmentSlug: environment.slug + environmentSlug: environment.slug, + usernameTemplate: + !usernameTemplate || isDefaultUsernameTemplate ? undefined : usernameTemplate }); onCompleted(); } catch { @@ -270,6 +277,25 @@ export const AwsElastiCacheInputForm = ({ Modify ElastiCache Statements + ( + + + + )} + /> val.toLowerCase() === val, "Must be lowercase"), - environment: z.object({ name: z.string(), slug: z.string() }) + environment: z.object({ name: z.string(), slug: z.string() }), + usernameTemplate: z.string().nullable().optional() }); type TForm = z.infer; @@ -70,7 +71,8 @@ export const AwsIamInputForm = ({ } = useForm({ resolver: zodResolver(formSchema), defaultValues: { - environment: isSingleEnvironmentMode ? environments[0] : undefined + environment: isSingleEnvironmentMode ? environments[0] : undefined, + usernameTemplate: "{{randomUsername}}" } }); @@ -81,12 +83,14 @@ export const AwsIamInputForm = ({ maxTTL, provider, defaultTTL, - environment + environment, + usernameTemplate }: TForm) => { // wait till previous request is finished if (createDynamicSecret.isPending) return; try { + const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}"; await createDynamicSecret.mutateAsync({ provider: { type: DynamicSecretProviders.AwsIam, inputs: provider }, maxTTL, @@ -94,7 +98,9 @@ export const AwsIamInputForm = ({ path: secretPath, defaultTTL, projectSlug, - environmentSlug: environment.slug + environmentSlug: environment.slug, + usernameTemplate: + !usernameTemplate || isDefaultUsernameTemplate ? undefined : usernameTemplate }); onCompleted(); } catch { @@ -300,6 +306,25 @@ export const AwsIamInputForm = ({ )} /> + ( + + + + )} + /> {!isSingleEnvironmentMode && ( val.toLowerCase() === val, "Must be lowercase"), - environment: z.object({ name: z.string(), slug: z.string() }) + environment: z.object({ name: z.string(), slug: z.string() }), + usernameTemplate: z.string().nullable().optional() }); type TForm = z.infer; @@ -93,7 +94,8 @@ export const CassandraInputForm = ({ resolver: zodResolver(formSchema), defaultValues: { provider: getSqlStatements(), - environment: isSingleEnvironmentMode ? environments[0] : undefined + environment: isSingleEnvironmentMode ? environments[0] : undefined, + usernameTemplate: "{{randomUsername}}" } }); @@ -104,11 +106,13 @@ export const CassandraInputForm = ({ maxTTL, provider, defaultTTL, - environment + environment, + usernameTemplate }: TForm) => { // wait till previous request is finished if (createDynamicSecret.isPending) return; + const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}"; try { await createDynamicSecret.mutateAsync({ provider: { type: DynamicSecretProviders.Cassandra, inputs: provider }, @@ -117,7 +121,9 @@ export const CassandraInputForm = ({ path: secretPath, defaultTTL, projectSlug, - environmentSlug: environment.slug + environmentSlug: environment.slug, + usernameTemplate: + !usernameTemplate || isDefaultUsernameTemplate ? undefined : usernameTemplate }); onCompleted(); } catch { @@ -298,6 +304,25 @@ export const CassandraInputForm = ({ Modify CQL Statements + ( + + + + )} + /> val.toLowerCase() === val, "Must be lowercase"), - environment: z.object({ name: z.string(), slug: z.string() }) + environment: z.object({ name: z.string(), slug: z.string() }), + usernameTemplate: z.string().nullable().optional() }); type TForm = z.infer; @@ -113,7 +114,8 @@ export const ElasticSearchInputForm = ({ roles: ["superuser"], port: 443 }, - environment: isSingleEnvironmentMode ? environments[0] : undefined + environment: isSingleEnvironmentMode ? environments[0] : undefined, + usernameTemplate: "{{randomUsername}}" } }); @@ -124,10 +126,12 @@ export const ElasticSearchInputForm = ({ maxTTL, provider, defaultTTL, - environment + environment, + usernameTemplate }: TForm) => { // wait till previous request is finished if (createDynamicSecret.isPending) return; + const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}"; try { await createDynamicSecret.mutateAsync({ provider: { type: DynamicSecretProviders.ElasticSearch, inputs: provider }, @@ -136,7 +140,9 @@ export const ElasticSearchInputForm = ({ path: secretPath, defaultTTL, projectSlug, - environmentSlug: environment.slug + environmentSlug: environment.slug, + usernameTemplate: + !usernameTemplate || isDefaultUsernameTemplate ? undefined : usernameTemplate }); onCompleted(); } catch { @@ -418,6 +424,25 @@ export const ElasticSearchInputForm = ({ )} /> + ( + + + + )} + /> {!isSingleEnvironmentMode && ( val.toLowerCase() === val, "Must be lowercase"), - environment: z.object({ name: z.string(), slug: z.string() }) + environment: z.object({ name: z.string(), slug: z.string() }), + usernameTemplate: z.string().nullable().optional() }); type TForm = z.infer; @@ -120,7 +121,8 @@ export const LdapInputForm = ({ rollbackLdif: "", credentialType: CredentialType.Dynamic }, - environment: isSingleEnvironmentMode ? environments[0] : undefined + environment: isSingleEnvironmentMode ? environments[0] : undefined, + usernameTemplate: "{{randomUsername}}" } }); @@ -133,10 +135,13 @@ export const LdapInputForm = ({ maxTTL, provider, defaultTTL, - environment + environment, + usernameTemplate }: TForm) => { // wait till previous request is finished if (createDynamicSecret.isPending) return; + + const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}"; try { await createDynamicSecret.mutateAsync({ provider: { type: DynamicSecretProviders.Ldap, inputs: provider }, @@ -145,6 +150,8 @@ export const LdapInputForm = ({ path: secretPath, defaultTTL, projectSlug, + usernameTemplate: + !usernameTemplate || isDefaultUsernameTemplate ? undefined : usernameTemplate, environmentSlug: environment.slug }); onCompleted(); @@ -413,6 +420,25 @@ export const LdapInputForm = ({ )} + ( + + + + )} + /> diff --git a/frontend/src/pages/secret-manager/SecretDashboardPage/components/ActionBar/CreateDynamicSecretForm/MongoAtlasInputForm.tsx b/frontend/src/pages/secret-manager/SecretDashboardPage/components/ActionBar/CreateDynamicSecretForm/MongoAtlasInputForm.tsx index 821802954..c39791ea6 100644 --- a/frontend/src/pages/secret-manager/SecretDashboardPage/components/ActionBar/CreateDynamicSecretForm/MongoAtlasInputForm.tsx +++ b/frontend/src/pages/secret-manager/SecretDashboardPage/components/ActionBar/CreateDynamicSecretForm/MongoAtlasInputForm.tsx @@ -66,7 +66,8 @@ const formSchema = z.object({ 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"), - environment: z.object({ name: z.string(), slug: z.string() }) + environment: z.object({ name: z.string(), slug: z.string() }), + usernameTemplate: z.string().nullable().optional() }); type TForm = z.infer; @@ -114,7 +115,8 @@ export const MongoAtlasInputForm = ({ provider: { roles: [{ databaseName: "", roleName: "" }] }, - environment: isSingleEnvironmentMode ? environments[0] : undefined + environment: isSingleEnvironmentMode ? environments[0] : undefined, + usernameTemplate: "{{randomUsername}}" } }); @@ -135,10 +137,13 @@ export const MongoAtlasInputForm = ({ maxTTL, provider, defaultTTL, - environment + environment, + usernameTemplate }: TForm) => { // wait till previous request is finished if (createDynamicSecret.isPending) return; + + const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}"; try { await createDynamicSecret.mutateAsync({ provider: { type: DynamicSecretProviders.MongoAtlas, inputs: provider }, @@ -147,7 +152,9 @@ export const MongoAtlasInputForm = ({ path: secretPath, defaultTTL, projectSlug, - environmentSlug: environment.slug + environmentSlug: environment.slug, + usernameTemplate: + !usernameTemplate || isDefaultUsernameTemplate ? undefined : usernameTemplate }); onCompleted(); } catch { @@ -312,7 +319,7 @@ export const MongoAtlasInputForm = ({ label="Role" className="text-xs text-mineshaft-400" tooltipClassName="max-w-md whitespace-pre-line" - tooltipText={`Human-readable label that identifies a group of privileges assigned to a database user. This value can either be a built-in role or a custom role. + tooltipText={`Human-readable label that identifies a group of privileges assigned to a database user. This value can either be a built-in role or a custom role. Built-in: atlasAdmin, backup, clusterMonitor, dbAdmin, dbAdminAnyDatabase, enableSharding, read, readAnyDatabase, readWrite, readWriteAnyDatabase.`} /> )} @@ -362,6 +369,26 @@ export const MongoAtlasInputForm = ({ Advanced + ( + + + + )} + /> val.toLowerCase() === val, "Must be lowercase"), - environment: z.object({ name: z.string(), slug: z.string() }) + environment: z.object({ name: z.string(), slug: z.string() }), + usernameTemplate: z.string().nullable().optional() }); type TForm = z.infer; @@ -89,7 +90,8 @@ export const MongoDBDatabaseInputForm = ({ provider: { roles: [{ roleName: "readWrite" }] }, - environment: isSingleEnvironmentMode ? environments[0] : undefined + environment: isSingleEnvironmentMode ? environments[0] : undefined, + usernameTemplate: "{{randomUsername}}" } }); @@ -105,10 +107,13 @@ export const MongoDBDatabaseInputForm = ({ maxTTL, provider, defaultTTL, - environment + environment, + usernameTemplate }: TForm) => { // wait till previous request is finished if (createDynamicSecret.isPending) return; + + const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}"; try { await createDynamicSecret.mutateAsync({ provider: { @@ -124,7 +129,9 @@ export const MongoDBDatabaseInputForm = ({ path: secretPath, defaultTTL, projectSlug, - environmentSlug: environment.slug + environmentSlug: environment.slug, + usernameTemplate: + !usernameTemplate || isDefaultUsernameTemplate ? undefined : usernameTemplate }); onCompleted(); } catch { @@ -272,7 +279,7 @@ export const MongoDBDatabaseInputForm = ({
@@ -340,6 +347,25 @@ export const MongoDBDatabaseInputForm = ({ )} />
+ ( + + + + )} + /> {!isSingleEnvironmentMode && ( val.toLowerCase() === val, "Must be lowercase"), - environment: z.object({ name: z.string(), slug: z.string() }) + environment: z.object({ name: z.string(), slug: z.string() }), + usernameTemplate: z.string().nullable().optional() }); type TForm = z.infer; @@ -102,7 +103,8 @@ export const RabbitMqInputForm = ({ }, tags: [] }, - environment: isSingleEnvironmentMode ? environments[0] : undefined + environment: isSingleEnvironmentMode ? environments[0] : undefined, + usernameTemplate: "{{randomUsername}}" } }); @@ -113,10 +115,13 @@ export const RabbitMqInputForm = ({ maxTTL, provider, defaultTTL, - environment + environment, + usernameTemplate }: TForm) => { // wait till previous request is finished if (createDynamicSecret.isPending) return; + + const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}"; try { await createDynamicSecret.mutateAsync({ provider: { type: DynamicSecretProviders.RabbitMq, inputs: provider }, @@ -125,7 +130,9 @@ export const RabbitMqInputForm = ({ path: secretPath, defaultTTL, projectSlug, - environmentSlug: environment.slug + environmentSlug: environment.slug, + usernameTemplate: + !usernameTemplate || isDefaultUsernameTemplate ? undefined : usernameTemplate }); onCompleted(); } catch { @@ -424,6 +431,25 @@ export const RabbitMqInputForm = ({ )} /> + ( + + + + )} + /> {!isSingleEnvironmentMode && ( val.toLowerCase() === val, "Must be lowercase"), - environment: z.object({ name: z.string(), slug: z.string() }) + environment: z.object({ name: z.string(), slug: z.string() }), + usernameTemplate: z.string().nullable().optional() }); type TForm = z.infer; @@ -87,7 +88,8 @@ export const RedisInputForm = ({ creationStatement: "ACL SETUSER {{username}} on >{{password}} ~* &* +@all", revocationStatement: "ACL DELUSER {{username}}" }, - environment: isSingleEnvironmentMode ? environments[0] : undefined + environment: isSingleEnvironmentMode ? environments[0] : undefined, + usernameTemplate: "{{randomUsername}}" } }); @@ -98,11 +100,13 @@ export const RedisInputForm = ({ maxTTL, provider, defaultTTL, + usernameTemplate, environment }: TForm) => { // wait till previous request is finished if (createDynamicSecret.isPending) return; try { + const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}"; await createDynamicSecret.mutateAsync({ provider: { type: DynamicSecretProviders.Redis, inputs: provider }, maxTTL, @@ -110,7 +114,9 @@ export const RedisInputForm = ({ path: secretPath, defaultTTL, projectSlug, - environmentSlug: environment.slug + environmentSlug: environment.slug, + usernameTemplate: + !usernameTemplate || isDefaultUsernameTemplate ? undefined : usernameTemplate }); onCompleted(); } catch { @@ -265,6 +271,26 @@ export const RedisInputForm = ({ Modify Redis Statements + ( + + + + )} + /> val.toLowerCase() === val, "Must be lowercase"), - environment: z.object({ name: z.string(), slug: z.string() }) + environment: z.object({ name: z.string(), slug: z.string() }), + usernameTemplate: z.string().nullable().optional() }); type TForm = z.infer; @@ -88,7 +89,8 @@ sp_role 'grant', 'mon_role', '{{username}}';`, revocationStatement: `sp_dropuser '{{username}}'; sp_droplogin '{{username}}';` }, - environment: isSingleEnvironmentMode ? environments[0] : undefined + environment: isSingleEnvironmentMode ? environments[0] : undefined, + usernameTemplate: "{{randomUsername}}" } }); @@ -99,11 +101,13 @@ sp_droplogin '{{username}}';` maxTTL, provider, defaultTTL, - environment + environment, + usernameTemplate }: TForm) => { // wait till previous request is finished if (createDynamicSecret.isPending) return; try { + const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}"; await createDynamicSecret.mutateAsync({ provider: { type: DynamicSecretProviders.SapAse, inputs: provider }, maxTTL, @@ -111,7 +115,9 @@ sp_droplogin '{{username}}';` path: secretPath, defaultTTL, projectSlug, - environmentSlug: environment.slug + environmentSlug: environment.slug, + usernameTemplate: + !usernameTemplate || isDefaultUsernameTemplate ? undefined : usernameTemplate }); onCompleted(); } catch { @@ -262,6 +268,26 @@ sp_droplogin '{{username}}';` Modify SQL Statements + ( + + + + )} + /> val.toLowerCase() === val, "Must be lowercase"), - environment: z.object({ name: z.string(), slug: z.string() }) + environment: z.object({ name: z.string(), slug: z.string() }), + usernameTemplate: z.string().nullable().optional() }); type TForm = z.infer; @@ -88,7 +89,8 @@ GRANT "MONITORING" TO {{username}};`, DROP USER {{username}};`, renewStatement: "ALTER USER {{username}} VALID UNTIL '{{expiration}}';" }, - environment: isSingleEnvironmentMode ? environments[0] : undefined + environment: isSingleEnvironmentMode ? environments[0] : undefined, + usernameTemplate: "{{randomUsername}}" } }); @@ -96,6 +98,7 @@ DROP USER {{username}};`, const handleCreateDynamicSecret = async ({ name, + usernameTemplate, maxTTL, provider, defaultTTL, @@ -104,6 +107,7 @@ DROP USER {{username}};`, // wait till previous request is finished if (createDynamicSecret.isPending) return; try { + const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}"; await createDynamicSecret.mutateAsync({ provider: { type: DynamicSecretProviders.SapHana, inputs: provider }, maxTTL, @@ -111,6 +115,8 @@ DROP USER {{username}};`, path: secretPath, defaultTTL, projectSlug, + usernameTemplate: + !usernameTemplate || isDefaultUsernameTemplate ? undefined : usernameTemplate, environmentSlug: environment.slug }); onCompleted(); @@ -263,6 +269,26 @@ DROP USER {{username}};`, Modify SQL Statements + ( + + + + )} + /> val.toLowerCase() === val, "Must be lowercase"), - environment: z.object({ name: z.string(), slug: z.string() }) + environment: z.object({ name: z.string(), slug: z.string() }), + usernameTemplate: z.string().nullable().optional() }); type TForm = z.infer; @@ -91,7 +92,8 @@ export const SnowflakeInputForm = ({ revocationStatement: "DROP USER {{username}};", renewStatement: "ALTER USER {{username}} SET DAYS_TO_EXPIRY = {{expiration}};" }, - environment: isSingleEnvironmentMode ? environments[0] : undefined + environment: isSingleEnvironmentMode ? environments[0] : undefined, + usernameTemplate: "{{randomUsername}}" } }); @@ -102,11 +104,13 @@ export const SnowflakeInputForm = ({ maxTTL, provider, defaultTTL, - environment + environment, + usernameTemplate }: TForm) => { // wait till previous request is finished if (createDynamicSecret.isPending) return; try { + const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}"; await createDynamicSecret.mutateAsync({ provider: { type: DynamicSecretProviders.Snowflake, inputs: provider }, maxTTL, @@ -114,7 +118,9 @@ export const SnowflakeInputForm = ({ path: secretPath, defaultTTL, projectSlug, - environmentSlug: environment.slug + environmentSlug: environment.slug, + usernameTemplate: + !usernameTemplate || isDefaultUsernameTemplate ? undefined : usernameTemplate }); onCompleted(); } catch (err) { @@ -266,6 +272,26 @@ export const SnowflakeInputForm = ({ Modify SQL Statements + ( + + + + )} + /> ; @@ -191,7 +192,8 @@ export const SqlDatabaseInputForm = ({ allowedSymbols: "-_.~!*" } }, - environment: isSingleEnvironmentMode ? environments[0] : undefined + environment: isSingleEnvironmentMode ? environments[0] : undefined, + usernameTemplate: "{{randomUsername}}" } }); @@ -204,11 +206,13 @@ export const SqlDatabaseInputForm = ({ provider, defaultTTL, environment, - metadata + metadata, + usernameTemplate }: TForm) => { // wait till previous request is finished if (createDynamicSecret.isPending) return; + const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}"; try { await createDynamicSecret.mutateAsync({ provider: { type: DynamicSecretProviders.SqlDatabase, inputs: provider }, @@ -218,7 +222,9 @@ export const SqlDatabaseInputForm = ({ defaultTTL, projectSlug, environmentSlug: environment.slug, - metadata + metadata, + usernameTemplate: + !usernameTemplate || isDefaultUsernameTemplate ? undefined : usernameTemplate }); onCompleted(); } catch { @@ -474,6 +480,26 @@ export const SqlDatabaseInputForm = ({ Creation, Revocation & Renew Statements (optional) + ( + + + + )} + />
Customize SQL statements for managing database user lifecycle
diff --git a/frontend/src/pages/secret-manager/SecretDashboardPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretAwsElastiCacheProviderForm.tsx b/frontend/src/pages/secret-manager/SecretDashboardPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretAwsElastiCacheProviderForm.tsx index 47979af06..f589e64d0 100644 --- a/frontend/src/pages/secret-manager/SecretDashboardPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretAwsElastiCacheProviderForm.tsx +++ b/frontend/src/pages/secret-manager/SecretDashboardPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretAwsElastiCacheProviderForm.tsx @@ -56,7 +56,8 @@ const formSchema = z.object({ newName: z .string() .refine((val) => val.toLowerCase() === val, "Must be lowercase") - .optional() + .optional(), + usernameTemplate: z.string().trim().nullable().optional() }); type TForm = z.infer; @@ -85,6 +86,7 @@ export const EditDynamicSecretAwsElastiCacheProviderForm = ({ defaultTTL: dynamicSecret.defaultTTL, maxTTL: dynamicSecret.maxTTL, newName: dynamicSecret.name, + usernameTemplate: dynamicSecret?.usernameTemplate || "{{randomUsername}}", inputs: { ...(dynamicSecret.inputs as TForm["inputs"]) } @@ -93,9 +95,16 @@ export const EditDynamicSecretAwsElastiCacheProviderForm = ({ const updateDynamicSecret = useUpdateDynamicSecret(); - const handleUpdateDynamicSecret = async ({ inputs, maxTTL, defaultTTL, newName }: TForm) => { + const handleUpdateDynamicSecret = async ({ + inputs, + maxTTL, + defaultTTL, + newName, + usernameTemplate + }: TForm) => { // wait till previous request is finished if (updateDynamicSecret.isPending) return; + const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}"; try { await updateDynamicSecret.mutateAsync({ name: dynamicSecret.name, @@ -106,7 +115,8 @@ export const EditDynamicSecretAwsElastiCacheProviderForm = ({ maxTTL: maxTTL || undefined, defaultTTL, inputs, - newName: newName === dynamicSecret.name ? undefined : newName + newName: newName === dynamicSecret.name ? undefined : newName, + usernameTemplate: !usernameTemplate || isDefaultUsernameTemplate ? null : usernameTemplate } }); onClose(); @@ -261,6 +271,24 @@ export const EditDynamicSecretAwsElastiCacheProviderForm = ({ Modify ElastiCache Statements + ( + + + + )} + /> val.toLowerCase() === val, "Must be lowercase") - .optional() + .optional(), + usernameTemplate: z.string().trim().nullable().optional() }); type TForm = z.infer; @@ -75,6 +76,7 @@ export const EditDynamicSecretAwsIamForm = ({ defaultTTL: dynamicSecret.defaultTTL, maxTTL: dynamicSecret.maxTTL, newName: dynamicSecret.name, + usernameTemplate: dynamicSecret?.usernameTemplate || "{{randomUsername}}", inputs: { ...(dynamicSecret.inputs as TForm["inputs"]) } @@ -83,9 +85,16 @@ export const EditDynamicSecretAwsIamForm = ({ const updateDynamicSecret = useUpdateDynamicSecret(); - const handleUpdateDynamicSecret = async ({ inputs, maxTTL, defaultTTL, newName }: TForm) => { + const handleUpdateDynamicSecret = async ({ + inputs, + maxTTL, + defaultTTL, + newName, + usernameTemplate + }: TForm) => { // wait till previous request is finished if (updateDynamicSecret.isPending) return; + const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}"; try { await updateDynamicSecret.mutateAsync({ name: dynamicSecret.name, @@ -96,7 +105,8 @@ export const EditDynamicSecretAwsIamForm = ({ maxTTL: maxTTL || undefined, defaultTTL, inputs, - newName: newName === dynamicSecret.name ? undefined : newName + newName: newName === dynamicSecret.name ? undefined : newName, + usernameTemplate: !usernameTemplate || isDefaultUsernameTemplate ? null : usernameTemplate } }); onClose(); @@ -297,6 +307,24 @@ export const EditDynamicSecretAwsIamForm = ({ )} /> + ( + + + + )} + />
diff --git a/frontend/src/pages/secret-manager/SecretDashboardPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretCassandraForm.tsx b/frontend/src/pages/secret-manager/SecretDashboardPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretCassandraForm.tsx index 2047d924a..aac598674 100644 --- a/frontend/src/pages/secret-manager/SecretDashboardPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretCassandraForm.tsx +++ b/frontend/src/pages/secret-manager/SecretDashboardPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretCassandraForm.tsx @@ -58,7 +58,8 @@ const formSchema = z.object({ newName: z .string() .refine((val) => val.toLowerCase() === val, "Must be lowercase") - .optional() + .optional(), + usernameTemplate: z.string().trim().nullable().optional() }); type TForm = z.infer; @@ -89,15 +90,23 @@ export const EditDynamicSecretCassandraForm = ({ newName: dynamicSecret.name, inputs: { ...(dynamicSecret.inputs as TForm["inputs"]) - } + }, + usernameTemplate: dynamicSecret?.usernameTemplate || "{{randomUsername}}" } }); const updateDynamicSecret = useUpdateDynamicSecret(); - const handleUpdateDynamicSecret = async ({ inputs, maxTTL, defaultTTL, newName }: TForm) => { + const handleUpdateDynamicSecret = async ({ + inputs, + maxTTL, + defaultTTL, + newName, + usernameTemplate + }: TForm) => { // wait till previous request is finished if (updateDynamicSecret.isPending) return; + const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}"; try { await updateDynamicSecret.mutateAsync({ name: dynamicSecret.name, @@ -108,7 +117,8 @@ export const EditDynamicSecretCassandraForm = ({ maxTTL: maxTTL || undefined, defaultTTL, inputs, - newName: newName === dynamicSecret.name ? undefined : newName + newName: newName === dynamicSecret.name ? undefined : newName, + usernameTemplate: !usernameTemplate || isDefaultUsernameTemplate ? null : usernameTemplate } }); onClose(); @@ -292,6 +302,24 @@ export const EditDynamicSecretCassandraForm = ({ Modify CQL Statements + ( + + + + )} + /> val.toLowerCase() === val, "Must be lowercase") - .optional() + .optional(), + usernameTemplate: z.string().trim().nullable().optional() }); type TForm = z.infer; @@ -107,6 +108,7 @@ export const EditDynamicSecretElasticSearchForm = ({ defaultTTL: dynamicSecret.defaultTTL, maxTTL: dynamicSecret.maxTTL, newName: dynamicSecret.name, + usernameTemplate: dynamicSecret?.usernameTemplate || "{{randomUsername}}", inputs: { ...(dynamicSecret.inputs as TForm["inputs"]) } @@ -115,9 +117,16 @@ export const EditDynamicSecretElasticSearchForm = ({ const updateDynamicSecret = useUpdateDynamicSecret(); - const handleUpdateDynamicSecret = async ({ inputs, maxTTL, defaultTTL, newName }: TForm) => { + const handleUpdateDynamicSecret = async ({ + inputs, + maxTTL, + defaultTTL, + newName, + usernameTemplate + }: TForm) => { // wait till previous request is finished if (updateDynamicSecret.isPending) return; + const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}"; try { await updateDynamicSecret.mutateAsync({ name: dynamicSecret.name, @@ -128,7 +137,8 @@ export const EditDynamicSecretElasticSearchForm = ({ maxTTL: maxTTL || undefined, defaultTTL, inputs, - newName: newName === dynamicSecret.name ? undefined : newName + newName: newName === dynamicSecret.name ? undefined : newName, + usernameTemplate: !usernameTemplate || isDefaultUsernameTemplate ? null : usernameTemplate } }); onClose(); @@ -390,7 +400,6 @@ export const EditDynamicSecretElasticSearchForm = ({
-
+ ( + + + + )} + /> diff --git a/frontend/src/pages/secret-manager/SecretDashboardPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretLdapForm.tsx b/frontend/src/pages/secret-manager/SecretDashboardPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretLdapForm.tsx index 92744c719..a2530a046 100644 --- a/frontend/src/pages/secret-manager/SecretDashboardPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretLdapForm.tsx +++ b/frontend/src/pages/secret-manager/SecretDashboardPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretLdapForm.tsx @@ -70,7 +70,8 @@ const formSchema = z.object({ newName: z .string() .refine((val) => val.toLowerCase() === val, "Must be lowercase") - .optional() + .optional(), + usernameTemplate: z.string().trim().nullable().optional() }); type TForm = z.infer; @@ -101,6 +102,7 @@ export const EditDynamicSecretLdapForm = ({ defaultTTL: dynamicSecret.defaultTTL, maxTTL: dynamicSecret.maxTTL, newName: dynamicSecret.name, + usernameTemplate: dynamicSecret?.usernameTemplate || "{{randomUsername}}", inputs: { ...(dynamicSecret.inputs as TForm["inputs"]) } @@ -110,9 +112,17 @@ export const EditDynamicSecretLdapForm = ({ const updateDynamicSecret = useUpdateDynamicSecret(); const selectedCredentialType = watch("inputs.credentialType"); - const handleUpdateDynamicSecret = async ({ inputs, maxTTL, defaultTTL, newName }: TForm) => { + const handleUpdateDynamicSecret = async ({ + inputs, + maxTTL, + defaultTTL, + newName, + usernameTemplate + }: TForm) => { // wait till previous request is finished if (updateDynamicSecret.isPending) return; + + const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}"; try { await updateDynamicSecret.mutateAsync({ name: dynamicSecret.name, @@ -123,7 +133,8 @@ export const EditDynamicSecretLdapForm = ({ maxTTL: maxTTL || undefined, defaultTTL, inputs, - newName: newName === dynamicSecret.name ? undefined : newName + newName: newName === dynamicSecret.name ? undefined : newName, + usernameTemplate: !usernameTemplate || isDefaultUsernameTemplate ? null : usernameTemplate } }); onClose(); @@ -329,6 +340,24 @@ export const EditDynamicSecretLdapForm = ({ )} /> )} + ( + + + + )} + />
diff --git a/frontend/src/pages/secret-manager/SecretDashboardPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretRabbitMqForm.tsx b/frontend/src/pages/secret-manager/SecretDashboardPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretRabbitMqForm.tsx index 3fb90d2f8..a2c14b6c3 100644 --- a/frontend/src/pages/secret-manager/SecretDashboardPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretRabbitMqForm.tsx +++ b/frontend/src/pages/secret-manager/SecretDashboardPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretRabbitMqForm.tsx @@ -50,7 +50,8 @@ 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().refine((val) => val.toLowerCase() === val, "Must be lowercase"), + usernameTemplate: z.string().trim().nullable().optional() }); type TForm = z.infer; @@ -80,6 +81,7 @@ export const EditDynamicSecretRabbitMqForm = ({ defaultTTL: dynamicSecret.defaultTTL, maxTTL: dynamicSecret.maxTTL, newName: dynamicSecret.name, + usernameTemplate: dynamicSecret?.usernameTemplate || "{{randomUsername}}", inputs: { ...(dynamicSecret.inputs as TForm["inputs"]) } @@ -88,9 +90,17 @@ export const EditDynamicSecretRabbitMqForm = ({ const updateDynamicSecret = useUpdateDynamicSecret(); - const handleUpdateDynamicSecret = async ({ inputs, maxTTL, defaultTTL, newName }: TForm) => { + const handleUpdateDynamicSecret = async ({ + inputs, + maxTTL, + defaultTTL, + newName, + usernameTemplate + }: TForm) => { // wait till previous request is finished if (updateDynamicSecret.isPending) return; + + const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}"; try { await updateDynamicSecret.mutateAsync({ name: dynamicSecret.name, @@ -101,7 +111,8 @@ export const EditDynamicSecretRabbitMqForm = ({ maxTTL: maxTTL || undefined, defaultTTL, inputs, - newName: newName === dynamicSecret.name ? undefined : newName + newName: newName === dynamicSecret.name ? undefined : newName, + usernameTemplate: !usernameTemplate || isDefaultUsernameTemplate ? null : usernameTemplate } }); onClose(); @@ -405,6 +416,26 @@ export const EditDynamicSecretRabbitMqForm = ({ )} />
+
+ ( + + + + )} + /> +
diff --git a/frontend/src/pages/secret-manager/SecretDashboardPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretRedisProviderForm.tsx b/frontend/src/pages/secret-manager/SecretDashboardPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretRedisProviderForm.tsx index f315f9f7b..c23c1294b 100644 --- a/frontend/src/pages/secret-manager/SecretDashboardPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretRedisProviderForm.tsx +++ b/frontend/src/pages/secret-manager/SecretDashboardPage/components/DynamicSecretListView/EditDynamicSecretForm/EditDynamicSecretRedisProviderForm.tsx @@ -57,7 +57,8 @@ const formSchema = z.object({ newName: z .string() .refine((val) => val.toLowerCase() === val, "Must be lowercase") - .optional() + .optional(), + usernameTemplate: z.string().trim().nullable().optional() }); type TForm = z.infer; @@ -86,6 +87,7 @@ export const EditDynamicSecretRedisProviderForm = ({ defaultTTL: dynamicSecret.defaultTTL, maxTTL: dynamicSecret.maxTTL, newName: dynamicSecret.name, + usernameTemplate: dynamicSecret?.usernameTemplate || "{{randomUsername}}", inputs: { ...(dynamicSecret.inputs as TForm["inputs"]) } @@ -94,9 +96,16 @@ export const EditDynamicSecretRedisProviderForm = ({ const updateDynamicSecret = useUpdateDynamicSecret(); - const handleUpdateDynamicSecret = async ({ inputs, maxTTL, defaultTTL, newName }: TForm) => { + const handleUpdateDynamicSecret = async ({ + inputs, + maxTTL, + defaultTTL, + newName, + usernameTemplate + }: TForm) => { // wait till previous request is finished if (updateDynamicSecret.isPending) return; + const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}"; try { await updateDynamicSecret.mutateAsync({ name: dynamicSecret.name, @@ -104,6 +113,8 @@ export const EditDynamicSecretRedisProviderForm = ({ projectSlug, environmentSlug: environment, data: { + usernameTemplate: + !usernameTemplate || isDefaultUsernameTemplate ? null : usernameTemplate, maxTTL: maxTTL || undefined, defaultTTL, inputs, @@ -262,6 +273,24 @@ export const EditDynamicSecretRedisProviderForm = ({ Modify Redis Statements + ( + + + + )} + /> 24 * 60 * 60 * 1000) ctx.addIssue({ code: z.ZodIssueCode.custom, message: "TTL must be less than a day" }); }), + usernameTemplate: z.string().trim().nullable().optional(), maxTTL: z .string() .optional() @@ -81,6 +82,7 @@ export const EditDynamicSecretSapAseForm = ({ defaultTTL: dynamicSecret.defaultTTL, maxTTL: dynamicSecret.maxTTL, newName: dynamicSecret.name, + usernameTemplate: dynamicSecret?.usernameTemplate || "{{randomUsername}}", inputs: { ...(dynamicSecret.inputs as TForm["inputs"]) } @@ -89,9 +91,17 @@ export const EditDynamicSecretSapAseForm = ({ const updateDynamicSecret = useUpdateDynamicSecret(); - const handleUpdateDynamicSecret = async ({ inputs, maxTTL, defaultTTL, newName }: TForm) => { + const handleUpdateDynamicSecret = async ({ + inputs, + maxTTL, + defaultTTL, + newName, + usernameTemplate + }: TForm) => { // wait till previous request is finished if (updateDynamicSecret.isPending) return; + + const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}"; try { await updateDynamicSecret.mutateAsync({ name: dynamicSecret.name, @@ -102,7 +112,8 @@ export const EditDynamicSecretSapAseForm = ({ maxTTL: maxTTL || undefined, defaultTTL, inputs, - newName: newName === dynamicSecret.name ? undefined : newName + newName: newName === dynamicSecret.name ? undefined : newName, + usernameTemplate: !usernameTemplate || isDefaultUsernameTemplate ? null : usernameTemplate } }); onClose(); @@ -272,6 +283,24 @@ export const EditDynamicSecretSapAseForm = ({ Modify SQL Statements + ( + + + + )} + /> 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().refine((val) => val.toLowerCase() === val, "Must be lowercase"), + usernameTemplate: z.string().trim().nullable().optional() }); type TForm = z.infer; @@ -81,6 +82,7 @@ export const EditDynamicSecretSapHanaForm = ({ defaultTTL: dynamicSecret.defaultTTL, maxTTL: dynamicSecret.maxTTL, newName: dynamicSecret.name, + usernameTemplate: dynamicSecret?.usernameTemplate || "{{randomUsername}}", inputs: { ...(dynamicSecret.inputs as TForm["inputs"]) } @@ -89,9 +91,17 @@ export const EditDynamicSecretSapHanaForm = ({ const updateDynamicSecret = useUpdateDynamicSecret(); - const handleUpdateDynamicSecret = async ({ inputs, maxTTL, defaultTTL, newName }: TForm) => { + const handleUpdateDynamicSecret = async ({ + inputs, + maxTTL, + defaultTTL, + newName, + usernameTemplate + }: TForm) => { // wait till previous request is finished if (updateDynamicSecret.isPending) return; + + const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}"; try { await updateDynamicSecret.mutateAsync({ name: dynamicSecret.name, @@ -102,7 +112,8 @@ export const EditDynamicSecretSapHanaForm = ({ maxTTL: maxTTL || undefined, defaultTTL, inputs, - newName: newName === dynamicSecret.name ? undefined : newName + newName: newName === dynamicSecret.name ? undefined : newName, + usernameTemplate: !usernameTemplate || isDefaultUsernameTemplate ? null : usernameTemplate } }); onClose(); @@ -259,6 +270,24 @@ export const EditDynamicSecretSapHanaForm = ({ Modify SQL Statements + ( + + + + )} + /> val.toLowerCase() === val, "Must be lowercase") + .refine((val) => val.toLowerCase() === val, "Must be lowercase"), + usernameTemplate: z.string().trim().nullable().optional() }); type TForm = z.infer; @@ -85,6 +86,7 @@ export const EditDynamicSecretSnowflakeForm = ({ defaultTTL: dynamicSecret.defaultTTL, maxTTL: dynamicSecret.maxTTL, newName: dynamicSecret.name, + usernameTemplate: dynamicSecret?.usernameTemplate || "{{randomUsername}}", inputs: { ...(dynamicSecret.inputs as TForm["inputs"]) } @@ -93,10 +95,17 @@ export const EditDynamicSecretSnowflakeForm = ({ const updateDynamicSecret = useUpdateDynamicSecret(); - const handleUpdateDynamicSecret = async ({ inputs, maxTTL, defaultTTL, newName }: TForm) => { + const handleUpdateDynamicSecret = async ({ + inputs, + maxTTL, + defaultTTL, + newName, + usernameTemplate + }: TForm) => { // wait till previous request is finished if (updateDynamicSecret.isPending) return; try { + const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}"; await updateDynamicSecret.mutateAsync({ name: dynamicSecret.name, path: secretPath, @@ -106,7 +115,8 @@ export const EditDynamicSecretSnowflakeForm = ({ maxTTL: maxTTL || undefined, defaultTTL, inputs, - newName: newName === dynamicSecret.name ? undefined : newName + newName: newName === dynamicSecret.name ? undefined : newName, + usernameTemplate: !usernameTemplate || isDefaultUsernameTemplate ? null : usernameTemplate } }); onClose(); @@ -262,6 +272,24 @@ export const EditDynamicSecretSnowflakeForm = ({ Modify SQL Statements + ( + + + + )} + /> ; @@ -139,6 +140,7 @@ export const EditDynamicSecretSqlProviderForm = ({ maxTTL: dynamicSecret.maxTTL, newName: dynamicSecret.name, metadata: dynamicSecret.metadata, + usernameTemplate: dynamicSecret?.usernameTemplate || "{{randomUsername}}", inputs: { ...(dynamicSecret.inputs as TForm["inputs"]), passwordRequirements: @@ -161,11 +163,13 @@ export const EditDynamicSecretSqlProviderForm = ({ maxTTL, defaultTTL, newName, - metadata + metadata, + usernameTemplate }: TForm) => { // wait till previous request is finished if (updateDynamicSecret.isPending) return; try { + const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}"; await updateDynamicSecret.mutateAsync({ name: dynamicSecret.name, path: secretPath, @@ -179,7 +183,8 @@ export const EditDynamicSecretSqlProviderForm = ({ gatewayId: isGatewayInActive ? null : inputs.gatewayId }, newName: newName === dynamicSecret.name ? undefined : newName, - metadata + metadata, + usernameTemplate: !usernameTemplate || isDefaultUsernameTemplate ? null : usernameTemplate } }); onClose(); @@ -427,6 +432,25 @@ export const EditDynamicSecretSqlProviderForm = ({ Creation, Revocation & Renew Statements (optional) + ( + + + + )} + />
Customize SQL statements for managing database user lifecycle