feat: ui changes for input template

This commit is contained in:
=
2025-05-28 16:09:12 +05:30
parent 93d7c812e7
commit 8e3559828f
27 changed files with 800 additions and 90 deletions
@@ -13,6 +13,7 @@ export type TDynamicSecret = {
status?: DynamicSecretStatus; status?: DynamicSecretStatus;
statusDetails?: string; statusDetails?: string;
maxTTL: string; maxTTL: string;
usernameTemplate?: string | null;
metadata?: { key: string; value: string }[]; metadata?: { key: string; value: string }[];
}; };
@@ -272,6 +273,7 @@ export type TCreateDynamicSecretDTO = {
environmentSlug: string; environmentSlug: string;
name: string; name: string;
metadata?: { key: string; value: string }[]; metadata?: { key: string; value: string }[];
usernameTemplate?: string;
}; };
export type TUpdateDynamicSecretDTO = { export type TUpdateDynamicSecretDTO = {
@@ -285,6 +287,7 @@ export type TUpdateDynamicSecretDTO = {
defaultTTL?: string; defaultTTL?: string;
maxTTL?: string | null; maxTTL?: string | null;
inputs?: unknown; inputs?: unknown;
usernameTemplate?: string | null;
}; };
}; };
@@ -53,7 +53,8 @@ const formSchema = z.object({
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" });
}), }),
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"), 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<typeof formSchema>; type TForm = z.infer<typeof formSchema>;
@@ -93,7 +94,8 @@ export const AwsElastiCacheInputForm = ({
"UserId": "{{username}}" "UserId": "{{username}}"
}` }`
}, },
environment: isSingleEnvironmentMode ? environments[0] : undefined environment: isSingleEnvironmentMode ? environments[0] : undefined,
usernameTemplate: "{{randomUsername}}"
} }
}); });
@@ -104,10 +106,13 @@ export const AwsElastiCacheInputForm = ({
maxTTL, maxTTL,
provider, provider,
defaultTTL, defaultTTL,
environment environment,
usernameTemplate
}: TForm) => { }: TForm) => {
// wait till previous request is finished // wait till previous request is finished
if (createDynamicSecret.isPending) return; if (createDynamicSecret.isPending) return;
const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}";
try { try {
await createDynamicSecret.mutateAsync({ await createDynamicSecret.mutateAsync({
provider: { type: DynamicSecretProviders.AwsElastiCache, inputs: provider }, provider: { type: DynamicSecretProviders.AwsElastiCache, inputs: provider },
@@ -116,7 +121,9 @@ export const AwsElastiCacheInputForm = ({
path: secretPath, path: secretPath,
defaultTTL, defaultTTL,
projectSlug, projectSlug,
environmentSlug: environment.slug environmentSlug: environment.slug,
usernameTemplate:
!usernameTemplate || isDefaultUsernameTemplate ? undefined : usernameTemplate
}); });
onCompleted(); onCompleted();
} catch { } catch {
@@ -270,6 +277,25 @@ export const AwsElastiCacheInputForm = ({
<AccordionItem value="advance-statements"> <AccordionItem value="advance-statements">
<AccordionTrigger>Modify ElastiCache Statements</AccordionTrigger> <AccordionTrigger>Modify ElastiCache Statements</AccordionTrigger>
<AccordionContent> <AccordionContent>
<Controller
control={control}
name="usernameTemplate"
defaultValue=""
render={({ field, fieldState: { error } }) => (
<FormControl
label="Username Template"
isError={Boolean(error?.message)}
errorText={error?.message}
>
<Input
{...field}
value={field.value || undefined}
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
placeholder="{{randomUsername}}"
/>
</FormControl>
)}
/>
<Controller <Controller
control={control} control={control}
name="provider.creationStatement" name="provider.creationStatement"
@@ -42,7 +42,8 @@ const formSchema = z.object({
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" });
}), }),
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"), 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<typeof formSchema>; type TForm = z.infer<typeof formSchema>;
@@ -70,7 +71,8 @@ export const AwsIamInputForm = ({
} = useForm<TForm>({ } = useForm<TForm>({
resolver: zodResolver(formSchema), resolver: zodResolver(formSchema),
defaultValues: { defaultValues: {
environment: isSingleEnvironmentMode ? environments[0] : undefined environment: isSingleEnvironmentMode ? environments[0] : undefined,
usernameTemplate: "{{randomUsername}}"
} }
}); });
@@ -81,12 +83,14 @@ export const AwsIamInputForm = ({
maxTTL, maxTTL,
provider, provider,
defaultTTL, defaultTTL,
environment environment,
usernameTemplate
}: TForm) => { }: TForm) => {
// wait till previous request is finished // wait till previous request is finished
if (createDynamicSecret.isPending) return; if (createDynamicSecret.isPending) return;
try { try {
const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}";
await createDynamicSecret.mutateAsync({ await createDynamicSecret.mutateAsync({
provider: { type: DynamicSecretProviders.AwsIam, inputs: provider }, provider: { type: DynamicSecretProviders.AwsIam, inputs: provider },
maxTTL, maxTTL,
@@ -94,7 +98,9 @@ export const AwsIamInputForm = ({
path: secretPath, path: secretPath,
defaultTTL, defaultTTL,
projectSlug, projectSlug,
environmentSlug: environment.slug environmentSlug: environment.slug,
usernameTemplate:
!usernameTemplate || isDefaultUsernameTemplate ? undefined : usernameTemplate
}); });
onCompleted(); onCompleted();
} catch { } catch {
@@ -300,6 +306,25 @@ export const AwsIamInputForm = ({
</FormControl> </FormControl>
)} )}
/> />
<Controller
control={control}
name="usernameTemplate"
defaultValue=""
render={({ field, fieldState: { error } }) => (
<FormControl
label="Username Template"
isError={Boolean(error?.message)}
errorText={error?.message}
>
<Input
{...field}
value={field.value || undefined}
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
placeholder="{{randomUsername}}"
/>
</FormControl>
)}
/>
{!isSingleEnvironmentMode && ( {!isSingleEnvironmentMode && (
<Controller <Controller
control={control} control={control}
@@ -55,7 +55,8 @@ const formSchema = z.object({
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" });
}), }),
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"), 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<typeof formSchema>; type TForm = z.infer<typeof formSchema>;
@@ -93,7 +94,8 @@ export const CassandraInputForm = ({
resolver: zodResolver(formSchema), resolver: zodResolver(formSchema),
defaultValues: { defaultValues: {
provider: getSqlStatements(), provider: getSqlStatements(),
environment: isSingleEnvironmentMode ? environments[0] : undefined environment: isSingleEnvironmentMode ? environments[0] : undefined,
usernameTemplate: "{{randomUsername}}"
} }
}); });
@@ -104,11 +106,13 @@ export const CassandraInputForm = ({
maxTTL, maxTTL,
provider, provider,
defaultTTL, defaultTTL,
environment environment,
usernameTemplate
}: TForm) => { }: TForm) => {
// wait till previous request is finished // wait till previous request is finished
if (createDynamicSecret.isPending) return; if (createDynamicSecret.isPending) return;
const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}";
try { try {
await createDynamicSecret.mutateAsync({ await createDynamicSecret.mutateAsync({
provider: { type: DynamicSecretProviders.Cassandra, inputs: provider }, provider: { type: DynamicSecretProviders.Cassandra, inputs: provider },
@@ -117,7 +121,9 @@ export const CassandraInputForm = ({
path: secretPath, path: secretPath,
defaultTTL, defaultTTL,
projectSlug, projectSlug,
environmentSlug: environment.slug environmentSlug: environment.slug,
usernameTemplate:
!usernameTemplate || isDefaultUsernameTemplate ? undefined : usernameTemplate
}); });
onCompleted(); onCompleted();
} catch { } catch {
@@ -298,6 +304,25 @@ export const CassandraInputForm = ({
<AccordionItem value="advance-statements"> <AccordionItem value="advance-statements">
<AccordionTrigger>Modify CQL Statements</AccordionTrigger> <AccordionTrigger>Modify CQL Statements</AccordionTrigger>
<AccordionContent> <AccordionContent>
<Controller
control={control}
name="usernameTemplate"
defaultValue=""
render={({ field, fieldState: { error } }) => (
<FormControl
label="Username Template"
isError={Boolean(error?.message)}
errorText={error?.message}
>
<Input
{...field}
value={field.value || undefined}
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
placeholder="{{randomUsername}}"
/>
</FormControl>
)}
/>
<Controller <Controller
control={control} control={control}
name="provider.creationStatement" name="provider.creationStatement"
@@ -76,7 +76,8 @@ const formSchema = z.object({
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" });
}), }),
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"), 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<typeof formSchema>; type TForm = z.infer<typeof formSchema>;
@@ -113,7 +114,8 @@ export const ElasticSearchInputForm = ({
roles: ["superuser"], roles: ["superuser"],
port: 443 port: 443
}, },
environment: isSingleEnvironmentMode ? environments[0] : undefined environment: isSingleEnvironmentMode ? environments[0] : undefined,
usernameTemplate: "{{randomUsername}}"
} }
}); });
@@ -124,10 +126,12 @@ export const ElasticSearchInputForm = ({
maxTTL, maxTTL,
provider, provider,
defaultTTL, defaultTTL,
environment environment,
usernameTemplate
}: TForm) => { }: TForm) => {
// wait till previous request is finished // wait till previous request is finished
if (createDynamicSecret.isPending) return; if (createDynamicSecret.isPending) return;
const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}";
try { try {
await createDynamicSecret.mutateAsync({ await createDynamicSecret.mutateAsync({
provider: { type: DynamicSecretProviders.ElasticSearch, inputs: provider }, provider: { type: DynamicSecretProviders.ElasticSearch, inputs: provider },
@@ -136,7 +140,9 @@ export const ElasticSearchInputForm = ({
path: secretPath, path: secretPath,
defaultTTL, defaultTTL,
projectSlug, projectSlug,
environmentSlug: environment.slug environmentSlug: environment.slug,
usernameTemplate:
!usernameTemplate || isDefaultUsernameTemplate ? undefined : usernameTemplate
}); });
onCompleted(); onCompleted();
} catch { } catch {
@@ -418,6 +424,25 @@ export const ElasticSearchInputForm = ({
)} )}
/> />
</div> </div>
<Controller
control={control}
name="usernameTemplate"
defaultValue=""
render={({ field, fieldState: { error } }) => (
<FormControl
label="Username Template"
isError={Boolean(error?.message)}
errorText={error?.message}
>
<Input
{...field}
value={field.value || undefined}
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
placeholder="{{randomUsername}}"
/>
</FormControl>
)}
/>
{!isSingleEnvironmentMode && ( {!isSingleEnvironmentMode && (
<Controller <Controller
control={control} control={control}
@@ -79,7 +79,8 @@ const formSchema = z.object({
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" });
}), }),
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"), 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<typeof formSchema>; type TForm = z.infer<typeof formSchema>;
@@ -120,7 +121,8 @@ export const LdapInputForm = ({
rollbackLdif: "", rollbackLdif: "",
credentialType: CredentialType.Dynamic credentialType: CredentialType.Dynamic
}, },
environment: isSingleEnvironmentMode ? environments[0] : undefined environment: isSingleEnvironmentMode ? environments[0] : undefined,
usernameTemplate: "{{randomUsername}}"
} }
}); });
@@ -133,10 +135,13 @@ export const LdapInputForm = ({
maxTTL, maxTTL,
provider, provider,
defaultTTL, defaultTTL,
environment environment,
usernameTemplate
}: TForm) => { }: TForm) => {
// wait till previous request is finished // wait till previous request is finished
if (createDynamicSecret.isPending) return; if (createDynamicSecret.isPending) return;
const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}";
try { try {
await createDynamicSecret.mutateAsync({ await createDynamicSecret.mutateAsync({
provider: { type: DynamicSecretProviders.Ldap, inputs: provider }, provider: { type: DynamicSecretProviders.Ldap, inputs: provider },
@@ -145,6 +150,8 @@ export const LdapInputForm = ({
path: secretPath, path: secretPath,
defaultTTL, defaultTTL,
projectSlug, projectSlug,
usernameTemplate:
!usernameTemplate || isDefaultUsernameTemplate ? undefined : usernameTemplate,
environmentSlug: environment.slug environmentSlug: environment.slug
}); });
onCompleted(); onCompleted();
@@ -413,6 +420,25 @@ export const LdapInputForm = ({
)} )}
</div> </div>
</div> </div>
<Controller
control={control}
name="usernameTemplate"
defaultValue=""
render={({ field, fieldState: { error } }) => (
<FormControl
label="Username Template"
isError={Boolean(error?.message)}
errorText={error?.message}
>
<Input
{...field}
value={field.value || undefined}
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
placeholder="{{randomUsername}}"
/>
</FormControl>
)}
/>
</div> </div>
</div> </div>
</div> </div>
@@ -66,7 +66,8 @@ const formSchema = z.object({
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" });
}), }),
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"), 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<typeof formSchema>; type TForm = z.infer<typeof formSchema>;
@@ -114,7 +115,8 @@ export const MongoAtlasInputForm = ({
provider: { provider: {
roles: [{ databaseName: "", roleName: "" }] roles: [{ databaseName: "", roleName: "" }]
}, },
environment: isSingleEnvironmentMode ? environments[0] : undefined environment: isSingleEnvironmentMode ? environments[0] : undefined,
usernameTemplate: "{{randomUsername}}"
} }
}); });
@@ -135,10 +137,13 @@ export const MongoAtlasInputForm = ({
maxTTL, maxTTL,
provider, provider,
defaultTTL, defaultTTL,
environment environment,
usernameTemplate
}: TForm) => { }: TForm) => {
// wait till previous request is finished // wait till previous request is finished
if (createDynamicSecret.isPending) return; if (createDynamicSecret.isPending) return;
const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}";
try { try {
await createDynamicSecret.mutateAsync({ await createDynamicSecret.mutateAsync({
provider: { type: DynamicSecretProviders.MongoAtlas, inputs: provider }, provider: { type: DynamicSecretProviders.MongoAtlas, inputs: provider },
@@ -147,7 +152,9 @@ export const MongoAtlasInputForm = ({
path: secretPath, path: secretPath,
defaultTTL, defaultTTL,
projectSlug, projectSlug,
environmentSlug: environment.slug environmentSlug: environment.slug,
usernameTemplate:
!usernameTemplate || isDefaultUsernameTemplate ? undefined : usernameTemplate
}); });
onCompleted(); onCompleted();
} catch { } catch {
@@ -312,7 +319,7 @@ export const MongoAtlasInputForm = ({
label="Role" label="Role"
className="text-xs text-mineshaft-400" className="text-xs text-mineshaft-400"
tooltipClassName="max-w-md whitespace-pre-line" 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.`} Built-in: atlasAdmin, backup, clusterMonitor, dbAdmin, dbAdminAnyDatabase, enableSharding, read, readAnyDatabase, readWrite, readWriteAnyDatabase.`}
/> />
)} )}
@@ -362,6 +369,26 @@ export const MongoAtlasInputForm = ({
<AccordionItem value="advance-section"> <AccordionItem value="advance-section">
<AccordionTrigger>Advanced</AccordionTrigger> <AccordionTrigger>Advanced</AccordionTrigger>
<AccordionContent> <AccordionContent>
<Controller
control={control}
name="usernameTemplate"
defaultValue=""
render={({ field, fieldState: { error } }) => (
<FormControl
label="Username Template"
isError={Boolean(error?.message)}
errorText={error?.message}
tooltipText="randomUsername: Function used to generate random username"
>
<Input
{...field}
value={field.value || undefined}
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
placeholder="{{randomUsername}}"
/>
</FormControl>
)}
/>
<FormLabel <FormLabel
label="Scopes" label="Scopes"
isOptional isOptional
@@ -56,7 +56,8 @@ const formSchema = z.object({
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" });
}), }),
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"), 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<typeof formSchema>; type TForm = z.infer<typeof formSchema>;
@@ -89,7 +90,8 @@ export const MongoDBDatabaseInputForm = ({
provider: { provider: {
roles: [{ roleName: "readWrite" }] roles: [{ roleName: "readWrite" }]
}, },
environment: isSingleEnvironmentMode ? environments[0] : undefined environment: isSingleEnvironmentMode ? environments[0] : undefined,
usernameTemplate: "{{randomUsername}}"
} }
}); });
@@ -105,10 +107,13 @@ export const MongoDBDatabaseInputForm = ({
maxTTL, maxTTL,
provider, provider,
defaultTTL, defaultTTL,
environment environment,
usernameTemplate
}: TForm) => { }: TForm) => {
// wait till previous request is finished // wait till previous request is finished
if (createDynamicSecret.isPending) return; if (createDynamicSecret.isPending) return;
const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}";
try { try {
await createDynamicSecret.mutateAsync({ await createDynamicSecret.mutateAsync({
provider: { provider: {
@@ -124,7 +129,9 @@ export const MongoDBDatabaseInputForm = ({
path: secretPath, path: secretPath,
defaultTTL, defaultTTL,
projectSlug, projectSlug,
environmentSlug: environment.slug environmentSlug: environment.slug,
usernameTemplate:
!usernameTemplate || isDefaultUsernameTemplate ? undefined : usernameTemplate
}); });
onCompleted(); onCompleted();
} catch { } catch {
@@ -272,7 +279,7 @@ export const MongoDBDatabaseInputForm = ({
<FormLabel <FormLabel
label="Roles" label="Roles"
tooltipClassName="max-w-md whitespace-pre-line" 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.`} Built-in: atlasAdmin, backup, clusterMonitor, dbAdmin, dbAdminAnyDatabase, enableSharding, read, readAnyDatabase, readWrite, readWriteAnyDatabase.`}
/> />
<div className="mb-3 mt-1 flex flex-col space-y-2"> <div className="mb-3 mt-1 flex flex-col space-y-2">
@@ -340,6 +347,25 @@ export const MongoDBDatabaseInputForm = ({
)} )}
/> />
</div> </div>
<Controller
control={control}
name="usernameTemplate"
defaultValue=""
render={({ field, fieldState: { error } }) => (
<FormControl
label="Username Template"
isError={Boolean(error?.message)}
errorText={error?.message}
>
<Input
{...field}
value={field.value || undefined}
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
placeholder="{{randomUsername}}"
/>
</FormControl>
)}
/>
{!isSingleEnvironmentMode && ( {!isSingleEnvironmentMode && (
<Controller <Controller
control={control} control={control}
@@ -60,7 +60,8 @@ const formSchema = z.object({
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" });
}), }),
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"), 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<typeof formSchema>; type TForm = z.infer<typeof formSchema>;
@@ -102,7 +103,8 @@ export const RabbitMqInputForm = ({
}, },
tags: [] tags: []
}, },
environment: isSingleEnvironmentMode ? environments[0] : undefined environment: isSingleEnvironmentMode ? environments[0] : undefined,
usernameTemplate: "{{randomUsername}}"
} }
}); });
@@ -113,10 +115,13 @@ export const RabbitMqInputForm = ({
maxTTL, maxTTL,
provider, provider,
defaultTTL, defaultTTL,
environment environment,
usernameTemplate
}: TForm) => { }: TForm) => {
// wait till previous request is finished // wait till previous request is finished
if (createDynamicSecret.isPending) return; if (createDynamicSecret.isPending) return;
const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}";
try { try {
await createDynamicSecret.mutateAsync({ await createDynamicSecret.mutateAsync({
provider: { type: DynamicSecretProviders.RabbitMq, inputs: provider }, provider: { type: DynamicSecretProviders.RabbitMq, inputs: provider },
@@ -125,7 +130,9 @@ export const RabbitMqInputForm = ({
path: secretPath, path: secretPath,
defaultTTL, defaultTTL,
projectSlug, projectSlug,
environmentSlug: environment.slug environmentSlug: environment.slug,
usernameTemplate:
!usernameTemplate || isDefaultUsernameTemplate ? undefined : usernameTemplate
}); });
onCompleted(); onCompleted();
} catch { } catch {
@@ -424,6 +431,25 @@ export const RabbitMqInputForm = ({
)} )}
/> />
</div> </div>
<Controller
control={control}
name="usernameTemplate"
defaultValue=""
render={({ field, fieldState: { error } }) => (
<FormControl
label="Username Template"
isError={Boolean(error?.message)}
errorText={error?.message}
>
<Input
{...field}
value={field.value || undefined}
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
placeholder="{{randomUsername}}"
/>
</FormControl>
)}
/>
{!isSingleEnvironmentMode && ( {!isSingleEnvironmentMode && (
<Controller <Controller
control={control} control={control}
@@ -53,7 +53,8 @@ const formSchema = z.object({
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" });
}), }),
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"), 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<typeof formSchema>; type TForm = z.infer<typeof formSchema>;
@@ -87,7 +88,8 @@ export const RedisInputForm = ({
creationStatement: "ACL SETUSER {{username}} on >{{password}} ~* &* +@all", creationStatement: "ACL SETUSER {{username}} on >{{password}} ~* &* +@all",
revocationStatement: "ACL DELUSER {{username}}" revocationStatement: "ACL DELUSER {{username}}"
}, },
environment: isSingleEnvironmentMode ? environments[0] : undefined environment: isSingleEnvironmentMode ? environments[0] : undefined,
usernameTemplate: "{{randomUsername}}"
} }
}); });
@@ -98,11 +100,13 @@ export const RedisInputForm = ({
maxTTL, maxTTL,
provider, provider,
defaultTTL, defaultTTL,
usernameTemplate,
environment environment
}: TForm) => { }: TForm) => {
// wait till previous request is finished // wait till previous request is finished
if (createDynamicSecret.isPending) return; if (createDynamicSecret.isPending) return;
try { try {
const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}";
await createDynamicSecret.mutateAsync({ await createDynamicSecret.mutateAsync({
provider: { type: DynamicSecretProviders.Redis, inputs: provider }, provider: { type: DynamicSecretProviders.Redis, inputs: provider },
maxTTL, maxTTL,
@@ -110,7 +114,9 @@ export const RedisInputForm = ({
path: secretPath, path: secretPath,
defaultTTL, defaultTTL,
projectSlug, projectSlug,
environmentSlug: environment.slug environmentSlug: environment.slug,
usernameTemplate:
!usernameTemplate || isDefaultUsernameTemplate ? undefined : usernameTemplate
}); });
onCompleted(); onCompleted();
} catch { } catch {
@@ -265,6 +271,26 @@ export const RedisInputForm = ({
<AccordionItem value="advance-statements"> <AccordionItem value="advance-statements">
<AccordionTrigger>Modify Redis Statements</AccordionTrigger> <AccordionTrigger>Modify Redis Statements</AccordionTrigger>
<AccordionContent> <AccordionContent>
<Controller
control={control}
name="usernameTemplate"
defaultValue=""
render={({ field, fieldState: { error } }) => (
<FormControl
label="Username Template"
isError={Boolean(error?.message)}
errorText={error?.message}
tooltipText="randomUsername: Function used to generate random username"
>
<Input
{...field}
value={field.value || undefined}
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
placeholder="{{randomUsername}}"
/>
</FormControl>
)}
/>
<Controller <Controller
control={control} control={control}
name="provider.creationStatement" name="provider.creationStatement"
@@ -51,7 +51,8 @@ const formSchema = z.object({
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" });
}), }),
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"), 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<typeof formSchema>; type TForm = z.infer<typeof formSchema>;
@@ -88,7 +89,8 @@ sp_role 'grant', 'mon_role', '{{username}}';`,
revocationStatement: `sp_dropuser '{{username}}'; revocationStatement: `sp_dropuser '{{username}}';
sp_droplogin '{{username}}';` sp_droplogin '{{username}}';`
}, },
environment: isSingleEnvironmentMode ? environments[0] : undefined environment: isSingleEnvironmentMode ? environments[0] : undefined,
usernameTemplate: "{{randomUsername}}"
} }
}); });
@@ -99,11 +101,13 @@ sp_droplogin '{{username}}';`
maxTTL, maxTTL,
provider, provider,
defaultTTL, defaultTTL,
environment environment,
usernameTemplate
}: TForm) => { }: TForm) => {
// wait till previous request is finished // wait till previous request is finished
if (createDynamicSecret.isPending) return; if (createDynamicSecret.isPending) return;
try { try {
const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}";
await createDynamicSecret.mutateAsync({ await createDynamicSecret.mutateAsync({
provider: { type: DynamicSecretProviders.SapAse, inputs: provider }, provider: { type: DynamicSecretProviders.SapAse, inputs: provider },
maxTTL, maxTTL,
@@ -111,7 +115,9 @@ sp_droplogin '{{username}}';`
path: secretPath, path: secretPath,
defaultTTL, defaultTTL,
projectSlug, projectSlug,
environmentSlug: environment.slug environmentSlug: environment.slug,
usernameTemplate:
!usernameTemplate || isDefaultUsernameTemplate ? undefined : usernameTemplate
}); });
onCompleted(); onCompleted();
} catch { } catch {
@@ -262,6 +268,26 @@ sp_droplogin '{{username}}';`
<AccordionItem value="advance-statements"> <AccordionItem value="advance-statements">
<AccordionTrigger>Modify SQL Statements</AccordionTrigger> <AccordionTrigger>Modify SQL Statements</AccordionTrigger>
<AccordionContent> <AccordionContent>
<Controller
control={control}
name="usernameTemplate"
defaultValue=""
render={({ field, fieldState: { error } }) => (
<FormControl
label="Username Template"
isError={Boolean(error?.message)}
errorText={error?.message}
tooltipText="randomUsername: Function used to generate random username"
>
<Input
{...field}
value={field.value || undefined}
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
placeholder="{{randomUsername}}"
/>
</FormControl>
)}
/>
<Controller <Controller
control={control} control={control}
name="provider.creationStatement" name="provider.creationStatement"
@@ -53,7 +53,8 @@ const formSchema = z.object({
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" });
}), }),
name: z.string().refine((val) => val.toLowerCase() === val, "Must be lowercase"), 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<typeof formSchema>; type TForm = z.infer<typeof formSchema>;
@@ -88,7 +89,8 @@ GRANT "MONITORING" TO {{username}};`,
DROP USER {{username}};`, DROP USER {{username}};`,
renewStatement: "ALTER USER {{username}} VALID UNTIL '{{expiration}}';" 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 ({ const handleCreateDynamicSecret = async ({
name, name,
usernameTemplate,
maxTTL, maxTTL,
provider, provider,
defaultTTL, defaultTTL,
@@ -104,6 +107,7 @@ DROP USER {{username}};`,
// wait till previous request is finished // wait till previous request is finished
if (createDynamicSecret.isPending) return; if (createDynamicSecret.isPending) return;
try { try {
const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}";
await createDynamicSecret.mutateAsync({ await createDynamicSecret.mutateAsync({
provider: { type: DynamicSecretProviders.SapHana, inputs: provider }, provider: { type: DynamicSecretProviders.SapHana, inputs: provider },
maxTTL, maxTTL,
@@ -111,6 +115,8 @@ DROP USER {{username}};`,
path: secretPath, path: secretPath,
defaultTTL, defaultTTL,
projectSlug, projectSlug,
usernameTemplate:
!usernameTemplate || isDefaultUsernameTemplate ? undefined : usernameTemplate,
environmentSlug: environment.slug environmentSlug: environment.slug
}); });
onCompleted(); onCompleted();
@@ -263,6 +269,26 @@ DROP USER {{username}};`,
<AccordionItem value="advance-statements"> <AccordionItem value="advance-statements">
<AccordionTrigger>Modify SQL Statements</AccordionTrigger> <AccordionTrigger>Modify SQL Statements</AccordionTrigger>
<AccordionContent> <AccordionContent>
<Controller
control={control}
name="usernameTemplate"
defaultValue=""
render={({ field, fieldState: { error } }) => (
<FormControl
label="Username Template"
isError={Boolean(error?.message)}
errorText={error?.message}
tooltipText="randomUsername: Function used to generate random username"
>
<Input
{...field}
value={field.value || undefined}
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
placeholder="{{randomUsername}}"
/>
</FormControl>
)}
/>
<Controller <Controller
control={control} control={control}
name="provider.creationStatement" name="provider.creationStatement"
@@ -57,7 +57,8 @@ const formSchema = z.object({
.trim() .trim()
.min(1) .min(1)
.refine((val) => val.toLowerCase() === val, "Must be lowercase"), .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<typeof formSchema>; type TForm = z.infer<typeof formSchema>;
@@ -91,7 +92,8 @@ export const SnowflakeInputForm = ({
revocationStatement: "DROP USER {{username}};", revocationStatement: "DROP USER {{username}};",
renewStatement: "ALTER USER {{username}} SET DAYS_TO_EXPIRY = {{expiration}};" 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, maxTTL,
provider, provider,
defaultTTL, defaultTTL,
environment environment,
usernameTemplate
}: TForm) => { }: TForm) => {
// wait till previous request is finished // wait till previous request is finished
if (createDynamicSecret.isPending) return; if (createDynamicSecret.isPending) return;
try { try {
const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}";
await createDynamicSecret.mutateAsync({ await createDynamicSecret.mutateAsync({
provider: { type: DynamicSecretProviders.Snowflake, inputs: provider }, provider: { type: DynamicSecretProviders.Snowflake, inputs: provider },
maxTTL, maxTTL,
@@ -114,7 +118,9 @@ export const SnowflakeInputForm = ({
path: secretPath, path: secretPath,
defaultTTL, defaultTTL,
projectSlug, projectSlug,
environmentSlug: environment.slug environmentSlug: environment.slug,
usernameTemplate:
!usernameTemplate || isDefaultUsernameTemplate ? undefined : usernameTemplate
}); });
onCompleted(); onCompleted();
} catch (err) { } catch (err) {
@@ -266,6 +272,26 @@ export const SnowflakeInputForm = ({
<AccordionItem value="advance-statements"> <AccordionItem value="advance-statements">
<AccordionTrigger>Modify SQL Statements</AccordionTrigger> <AccordionTrigger>Modify SQL Statements</AccordionTrigger>
<AccordionContent> <AccordionContent>
<Controller
control={control}
name="usernameTemplate"
defaultValue=""
render={({ field, fieldState: { error } }) => (
<FormControl
label="Username Template"
isError={Boolean(error?.message)}
errorText={error?.message}
tooltipText="randomUsername: Function used to generate random username"
>
<Input
{...field}
value={field.value || undefined}
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
placeholder="{{randomUsername}}"
/>
</FormControl>
)}
/>
<Controller <Controller
control={control} control={control}
name="provider.creationStatement" name="provider.creationStatement"
@@ -96,7 +96,8 @@ const formSchema = z.object({
value: z.string().trim().default("") value: z.string().trim().default("")
}) })
.array() .array()
.optional() .optional(),
usernameTemplate: z.string().nullable().optional()
}); });
type TForm = z.infer<typeof formSchema>; type TForm = z.infer<typeof formSchema>;
@@ -191,7 +192,8 @@ export const SqlDatabaseInputForm = ({
allowedSymbols: "-_.~!*" allowedSymbols: "-_.~!*"
} }
}, },
environment: isSingleEnvironmentMode ? environments[0] : undefined environment: isSingleEnvironmentMode ? environments[0] : undefined,
usernameTemplate: "{{randomUsername}}"
} }
}); });
@@ -204,11 +206,13 @@ export const SqlDatabaseInputForm = ({
provider, provider,
defaultTTL, defaultTTL,
environment, environment,
metadata metadata,
usernameTemplate
}: TForm) => { }: TForm) => {
// wait till previous request is finished // wait till previous request is finished
if (createDynamicSecret.isPending) return; if (createDynamicSecret.isPending) return;
const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}";
try { try {
await createDynamicSecret.mutateAsync({ await createDynamicSecret.mutateAsync({
provider: { type: DynamicSecretProviders.SqlDatabase, inputs: provider }, provider: { type: DynamicSecretProviders.SqlDatabase, inputs: provider },
@@ -218,7 +222,9 @@ export const SqlDatabaseInputForm = ({
defaultTTL, defaultTTL,
projectSlug, projectSlug,
environmentSlug: environment.slug, environmentSlug: environment.slug,
metadata metadata,
usernameTemplate:
!usernameTemplate || isDefaultUsernameTemplate ? undefined : usernameTemplate
}); });
onCompleted(); onCompleted();
} catch { } catch {
@@ -474,6 +480,26 @@ export const SqlDatabaseInputForm = ({
Creation, Revocation & Renew Statements (optional) Creation, Revocation & Renew Statements (optional)
</AccordionTrigger> </AccordionTrigger>
<AccordionContent> <AccordionContent>
<Controller
control={control}
name="usernameTemplate"
defaultValue=""
render={({ field, fieldState: { error } }) => (
<FormControl
label="Username Template"
isError={Boolean(error?.message)}
errorText={error?.message}
tooltipText="randomUsername: Function used to generate random username"
>
<Input
{...field}
value={field.value || undefined}
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
placeholder="{{randomUsername}}"
/>
</FormControl>
)}
/>
<div className="mb-4 text-sm text-mineshaft-300"> <div className="mb-4 text-sm text-mineshaft-300">
Customize SQL statements for managing database user lifecycle Customize SQL statements for managing database user lifecycle
</div> </div>
@@ -56,7 +56,8 @@ const formSchema = z.object({
newName: z newName: z
.string() .string()
.refine((val) => val.toLowerCase() === val, "Must be lowercase") .refine((val) => val.toLowerCase() === val, "Must be lowercase")
.optional() .optional(),
usernameTemplate: z.string().trim().nullable().optional()
}); });
type TForm = z.infer<typeof formSchema>; type TForm = z.infer<typeof formSchema>;
@@ -85,6 +86,7 @@ export const EditDynamicSecretAwsElastiCacheProviderForm = ({
defaultTTL: dynamicSecret.defaultTTL, defaultTTL: dynamicSecret.defaultTTL,
maxTTL: dynamicSecret.maxTTL, maxTTL: dynamicSecret.maxTTL,
newName: dynamicSecret.name, newName: dynamicSecret.name,
usernameTemplate: dynamicSecret?.usernameTemplate || "{{randomUsername}}",
inputs: { inputs: {
...(dynamicSecret.inputs as TForm["inputs"]) ...(dynamicSecret.inputs as TForm["inputs"])
} }
@@ -93,9 +95,16 @@ export const EditDynamicSecretAwsElastiCacheProviderForm = ({
const updateDynamicSecret = useUpdateDynamicSecret(); 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 // wait till previous request is finished
if (updateDynamicSecret.isPending) return; if (updateDynamicSecret.isPending) return;
const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}";
try { try {
await updateDynamicSecret.mutateAsync({ await updateDynamicSecret.mutateAsync({
name: dynamicSecret.name, name: dynamicSecret.name,
@@ -106,7 +115,8 @@ export const EditDynamicSecretAwsElastiCacheProviderForm = ({
maxTTL: maxTTL || undefined, maxTTL: maxTTL || undefined,
defaultTTL, defaultTTL,
inputs, inputs,
newName: newName === dynamicSecret.name ? undefined : newName newName: newName === dynamicSecret.name ? undefined : newName,
usernameTemplate: !usernameTemplate || isDefaultUsernameTemplate ? null : usernameTemplate
} }
}); });
onClose(); onClose();
@@ -261,6 +271,24 @@ export const EditDynamicSecretAwsElastiCacheProviderForm = ({
<AccordionItem value="advance-statements"> <AccordionItem value="advance-statements">
<AccordionTrigger>Modify ElastiCache Statements</AccordionTrigger> <AccordionTrigger>Modify ElastiCache Statements</AccordionTrigger>
<AccordionContent> <AccordionContent>
<Controller
control={control}
name="usernameTemplate"
defaultValue=""
render={({ field, fieldState: { error } }) => (
<FormControl
label="Username Template"
isError={Boolean(error?.message)}
errorText={error?.message}
>
<Input
{...field}
value={field.value || undefined}
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
/>
</FormControl>
)}
/>
<Controller <Controller
control={control} control={control}
name="inputs.creationStatement" name="inputs.creationStatement"
@@ -46,7 +46,8 @@ const formSchema = z.object({
newName: z newName: z
.string() .string()
.refine((val) => val.toLowerCase() === val, "Must be lowercase") .refine((val) => val.toLowerCase() === val, "Must be lowercase")
.optional() .optional(),
usernameTemplate: z.string().trim().nullable().optional()
}); });
type TForm = z.infer<typeof formSchema>; type TForm = z.infer<typeof formSchema>;
@@ -75,6 +76,7 @@ export const EditDynamicSecretAwsIamForm = ({
defaultTTL: dynamicSecret.defaultTTL, defaultTTL: dynamicSecret.defaultTTL,
maxTTL: dynamicSecret.maxTTL, maxTTL: dynamicSecret.maxTTL,
newName: dynamicSecret.name, newName: dynamicSecret.name,
usernameTemplate: dynamicSecret?.usernameTemplate || "{{randomUsername}}",
inputs: { inputs: {
...(dynamicSecret.inputs as TForm["inputs"]) ...(dynamicSecret.inputs as TForm["inputs"])
} }
@@ -83,9 +85,16 @@ export const EditDynamicSecretAwsIamForm = ({
const updateDynamicSecret = useUpdateDynamicSecret(); 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 // wait till previous request is finished
if (updateDynamicSecret.isPending) return; if (updateDynamicSecret.isPending) return;
const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}";
try { try {
await updateDynamicSecret.mutateAsync({ await updateDynamicSecret.mutateAsync({
name: dynamicSecret.name, name: dynamicSecret.name,
@@ -96,7 +105,8 @@ export const EditDynamicSecretAwsIamForm = ({
maxTTL: maxTTL || undefined, maxTTL: maxTTL || undefined,
defaultTTL, defaultTTL,
inputs, inputs,
newName: newName === dynamicSecret.name ? undefined : newName newName: newName === dynamicSecret.name ? undefined : newName,
usernameTemplate: !usernameTemplate || isDefaultUsernameTemplate ? null : usernameTemplate
} }
}); });
onClose(); onClose();
@@ -297,6 +307,24 @@ export const EditDynamicSecretAwsIamForm = ({
</FormControl> </FormControl>
)} )}
/> />
<Controller
control={control}
name="usernameTemplate"
defaultValue=""
render={({ field, fieldState: { error } }) => (
<FormControl
label="Username Template"
isError={Boolean(error?.message)}
errorText={error?.message}
>
<Input
{...field}
value={field.value || undefined}
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
/>
</FormControl>
)}
/>
</div> </div>
</div> </div>
<div className="mt-4 flex items-center space-x-4"> <div className="mt-4 flex items-center space-x-4">
@@ -58,7 +58,8 @@ const formSchema = z.object({
newName: z newName: z
.string() .string()
.refine((val) => val.toLowerCase() === val, "Must be lowercase") .refine((val) => val.toLowerCase() === val, "Must be lowercase")
.optional() .optional(),
usernameTemplate: z.string().trim().nullable().optional()
}); });
type TForm = z.infer<typeof formSchema>; type TForm = z.infer<typeof formSchema>;
@@ -89,15 +90,23 @@ export const EditDynamicSecretCassandraForm = ({
newName: dynamicSecret.name, newName: dynamicSecret.name,
inputs: { inputs: {
...(dynamicSecret.inputs as TForm["inputs"]) ...(dynamicSecret.inputs as TForm["inputs"])
} },
usernameTemplate: dynamicSecret?.usernameTemplate || "{{randomUsername}}"
} }
}); });
const updateDynamicSecret = useUpdateDynamicSecret(); 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 // wait till previous request is finished
if (updateDynamicSecret.isPending) return; if (updateDynamicSecret.isPending) return;
const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}";
try { try {
await updateDynamicSecret.mutateAsync({ await updateDynamicSecret.mutateAsync({
name: dynamicSecret.name, name: dynamicSecret.name,
@@ -108,7 +117,8 @@ export const EditDynamicSecretCassandraForm = ({
maxTTL: maxTTL || undefined, maxTTL: maxTTL || undefined,
defaultTTL, defaultTTL,
inputs, inputs,
newName: newName === dynamicSecret.name ? undefined : newName newName: newName === dynamicSecret.name ? undefined : newName,
usernameTemplate: !usernameTemplate || isDefaultUsernameTemplate ? null : usernameTemplate
} }
}); });
onClose(); onClose();
@@ -292,6 +302,24 @@ export const EditDynamicSecretCassandraForm = ({
<AccordionItem value="modify-sql-statement"> <AccordionItem value="modify-sql-statement">
<AccordionTrigger>Modify CQL Statements</AccordionTrigger> <AccordionTrigger>Modify CQL Statements</AccordionTrigger>
<AccordionContent> <AccordionContent>
<Controller
control={control}
name="usernameTemplate"
defaultValue=""
render={({ field, fieldState: { error } }) => (
<FormControl
label="Username Template"
isError={Boolean(error?.message)}
errorText={error?.message}
>
<Input
{...field}
value={field.value || undefined}
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
/>
</FormControl>
)}
/>
<Controller <Controller
control={control} control={control}
name="inputs.creationStatement" name="inputs.creationStatement"
@@ -76,7 +76,8 @@ const formSchema = z.object({
newName: z newName: z
.string() .string()
.refine((val) => val.toLowerCase() === val, "Must be lowercase") .refine((val) => val.toLowerCase() === val, "Must be lowercase")
.optional() .optional(),
usernameTemplate: z.string().trim().nullable().optional()
}); });
type TForm = z.infer<typeof formSchema>; type TForm = z.infer<typeof formSchema>;
@@ -107,6 +108,7 @@ export const EditDynamicSecretElasticSearchForm = ({
defaultTTL: dynamicSecret.defaultTTL, defaultTTL: dynamicSecret.defaultTTL,
maxTTL: dynamicSecret.maxTTL, maxTTL: dynamicSecret.maxTTL,
newName: dynamicSecret.name, newName: dynamicSecret.name,
usernameTemplate: dynamicSecret?.usernameTemplate || "{{randomUsername}}",
inputs: { inputs: {
...(dynamicSecret.inputs as TForm["inputs"]) ...(dynamicSecret.inputs as TForm["inputs"])
} }
@@ -115,9 +117,16 @@ export const EditDynamicSecretElasticSearchForm = ({
const updateDynamicSecret = useUpdateDynamicSecret(); 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 // wait till previous request is finished
if (updateDynamicSecret.isPending) return; if (updateDynamicSecret.isPending) return;
const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}";
try { try {
await updateDynamicSecret.mutateAsync({ await updateDynamicSecret.mutateAsync({
name: dynamicSecret.name, name: dynamicSecret.name,
@@ -128,7 +137,8 @@ export const EditDynamicSecretElasticSearchForm = ({
maxTTL: maxTTL || undefined, maxTTL: maxTTL || undefined,
defaultTTL, defaultTTL,
inputs, inputs,
newName: newName === dynamicSecret.name ? undefined : newName newName: newName === dynamicSecret.name ? undefined : newName,
usernameTemplate: !usernameTemplate || isDefaultUsernameTemplate ? null : usernameTemplate
} }
}); });
onClose(); onClose();
@@ -390,7 +400,6 @@ export const EditDynamicSecretElasticSearchForm = ({
</Button> </Button>
</div> </div>
</div> </div>
<div> <div>
<Controller <Controller
control={control} control={control}
@@ -410,6 +419,24 @@ export const EditDynamicSecretElasticSearchForm = ({
)} )}
/> />
</div> </div>
<Controller
control={control}
name="usernameTemplate"
defaultValue=""
render={({ field, fieldState: { error } }) => (
<FormControl
label="Username Template"
isError={Boolean(error?.message)}
errorText={error?.message}
>
<Input
{...field}
value={field.value || undefined}
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
/>
</FormControl>
)}
/>
</div> </div>
</div> </div>
</div> </div>
@@ -70,7 +70,8 @@ const formSchema = z.object({
newName: z newName: z
.string() .string()
.refine((val) => val.toLowerCase() === val, "Must be lowercase") .refine((val) => val.toLowerCase() === val, "Must be lowercase")
.optional() .optional(),
usernameTemplate: z.string().trim().nullable().optional()
}); });
type TForm = z.infer<typeof formSchema>; type TForm = z.infer<typeof formSchema>;
@@ -101,6 +102,7 @@ export const EditDynamicSecretLdapForm = ({
defaultTTL: dynamicSecret.defaultTTL, defaultTTL: dynamicSecret.defaultTTL,
maxTTL: dynamicSecret.maxTTL, maxTTL: dynamicSecret.maxTTL,
newName: dynamicSecret.name, newName: dynamicSecret.name,
usernameTemplate: dynamicSecret?.usernameTemplate || "{{randomUsername}}",
inputs: { inputs: {
...(dynamicSecret.inputs as TForm["inputs"]) ...(dynamicSecret.inputs as TForm["inputs"])
} }
@@ -110,9 +112,17 @@ export const EditDynamicSecretLdapForm = ({
const updateDynamicSecret = useUpdateDynamicSecret(); const updateDynamicSecret = useUpdateDynamicSecret();
const selectedCredentialType = watch("inputs.credentialType"); 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 // wait till previous request is finished
if (updateDynamicSecret.isPending) return; if (updateDynamicSecret.isPending) return;
const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}";
try { try {
await updateDynamicSecret.mutateAsync({ await updateDynamicSecret.mutateAsync({
name: dynamicSecret.name, name: dynamicSecret.name,
@@ -123,7 +133,8 @@ export const EditDynamicSecretLdapForm = ({
maxTTL: maxTTL || undefined, maxTTL: maxTTL || undefined,
defaultTTL, defaultTTL,
inputs, inputs,
newName: newName === dynamicSecret.name ? undefined : newName newName: newName === dynamicSecret.name ? undefined : newName,
usernameTemplate: !usernameTemplate || isDefaultUsernameTemplate ? null : usernameTemplate
} }
}); });
onClose(); onClose();
@@ -329,6 +340,24 @@ export const EditDynamicSecretLdapForm = ({
)} )}
/> />
)} )}
<Controller
control={control}
name="usernameTemplate"
defaultValue=""
render={({ field, fieldState: { error } }) => (
<FormControl
label="Username Template"
isError={Boolean(error?.message)}
errorText={error?.message}
>
<Input
{...field}
value={field.value || undefined}
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
/>
</FormControl>
)}
/>
</div> </div>
<div className="mt-4 flex items-center space-x-4"> <div className="mt-4 flex items-center space-x-4">
<Button type="submit" isLoading={isSubmitting}> <Button type="submit" isLoading={isSubmitting}>
@@ -69,7 +69,8 @@ const formSchema = z.object({
newName: z newName: z
.string() .string()
.refine((val) => val.toLowerCase() === val, "Must be lowercase") .refine((val) => val.toLowerCase() === val, "Must be lowercase")
.optional() .optional(),
usernameTemplate: z.string().trim().nullable().optional()
}); });
type TForm = z.infer<typeof formSchema>; type TForm = z.infer<typeof formSchema>;
@@ -115,6 +116,7 @@ export const EditDynamicSecretMongoAtlasForm = ({
defaultTTL: dynamicSecret.defaultTTL, defaultTTL: dynamicSecret.defaultTTL,
maxTTL: dynamicSecret.maxTTL, maxTTL: dynamicSecret.maxTTL,
newName: dynamicSecret.name, newName: dynamicSecret.name,
usernameTemplate: dynamicSecret?.usernameTemplate || "{{randomUsername}}",
inputs: { inputs: {
...(dynamicSecret.inputs as TForm["inputs"]) ...(dynamicSecret.inputs as TForm["inputs"])
} }
@@ -133,9 +135,17 @@ export const EditDynamicSecretMongoAtlasForm = ({
const updateDynamicSecret = useUpdateDynamicSecret(); 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 // wait till previous request is finished
if (updateDynamicSecret.isPending) return; if (updateDynamicSecret.isPending) return;
const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}";
try { try {
await updateDynamicSecret.mutateAsync({ await updateDynamicSecret.mutateAsync({
name: dynamicSecret.name, name: dynamicSecret.name,
@@ -146,7 +156,8 @@ export const EditDynamicSecretMongoAtlasForm = ({
maxTTL: maxTTL || undefined, maxTTL: maxTTL || undefined,
defaultTTL, defaultTTL,
inputs, inputs,
newName: newName === dynamicSecret.name ? undefined : newName newName: newName === dynamicSecret.name ? undefined : newName,
usernameTemplate: !usernameTemplate || isDefaultUsernameTemplate ? null : usernameTemplate
} }
}); });
onClose(); onClose();
@@ -312,7 +323,7 @@ export const EditDynamicSecretMongoAtlasForm = ({
label="Role" label="Role"
className="text-xs text-mineshaft-400" className="text-xs text-mineshaft-400"
tooltipClassName="max-w-md whitespace-pre-line" 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.`} Built-in: atlasAdmin, backup, clusterMonitor, dbAdmin, dbAdminAnyDatabase, enableSharding, read, readAnyDatabase, readWrite, readWriteAnyDatabase.`}
/> />
)} )}
@@ -362,6 +373,24 @@ export const EditDynamicSecretMongoAtlasForm = ({
<AccordionItem value="advance-section"> <AccordionItem value="advance-section">
<AccordionTrigger>Advanced</AccordionTrigger> <AccordionTrigger>Advanced</AccordionTrigger>
<AccordionContent> <AccordionContent>
<Controller
control={control}
name="usernameTemplate"
defaultValue=""
render={({ field, fieldState: { error } }) => (
<FormControl
label="Username Template"
isError={Boolean(error?.message)}
errorText={error?.message}
>
<Input
{...field}
value={field.value || undefined}
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
/>
</FormControl>
)}
/>
<FormLabel <FormLabel
label="Scopes" label="Scopes"
isOptional isOptional
@@ -52,7 +52,8 @@ const formSchema = z.object({
newName: z newName: z
.string() .string()
.refine((val) => val.toLowerCase() === val, "Must be lowercase") .refine((val) => val.toLowerCase() === val, "Must be lowercase")
.optional() .optional(),
usernameTemplate: z.string().trim().nullable().optional()
}); });
type TForm = z.infer<typeof formSchema>; type TForm = z.infer<typeof formSchema>;
@@ -83,6 +84,7 @@ export const EditDynamicSecretMongoDBForm = ({
defaultTTL: dynamicSecret.defaultTTL, defaultTTL: dynamicSecret.defaultTTL,
maxTTL: dynamicSecret.maxTTL, maxTTL: dynamicSecret.maxTTL,
newName: dynamicSecret.name, newName: dynamicSecret.name,
usernameTemplate: dynamicSecret?.usernameTemplate || "{{randomUsername}}",
inputs: { inputs: {
...(dynamicSecret.inputs as TForm["inputs"]), ...(dynamicSecret.inputs as TForm["inputs"]),
roles: (dynamicSecret.inputs as { roles: string[] }).roles?.map((roleName) => ({ roles: (dynamicSecret.inputs as { roles: string[] }).roles?.map((roleName) => ({
@@ -99,9 +101,17 @@ export const EditDynamicSecretMongoDBForm = ({
const updateDynamicSecret = useUpdateDynamicSecret(); 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 // wait till previous request is finished
if (updateDynamicSecret.isPending) return; if (updateDynamicSecret.isPending) return;
const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}";
try { try {
await updateDynamicSecret.mutateAsync({ await updateDynamicSecret.mutateAsync({
name: dynamicSecret.name, name: dynamicSecret.name,
@@ -116,6 +126,8 @@ export const EditDynamicSecretMongoDBForm = ({
port: inputs?.port ? inputs.port : undefined, port: inputs?.port ? inputs.port : undefined,
roles: inputs?.roles?.map((el) => el.roleName) roles: inputs?.roles?.map((el) => el.roleName)
}, },
usernameTemplate:
!usernameTemplate || isDefaultUsernameTemplate ? null : usernameTemplate,
newName: newName === dynamicSecret.name ? undefined : newName newName: newName === dynamicSecret.name ? undefined : newName
} }
}); });
@@ -265,7 +277,7 @@ export const EditDynamicSecretMongoDBForm = ({
<FormLabel <FormLabel
label="Roles" label="Roles"
tooltipClassName="max-w-md whitespace-pre-line" 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.`} Built-in: atlasAdmin, backup, clusterMonitor, dbAdmin, dbAdminAnyDatabase, enableSharding, read, readAnyDatabase, readWrite, readWriteAnyDatabase.`}
/> />
<div className="mb-3 mt-1 flex flex-col space-y-2"> <div className="mb-3 mt-1 flex flex-col space-y-2">
@@ -333,6 +345,26 @@ export const EditDynamicSecretMongoDBForm = ({
)} )}
/> />
</div> </div>
<div>
<Controller
control={control}
name="usernameTemplate"
defaultValue=""
render={({ field, fieldState: { error } }) => (
<FormControl
label="Username Template"
isError={Boolean(error?.message)}
errorText={error?.message}
>
<Input
{...field}
value={field.value || undefined}
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
/>
</FormControl>
)}
/>
</div>
</div> </div>
</div> </div>
<div className="mt-4 flex items-center space-x-4"> <div className="mt-4 flex items-center space-x-4">
@@ -50,7 +50,8 @@ const formSchema = z.object({
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" });
}), }),
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<typeof formSchema>; type TForm = z.infer<typeof formSchema>;
@@ -80,6 +81,7 @@ export const EditDynamicSecretRabbitMqForm = ({
defaultTTL: dynamicSecret.defaultTTL, defaultTTL: dynamicSecret.defaultTTL,
maxTTL: dynamicSecret.maxTTL, maxTTL: dynamicSecret.maxTTL,
newName: dynamicSecret.name, newName: dynamicSecret.name,
usernameTemplate: dynamicSecret?.usernameTemplate || "{{randomUsername}}",
inputs: { inputs: {
...(dynamicSecret.inputs as TForm["inputs"]) ...(dynamicSecret.inputs as TForm["inputs"])
} }
@@ -88,9 +90,17 @@ export const EditDynamicSecretRabbitMqForm = ({
const updateDynamicSecret = useUpdateDynamicSecret(); 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 // wait till previous request is finished
if (updateDynamicSecret.isPending) return; if (updateDynamicSecret.isPending) return;
const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}";
try { try {
await updateDynamicSecret.mutateAsync({ await updateDynamicSecret.mutateAsync({
name: dynamicSecret.name, name: dynamicSecret.name,
@@ -101,7 +111,8 @@ export const EditDynamicSecretRabbitMqForm = ({
maxTTL: maxTTL || undefined, maxTTL: maxTTL || undefined,
defaultTTL, defaultTTL,
inputs, inputs,
newName: newName === dynamicSecret.name ? undefined : newName newName: newName === dynamicSecret.name ? undefined : newName,
usernameTemplate: !usernameTemplate || isDefaultUsernameTemplate ? null : usernameTemplate
} }
}); });
onClose(); onClose();
@@ -405,6 +416,26 @@ export const EditDynamicSecretRabbitMqForm = ({
)} )}
/> />
</div> </div>
<div>
<Controller
control={control}
name="usernameTemplate"
defaultValue=""
render={({ field, fieldState: { error } }) => (
<FormControl
label="Username Template"
isError={Boolean(error?.message)}
errorText={error?.message}
>
<Input
{...field}
value={field.value || undefined}
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
/>
</FormControl>
)}
/>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -57,7 +57,8 @@ const formSchema = z.object({
newName: z newName: z
.string() .string()
.refine((val) => val.toLowerCase() === val, "Must be lowercase") .refine((val) => val.toLowerCase() === val, "Must be lowercase")
.optional() .optional(),
usernameTemplate: z.string().trim().nullable().optional()
}); });
type TForm = z.infer<typeof formSchema>; type TForm = z.infer<typeof formSchema>;
@@ -86,6 +87,7 @@ export const EditDynamicSecretRedisProviderForm = ({
defaultTTL: dynamicSecret.defaultTTL, defaultTTL: dynamicSecret.defaultTTL,
maxTTL: dynamicSecret.maxTTL, maxTTL: dynamicSecret.maxTTL,
newName: dynamicSecret.name, newName: dynamicSecret.name,
usernameTemplate: dynamicSecret?.usernameTemplate || "{{randomUsername}}",
inputs: { inputs: {
...(dynamicSecret.inputs as TForm["inputs"]) ...(dynamicSecret.inputs as TForm["inputs"])
} }
@@ -94,9 +96,16 @@ export const EditDynamicSecretRedisProviderForm = ({
const updateDynamicSecret = useUpdateDynamicSecret(); 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 // wait till previous request is finished
if (updateDynamicSecret.isPending) return; if (updateDynamicSecret.isPending) return;
const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}";
try { try {
await updateDynamicSecret.mutateAsync({ await updateDynamicSecret.mutateAsync({
name: dynamicSecret.name, name: dynamicSecret.name,
@@ -104,6 +113,8 @@ export const EditDynamicSecretRedisProviderForm = ({
projectSlug, projectSlug,
environmentSlug: environment, environmentSlug: environment,
data: { data: {
usernameTemplate:
!usernameTemplate || isDefaultUsernameTemplate ? null : usernameTemplate,
maxTTL: maxTTL || undefined, maxTTL: maxTTL || undefined,
defaultTTL, defaultTTL,
inputs, inputs,
@@ -262,6 +273,24 @@ export const EditDynamicSecretRedisProviderForm = ({
<AccordionItem value="advance-statements"> <AccordionItem value="advance-statements">
<AccordionTrigger>Modify Redis Statements</AccordionTrigger> <AccordionTrigger>Modify Redis Statements</AccordionTrigger>
<AccordionContent> <AccordionContent>
<Controller
control={control}
name="usernameTemplate"
defaultValue=""
render={({ field, fieldState: { error } }) => (
<FormControl
label="Username Template"
isError={Boolean(error?.message)}
errorText={error?.message}
>
<Input
{...field}
value={field.value || undefined}
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
/>
</FormControl>
)}
/>
<Controller <Controller
control={control} control={control}
name="inputs.creationStatement" name="inputs.creationStatement"
@@ -40,6 +40,7 @@ const formSchema = z.object({
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" });
}), }),
usernameTemplate: z.string().trim().nullable().optional(),
maxTTL: z maxTTL: z
.string() .string()
.optional() .optional()
@@ -81,6 +82,7 @@ export const EditDynamicSecretSapAseForm = ({
defaultTTL: dynamicSecret.defaultTTL, defaultTTL: dynamicSecret.defaultTTL,
maxTTL: dynamicSecret.maxTTL, maxTTL: dynamicSecret.maxTTL,
newName: dynamicSecret.name, newName: dynamicSecret.name,
usernameTemplate: dynamicSecret?.usernameTemplate || "{{randomUsername}}",
inputs: { inputs: {
...(dynamicSecret.inputs as TForm["inputs"]) ...(dynamicSecret.inputs as TForm["inputs"])
} }
@@ -89,9 +91,17 @@ export const EditDynamicSecretSapAseForm = ({
const updateDynamicSecret = useUpdateDynamicSecret(); 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 // wait till previous request is finished
if (updateDynamicSecret.isPending) return; if (updateDynamicSecret.isPending) return;
const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}";
try { try {
await updateDynamicSecret.mutateAsync({ await updateDynamicSecret.mutateAsync({
name: dynamicSecret.name, name: dynamicSecret.name,
@@ -102,7 +112,8 @@ export const EditDynamicSecretSapAseForm = ({
maxTTL: maxTTL || undefined, maxTTL: maxTTL || undefined,
defaultTTL, defaultTTL,
inputs, inputs,
newName: newName === dynamicSecret.name ? undefined : newName newName: newName === dynamicSecret.name ? undefined : newName,
usernameTemplate: !usernameTemplate || isDefaultUsernameTemplate ? null : usernameTemplate
} }
}); });
onClose(); onClose();
@@ -272,6 +283,24 @@ export const EditDynamicSecretSapAseForm = ({
<AccordionItem value="advance-statements"> <AccordionItem value="advance-statements">
<AccordionTrigger>Modify SQL Statements</AccordionTrigger> <AccordionTrigger>Modify SQL Statements</AccordionTrigger>
<AccordionContent> <AccordionContent>
<Controller
control={control}
name="usernameTemplate"
defaultValue=""
render={({ field, fieldState: { error } }) => (
<FormControl
label="Username Template"
isError={Boolean(error?.message)}
errorText={error?.message}
>
<Input
{...field}
value={field.value || undefined}
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
/>
</FormControl>
)}
/>
<Controller <Controller
control={control} control={control}
name="inputs.creationStatement" name="inputs.creationStatement"
@@ -52,7 +52,8 @@ const formSchema = z.object({
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" });
}), }),
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<typeof formSchema>; type TForm = z.infer<typeof formSchema>;
@@ -81,6 +82,7 @@ export const EditDynamicSecretSapHanaForm = ({
defaultTTL: dynamicSecret.defaultTTL, defaultTTL: dynamicSecret.defaultTTL,
maxTTL: dynamicSecret.maxTTL, maxTTL: dynamicSecret.maxTTL,
newName: dynamicSecret.name, newName: dynamicSecret.name,
usernameTemplate: dynamicSecret?.usernameTemplate || "{{randomUsername}}",
inputs: { inputs: {
...(dynamicSecret.inputs as TForm["inputs"]) ...(dynamicSecret.inputs as TForm["inputs"])
} }
@@ -89,9 +91,17 @@ export const EditDynamicSecretSapHanaForm = ({
const updateDynamicSecret = useUpdateDynamicSecret(); 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 // wait till previous request is finished
if (updateDynamicSecret.isPending) return; if (updateDynamicSecret.isPending) return;
const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}";
try { try {
await updateDynamicSecret.mutateAsync({ await updateDynamicSecret.mutateAsync({
name: dynamicSecret.name, name: dynamicSecret.name,
@@ -102,7 +112,8 @@ export const EditDynamicSecretSapHanaForm = ({
maxTTL: maxTTL || undefined, maxTTL: maxTTL || undefined,
defaultTTL, defaultTTL,
inputs, inputs,
newName: newName === dynamicSecret.name ? undefined : newName newName: newName === dynamicSecret.name ? undefined : newName,
usernameTemplate: !usernameTemplate || isDefaultUsernameTemplate ? null : usernameTemplate
} }
}); });
onClose(); onClose();
@@ -259,6 +270,24 @@ export const EditDynamicSecretSapHanaForm = ({
<AccordionItem value="advance-statements"> <AccordionItem value="advance-statements">
<AccordionTrigger>Modify SQL Statements</AccordionTrigger> <AccordionTrigger>Modify SQL Statements</AccordionTrigger>
<AccordionContent> <AccordionContent>
<Controller
control={control}
name="usernameTemplate"
defaultValue=""
render={({ field, fieldState: { error } }) => (
<FormControl
label="Username Template"
isError={Boolean(error?.message)}
errorText={error?.message}
>
<Input
{...field}
value={field.value || undefined}
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
/>
</FormControl>
)}
/>
<Controller <Controller
control={control} control={control}
name="inputs.creationStatement" name="inputs.creationStatement"
@@ -56,7 +56,8 @@ const formSchema = z.object({
.string() .string()
.trim() .trim()
.min(1) .min(1)
.refine((val) => val.toLowerCase() === val, "Must be lowercase") .refine((val) => val.toLowerCase() === val, "Must be lowercase"),
usernameTemplate: z.string().trim().nullable().optional()
}); });
type TForm = z.infer<typeof formSchema>; type TForm = z.infer<typeof formSchema>;
@@ -85,6 +86,7 @@ export const EditDynamicSecretSnowflakeForm = ({
defaultTTL: dynamicSecret.defaultTTL, defaultTTL: dynamicSecret.defaultTTL,
maxTTL: dynamicSecret.maxTTL, maxTTL: dynamicSecret.maxTTL,
newName: dynamicSecret.name, newName: dynamicSecret.name,
usernameTemplate: dynamicSecret?.usernameTemplate || "{{randomUsername}}",
inputs: { inputs: {
...(dynamicSecret.inputs as TForm["inputs"]) ...(dynamicSecret.inputs as TForm["inputs"])
} }
@@ -93,10 +95,17 @@ export const EditDynamicSecretSnowflakeForm = ({
const updateDynamicSecret = useUpdateDynamicSecret(); 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 // wait till previous request is finished
if (updateDynamicSecret.isPending) return; if (updateDynamicSecret.isPending) return;
try { try {
const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}";
await updateDynamicSecret.mutateAsync({ await updateDynamicSecret.mutateAsync({
name: dynamicSecret.name, name: dynamicSecret.name,
path: secretPath, path: secretPath,
@@ -106,7 +115,8 @@ export const EditDynamicSecretSnowflakeForm = ({
maxTTL: maxTTL || undefined, maxTTL: maxTTL || undefined,
defaultTTL, defaultTTL,
inputs, inputs,
newName: newName === dynamicSecret.name ? undefined : newName newName: newName === dynamicSecret.name ? undefined : newName,
usernameTemplate: !usernameTemplate || isDefaultUsernameTemplate ? null : usernameTemplate
} }
}); });
onClose(); onClose();
@@ -262,6 +272,24 @@ export const EditDynamicSecretSnowflakeForm = ({
<AccordionItem value="advance-statements"> <AccordionItem value="advance-statements">
<AccordionTrigger>Modify SQL Statements</AccordionTrigger> <AccordionTrigger>Modify SQL Statements</AccordionTrigger>
<AccordionContent> <AccordionContent>
<Controller
control={control}
name="usernameTemplate"
defaultValue=""
render={({ field, fieldState: { error } }) => (
<FormControl
label="Username Template"
isError={Boolean(error?.message)}
errorText={error?.message}
>
<Input
{...field}
value={field.value || undefined}
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
/>
</FormControl>
)}
/>
<Controller <Controller
control={control} control={control}
name="inputs.creationStatement" name="inputs.creationStatement"
@@ -97,7 +97,8 @@ const formSchema = z.object({
value: z.string().trim().default("") value: z.string().trim().default("")
}) })
.array() .array()
.optional() .optional(),
usernameTemplate: z.string().trim().nullable().optional()
}); });
type TForm = z.infer<typeof formSchema>; type TForm = z.infer<typeof formSchema>;
@@ -139,6 +140,7 @@ export const EditDynamicSecretSqlProviderForm = ({
maxTTL: dynamicSecret.maxTTL, maxTTL: dynamicSecret.maxTTL,
newName: dynamicSecret.name, newName: dynamicSecret.name,
metadata: dynamicSecret.metadata, metadata: dynamicSecret.metadata,
usernameTemplate: dynamicSecret?.usernameTemplate || "{{randomUsername}}",
inputs: { inputs: {
...(dynamicSecret.inputs as TForm["inputs"]), ...(dynamicSecret.inputs as TForm["inputs"]),
passwordRequirements: passwordRequirements:
@@ -161,11 +163,13 @@ export const EditDynamicSecretSqlProviderForm = ({
maxTTL, maxTTL,
defaultTTL, defaultTTL,
newName, newName,
metadata metadata,
usernameTemplate
}: TForm) => { }: TForm) => {
// wait till previous request is finished // wait till previous request is finished
if (updateDynamicSecret.isPending) return; if (updateDynamicSecret.isPending) return;
try { try {
const isDefaultUsernameTemplate = usernameTemplate === "{{randomUsername}}";
await updateDynamicSecret.mutateAsync({ await updateDynamicSecret.mutateAsync({
name: dynamicSecret.name, name: dynamicSecret.name,
path: secretPath, path: secretPath,
@@ -179,7 +183,8 @@ export const EditDynamicSecretSqlProviderForm = ({
gatewayId: isGatewayInActive ? null : inputs.gatewayId gatewayId: isGatewayInActive ? null : inputs.gatewayId
}, },
newName: newName === dynamicSecret.name ? undefined : newName, newName: newName === dynamicSecret.name ? undefined : newName,
metadata metadata,
usernameTemplate: !usernameTemplate || isDefaultUsernameTemplate ? null : usernameTemplate
} }
}); });
onClose(); onClose();
@@ -427,6 +432,25 @@ export const EditDynamicSecretSqlProviderForm = ({
Creation, Revocation & Renew Statements (optional) Creation, Revocation & Renew Statements (optional)
</AccordionTrigger> </AccordionTrigger>
<AccordionContent> <AccordionContent>
<Controller
control={control}
name="usernameTemplate"
defaultValue=""
render={({ field, fieldState: { error } }) => (
<FormControl
label="Username Template"
isError={Boolean(error?.message)}
errorText={error?.message}
helperText="randomUsername: Function used to generate random username"
>
<Input
{...field}
value={field.value || undefined}
className="border-mineshaft-600 bg-mineshaft-900 text-sm"
/>
</FormControl>
)}
/>
<div className="mb-4 text-sm text-mineshaft-300"> <div className="mb-4 text-sm text-mineshaft-300">
Customize SQL statements for managing database user lifecycle Customize SQL statements for managing database user lifecycle
</div> </div>