Add identityName to Dynamic Secrets userName template

This commit is contained in:
carlosmonastyrski
2025-06-03 21:21:36 -03:00
parent dcb77bbdd4
commit 419e9ac755
33 changed files with 168 additions and 70 deletions
@@ -33,7 +33,7 @@ const userTemplateSchema = z
.refine((el) => validateUsernameTemplateCharacters(el)) .refine((el) => validateUsernameTemplateCharacters(el))
.refine((el) => .refine((el) =>
isValidHandleBarTemplate(el, { isValidHandleBarTemplate(el, {
allowedExpressions: (val) => ["randomUsername", "unixTimestamp"].includes(val) allowedExpressions: (val) => ["randomUsername", "unixTimestamp", "identityName"].includes(val)
}) })
); );
@@ -1,4 +1,5 @@
import { ForbiddenError, subject } from "@casl/ability"; import { ForbiddenError, subject } from "@casl/ability";
import RE2 from "re2";
import { ActionProjectType } from "@app/db/schemas"; import { ActionProjectType } from "@app/db/schemas";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; 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 { BadRequestError, NotFoundError } from "@app/lib/errors";
import { logger } from "@app/lib/logger"; import { logger } from "@app/lib/logger";
import { ms } from "@app/lib/ms"; 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 { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { KmsDataKey } from "@app/services/kms/kms-types"; import { KmsDataKey } from "@app/services/kms/kms-types";
import { TProjectDALFactory } from "@app/services/project/project-dal"; import { TProjectDALFactory } from "@app/services/project/project-dal";
import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-folder-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 { TDynamicSecretDALFactory } from "../dynamic-secret/dynamic-secret-dal";
import { DynamicSecretProviders, TDynamicProviderFns } from "../dynamic-secret/providers/models"; import { DynamicSecretProviders, TDynamicProviderFns } from "../dynamic-secret/providers/models";
@@ -39,6 +43,8 @@ type TDynamicSecretLeaseServiceFactoryDep = {
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">; permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
projectDAL: Pick<TProjectDALFactory, "findProjectBySlug">; projectDAL: Pick<TProjectDALFactory, "findProjectBySlug">;
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">; kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
userDAL: Pick<TUserDALFactory, "findById">;
identityDAL: TIdentityDALFactory;
}; };
export type TDynamicSecretLeaseServiceFactory = ReturnType<typeof dynamicSecretLeaseServiceFactory>; export type TDynamicSecretLeaseServiceFactory = ReturnType<typeof dynamicSecretLeaseServiceFactory>;
@@ -52,8 +58,16 @@ export const dynamicSecretLeaseServiceFactory = ({
dynamicSecretQueueService, dynamicSecretQueueService,
projectDAL, projectDAL,
licenseService, licenseService,
kmsService kmsService,
userDAL,
identityDAL
}: TDynamicSecretLeaseServiceFactoryDep) => { }: TDynamicSecretLeaseServiceFactoryDep) => {
const extractEmailUsername = (email: string) => {
const regex = new RE2(/^([^@]+)/);
const match = email.match(regex);
return match ? match[1] : email;
};
const create = async ({ const create = async ({
environmentSlug, environmentSlug,
path, path,
@@ -132,10 +146,23 @@ export const dynamicSecretLeaseServiceFactory = ({
let result; let result;
try { try {
let identityName = "";
if (actor === ActorType.USER) {
const user = await userDAL.findById(actorId);
if (user) {
identityName = extractEmailUsername(user.username);
}
} else if (actor === ActorType.Machine) {
const identity = await identityDAL.findById(actorId);
if (identity) {
identityName = identity.name;
}
}
result = await selectedProvider.create({ result = await selectedProvider.create({
inputs: decryptedStoredInput, inputs: decryptedStoredInput,
expireAt: expireAt.getTime(), expireAt: expireAt.getTime(),
usernameTemplate: dynamicSecretCfg.usernameTemplate usernameTemplate: dynamicSecretCfg.usernameTemplate,
identityName
}); });
} catch (error: unknown) { } catch (error: unknown) {
if (error && typeof error === "object" && error !== null && "sqlMessage" in error) { if (error && typeof error === "object" && error !== null && "sqlMessage" in error) {
@@ -132,14 +132,15 @@ const generatePassword = () => {
return customAlphabet(charset, 64)(); return customAlphabet(charset, 64)();
}; };
const generateUsername = (usernameTemplate?: string | null) => { const generateUsername = (usernameTemplate?: string | null, identityName?: string) => {
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-"; const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-";
const randomUsername = `inf-${customAlphabet(charset, 32)()}`; const randomUsername = `inf-${customAlphabet(charset, 32)()}`;
if (!usernameTemplate) return randomUsername; if (!usernameTemplate) return randomUsername;
return handlebars.compile(usernameTemplate)({ return handlebars.compile(usernameTemplate)({
randomUsername, randomUsername,
unixTimestamp: Math.floor(Date.now() / 100) unixTimestamp: Math.floor(Date.now() / 100),
identityName
}); });
}; };
@@ -174,14 +175,19 @@ export const AwsElastiCacheDatabaseProvider = (): TDynamicProviderFns => {
return true; return true;
}; };
const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => { const create = async (data: {
const { inputs, expireAt, usernameTemplate } = data; inputs: unknown;
expireAt: number;
usernameTemplate?: string | null;
identityName?: string;
}) => {
const { inputs, expireAt, usernameTemplate, identityName } = data;
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
if (!(await validateConnection(providerInputs))) { if (!(await validateConnection(providerInputs))) {
throw new BadRequestError({ message: "Failed to establish connection" }); throw new BadRequestError({ message: "Failed to establish connection" });
} }
const leaseUsername = generateUsername(usernameTemplate); const leaseUsername = generateUsername(usernameTemplate, identityName);
const leasePassword = generatePassword(); const leasePassword = generatePassword();
const leaseExpiration = new Date(expireAt).toISOString(); const leaseExpiration = new Date(expireAt).toISOString();
@@ -24,13 +24,14 @@ import { alphaNumericNanoId } from "@app/lib/nanoid";
import { DynamicSecretAwsIamSchema, TDynamicProviderFns } from "./models"; import { DynamicSecretAwsIamSchema, TDynamicProviderFns } from "./models";
const generateUsername = (usernameTemplate?: string | null) => { const generateUsername = (usernameTemplate?: string | null, identityName?: string) => {
const randomUsername = alphaNumericNanoId(32); const randomUsername = alphaNumericNanoId(32);
if (!usernameTemplate) return randomUsername; if (!usernameTemplate) return randomUsername;
return handlebars.compile(usernameTemplate)({ return handlebars.compile(usernameTemplate)({
randomUsername, randomUsername,
unixTimestamp: Math.floor(Date.now() / 100) unixTimestamp: Math.floor(Date.now() / 100),
identityName
}); });
}; };
@@ -60,13 +61,18 @@ export const AwsIamProvider = (): TDynamicProviderFns => {
return isConnected; return isConnected;
}; };
const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => { const create = async (data: {
const { inputs, usernameTemplate } = data; inputs: unknown;
expireAt: number;
usernameTemplate?: string | null;
identityName?: string;
}) => {
const { inputs, usernameTemplate, identityName } = data;
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
const client = await $getClient(providerInputs); const client = await $getClient(providerInputs);
const username = generateUsername(usernameTemplate); const username = generateUsername(usernameTemplate, identityName);
const { policyArns, userGroups, policyDocument, awsPath, permissionBoundaryPolicyArn } = providerInputs; const { policyArns, userGroups, policyDocument, awsPath, permissionBoundaryPolicyArn } = providerInputs;
const createUserRes = await client.send( const createUserRes = await client.send(
new CreateUserCommand({ new CreateUserCommand({
@@ -14,13 +14,14 @@ const generatePassword = (size = 48) => {
return customAlphabet(charset, 48)(size); return customAlphabet(charset, 48)(size);
}; };
const generateUsername = (usernameTemplate?: string | null) => { const generateUsername = (usernameTemplate?: string | null, identityName?: string) => {
const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-" const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-"
if (!usernameTemplate) return randomUsername; if (!usernameTemplate) return randomUsername;
return handlebars.compile(usernameTemplate)({ return handlebars.compile(usernameTemplate)({
randomUsername, randomUsername,
unixTimestamp: Math.floor(Date.now() / 100) unixTimestamp: Math.floor(Date.now() / 100),
identityName
}); });
}; };
@@ -75,12 +76,17 @@ export const CassandraProvider = (): TDynamicProviderFns => {
return isConnected; return isConnected;
}; };
const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => { const create = async (data: {
const { inputs, expireAt, usernameTemplate } = data; inputs: unknown;
expireAt: number;
usernameTemplate?: string | null;
identityName?: string;
}) => {
const { inputs, expireAt, usernameTemplate, identityName } = data;
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
const client = await $getClient(providerInputs); const client = await $getClient(providerInputs);
const username = generateUsername(usernameTemplate); const username = generateUsername(usernameTemplate, identityName);
const password = generatePassword(); const password = generatePassword();
const { keyspace } = providerInputs; const { keyspace } = providerInputs;
const expiration = new Date(expireAt).toISOString(); const expiration = new Date(expireAt).toISOString();
@@ -13,13 +13,14 @@ const generatePassword = () => {
return customAlphabet(charset, 64)(); return customAlphabet(charset, 64)();
}; };
const generateUsername = (usernameTemplate?: string | null) => { const generateUsername = (usernameTemplate?: string | null, identityName?: string) => {
const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-" const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-"
if (!usernameTemplate) return randomUsername; if (!usernameTemplate) return randomUsername;
return handlebars.compile(usernameTemplate)({ return handlebars.compile(usernameTemplate)({
randomUsername, randomUsername,
unixTimestamp: Math.floor(Date.now() / 100) unixTimestamp: Math.floor(Date.now() / 100),
identityName
}); });
}; };
@@ -71,12 +72,12 @@ export const ElasticSearchProvider = (): TDynamicProviderFns => {
return infoResponse; return infoResponse;
}; };
const create = async (data: { inputs: unknown; usernameTemplate?: string | null }) => { const create = async (data: { inputs: unknown; usernameTemplate?: string | null; identityName?: string }) => {
const { inputs, usernameTemplate } = data; const { inputs, usernameTemplate, identityName } = data;
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
const connection = await $getClient(providerInputs); const connection = await $getClient(providerInputs);
const username = generateUsername(usernameTemplate); const username = generateUsername(usernameTemplate, identityName);
const password = generatePassword(); const password = generatePassword();
await connection.security.putUser({ await connection.security.putUser({
@@ -22,13 +22,14 @@ const encodePassword = (password?: string) => {
return base64Password; return base64Password;
}; };
const generateUsername = (usernameTemplate?: string | null) => { const generateUsername = (usernameTemplate?: string | null, identityName?: string) => {
const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-" const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-"
if (!usernameTemplate) return randomUsername; if (!usernameTemplate) return randomUsername;
return handlebars.compile(usernameTemplate)({ return handlebars.compile(usernameTemplate)({
randomUsername, randomUsername,
unixTimestamp: Math.floor(Date.now() / 100) unixTimestamp: Math.floor(Date.now() / 100),
identityName
}); });
}; };
@@ -196,8 +197,8 @@ export const LdapProvider = (): TDynamicProviderFns => {
return dnArray; return dnArray;
}; };
const create = async (data: { inputs: unknown; usernameTemplate?: string | null }) => { const create = async (data: { inputs: unknown; usernameTemplate?: string | null; identityName?: string }) => {
const { inputs, usernameTemplate } = data; const { inputs, usernameTemplate, identityName } = data;
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
const client = await $getClient(providerInputs); const client = await $getClient(providerInputs);
@@ -224,7 +225,7 @@ export const LdapProvider = (): TDynamicProviderFns => {
}); });
} }
} else { } else {
const username = generateUsername(usernameTemplate); const username = generateUsername(usernameTemplate, identityName);
const password = generatePassword(); const password = generatePassword();
const generatedLdif = generateLDIF({ username, password, ldifTemplate: providerInputs.creationLdif }); const generatedLdif = generateLDIF({ username, password, ldifTemplate: providerInputs.creationLdif });
@@ -364,6 +364,7 @@ export type TDynamicProviderFns = {
inputs: unknown; inputs: unknown;
expireAt: number; expireAt: number;
usernameTemplate?: string | null; usernameTemplate?: string | null;
identityName?: string;
}) => Promise<{ entityId: string; data: unknown }>; }) => Promise<{ entityId: string; data: unknown }>;
validateConnection: (inputs: unknown) => Promise<boolean>; validateConnection: (inputs: unknown) => Promise<boolean>;
validateProviderInputs: (inputs: object) => Promise<unknown>; validateProviderInputs: (inputs: object) => Promise<unknown>;
@@ -13,13 +13,14 @@ const generatePassword = (size = 48) => {
return customAlphabet(charset, 48)(size); return customAlphabet(charset, 48)(size);
}; };
const generateUsername = (usernameTemplate?: string | null) => { const generateUsername = (usernameTemplate?: string | null, identityName?: string) => {
const randomUsername = alphaNumericNanoId(32); const randomUsername = alphaNumericNanoId(32);
if (!usernameTemplate) return randomUsername; if (!usernameTemplate) return randomUsername;
return handlebars.compile(usernameTemplate)({ return handlebars.compile(usernameTemplate)({
randomUsername, randomUsername,
unixTimestamp: Math.floor(Date.now() / 100) unixTimestamp: Math.floor(Date.now() / 100),
identityName
}); });
}; };
@@ -64,12 +65,17 @@ export const MongoAtlasProvider = (): TDynamicProviderFns => {
return isConnected; return isConnected;
}; };
const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => { const create = async (data: {
const { inputs, expireAt, usernameTemplate } = data; inputs: unknown;
expireAt: number;
usernameTemplate?: string | null;
identityName?: string;
}) => {
const { inputs, expireAt, usernameTemplate, identityName } = data;
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
const client = await $getClient(providerInputs); const client = await $getClient(providerInputs);
const username = generateUsername(usernameTemplate); const username = generateUsername(usernameTemplate, identityName);
const password = generatePassword(); const password = generatePassword();
const expiration = new Date(expireAt).toISOString(); const expiration = new Date(expireAt).toISOString();
await client({ await client({
@@ -13,13 +13,14 @@ const generatePassword = (size = 48) => {
return customAlphabet(charset, 48)(size); return customAlphabet(charset, 48)(size);
}; };
const generateUsername = (usernameTemplate?: string | null) => { const generateUsername = (usernameTemplate?: string | null, identityName?: string) => {
const randomUsername = alphaNumericNanoId(32); const randomUsername = alphaNumericNanoId(32);
if (!usernameTemplate) return randomUsername; if (!usernameTemplate) return randomUsername;
return handlebars.compile(usernameTemplate)({ return handlebars.compile(usernameTemplate)({
randomUsername, randomUsername,
unixTimestamp: Math.floor(Date.now() / 100) unixTimestamp: Math.floor(Date.now() / 100),
identityName
}); });
}; };
@@ -60,12 +61,12 @@ export const MongoDBProvider = (): TDynamicProviderFns => {
return isConnected; return isConnected;
}; };
const create = async (data: { inputs: unknown; usernameTemplate?: string | null }) => { const create = async (data: { inputs: unknown; usernameTemplate?: string | null; identityName?: string }) => {
const { inputs, usernameTemplate } = data; const { inputs, usernameTemplate, identityName } = data;
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
const client = await $getClient(providerInputs); const client = await $getClient(providerInputs);
const username = generateUsername(usernameTemplate); const username = generateUsername(usernameTemplate, identityName);
const password = generatePassword(); const password = generatePassword();
const db = client.db(providerInputs.database); const db = client.db(providerInputs.database);
@@ -15,13 +15,14 @@ const generatePassword = () => {
return customAlphabet(charset, 64)(); return customAlphabet(charset, 64)();
}; };
const generateUsername = (usernameTemplate?: string | null) => { const generateUsername = (usernameTemplate?: string | null, identityName?: string) => {
const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-" const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-"
if (!usernameTemplate) return randomUsername; if (!usernameTemplate) return randomUsername;
return handlebars.compile(usernameTemplate)({ return handlebars.compile(usernameTemplate)({
randomUsername, randomUsername,
unixTimestamp: Math.floor(Date.now() / 100) unixTimestamp: Math.floor(Date.now() / 100),
identityName
}); });
}; };
@@ -117,12 +118,12 @@ export const RabbitMqProvider = (): TDynamicProviderFns => {
return infoResponse; return infoResponse;
}; };
const create = async (data: { inputs: unknown; usernameTemplate?: string | null }) => { const create = async (data: { inputs: unknown; usernameTemplate?: string | null; identityName?: string }) => {
const { inputs, usernameTemplate } = data; const { inputs, usernameTemplate, identityName } = data;
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
const connection = await $getClient(providerInputs); const connection = await $getClient(providerInputs);
const username = generateUsername(usernameTemplate); const username = generateUsername(usernameTemplate, identityName);
const password = generatePassword(); const password = generatePassword();
await createRabbitMqUser({ await createRabbitMqUser({
@@ -15,13 +15,14 @@ const generatePassword = () => {
return customAlphabet(charset, 64)(); return customAlphabet(charset, 64)();
}; };
const generateUsername = (usernameTemplate?: string | null) => { const generateUsername = (usernameTemplate?: string | null, identityName?: string) => {
const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-" const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-"
if (!usernameTemplate) return randomUsername; if (!usernameTemplate) return randomUsername;
return handlebars.compile(usernameTemplate)({ return handlebars.compile(usernameTemplate)({
randomUsername, randomUsername,
unixTimestamp: Math.floor(Date.now() / 100) unixTimestamp: Math.floor(Date.now() / 100),
identityName
}); });
}; };
@@ -121,12 +122,17 @@ export const RedisDatabaseProvider = (): TDynamicProviderFns => {
return pingResponse; return pingResponse;
}; };
const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => { const create = async (data: {
const { inputs, expireAt, usernameTemplate } = data; inputs: unknown;
expireAt: number;
usernameTemplate?: string | null;
identityName?: string;
}) => {
const { inputs, expireAt, usernameTemplate, identityName } = data;
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
const connection = await $getClient(providerInputs); const connection = await $getClient(providerInputs);
const username = generateUsername(usernameTemplate); const username = generateUsername(usernameTemplate, identityName);
const password = generatePassword(); const password = generatePassword();
const expiration = new Date(expireAt).toISOString(); const expiration = new Date(expireAt).toISOString();
@@ -15,13 +15,14 @@ const generatePassword = (size = 48) => {
return customAlphabet(charset, 48)(size); return customAlphabet(charset, 48)(size);
}; };
const generateUsername = (usernameTemplate?: string | null) => { const generateUsername = (usernameTemplate?: string | null, identityName?: string) => {
const randomUsername = `inf_${alphaNumericNanoId(25)}`; // Username must start with an ascii letter, so we prepend the username with "inf-" const randomUsername = `inf_${alphaNumericNanoId(25)}`; // Username must start with an ascii letter, so we prepend the username with "inf-"
if (!usernameTemplate) return randomUsername; if (!usernameTemplate) return randomUsername;
return handlebars.compile(usernameTemplate)({ return handlebars.compile(usernameTemplate)({
randomUsername, randomUsername,
unixTimestamp: Math.floor(Date.now() / 100) unixTimestamp: Math.floor(Date.now() / 100),
identityName
}); });
}; };
@@ -87,11 +88,11 @@ export const SapAseProvider = (): TDynamicProviderFns => {
return true; return true;
}; };
const create = async (data: { inputs: unknown; usernameTemplate?: string | null }) => { const create = async (data: { inputs: unknown; usernameTemplate?: string | null; identityName?: string }) => {
const { inputs, usernameTemplate } = data; const { inputs, usernameTemplate, identityName } = data;
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
const username = generateUsername(usernameTemplate); const username = generateUsername(usernameTemplate, identityName);
const password = generatePassword(); const password = generatePassword();
const client = await $getClient(providerInputs); const client = await $getClient(providerInputs);
@@ -21,13 +21,14 @@ const generatePassword = (size = 48) => {
return customAlphabet(charset, 48)(size); return customAlphabet(charset, 48)(size);
}; };
const generateUsername = (usernameTemplate?: string | null) => { const generateUsername = (usernameTemplate?: string | null, identityName?: string) => {
const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-" const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-"
if (!usernameTemplate) return randomUsername; if (!usernameTemplate) return randomUsername;
return handlebars.compile(usernameTemplate)({ return handlebars.compile(usernameTemplate)({
randomUsername, randomUsername,
unixTimestamp: Math.floor(Date.now() / 100) unixTimestamp: Math.floor(Date.now() / 100),
identityName
}); });
}; };
@@ -97,11 +98,16 @@ export const SapHanaProvider = (): TDynamicProviderFns => {
return testResult; return testResult;
}; };
const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => { const create = async (data: {
const { inputs, expireAt, usernameTemplate } = data; inputs: unknown;
expireAt: number;
usernameTemplate?: string | null;
identityName?: string;
}) => {
const { inputs, expireAt, usernameTemplate, identityName } = data;
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
const username = generateUsername(usernameTemplate); const username = generateUsername(usernameTemplate, identityName);
const password = generatePassword(); const password = generatePassword();
const expiration = new Date(expireAt).toISOString(); const expiration = new Date(expireAt).toISOString();
@@ -17,13 +17,14 @@ const generatePassword = (size = 48) => {
return customAlphabet(charset, 48)(size); return customAlphabet(charset, 48)(size);
}; };
const generateUsername = (usernameTemplate?: string | null) => { const generateUsername = (usernameTemplate?: string | null, identityName?: string) => {
const randomUsername = `infisical_${alphaNumericNanoId(32)}`; // Username must start with an ascii letter, so we prepend the username with "inf-" const randomUsername = `infisical_${alphaNumericNanoId(32)}`; // Username must start with an ascii letter, so we prepend the username with "inf-"
if (!usernameTemplate) return randomUsername; if (!usernameTemplate) return randomUsername;
return handlebars.compile(usernameTemplate)({ return handlebars.compile(usernameTemplate)({
randomUsername, randomUsername,
unixTimestamp: Math.floor(Date.now() / 100) unixTimestamp: Math.floor(Date.now() / 100),
identityName
}); });
}; };
@@ -88,13 +89,18 @@ export const SnowflakeProvider = (): TDynamicProviderFns => {
return isValidConnection; return isValidConnection;
}; };
const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => { const create = async (data: {
const { inputs, expireAt, usernameTemplate } = data; inputs: unknown;
expireAt: number;
usernameTemplate?: string | null;
identityName?: string;
}) => {
const { inputs, expireAt, usernameTemplate, identityName } = data;
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
const client = await $getClient(providerInputs); const client = await $getClient(providerInputs);
const username = generateUsername(usernameTemplate); const username = generateUsername(usernameTemplate, identityName);
const password = generatePassword(); const password = generatePassword();
try { try {
@@ -104,9 +104,8 @@ const generatePassword = (provider: SqlProviders, requirements?: PasswordRequire
} }
}; };
const generateUsername = (provider: SqlProviders, usernameTemplate?: string | null) => { const generateUsername = (provider: SqlProviders, usernameTemplate?: string | null, identityName?: string) => {
let randomUsername = ""; let randomUsername = "";
// For oracle, the client assumes everything is upper case when not using quotes around the password // For oracle, the client assumes everything is upper case when not using quotes around the password
if (provider === SqlProviders.Oracle) { if (provider === SqlProviders.Oracle) {
randomUsername = alphaNumericNanoId(32).toUpperCase(); randomUsername = alphaNumericNanoId(32).toUpperCase();
@@ -117,7 +116,8 @@ const generateUsername = (provider: SqlProviders, usernameTemplate?: string | nu
return handlebars.compile(usernameTemplate)({ return handlebars.compile(usernameTemplate)({
randomUsername, randomUsername,
unixTimestamp: Math.floor(Date.now() / 100) unixTimestamp: Math.floor(Date.now() / 100),
identityName
}); });
}; };
@@ -220,11 +220,16 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
return isConnected; return isConnected;
}; };
const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => { const create = async (data: {
const { inputs, expireAt, usernameTemplate } = data; inputs: unknown;
expireAt: number;
usernameTemplate?: string | null;
identityName?: string;
}) => {
const { inputs, expireAt, usernameTemplate, identityName } = data;
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
const username = generateUsername(providerInputs.client, usernameTemplate); const username = generateUsername(providerInputs.client, usernameTemplate, identityName);
const password = generatePassword(providerInputs.client, providerInputs.passwordRequirements); const password = generatePassword(providerInputs.client, providerInputs.passwordRequirements);
const gatewayCallback = async (host = providerInputs.host, port = providerInputs.port) => { const gatewayCallback = async (host = providerInputs.host, port = providerInputs.port) => {
+3 -1
View File
@@ -1508,7 +1508,9 @@ export const registerRoutes = async (
dynamicSecretProviders, dynamicSecretProviders,
folderDAL, folderDAL,
licenseService, licenseService,
kmsService kmsService,
userDAL,
identityDAL
}); });
const dailyResourceCleanUp = dailyResourceCleanUpQueueServiceFactory({ const dailyResourceCleanUp = dailyResourceCleanUpQueueServiceFactory({
auditLogDAL, auditLogDAL,
@@ -101,6 +101,7 @@ The Infisical AWS ElastiCache dynamic secret allows you to generate AWS ElastiCa
Allowed template variables are Allowed template variables are
- `{{randomUsername}}`: Random username string - `{{randomUsername}}`: Random username string
- `{{unixTimestamp}}`: Current Unix timestamp - `{{unixTimestamp}}`: Current Unix timestamp
- `{{identityName}}`: Name of the identity that is generating the secret
</ParamField> </ParamField>
<ParamField path="Customize ElastiCache Statement" type="string"> <ParamField path="Customize ElastiCache Statement" type="string">
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. 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.
@@ -111,6 +111,7 @@ Specifies a template for generating usernames. This field allows customization o
Allowed template variables are Allowed template variables are
- `{{randomUsername}}`: Random username string - `{{randomUsername}}`: Random username string
- `{{unixTimestamp}}`: Current Unix timestamp - `{{unixTimestamp}}`: Current Unix timestamp
- `{{identityName}}`: Name of the identity that is generating the secret
</ParamField> </ParamField>
![Dynamic Secret Setup Modal](../../../images/platform/dynamic-secrets/dynamic-secret-setup-modal-aws-iam.png) ![Dynamic Secret Setup Modal](../../../images/platform/dynamic-secrets/dynamic-secret-setup-modal-aws-iam.png)
@@ -85,6 +85,7 @@ The above configuration allows user creation and granting permissions.
Allowed template variables are Allowed template variables are
- `{{randomUsername}}`: Random username string - `{{randomUsername}}`: Random username string
- `{{unixTimestamp}}`: Current Unix timestamp - `{{unixTimestamp}}`: Current Unix timestamp
- `{{identityName}}`: Name of the identity that is generating the secret
</ParamField> </ParamField>
<ParamField path="Customize CQL Statement" type="string"> <ParamField path="Customize CQL Statement" type="string">
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). 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).
@@ -93,6 +93,7 @@ The port that your Elasticsearch instance is running on. _(Example: 9200)_
Allowed template variables are Allowed template variables are
- `{{randomUsername}}`: Random username string - `{{randomUsername}}`: Random username string
- `{{unixTimestamp}}`: Current Unix timestamp - `{{unixTimestamp}}`: Current Unix timestamp
- `{{identityName}}`: Name of the identity that is generating the secret
</ParamField> </ParamField>
![Dynamic Secret Setup Modal](../../../images/platform/dynamic-secrets/dynamic-secret-input-modal-elastic-search.png) ![Dynamic Secret Setup Modal](../../../images/platform/dynamic-secrets/dynamic-secret-input-modal-elastic-search.png)
@@ -129,6 +129,7 @@ The Infisical LDAP dynamic secret allows you to generate user credentials on dem
Allowed template variables are Allowed template variables are
- `{{randomUsername}}`: Random username string - `{{randomUsername}}`: Random username string
- `{{unixTimestamp}}`: Current Unix timestamp - `{{unixTimestamp}}`: Current Unix timestamp
- `{{identityName}}`: Name of the identity that is generating the secret
</ParamField> </ParamField>
</Step> </Step>
@@ -69,6 +69,7 @@ Create a project scoped API Key with the required permission in your Mongo Atlas
Allowed template variables are Allowed template variables are
- `{{randomUsername}}`: Random username string - `{{randomUsername}}`: Random username string
- `{{unixTimestamp}}`: Current Unix timestamp - `{{unixTimestamp}}`: Current Unix timestamp
- `{{identityName}}`: Name of the identity that is generating the secret
</ParamField> </ParamField>
<ParamField path="Customize Scope" type="string"> <ParamField path="Customize Scope" type="string">
@@ -72,6 +72,7 @@ Create a user with the required permission in your MongoDB instance. This user w
Allowed template variables are Allowed template variables are
- `{{randomUsername}}`: Random username string - `{{randomUsername}}`: Random username string
- `{{unixTimestamp}}`: Current Unix timestamp - `{{unixTimestamp}}`: Current Unix timestamp
- `{{identityName}}`: Name of the identity that is generating the secret
</ParamField> </ParamField>
![Dynamic Secret Setup Modal](../../../images/platform/dynamic-secrets/dynamic-secret-mongodb.png) ![Dynamic Secret Setup Modal](../../../images/platform/dynamic-secrets/dynamic-secret-mongodb.png)
@@ -78,6 +78,7 @@ Create a user with the required permission in your SQL instance. This user will
Allowed template variables are Allowed template variables are
- `{{randomUsername}}`: Random username string - `{{randomUsername}}`: Random username string
- `{{unixTimestamp}}`: Current Unix timestamp - `{{unixTimestamp}}`: Current Unix timestamp
- `{{identityName}}`: Name of the identity that is generating the secret
</ParamField> </ParamField>
<ParamField path="Customize SQL Statement" type="string"> <ParamField path="Customize SQL Statement" type="string">
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). 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).
@@ -75,6 +75,7 @@ Create a user with the required permission in your SQL instance. This user will
Allowed template variables are Allowed template variables are
- `{{randomUsername}}`: Random username string - `{{randomUsername}}`: Random username string
- `{{unixTimestamp}}`: Current Unix timestamp - `{{unixTimestamp}}`: Current Unix timestamp
- `{{identityName}}`: Name of the identity that is generating the secret
</ParamField> </ParamField>
<ParamField path="Customize SQL Statement" type="string"> <ParamField path="Customize SQL Statement" type="string">
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). 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).
@@ -77,6 +77,7 @@ Create a user with the required permission in your SQL instance. This user will
Allowed template variables are Allowed template variables are
- `{{randomUsername}}`: Random username string - `{{randomUsername}}`: Random username string
- `{{unixTimestamp}}`: Current Unix timestamp - `{{unixTimestamp}}`: Current Unix timestamp
- `{{identityName}}`: Name of the identity that is generating the secret
</ParamField> </ParamField>
<ParamField path="Customize SQL Statement" type="string"> <ParamField path="Customize SQL Statement" type="string">
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). 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).
@@ -78,6 +78,7 @@ Create a user with the required permission in your SQL instance. This user will
Allowed template variables are Allowed template variables are
- `{{randomUsername}}`: Random username string - `{{randomUsername}}`: Random username string
- `{{unixTimestamp}}`: Current Unix timestamp - `{{unixTimestamp}}`: Current Unix timestamp
- `{{identityName}}`: Name of the identity that is generating the secret
</ParamField> </ParamField>
<ParamField path="Customize SQL Statement" type="string"> <ParamField path="Customize SQL Statement" type="string">
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). 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).
@@ -71,6 +71,7 @@ Specifies a template for generating usernames. This field allows customization o
Allowed template variables are Allowed template variables are
- `{{randomUsername}}`: Random username string - `{{randomUsername}}`: Random username string
- `{{unixTimestamp}}`: Current Unix timestamp - `{{unixTimestamp}}`: Current Unix timestamp
- `{{identityName}}`: Name of the identity that is generating the secret
</ParamField> </ParamField>
<ParamField path="CA(SSL)" type="string"> <ParamField path="CA(SSL)" type="string">
@@ -63,6 +63,7 @@ Create a user with the required permission in your Redis instance. This user wil
Allowed template variables are Allowed template variables are
- `{{randomUsername}}`: Random username string - `{{randomUsername}}`: Random username string
- `{{unixTimestamp}}`: Current Unix timestamp - `{{unixTimestamp}}`: Current Unix timestamp
- `{{identityName}}`: Name of the identity that is generating the secret
</ParamField> </ParamField>
<ParamField path="Customize Redis Statement" type="string"> <ParamField path="Customize Redis Statement" type="string">
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). 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).
@@ -70,6 +70,7 @@ The Infisical SAP ASE dynamic secret allows you to generate SAP ASE database cre
Allowed template variables are Allowed template variables are
- `{{randomUsername}}`: Random username string - `{{randomUsername}}`: Random username string
- `{{unixTimestamp}}`: Current Unix timestamp - `{{unixTimestamp}}`: Current Unix timestamp
- `{{identityName}}`: Name of the identity that is generating the secret
</ParamField> </ParamField>
<ParamField path="Customize Statement" type="string"> <ParamField path="Customize Statement" type="string">
@@ -70,6 +70,7 @@ The Infisical SAP HANA dynamic secret allows you to generate SAP HANA database c
Allowed template variables are Allowed template variables are
- `{{randomUsername}}`: Random username string - `{{randomUsername}}`: Random username string
- `{{unixTimestamp}}`: Current Unix timestamp - `{{unixTimestamp}}`: Current Unix timestamp
- `{{identityName}}`: Name of the identity that is generating the secret
</ParamField> </ParamField>
<ParamField path="Customize Statement" type="string"> <ParamField path="Customize Statement" type="string">
@@ -83,6 +83,7 @@ Infisical's Snowflake dynamic secrets allow you to generate Snowflake user crede
Allowed template variables are Allowed template variables are
- `{{randomUsername}}`: Random username string - `{{randomUsername}}`: Random username string
- `{{unixTimestamp}}`: Current Unix timestamp - `{{unixTimestamp}}`: Current Unix timestamp
- `{{identityName}}`: Name of the identity that is generating the secret
</ParamField> </ParamField>
<ParamField path="Customize Statement" type="string"> <ParamField path="Customize Statement" type="string">