diff --git a/backend/src/ee/routes/v1/dynamic-secret-router.ts b/backend/src/ee/routes/v1/dynamic-secret-router.ts index bf5cce7d5..b916bab67 100644 --- a/backend/src/ee/routes/v1/dynamic-secret-router.ts +++ b/backend/src/ee/routes/v1/dynamic-secret-router.ts @@ -23,7 +23,10 @@ const validateUsernameTemplateCharacters = characterValidator([ CharacterType.CloseBrace, CharacterType.CloseBracket, CharacterType.OpenBracket, - CharacterType.Fullstop + CharacterType.Fullstop, + CharacterType.SingleQuote, + CharacterType.Spaces, + CharacterType.Pipe ]); const userTemplateSchema = z @@ -33,7 +36,7 @@ const userTemplateSchema = z .refine((el) => validateUsernameTemplateCharacters(el)) .refine((el) => isValidHandleBarTemplate(el, { - allowedExpressions: (val) => ["randomUsername", "unixTimestamp"].includes(val) + allowedExpressions: (val) => ["randomUsername", "unixTimestamp", "identity.name"].includes(val) }) ); diff --git a/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-service.ts b/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-service.ts index 14666cb40..561b2170c 100644 --- a/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-service.ts +++ b/backend/src/ee/services/dynamic-secret-lease/dynamic-secret-lease-service.ts @@ -1,4 +1,5 @@ import { ForbiddenError, subject } from "@casl/ability"; +import RE2 from "re2"; import { ActionProjectType } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; @@ -11,10 +12,13 @@ import { getConfig } from "@app/lib/config/env"; import { BadRequestError, NotFoundError } from "@app/lib/errors"; import { logger } from "@app/lib/logger"; import { ms } from "@app/lib/ms"; +import { ActorType } from "@app/services/auth/auth-type"; +import { TIdentityDALFactory } from "@app/services/identity/identity-dal"; import { TKmsServiceFactory } from "@app/services/kms/kms-service"; import { KmsDataKey } from "@app/services/kms/kms-types"; import { TProjectDALFactory } from "@app/services/project/project-dal"; import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-folder-dal"; +import { TUserDALFactory } from "@app/services/user/user-dal"; import { TDynamicSecretDALFactory } from "../dynamic-secret/dynamic-secret-dal"; import { DynamicSecretProviders, TDynamicProviderFns } from "../dynamic-secret/providers/models"; @@ -39,6 +43,8 @@ type TDynamicSecretLeaseServiceFactoryDep = { permissionService: Pick; projectDAL: Pick; kmsService: Pick; + userDAL: Pick; + identityDAL: TIdentityDALFactory; }; export type TDynamicSecretLeaseServiceFactory = ReturnType; @@ -52,8 +58,16 @@ export const dynamicSecretLeaseServiceFactory = ({ dynamicSecretQueueService, projectDAL, licenseService, - kmsService + kmsService, + userDAL, + identityDAL }: TDynamicSecretLeaseServiceFactoryDep) => { + const extractEmailUsername = (email: string) => { + const regex = new RE2(/^([^@]+)/); + const match = email.match(regex); + return match ? match[1] : email; + }; + const create = async ({ environmentSlug, path, @@ -132,10 +146,23 @@ export const dynamicSecretLeaseServiceFactory = ({ let result; try { + const identity: { name: string } = { name: "" }; + if (actor === ActorType.USER) { + const user = await userDAL.findById(actorId); + if (user) { + identity.name = extractEmailUsername(user.username); + } + } else if (actor === ActorType.Machine) { + const machineIdentity = await identityDAL.findById(actorId); + if (machineIdentity) { + identity.name = machineIdentity.name; + } + } result = await selectedProvider.create({ inputs: decryptedStoredInput, expireAt: expireAt.getTime(), usernameTemplate: dynamicSecretCfg.usernameTemplate, + identity, metadata: { projectId } }); } catch (error: unknown) { diff --git a/backend/src/ee/services/dynamic-secret/providers/aws-elasticache.ts b/backend/src/ee/services/dynamic-secret/providers/aws-elasticache.ts index 56fa110d1..89371f1bd 100644 --- a/backend/src/ee/services/dynamic-secret/providers/aws-elasticache.ts +++ b/backend/src/ee/services/dynamic-secret/providers/aws-elasticache.ts @@ -16,6 +16,7 @@ import { BadRequestError } from "@app/lib/errors"; import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars"; import { DynamicSecretAwsElastiCacheSchema, TDynamicProviderFns } from "./models"; +import { compileUsernameTemplate } from "./templateUtils"; const CreateElastiCacheUserSchema = z.object({ UserId: z.string().trim().min(1), @@ -132,14 +133,14 @@ const generatePassword = () => { return customAlphabet(charset, 64)(); }; -const generateUsername = (usernameTemplate?: string | null) => { +const generateUsername = (usernameTemplate?: string | null, identity?: { name: string }) => { const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-"; const randomUsername = `inf-${customAlphabet(charset, 32)()}`; if (!usernameTemplate) return randomUsername; - - return handlebars.compile(usernameTemplate)({ + return compileUsernameTemplate({ + usernameTemplate, randomUsername, - unixTimestamp: Math.floor(Date.now() / 100) + identity }); }; @@ -174,14 +175,21 @@ export const AwsElastiCacheDatabaseProvider = (): TDynamicProviderFns => { return true; }; - const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => { - const { inputs, expireAt, usernameTemplate } = data; + const create = async (data: { + inputs: unknown; + expireAt: number; + usernameTemplate?: string | null; + identity?: { + name: string; + }; + }) => { + const { inputs, expireAt, usernameTemplate, identity } = data; const providerInputs = await validateProviderInputs(inputs); if (!(await validateConnection(providerInputs))) { throw new BadRequestError({ message: "Failed to establish connection" }); } - const leaseUsername = generateUsername(usernameTemplate); + const leaseUsername = generateUsername(usernameTemplate, identity); const leasePassword = generatePassword(); const leaseExpiration = new Date(expireAt).toISOString(); diff --git a/backend/src/ee/services/dynamic-secret/providers/aws-iam.ts b/backend/src/ee/services/dynamic-secret/providers/aws-iam.ts index b40eb69b3..f7383d4ac 100644 --- a/backend/src/ee/services/dynamic-secret/providers/aws-iam.ts +++ b/backend/src/ee/services/dynamic-secret/providers/aws-iam.ts @@ -18,7 +18,6 @@ import { } from "@aws-sdk/client-iam"; import { AssumeRoleCommand, STSClient } from "@aws-sdk/client-sts"; import { randomUUID } from "crypto"; -import handlebars from "handlebars"; import { z } from "zod"; import { getConfig } from "@app/lib/config/env"; @@ -26,14 +25,16 @@ import { BadRequestError } from "@app/lib/errors"; import { alphaNumericNanoId } from "@app/lib/nanoid"; import { AwsIamAuthType, DynamicSecretAwsIamSchema, TDynamicProviderFns } from "./models"; +import { compileUsernameTemplate } from "./templateUtils"; -const generateUsername = (usernameTemplate?: string | null) => { +const generateUsername = (usernameTemplate?: string | null, identity?: { name: string }) => { const randomUsername = alphaNumericNanoId(32); if (!usernameTemplate) return randomUsername; - return handlebars.compile(usernameTemplate)({ + return compileUsernameTemplate({ + usernameTemplate, randomUsername, - unixTimestamp: Math.floor(Date.now() / 100) + identity }); }; @@ -115,14 +116,17 @@ export const AwsIamProvider = (): TDynamicProviderFns => { inputs: unknown; expireAt: number; usernameTemplate?: string | null; + identity?: { + name: string; + }; metadata: { projectId: string }; }) => { - const { inputs, usernameTemplate, metadata } = data; + const { inputs, usernameTemplate, metadata, identity } = data; const providerInputs = await validateProviderInputs(inputs); const client = await $getClient(providerInputs, metadata.projectId); - const username = generateUsername(usernameTemplate); + const username = generateUsername(usernameTemplate, identity); const { policyArns, userGroups, policyDocument, awsPath, permissionBoundaryPolicyArn } = providerInputs; const createUserRes = await client.send( new CreateUserCommand({ diff --git a/backend/src/ee/services/dynamic-secret/providers/cassandra.ts b/backend/src/ee/services/dynamic-secret/providers/cassandra.ts index fce23b56f..b939dcad6 100644 --- a/backend/src/ee/services/dynamic-secret/providers/cassandra.ts +++ b/backend/src/ee/services/dynamic-secret/providers/cassandra.ts @@ -8,19 +8,20 @@ import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars import { verifyHostInputValidity } from "../dynamic-secret-fns"; import { DynamicSecretCassandraSchema, TDynamicProviderFns } from "./models"; +import { compileUsernameTemplate } from "./templateUtils"; const generatePassword = (size = 48) => { const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~!*"; return customAlphabet(charset, 48)(size); }; -const generateUsername = (usernameTemplate?: string | null) => { +const generateUsername = (usernameTemplate?: string | null, identity?: { name: string }) => { const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-" if (!usernameTemplate) return randomUsername; - - return handlebars.compile(usernameTemplate)({ + return compileUsernameTemplate({ + usernameTemplate, randomUsername, - unixTimestamp: Math.floor(Date.now() / 100) + identity }); }; @@ -75,12 +76,17 @@ export const CassandraProvider = (): TDynamicProviderFns => { return isConnected; }; - const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => { - const { inputs, expireAt, usernameTemplate } = data; + const create = async (data: { + inputs: unknown; + expireAt: number; + usernameTemplate?: string | null; + identity?: { name: string }; + }) => { + const { inputs, expireAt, usernameTemplate, identity } = data; const providerInputs = await validateProviderInputs(inputs); const client = await $getClient(providerInputs); - const username = generateUsername(usernameTemplate); + const username = generateUsername(usernameTemplate, identity); const password = generatePassword(); const { keyspace } = providerInputs; const expiration = new Date(expireAt).toISOString(); diff --git a/backend/src/ee/services/dynamic-secret/providers/elastic-search.ts b/backend/src/ee/services/dynamic-secret/providers/elastic-search.ts index 066822827..32d21ee76 100644 --- a/backend/src/ee/services/dynamic-secret/providers/elastic-search.ts +++ b/backend/src/ee/services/dynamic-secret/providers/elastic-search.ts @@ -1,5 +1,4 @@ import { Client as ElasticSearchClient } from "@elastic/elasticsearch"; -import handlebars from "handlebars"; import { customAlphabet } from "nanoid"; import { z } from "zod"; @@ -7,19 +6,20 @@ import { alphaNumericNanoId } from "@app/lib/nanoid"; import { verifyHostInputValidity } from "../dynamic-secret-fns"; import { DynamicSecretElasticSearchSchema, ElasticSearchAuthTypes, TDynamicProviderFns } from "./models"; +import { compileUsernameTemplate } from "./templateUtils"; const generatePassword = () => { const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~!*"; return customAlphabet(charset, 64)(); }; -const generateUsername = (usernameTemplate?: string | null) => { +const generateUsername = (usernameTemplate?: string | null, identity?: { name: string }) => { const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-" if (!usernameTemplate) return randomUsername; - - return handlebars.compile(usernameTemplate)({ + return compileUsernameTemplate({ + usernameTemplate, randomUsername, - unixTimestamp: Math.floor(Date.now() / 100) + identity }); }; @@ -71,12 +71,12 @@ export const ElasticSearchProvider = (): TDynamicProviderFns => { return infoResponse; }; - const create = async (data: { inputs: unknown; usernameTemplate?: string | null }) => { - const { inputs, usernameTemplate } = data; + const create = async (data: { inputs: unknown; usernameTemplate?: string | null; identity?: { name: string } }) => { + const { inputs, usernameTemplate, identity } = data; const providerInputs = await validateProviderInputs(inputs); const connection = await $getClient(providerInputs); - const username = generateUsername(usernameTemplate); + const username = generateUsername(usernameTemplate, identity); const password = generatePassword(); await connection.security.putUser({ diff --git a/backend/src/ee/services/dynamic-secret/providers/ldap.ts b/backend/src/ee/services/dynamic-secret/providers/ldap.ts index d0e3fbe66..1de8aa1e6 100644 --- a/backend/src/ee/services/dynamic-secret/providers/ldap.ts +++ b/backend/src/ee/services/dynamic-secret/providers/ldap.ts @@ -9,6 +9,7 @@ import { BadRequestError } from "@app/lib/errors"; import { alphaNumericNanoId } from "@app/lib/nanoid"; import { LdapCredentialType, LdapSchema, TDynamicProviderFns } from "./models"; +import { compileUsernameTemplate } from "./templateUtils"; const generatePassword = () => { const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~!*$#"; @@ -22,13 +23,13 @@ const encodePassword = (password?: string) => { return base64Password; }; -const generateUsername = (usernameTemplate?: string | null) => { +const generateUsername = (usernameTemplate?: string | null, identity?: { name: string }) => { const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-" if (!usernameTemplate) return randomUsername; - - return handlebars.compile(usernameTemplate)({ + return compileUsernameTemplate({ + usernameTemplate, randomUsername, - unixTimestamp: Math.floor(Date.now() / 100) + identity }); }; @@ -196,8 +197,8 @@ export const LdapProvider = (): TDynamicProviderFns => { return dnArray; }; - const create = async (data: { inputs: unknown; usernameTemplate?: string | null }) => { - const { inputs, usernameTemplate } = data; + const create = async (data: { inputs: unknown; usernameTemplate?: string | null; identity?: { name: string } }) => { + const { inputs, usernameTemplate, identity } = data; const providerInputs = await validateProviderInputs(inputs); const client = await $getClient(providerInputs); @@ -224,7 +225,7 @@ export const LdapProvider = (): TDynamicProviderFns => { }); } } else { - const username = generateUsername(usernameTemplate); + const username = generateUsername(usernameTemplate, identity); const password = generatePassword(); const generatedLdif = generateLDIF({ username, password, ldifTemplate: providerInputs.creationLdif }); diff --git a/backend/src/ee/services/dynamic-secret/providers/models.ts b/backend/src/ee/services/dynamic-secret/providers/models.ts index e6fa27492..fe6830cb7 100644 --- a/backend/src/ee/services/dynamic-secret/providers/models.ts +++ b/backend/src/ee/services/dynamic-secret/providers/models.ts @@ -427,6 +427,9 @@ export type TDynamicProviderFns = { inputs: unknown; expireAt: number; usernameTemplate?: string | null; + identity?: { + name: string; + }; metadata: { projectId: string }; }) => Promise<{ entityId: string; data: unknown }>; validateConnection: (inputs: unknown, metadata: { projectId: string }) => Promise; diff --git a/backend/src/ee/services/dynamic-secret/providers/mongo-atlas.ts b/backend/src/ee/services/dynamic-secret/providers/mongo-atlas.ts index 8f8bf9430..6da8b4b4e 100644 --- a/backend/src/ee/services/dynamic-secret/providers/mongo-atlas.ts +++ b/backend/src/ee/services/dynamic-secret/providers/mongo-atlas.ts @@ -1,5 +1,4 @@ import axios, { AxiosError } from "axios"; -import handlebars from "handlebars"; import { customAlphabet } from "nanoid"; import { z } from "zod"; @@ -7,19 +6,20 @@ import { createDigestAuthRequestInterceptor } from "@app/lib/axios/digest-auth"; import { alphaNumericNanoId } from "@app/lib/nanoid"; import { DynamicSecretMongoAtlasSchema, TDynamicProviderFns } from "./models"; +import { compileUsernameTemplate } from "./templateUtils"; const generatePassword = (size = 48) => { const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~!*"; return customAlphabet(charset, 48)(size); }; -const generateUsername = (usernameTemplate?: string | null) => { +const generateUsername = (usernameTemplate?: string | null, identity?: { name: string }) => { const randomUsername = alphaNumericNanoId(32); if (!usernameTemplate) return randomUsername; - - return handlebars.compile(usernameTemplate)({ + return compileUsernameTemplate({ + usernameTemplate, randomUsername, - unixTimestamp: Math.floor(Date.now() / 100) + identity }); }; @@ -64,12 +64,17 @@ export const MongoAtlasProvider = (): TDynamicProviderFns => { return isConnected; }; - const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => { - const { inputs, expireAt, usernameTemplate } = data; + const create = async (data: { + inputs: unknown; + expireAt: number; + usernameTemplate?: string | null; + identity?: { name: string }; + }) => { + const { inputs, expireAt, usernameTemplate, identity } = data; const providerInputs = await validateProviderInputs(inputs); const client = await $getClient(providerInputs); - const username = generateUsername(usernameTemplate); + const username = generateUsername(usernameTemplate, identity); const password = generatePassword(); const expiration = new Date(expireAt).toISOString(); await client({ diff --git a/backend/src/ee/services/dynamic-secret/providers/mongo-db.ts b/backend/src/ee/services/dynamic-secret/providers/mongo-db.ts index 0a15209e0..331a355a7 100644 --- a/backend/src/ee/services/dynamic-secret/providers/mongo-db.ts +++ b/backend/src/ee/services/dynamic-secret/providers/mongo-db.ts @@ -1,4 +1,3 @@ -import handlebars from "handlebars"; import { MongoClient } from "mongodb"; import { customAlphabet } from "nanoid"; import { z } from "zod"; @@ -7,19 +6,20 @@ import { alphaNumericNanoId } from "@app/lib/nanoid"; import { verifyHostInputValidity } from "../dynamic-secret-fns"; import { DynamicSecretMongoDBSchema, TDynamicProviderFns } from "./models"; +import { compileUsernameTemplate } from "./templateUtils"; const generatePassword = (size = 48) => { const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~!*"; return customAlphabet(charset, 48)(size); }; -const generateUsername = (usernameTemplate?: string | null) => { +const generateUsername = (usernameTemplate?: string | null, identity?: { name: string }) => { const randomUsername = alphaNumericNanoId(32); if (!usernameTemplate) return randomUsername; - - return handlebars.compile(usernameTemplate)({ + return compileUsernameTemplate({ + usernameTemplate, randomUsername, - unixTimestamp: Math.floor(Date.now() / 100) + identity }); }; @@ -60,12 +60,12 @@ export const MongoDBProvider = (): TDynamicProviderFns => { return isConnected; }; - const create = async (data: { inputs: unknown; usernameTemplate?: string | null }) => { - const { inputs, usernameTemplate } = data; + const create = async (data: { inputs: unknown; usernameTemplate?: string | null; identity?: { name: string } }) => { + const { inputs, usernameTemplate, identity } = data; const providerInputs = await validateProviderInputs(inputs); const client = await $getClient(providerInputs); - const username = generateUsername(usernameTemplate); + const username = generateUsername(usernameTemplate, identity); const password = generatePassword(); const db = client.db(providerInputs.database); diff --git a/backend/src/ee/services/dynamic-secret/providers/rabbit-mq.ts b/backend/src/ee/services/dynamic-secret/providers/rabbit-mq.ts index e7d90d272..76081c86c 100644 --- a/backend/src/ee/services/dynamic-secret/providers/rabbit-mq.ts +++ b/backend/src/ee/services/dynamic-secret/providers/rabbit-mq.ts @@ -1,5 +1,4 @@ import axios, { Axios } from "axios"; -import handlebars from "handlebars"; import https from "https"; import { customAlphabet } from "nanoid"; import { z } from "zod"; @@ -9,19 +8,20 @@ import { alphaNumericNanoId } from "@app/lib/nanoid"; import { verifyHostInputValidity } from "../dynamic-secret-fns"; import { DynamicSecretRabbitMqSchema, TDynamicProviderFns } from "./models"; +import { compileUsernameTemplate } from "./templateUtils"; const generatePassword = () => { const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~!*"; return customAlphabet(charset, 64)(); }; -const generateUsername = (usernameTemplate?: string | null) => { +const generateUsername = (usernameTemplate?: string | null, identity?: { name: string }) => { const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-" if (!usernameTemplate) return randomUsername; - - return handlebars.compile(usernameTemplate)({ + return compileUsernameTemplate({ + usernameTemplate, randomUsername, - unixTimestamp: Math.floor(Date.now() / 100) + identity }); }; @@ -117,12 +117,12 @@ export const RabbitMqProvider = (): TDynamicProviderFns => { return infoResponse; }; - const create = async (data: { inputs: unknown; usernameTemplate?: string | null }) => { - const { inputs, usernameTemplate } = data; + const create = async (data: { inputs: unknown; usernameTemplate?: string | null; identity?: { name: string } }) => { + const { inputs, usernameTemplate, identity } = data; const providerInputs = await validateProviderInputs(inputs); const connection = await $getClient(providerInputs); - const username = generateUsername(usernameTemplate); + const username = generateUsername(usernameTemplate, identity); const password = generatePassword(); await createRabbitMqUser({ diff --git a/backend/src/ee/services/dynamic-secret/providers/redis.ts b/backend/src/ee/services/dynamic-secret/providers/redis.ts index 855af2e29..989ed96dc 100644 --- a/backend/src/ee/services/dynamic-secret/providers/redis.ts +++ b/backend/src/ee/services/dynamic-secret/providers/redis.ts @@ -9,19 +9,20 @@ import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars import { verifyHostInputValidity } from "../dynamic-secret-fns"; import { DynamicSecretRedisDBSchema, TDynamicProviderFns } from "./models"; +import { compileUsernameTemplate } from "./templateUtils"; const generatePassword = () => { const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~!*"; return customAlphabet(charset, 64)(); }; -const generateUsername = (usernameTemplate?: string | null) => { +const generateUsername = (usernameTemplate?: string | null, identity?: { name: string }) => { const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-" if (!usernameTemplate) return randomUsername; - - return handlebars.compile(usernameTemplate)({ + return compileUsernameTemplate({ + usernameTemplate, randomUsername, - unixTimestamp: Math.floor(Date.now() / 100) + identity }); }; @@ -121,12 +122,17 @@ export const RedisDatabaseProvider = (): TDynamicProviderFns => { return pingResponse; }; - const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => { - const { inputs, expireAt, usernameTemplate } = data; + const create = async (data: { + inputs: unknown; + expireAt: number; + usernameTemplate?: string | null; + identity?: { name: string }; + }) => { + const { inputs, expireAt, usernameTemplate, identity } = data; const providerInputs = await validateProviderInputs(inputs); const connection = await $getClient(providerInputs); - const username = generateUsername(usernameTemplate); + const username = generateUsername(usernameTemplate, identity); const password = generatePassword(); const expiration = new Date(expireAt).toISOString(); diff --git a/backend/src/ee/services/dynamic-secret/providers/sap-ase.ts b/backend/src/ee/services/dynamic-secret/providers/sap-ase.ts index af2431058..9c13d3efc 100644 --- a/backend/src/ee/services/dynamic-secret/providers/sap-ase.ts +++ b/backend/src/ee/services/dynamic-secret/providers/sap-ase.ts @@ -9,19 +9,20 @@ import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars import { verifyHostInputValidity } from "../dynamic-secret-fns"; import { DynamicSecretSapAseSchema, TDynamicProviderFns } from "./models"; +import { compileUsernameTemplate } from "./templateUtils"; const generatePassword = (size = 48) => { const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; return customAlphabet(charset, 48)(size); }; -const generateUsername = (usernameTemplate?: string | null) => { +const generateUsername = (usernameTemplate?: string | null, identity?: { name: string }) => { const randomUsername = `inf_${alphaNumericNanoId(25)}`; // Username must start with an ascii letter, so we prepend the username with "inf-" if (!usernameTemplate) return randomUsername; - - return handlebars.compile(usernameTemplate)({ + return compileUsernameTemplate({ + usernameTemplate, randomUsername, - unixTimestamp: Math.floor(Date.now() / 100) + identity }); }; @@ -87,11 +88,11 @@ export const SapAseProvider = (): TDynamicProviderFns => { return true; }; - const create = async (data: { inputs: unknown; usernameTemplate?: string | null }) => { - const { inputs, usernameTemplate } = data; + const create = async (data: { inputs: unknown; usernameTemplate?: string | null; identity?: { name: string } }) => { + const { inputs, usernameTemplate, identity } = data; const providerInputs = await validateProviderInputs(inputs); - const username = generateUsername(usernameTemplate); + const username = generateUsername(usernameTemplate, identity); const password = generatePassword(); const client = await $getClient(providerInputs); diff --git a/backend/src/ee/services/dynamic-secret/providers/sap-hana.ts b/backend/src/ee/services/dynamic-secret/providers/sap-hana.ts index 654e2d144..5c8a75555 100644 --- a/backend/src/ee/services/dynamic-secret/providers/sap-hana.ts +++ b/backend/src/ee/services/dynamic-secret/providers/sap-hana.ts @@ -15,19 +15,20 @@ import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars import { verifyHostInputValidity } from "../dynamic-secret-fns"; import { DynamicSecretSapHanaSchema, TDynamicProviderFns } from "./models"; +import { compileUsernameTemplate } from "./templateUtils"; const generatePassword = (size = 48) => { const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; return customAlphabet(charset, 48)(size); }; -const generateUsername = (usernameTemplate?: string | null) => { +const generateUsername = (usernameTemplate?: string | null, identity?: { name: string }) => { const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-" if (!usernameTemplate) return randomUsername; - - return handlebars.compile(usernameTemplate)({ + return compileUsernameTemplate({ + usernameTemplate, randomUsername, - unixTimestamp: Math.floor(Date.now() / 100) + identity }); }; @@ -97,11 +98,16 @@ export const SapHanaProvider = (): TDynamicProviderFns => { return testResult; }; - const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => { - const { inputs, expireAt, usernameTemplate } = data; + const create = async (data: { + inputs: unknown; + expireAt: number; + usernameTemplate?: string | null; + identity?: { name: string }; + }) => { + const { inputs, expireAt, usernameTemplate, identity } = data; const providerInputs = await validateProviderInputs(inputs); - const username = generateUsername(usernameTemplate); + const username = generateUsername(usernameTemplate, identity); const password = generatePassword(); const expiration = new Date(expireAt).toISOString(); diff --git a/backend/src/ee/services/dynamic-secret/providers/snowflake.ts b/backend/src/ee/services/dynamic-secret/providers/snowflake.ts index 571d488c9..9e97ecd30 100644 --- a/backend/src/ee/services/dynamic-secret/providers/snowflake.ts +++ b/backend/src/ee/services/dynamic-secret/providers/snowflake.ts @@ -8,6 +8,7 @@ import { alphaNumericNanoId } from "@app/lib/nanoid"; import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars"; import { DynamicSecretSnowflakeSchema, TDynamicProviderFns } from "./models"; +import { compileUsernameTemplate } from "./templateUtils"; // destroy client requires callback... const noop = () => {}; @@ -17,13 +18,13 @@ const generatePassword = (size = 48) => { return customAlphabet(charset, 48)(size); }; -const generateUsername = (usernameTemplate?: string | null) => { +const generateUsername = (usernameTemplate?: string | null, identity?: { name: string }) => { const randomUsername = `infisical_${alphaNumericNanoId(32)}`; // Username must start with an ascii letter, so we prepend the username with "inf-" if (!usernameTemplate) return randomUsername; - - return handlebars.compile(usernameTemplate)({ + return compileUsernameTemplate({ + usernameTemplate, randomUsername, - unixTimestamp: Math.floor(Date.now() / 100) + identity }); }; @@ -88,13 +89,18 @@ export const SnowflakeProvider = (): TDynamicProviderFns => { return isValidConnection; }; - const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => { - const { inputs, expireAt, usernameTemplate } = data; + const create = async (data: { + inputs: unknown; + expireAt: number; + usernameTemplate?: string | null; + identity?: { name: string }; + }) => { + const { inputs, expireAt, usernameTemplate, identity } = data; const providerInputs = await validateProviderInputs(inputs); const client = await $getClient(providerInputs); - const username = generateUsername(usernameTemplate); + const username = generateUsername(usernameTemplate, identity); const password = generatePassword(); try { diff --git a/backend/src/ee/services/dynamic-secret/providers/sql-database.ts b/backend/src/ee/services/dynamic-secret/providers/sql-database.ts index 39f8dd6de..d3217be37 100644 --- a/backend/src/ee/services/dynamic-secret/providers/sql-database.ts +++ b/backend/src/ee/services/dynamic-secret/providers/sql-database.ts @@ -10,6 +10,7 @@ import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars import { TGatewayServiceFactory } from "../../gateway/gateway-service"; import { verifyHostInputValidity } from "../dynamic-secret-fns"; import { DynamicSecretSqlDBSchema, PasswordRequirements, SqlProviders, TDynamicProviderFns } from "./models"; +import { compileUsernameTemplate } from "./templateUtils"; const EXTERNAL_REQUEST_TIMEOUT = 10 * 1000; @@ -104,9 +105,8 @@ const generatePassword = (provider: SqlProviders, requirements?: PasswordRequire } }; -const generateUsername = (provider: SqlProviders, usernameTemplate?: string | null) => { +const generateUsername = (provider: SqlProviders, usernameTemplate?: string | null, identity?: { name: string }) => { let randomUsername = ""; - // For oracle, the client assumes everything is upper case when not using quotes around the password if (provider === SqlProviders.Oracle) { randomUsername = alphaNumericNanoId(32).toUpperCase(); @@ -114,10 +114,13 @@ const generateUsername = (provider: SqlProviders, usernameTemplate?: string | nu randomUsername = alphaNumericNanoId(32); } if (!usernameTemplate) return randomUsername; - - return handlebars.compile(usernameTemplate)({ + return compileUsernameTemplate({ + usernameTemplate, randomUsername, - unixTimestamp: Math.floor(Date.now() / 100) + identity, + options: { + toUpperCase: provider === SqlProviders.Oracle + } }); }; @@ -221,11 +224,16 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO) return isConnected; }; - const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => { - const { inputs, expireAt, usernameTemplate } = data; + const create = async (data: { + inputs: unknown; + expireAt: number; + usernameTemplate?: string | null; + identity?: { name: string }; + }) => { + const { inputs, expireAt, usernameTemplate, identity } = data; const providerInputs = await validateProviderInputs(inputs); - const username = generateUsername(providerInputs.client, usernameTemplate); + const username = generateUsername(providerInputs.client, usernameTemplate, identity); const password = generatePassword(providerInputs.client, providerInputs.passwordRequirements); const gatewayCallback = async (host = providerInputs.host, port = providerInputs.port) => { diff --git a/backend/src/ee/services/dynamic-secret/providers/templateUtils.ts b/backend/src/ee/services/dynamic-secret/providers/templateUtils.ts new file mode 100644 index 000000000..70a083dbf --- /dev/null +++ b/backend/src/ee/services/dynamic-secret/providers/templateUtils.ts @@ -0,0 +1,80 @@ +/* eslint-disable func-names */ +import handlebars from "handlebars"; +import RE2 from "re2"; + +import { logger } from "@app/lib/logger"; +import { alphaNumericNanoId } from "@app/lib/nanoid"; + +export const compileUsernameTemplate = ({ + usernameTemplate, + randomUsername, + identity, + unixTimestamp, + options +}: { + usernameTemplate: string; + randomUsername: string; + identity?: { name: string }; + unixTimestamp?: number; + options?: { + toUpperCase?: boolean; + }; +}): string => { + // Create isolated handlebars instance + const hbs = handlebars.create(); + + // Register random helper on local instance + hbs.registerHelper("random", function (length: number) { + if (typeof length !== "number" || length <= 0 || length > 100) { + return ""; + } + return alphaNumericNanoId(length); + }); + + // Register replace helper on local instance + hbs.registerHelper("replace", function (text: string, searchValue: string, replaceValue: string) { + // Convert to string if it's not already + const textStr = String(text || ""); + if (!textStr) { + return textStr; + } + + try { + const re2Pattern = new RE2(searchValue, "g"); + // Replace all occurrences + return re2Pattern.replace(textStr, replaceValue); + } catch (error) { + logger.error(error, "RE2 pattern failed, using original template"); + return textStr; + } + }); + + // Register truncate helper on local instance + hbs.registerHelper("truncate", function (text: string, length: number) { + // Convert to string if it's not already + const textStr = String(text || ""); + if (!textStr) { + return textStr; + } + + if (typeof length !== "number" || length <= 0) return textStr; + return textStr.substring(0, length); + }); + + // Compile template with context using local instance + const context = { + randomUsername, + unixTimestamp: unixTimestamp || Math.floor(Date.now() / 100), + identity: { + name: identity?.name + } + }; + + const result = hbs.compile(usernameTemplate)(context); + + if (options?.toUpperCase) { + return result.toUpperCase(); + } + + return result; +}; diff --git a/backend/src/lib/template/validate-handlebars.ts b/backend/src/lib/template/validate-handlebars.ts index 08343e962..4aa0d1f63 100644 --- a/backend/src/lib/template/validate-handlebars.ts +++ b/backend/src/lib/template/validate-handlebars.ts @@ -7,13 +7,24 @@ type SanitizationArg = { allowedExpressions?: (arg: string) => boolean; }; +const isValidExpression = (expression: string, dto: SanitizationArg): boolean => { + // Allow helper functions (replace, truncate) + const allowedHelpers = ["replace", "truncate", "random"]; + if (allowedHelpers.includes(expression)) { + return true; + } + + // Check regular allowed expressions + return dto?.allowedExpressions?.(expression) || false; +}; + export const validateHandlebarTemplate = (templateName: string, template: string, dto: SanitizationArg) => { const parsedAst = handlebars.parse(template); parsedAst.body.forEach((el) => { if (el.type === "ContentStatement") return; if (el.type === "MustacheStatement" && "path" in el) { const { path } = el as { type: "MustacheStatement"; path: { type: "PathExpression"; original: string } }; - if (path.type === "PathExpression" && dto?.allowedExpressions?.(path.original)) return; + if (path.type === "PathExpression" && isValidExpression(path.original, dto)) return; } logger.error(el, "Template sanitization failed"); throw new BadRequestError({ message: `Template sanitization failed: ${templateName}` }); @@ -26,7 +37,7 @@ export const isValidHandleBarTemplate = (template: string, dto: SanitizationArg) if (el.type === "ContentStatement") return true; if (el.type === "MustacheStatement" && "path" in el) { const { path } = el as { type: "MustacheStatement"; path: { type: "PathExpression"; original: string } }; - if (path.type === "PathExpression" && dto?.allowedExpressions?.(path.original)) return true; + if (path.type === "PathExpression" && isValidExpression(path.original, dto)) return true; } return false; }); diff --git a/backend/src/server/routes/index.ts b/backend/src/server/routes/index.ts index 509a2ef1f..aa38bb0e8 100644 --- a/backend/src/server/routes/index.ts +++ b/backend/src/server/routes/index.ts @@ -1516,7 +1516,9 @@ export const registerRoutes = async ( dynamicSecretProviders, folderDAL, licenseService, - kmsService + kmsService, + userDAL, + identityDAL }); const dailyResourceCleanUp = dailyResourceCleanUpQueueServiceFactory({ auditLogDAL, diff --git a/docs/documentation/platform/dynamic-secrets/aws-elasticache.mdx b/docs/documentation/platform/dynamic-secrets/aws-elasticache.mdx index 66c4c706a..6c70612c8 100644 --- a/docs/documentation/platform/dynamic-secrets/aws-elasticache.mdx +++ b/docs/documentation/platform/dynamic-secrets/aws-elasticache.mdx @@ -95,12 +95,28 @@ The Infisical AWS ElastiCache dynamic secret allows you to generate AWS ElastiCa ![Modify ElastiCache Statements Modal](/images/platform/dynamic-secrets/modify-elasticache-statement.png) - - Specifies a template for generating usernames. This field allows customization of how usernames are automatically created. + + Specifies a template for generating usernames. This field allows customization of how usernames are automatically created. - Allowed template variables are - - `{{randomUsername}}`: Random username string - - `{{unixTimestamp}}`: Current Unix timestamp + Allowed template variables are + - `{{randomUsername}}`: Random username string + - `{{unixTimestamp}}`: Current Unix timestamp + - `{{identity.name}}`: Name of the identity that is generating the secret + - `{{random N}}`: Random string of N characters + + Allowed template functions are + - `truncate`: Truncates a string to a specified length + - `replace`: Replaces a substring with another value + + Examples: + ``` + {{randomUsername}} // 3POnzeFyK9gW2nioK0q2gMjr6CZqsRiX + {{unixTimestamp}} // 17490641580 + {{identity.name}} // testuser + {{random-5}} // x9k2m + {{truncate identity.name 4}} // test + {{replace identity.name 'user' 'replace'}} // testreplace + ``` If you want to provide specific privileges for the generated dynamic credentials, you can modify the ElastiCache statement to your needs. This is useful if you want to only give access to a specific resource. diff --git a/docs/documentation/platform/dynamic-secrets/aws-iam.mdx b/docs/documentation/platform/dynamic-secrets/aws-iam.mdx index 53d61b061..aa10e4029 100644 --- a/docs/documentation/platform/dynamic-secrets/aws-iam.mdx +++ b/docs/documentation/platform/dynamic-secrets/aws-iam.mdx @@ -137,9 +137,32 @@ Replace **\** with your AWS account id and **\** w Maximum time-to-live for a generated secret - - Select *Assume Role* method. - + + Specifies a template for generating usernames. This field allows customization of how usernames are automatically created. + + Allowed template variables are + - `{{randomUsername}}`: Random username string + - `{{unixTimestamp}}`: Current Unix timestamp + - `{{identity.name}}`: Name of the identity that is generating the secret + - `{{random N}}`: Random string of N characters + + Allowed template functions are + - `truncate`: Truncates a string to a specified length + - `replace`: Replaces a substring with another value + + Examples: + ``` + {{randomUsername}} // 3POnzeFyK9gW2nioK0q2gMjr6CZqsRiX + {{unixTimestamp}} // 17490641580 + {{identity.name}} // testuser + {{random-5}} // x9k2m + {{truncate identity.name 4}} // test + {{replace identity.name 'user' 'replace'}} // testreplace + ``` + + + Select *Assume Role* method. + The ARN of the AWS Role to assume. diff --git a/docs/documentation/platform/dynamic-secrets/cassandra.mdx b/docs/documentation/platform/dynamic-secrets/cassandra.mdx index 628432bea..56b7ec336 100644 --- a/docs/documentation/platform/dynamic-secrets/cassandra.mdx +++ b/docs/documentation/platform/dynamic-secrets/cassandra.mdx @@ -80,11 +80,27 @@ The above configuration allows user creation and granting permissions. ![Modify CQL Statements Modal](../../../images/platform/dynamic-secrets/modify-cql-statements.png) - Specifies a template for generating usernames. This field allows customization of how usernames are automatically created. + Specifies a template for generating usernames. This field allows customization of how usernames are automatically created. - Allowed template variables are - - `{{randomUsername}}`: Random username string - - `{{unixTimestamp}}`: Current Unix timestamp + Allowed template variables are + - `{{randomUsername}}`: Random username string + - `{{unixTimestamp}}`: Current Unix timestamp + - `{{identity.name}}`: Name of the identity that is generating the secret + - `{{random N}}`: Random string of N characters + + Allowed template functions are + - `truncate`: Truncates a string to a specified length + - `replace`: Replaces a substring with another value + + Examples: + ``` + {{randomUsername}} // 3POnzeFyK9gW2nioK0q2gMjr6CZqsRiX + {{unixTimestamp}} // 17490641580 + {{identity.name}} // testuser + {{random-5}} // x9k2m + {{truncate identity.name 4}} // test + {{replace identity.name 'user' 'replace'}} // testreplace + ``` If you want to provide specific privileges for the generated dynamic credentials, you can modify the CQL statement to your needs. This is useful if you want to only give access to a specific key-space(s). diff --git a/docs/documentation/platform/dynamic-secrets/elastic-search.mdx b/docs/documentation/platform/dynamic-secrets/elastic-search.mdx index 6c5028bb2..06d1102e2 100644 --- a/docs/documentation/platform/dynamic-secrets/elastic-search.mdx +++ b/docs/documentation/platform/dynamic-secrets/elastic-search.mdx @@ -87,13 +87,29 @@ The port that your Elasticsearch instance is running on. _(Example: 9200)_ A CA may be required if your DB requires it for incoming connections. This is often the case when connecting to a managed service. - - Specifies a template for generating usernames. This field allows customization of how usernames are automatically created. + + Specifies a template for generating usernames. This field allows customization of how usernames are automatically created. - Allowed template variables are - - `{{randomUsername}}`: Random username string - - `{{unixTimestamp}}`: Current Unix timestamp - + Allowed template variables are + - `{{randomUsername}}`: Random username string + - `{{unixTimestamp}}`: Current Unix timestamp + - `{{identity.name}}`: Name of the identity that is generating the secret + - `{{random N}}`: Random string of N characters + + Allowed template functions are + - `truncate`: Truncates a string to a specified length + - `replace`: Replaces a substring with another value + + Examples: + ``` + {{randomUsername}} // 3POnzeFyK9gW2nioK0q2gMjr6CZqsRiX + {{unixTimestamp}} // 17490641580 + {{identity.name}} // testuser + {{random-5}} // x9k2m + {{truncate identity.name 4}} // test + {{replace identity.name 'user' 'replace'}} // testreplace + ``` + ![Dynamic Secret Setup Modal](../../../images/platform/dynamic-secrets/dynamic-secret-input-modal-elastic-search.png) diff --git a/docs/documentation/platform/dynamic-secrets/ldap.mdx b/docs/documentation/platform/dynamic-secrets/ldap.mdx index 1a3eca404..ddaaf9101 100644 --- a/docs/documentation/platform/dynamic-secrets/ldap.mdx +++ b/docs/documentation/platform/dynamic-secrets/ldap.mdx @@ -123,13 +123,29 @@ The Infisical LDAP dynamic secret allows you to generate user credentials on dem changetype: delete ``` - + Specifies a template for generating usernames. This field allows customization of how usernames are automatically created. Allowed template variables are - `{{randomUsername}}`: Random username string - `{{unixTimestamp}}`: Current Unix timestamp - + - `{{identity.name}}`: Name of the identity that is generating the secret + - `{{random N}}`: Random string of N characters + + Allowed template functions are + - `truncate`: Truncates a string to a specified length + - `replace`: Replaces a substring with another value + + Examples: + ``` + {{randomUsername}} // 3POnzeFyK9gW2nioK0q2gMjr6CZqsRiX + {{unixTimestamp}} // 17490641580 + {{identity.name}} // testuser + {{random-5}} // x9k2m + {{truncate identity.name 4}} // test + {{replace identity.name 'user' 'replace'}} // testreplace + ``` + diff --git a/docs/documentation/platform/dynamic-secrets/mongo-atlas.mdx b/docs/documentation/platform/dynamic-secrets/mongo-atlas.mdx index 5d27d16e2..f167a0641 100644 --- a/docs/documentation/platform/dynamic-secrets/mongo-atlas.mdx +++ b/docs/documentation/platform/dynamic-secrets/mongo-atlas.mdx @@ -63,13 +63,29 @@ Create a project scoped API Key with the required permission in your Mongo Atlas ![Modify Scope Modal](../../../images/platform/dynamic-secrets/advanced-option-atlas.png) - - Specifies a template for generating usernames. This field allows customization of how usernames are automatically created. + + Specifies a template for generating usernames. This field allows customization of how usernames are automatically created. - Allowed template variables are - - `{{randomUsername}}`: Random username string - - `{{unixTimestamp}}`: Current Unix timestamp - + Allowed template variables are + - `{{randomUsername}}`: Random username string + - `{{unixTimestamp}}`: Current Unix timestamp + - `{{identity.name}}`: Name of the identity that is generating the secret + - `{{random N}}`: Random string of N characters + + Allowed template functions are + - `truncate`: Truncates a string to a specified length + - `replace`: Replaces a substring with another value + + Examples: + ``` + {{randomUsername}} // 3POnzeFyK9gW2nioK0q2gMjr6CZqsRiX + {{unixTimestamp}} // 17490641580 + {{identity.name}} // testuser + {{random-5}} // x9k2m + {{truncate identity.name 4}} // test + {{replace identity.name 'user' 'replace'}} // testreplace + ``` + List that contains clusters, MongoDB Atlas Data Lakes, and MongoDB Atlas Streams Instances that this database user can access. If omitted, MongoDB Cloud grants the database user access to all the clusters, MongoDB Atlas Data Lakes, and MongoDB Atlas Streams Instances in the project. diff --git a/docs/documentation/platform/dynamic-secrets/mongo-db.mdx b/docs/documentation/platform/dynamic-secrets/mongo-db.mdx index f71922473..7c4ebb568 100644 --- a/docs/documentation/platform/dynamic-secrets/mongo-db.mdx +++ b/docs/documentation/platform/dynamic-secrets/mongo-db.mdx @@ -66,12 +66,28 @@ Create a user with the required permission in your MongoDB instance. This user w A CA may be required if your DB requires it for incoming connections. - + Specifies a template for generating usernames. This field allows customization of how usernames are automatically created. Allowed template variables are - `{{randomUsername}}`: Random username string - `{{unixTimestamp}}`: Current Unix timestamp + - `{{identity.name}}`: Name of the identity that is generating the secret + - `{{random N}}`: Random string of N characters + + Allowed template functions are + - `truncate`: Truncates a string to a specified length + - `replace`: Replaces a substring with another value + + Examples: + ``` + {{randomUsername}} // 3POnzeFyK9gW2nioK0q2gMjr6CZqsRiX + {{unixTimestamp}} // 17490641580 + {{identity.name}} // testuser + {{random-5}} // x9k2m + {{truncate identity.name 4}} // test + {{replace identity.name 'user' 'replace'}} // testreplace + ``` ![Dynamic Secret Setup Modal](../../../images/platform/dynamic-secrets/dynamic-secret-mongodb.png) diff --git a/docs/documentation/platform/dynamic-secrets/mssql.mdx b/docs/documentation/platform/dynamic-secrets/mssql.mdx index 0a73bf129..6b6cef982 100644 --- a/docs/documentation/platform/dynamic-secrets/mssql.mdx +++ b/docs/documentation/platform/dynamic-secrets/mssql.mdx @@ -9,7 +9,6 @@ The Infisical MS SQL dynamic secret allows you to generate Microsoft SQL server Create a user with the required permission in your SQL instance. This user will be used to create new accounts on-demand. - ## Set up Dynamic Secrets with MS SQL @@ -27,104 +26,123 @@ Create a user with the required permission in your SQL instance. This user will Name by which you want the secret to be referenced - - Default time-to-live for a generated secret (it is possible to modify this value after a secret is generated) - + + Default time-to-live for a generated secret (it is possible to modify this value after a secret is generated) + - - Maximum time-to-live for a generated secret - + + Maximum time-to-live for a generated secret + - - List of key/value metadata pairs - + + List of key/value metadata pairs + - - Choose the service you want to generate dynamic secrets for. This must be selected as **MS SQL**. - + + Choose the service you want to generate dynamic secrets for. This must be selected as **MS SQL**. + - - Database host - + + Database host + - - Database port - + + Database port + - - Username that will be used to create dynamic secrets - + + Username that will be used to create dynamic secrets + - - Password that will be used to create dynamic secrets - + + Password that will be used to create dynamic secrets + - - Name of the database for which you want to create dynamic secrets - + + Name of the database for which you want to create dynamic secrets + - - A CA may be required if your DB requires it for incoming connections. AWS RDS instances with default settings will requires a CA which can be downloaded [here](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.SSL.html#UsingWithRDS.SSL.CertificatesAllRegions). - + + A CA may be required if your DB requires it for incoming connections. AWS RDS instances with default settings will requires a CA which can be downloaded [here](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.SSL.html#UsingWithRDS.SSL.CertificatesAllRegions). + - ![Dynamic Secret Setup Modal](../../../images/platform/dynamic-secrets/dynamic-secret-setup-modal-mssql.png) + ![Dynamic Secret Setup Modal](../../../images/platform/dynamic-secrets/dynamic-secret-setup-modal-mssql.png) ![Modify SQL Statements Modal](../../../images/platform/dynamic-secrets/modify-sql-statements-mssql.png) - - Specifies a template for generating usernames. This field allows customization of how usernames are automatically created. + + Specifies a template for generating usernames. This field allows customization of how usernames are automatically created. + + Allowed template variables are + - `{{randomUsername}}`: Random username string + - `{{unixTimestamp}}`: Current Unix timestamp + - `{{identity.name}}`: Name of the identity that is generating the secret + - `{{random N}}`: Random string of N characters + + Allowed template functions are + - `truncate`: Truncates a string to a specified length + - `replace`: Replaces a substring with another value + + Examples: + ``` + {{randomUsername}} // 3POnzeFyK9gW2nioK0q2gMjr6CZqsRiX + {{unixTimestamp}} // 17490641580 + {{identity.name}} // testuser + {{random-5}} // x9k2m + {{truncate identity.name 4}} // test + {{replace identity.name 'user' 'replace'}} // testreplace + ``` + - Allowed template variables are - - `{{randomUsername}}`: Random username string - - `{{unixTimestamp}}`: Current Unix timestamp - - - If you want to provide specific privileges for the generated dynamic credentials, you can modify the SQL statement to your needs. This is useful if you want to only give access to a specific table(s). - After submitting the form, you will see a dynamic secret created in the dashboard. - - If this step fails, you may have to add the CA certificate. - + + If this step fails, you may have to add the CA certificate. + + + ![Dynamic Secret](../../../images/platform/dynamic-secrets/dynamic-secret.png) - ![Dynamic Secret](../../../images/platform/dynamic-secrets/dynamic-secret.png) Once you've successfully configured the dynamic secret, you're ready to generate on-demand credentials. To do this, simply click on the 'Generate' button which appears when hovering over the dynamic secret item. Alternatively, you can initiate the creation of a new lease by selecting 'New Lease' from the dynamic secret lease list section. - ![Dynamic Secret](/images/platform/dynamic-secrets/dynamic-secret-generate.png) - ![Dynamic Secret](/images/platform/dynamic-secrets/dynamic-secret-lease-empty.png) + ![Dynamic Secret](/images/platform/dynamic-secrets/dynamic-secret-generate.png) + ![Dynamic Secret](/images/platform/dynamic-secrets/dynamic-secret-lease-empty.png) - When generating these secrets, it's important to specify a Time-to-Live (TTL) duration. This will dictate how long the credentials are valid for. + When generating these secrets, it's important to specify a Time-to-Live (TTL) duration. This will dictate how long the credentials are valid for. - ![Provision Lease](/images/platform/dynamic-secrets/provision-lease.png) + ![Provision Lease](/images/platform/dynamic-secrets/provision-lease.png) - - Ensure that the TTL for the lease fall within the maximum TTL defined when configuring the dynamic secret. - + + Ensure that the TTL for the lease fall within the maximum TTL defined when configuring the dynamic secret. + - Once you click the `Submit` button, a new secret lease will be generated and the credentials for it will be shown to you. + Once you click the `Submit` button, a new secret lease will be generated and the credentials for it will be shown to you. + + ![Provision Lease](/images/platform/dynamic-secrets/lease-values.png) - ![Provision Lease](/images/platform/dynamic-secrets/lease-values.png) ## Audit or Revoke Leases + Once you have created one or more leases, you will be able to access them by clicking on the respective dynamic secret item on the dashboard. This will allow you to see the expiration time of the lease or delete the lease before it's set time to live. ![Provision Lease](/images/platform/dynamic-secrets/lease-data.png) ## Renew Leases + To extend the life of the generated dynamic secret leases past its initial time to live, simply click on the **Renew** button as illustrated below. ![Provision Lease](/images/platform/dynamic-secrets/dynamic-secret-lease-renew.png) - Lease renewals cannot exceed the maximum TTL set when configuring the dynamic secret + Lease renewals cannot exceed the maximum TTL set when configuring the dynamic + secret diff --git a/docs/documentation/platform/dynamic-secrets/mysql.mdx b/docs/documentation/platform/dynamic-secrets/mysql.mdx index 6f708ebba..33d11a4fc 100644 --- a/docs/documentation/platform/dynamic-secrets/mysql.mdx +++ b/docs/documentation/platform/dynamic-secrets/mysql.mdx @@ -69,15 +69,28 @@ Create a user with the required permission in your SQL instance. This user will ![Modify SQL Statements Modal](../../../images/platform/dynamic-secrets/modify-sql-statement-mysql.png) - - Specifies a template for generating usernames. This field allows customization of how usernames are automatically created. + + Specifies a template for generating usernames. This field allows customization of how usernames are automatically created. - Allowed template variables are - - `{{randomUsername}}`: Random username string - - `{{unixTimestamp}}`: Current Unix timestamp - - - If you want to provide specific privileges for the generated dynamic credentials, you can modify the SQL statement to your needs. This is useful if you want to only give access to a specific table(s). + Allowed template variables are + - `{{randomUsername}}`: Random username string + - `{{unixTimestamp}}`: Current Unix timestamp + - `{{identity.name}}`: Name of the identity that is generating the secret + - `{{random N}}`: Random string of N characters + + Allowed template functions are + - `truncate`: Truncates a string to a specified length + - `replace`: Replaces a substring with another value + + Examples: + ``` + {{randomUsername}} // 3POnzeFyK9gW2nioK0q2gMjr6CZqsRiX + {{unixTimestamp}} // 17490641580 + {{identity.name}} // testuser + {{random-5}} // x9k2m + {{truncate identity.name 4}} // test + {{replace identity.name 'user' 'replace'}} // testreplace + ``` diff --git a/docs/documentation/platform/dynamic-secrets/oracle.mdx b/docs/documentation/platform/dynamic-secrets/oracle.mdx index 02b379b98..3c83f3359 100644 --- a/docs/documentation/platform/dynamic-secrets/oracle.mdx +++ b/docs/documentation/platform/dynamic-secrets/oracle.mdx @@ -71,15 +71,28 @@ Create a user with the required permission in your SQL instance. This user will ![Modify SQL Statements Modal](../../../images/platform/dynamic-secrets/modify-sql-statement-oracle.png) - - Specifies a template for generating usernames. This field allows customization of how usernames are automatically created. + + Specifies a template for generating usernames. This field allows customization of how usernames are automatically created. - Allowed template variables are - - `{{randomUsername}}`: Random username string - - `{{unixTimestamp}}`: Current Unix timestamp - - - If you want to provide specific privileges for the generated dynamic credentials, you can modify the SQL statement to your needs. This is useful if you want to only give access to a specific table(s). + Allowed template variables are + - `{{randomUsername}}`: Random username string + - `{{unixTimestamp}}`: Current Unix timestamp + - `{{identity.name}}`: Name of the identity that is generating the secret + - `{{random N}}`: Random string of N characters + + Allowed template functions are + - `truncate`: Truncates a string to a specified length + - `replace`: Replaces a substring with another value + + Examples: + ``` + {{randomUsername}} // 3POnzeFyK9gW2nioK0q2gMjr6CZqsRiX + {{unixTimestamp}} // 17490641580 + {{identity.name}} // testuser + {{random-5}} // x9k2m + {{truncate identity.name 4}} // test + {{replace identity.name 'user' 'replace'}} // testreplace + ``` diff --git a/docs/documentation/platform/dynamic-secrets/postgresql.mdx b/docs/documentation/platform/dynamic-secrets/postgresql.mdx index f13c9c762..974066e14 100644 --- a/docs/documentation/platform/dynamic-secrets/postgresql.mdx +++ b/docs/documentation/platform/dynamic-secrets/postgresql.mdx @@ -72,12 +72,28 @@ Create a user with the required permission in your SQL instance. This user will ![Modify SQL Statements Modal](../../../images/platform/dynamic-secrets/modify-sql-statements.png) - - Specifies a template for generating usernames. This field allows customization of how usernames are automatically created. + + Specifies a template for generating usernames. This field allows customization of how usernames are automatically created. - Allowed template variables are - - `{{randomUsername}}`: Random username string - - `{{unixTimestamp}}`: Current Unix timestamp + Allowed template variables are + - `{{randomUsername}}`: Random username string + - `{{unixTimestamp}}`: Current Unix timestamp + - `{{identity.name}}`: Name of the identity that is generating the secret + - `{{random N}}`: Random string of N characters + + Allowed template functions are + - `truncate`: Truncates a string to a specified length + - `replace`: Replaces a substring with another value + + Examples: + ``` + {{randomUsername}} // 3POnzeFyK9gW2nioK0q2gMjr6CZqsRiX + {{unixTimestamp}} // 17490641580 + {{identity.name}} // testuser + {{random-5}} // x9k2m + {{truncate identity.name 4}} // test + {{replace identity.name 'user' 'replace'}} // testreplace + ``` If you want to provide specific privileges for the generated dynamic credentials, you can modify the SQL statement to your needs. This is useful if you want to only give access to a specific table(s). diff --git a/docs/documentation/platform/dynamic-secrets/rabbit-mq.mdx b/docs/documentation/platform/dynamic-secrets/rabbit-mq.mdx index 09c04e61b..be41901b7 100644 --- a/docs/documentation/platform/dynamic-secrets/rabbit-mq.mdx +++ b/docs/documentation/platform/dynamic-secrets/rabbit-mq.mdx @@ -66,12 +66,28 @@ The port that the RabbitMQ management plugin is listening on. This is `15672` by -Specifies a template for generating usernames. This field allows customization of how usernames are automatically created. + Specifies a template for generating usernames. This field allows customization of how usernames are automatically created. -Allowed template variables are -- `{{randomUsername}}`: Random username string -- `{{unixTimestamp}}`: Current Unix timestamp - + Allowed template variables are + - `{{randomUsername}}`: Random username string + - `{{unixTimestamp}}`: Current Unix timestamp + - `{{identity.name}}`: Name of the identity that is generating the secret + - `{{random N}}`: Random string of N characters + + Allowed template functions are + - `truncate`: Truncates a string to a specified length + - `replace`: Replaces a substring with another value + + Examples: + ``` + {{randomUsername}} // 3POnzeFyK9gW2nioK0q2gMjr6CZqsRiX + {{unixTimestamp}} // 17490641580 + {{identity.name}} // testuser + {{random-5}} // x9k2m + {{truncate identity.name 4}} // test + {{replace identity.name 'user' 'replace'}} // testreplace + ``` + A CA may be required if your DB requires it for incoming connections. This is often the case when connecting to a managed service. diff --git a/docs/documentation/platform/dynamic-secrets/redis.mdx b/docs/documentation/platform/dynamic-secrets/redis.mdx index b3e585204..8e28c3cb2 100644 --- a/docs/documentation/platform/dynamic-secrets/redis.mdx +++ b/docs/documentation/platform/dynamic-secrets/redis.mdx @@ -57,12 +57,28 @@ Create a user with the required permission in your Redis instance. This user wil ![Modify Redis Statements Modal](/images/platform/dynamic-secrets/modify-redis-statement.png) - - Specifies a template for generating usernames. This field allows customization of how usernames are automatically created. + + Specifies a template for generating usernames. This field allows customization of how usernames are automatically created. - Allowed template variables are - - `{{randomUsername}}`: Random username string - - `{{unixTimestamp}}`: Current Unix timestamp + Allowed template variables are + - `{{randomUsername}}`: Random username string + - `{{unixTimestamp}}`: Current Unix timestamp + - `{{identity.name}}`: Name of the identity that is generating the secret + - `{{random N}}`: Random string of N characters + + Allowed template functions are + - `truncate`: Truncates a string to a specified length + - `replace`: Replaces a substring with another value + + Examples: + ``` + {{randomUsername}} // 3POnzeFyK9gW2nioK0q2gMjr6CZqsRiX + {{unixTimestamp}} // 17490641580 + {{identity.name}} // testuser + {{random-5}} // x9k2m + {{truncate identity.name 4}} // test + {{replace identity.name 'user' 'replace'}} // testreplace + ``` If you want to provide specific privileges for the generated dynamic credentials, you can modify the Redis statement to your needs. This is useful if you want to only give access to a specific table(s). diff --git a/docs/documentation/platform/dynamic-secrets/sap-ase.mdx b/docs/documentation/platform/dynamic-secrets/sap-ase.mdx index 2737ab084..6da572f35 100644 --- a/docs/documentation/platform/dynamic-secrets/sap-ase.mdx +++ b/docs/documentation/platform/dynamic-secrets/sap-ase.mdx @@ -64,13 +64,29 @@ The Infisical SAP ASE dynamic secret allows you to generate SAP ASE database cre ![Modify SQL Statements Modal](../../../images/platform/dynamic-secrets/sap-ase/dynamic-secret-sap-ase-statements.png) - - Specifies a template for generating usernames. This field allows customization of how usernames are automatically created. + + Specifies a template for generating usernames. This field allows customization of how usernames are automatically created. - Allowed template variables are - - `{{randomUsername}}`: Random username string - - `{{unixTimestamp}}`: Current Unix timestamp - + Allowed template variables are + - `{{randomUsername}}`: Random username string + - `{{unixTimestamp}}`: Current Unix timestamp + - `{{identity.name}}`: Name of the identity that is generating the secret + - `{{random N}}`: Random string of N characters + + Allowed template functions are + - `truncate`: Truncates a string to a specified length + - `replace`: Replaces a substring with another value + + Examples: + ``` + {{randomUsername}} // 3POnzeFyK9gW2nioK0q2gMjr6CZqsRiX + {{unixTimestamp}} // 17490641580 + {{identity.name}} // testuser + {{random-5}} // x9k2m + {{truncate identity.name 4}} // test + {{replace identity.name 'user' 'replace'}} // testreplace + ``` + If you want to provide specific privileges for the generated dynamic credentials, you can modify the SQL statement to your needs. diff --git a/docs/documentation/platform/dynamic-secrets/sap-hana.mdx b/docs/documentation/platform/dynamic-secrets/sap-hana.mdx index 597d69803..8ccd842f2 100644 --- a/docs/documentation/platform/dynamic-secrets/sap-hana.mdx +++ b/docs/documentation/platform/dynamic-secrets/sap-hana.mdx @@ -64,12 +64,28 @@ The Infisical SAP HANA dynamic secret allows you to generate SAP HANA database c ![Modify SQL Statements Modal](../../../images/platform/dynamic-secrets/modify-sap-hana-sql-statements.png) - - Specifies a template for generating usernames. This field allows customization of how usernames are automatically created. + + Specifies a template for generating usernames. This field allows customization of how usernames are automatically created. - Allowed template variables are - - `{{randomUsername}}`: Random username string - - `{{unixTimestamp}}`: Current Unix timestamp + Allowed template variables are + - `{{randomUsername}}`: Random username string + - `{{unixTimestamp}}`: Current Unix timestamp + - `{{identity.name}}`: Name of the identity that is generating the secret + - `{{random N}}`: Random string of N characters + + Allowed template functions are + - `truncate`: Truncates a string to a specified length + - `replace`: Replaces a substring with another value + + Examples: + ``` + {{randomUsername}} // 3POnzeFyK9gW2nioK0q2gMjr6CZqsRiX + {{unixTimestamp}} // 17490641580 + {{identity.name}} // testuser + {{random-5}} // x9k2m + {{truncate identity.name 4}} // test + {{replace identity.name 'user' 'replace'}} // testreplace + ``` diff --git a/docs/documentation/platform/dynamic-secrets/snowflake.mdx b/docs/documentation/platform/dynamic-secrets/snowflake.mdx index 86378bbf6..f0bbaa08c 100644 --- a/docs/documentation/platform/dynamic-secrets/snowflake.mdx +++ b/docs/documentation/platform/dynamic-secrets/snowflake.mdx @@ -78,12 +78,28 @@ Infisical's Snowflake dynamic secrets allow you to generate Snowflake user crede ![Modify SQL Statements Modal](/images/platform/dynamic-secrets/snowflake/dynamic-secret-snowflake-sql-statements.png) - Specifies a template for generating usernames. This field allows customization of how usernames are automatically created. + Specifies a template for generating usernames. This field allows customization of how usernames are automatically created. - Allowed template variables are - - `{{randomUsername}}`: Random username string - - `{{unixTimestamp}}`: Current Unix timestamp - + Allowed template variables are + - `{{randomUsername}}`: Random username string + - `{{unixTimestamp}}`: Current Unix timestamp + - `{{identity.name}}`: Name of the identity that is generating the secret + - `{{random N}}`: Random string of N characters + + Allowed template functions are + - `truncate`: Truncates a string to a specified length + - `replace`: Replaces a substring with another value + + Examples: + ``` + {{randomUsername}} // 3POnzeFyK9gW2nioK0q2gMjr6CZqsRiX + {{unixTimestamp}} // 17490641580 + {{identity.name}} // testuser + {{random-5}} // x9k2m + {{truncate identity.name 4}} // test + {{replace identity.name 'user' 'replace'}} // testreplace + ``` + If you want to provide specific privileges for the generated dynamic credentials, you can modify the SQL