Merge pull request #4328 from Infisical/feat/error-log

feat: better error notification for dynamic secret
This commit is contained in:
Akhil Mohan
2025-08-08 22:59:12 +05:30
committed by GitHub
20 changed files with 1152 additions and 499 deletions
@@ -15,6 +15,7 @@ import { z } from "zod";
import { CustomAWSHasher } from "@app/lib/aws/hashing"; import { CustomAWSHasher } from "@app/lib/aws/hashing";
import { crypto } from "@app/lib/crypto"; import { crypto } from "@app/lib/crypto";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { sanitizeString } from "@app/lib/fn";
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars"; import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
import { DynamicSecretAwsElastiCacheSchema, TDynamicProviderFns } from "./models"; import { DynamicSecretAwsElastiCacheSchema, TDynamicProviderFns } from "./models";
@@ -170,6 +171,7 @@ export const AwsElastiCacheDatabaseProvider = (): TDynamicProviderFns => {
}; };
const validateConnection = async (inputs: unknown) => { const validateConnection = async (inputs: unknown) => {
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
try {
await ElastiCacheUserManager( await ElastiCacheUserManager(
{ {
accessKeyId: providerInputs.accessKeyId, accessKeyId: providerInputs.accessKeyId,
@@ -178,6 +180,20 @@ export const AwsElastiCacheDatabaseProvider = (): TDynamicProviderFns => {
providerInputs.region providerInputs.region
).verifyCredentials(providerInputs.clusterName); ).verifyCredentials(providerInputs.clusterName);
return true; return true;
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [
providerInputs.accessKeyId,
providerInputs.secretAccessKey,
providerInputs.clusterName,
providerInputs.region
]
});
throw new BadRequestError({
message: `Failed to connect with provider: ${sanitizedErrorMessage}`
});
}
}; };
const create = async (data: { const create = async (data: {
@@ -206,6 +222,7 @@ export const AwsElastiCacheDatabaseProvider = (): TDynamicProviderFns => {
const parsedStatement = CreateElastiCacheUserSchema.parse(JSON.parse(creationStatement)); const parsedStatement = CreateElastiCacheUserSchema.parse(JSON.parse(creationStatement));
try {
await ElastiCacheUserManager( await ElastiCacheUserManager(
{ {
accessKeyId: providerInputs.accessKeyId, accessKeyId: providerInputs.accessKeyId,
@@ -221,6 +238,21 @@ export const AwsElastiCacheDatabaseProvider = (): TDynamicProviderFns => {
DB_PASSWORD: leasePassword DB_PASSWORD: leasePassword
} }
}; };
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [
leaseUsername,
leasePassword,
providerInputs.accessKeyId,
providerInputs.secretAccessKey,
providerInputs.clusterName
]
});
throw new BadRequestError({
message: `Failed to create lease from provider: ${sanitizedErrorMessage}`
});
}
}; };
const revoke = async (inputs: unknown, entityId: string) => { const revoke = async (inputs: unknown, entityId: string) => {
@@ -229,6 +261,7 @@ export const AwsElastiCacheDatabaseProvider = (): TDynamicProviderFns => {
const revokeStatement = handlebars.compile(providerInputs.revocationStatement)({ username: entityId }); const revokeStatement = handlebars.compile(providerInputs.revocationStatement)({ username: entityId });
const parsedStatement = DeleteElasticCacheUserSchema.parse(JSON.parse(revokeStatement)); const parsedStatement = DeleteElasticCacheUserSchema.parse(JSON.parse(revokeStatement));
try {
await ElastiCacheUserManager( await ElastiCacheUserManager(
{ {
accessKeyId: providerInputs.accessKeyId, accessKeyId: providerInputs.accessKeyId,
@@ -238,6 +271,15 @@ export const AwsElastiCacheDatabaseProvider = (): TDynamicProviderFns => {
).deleteUser(parsedStatement); ).deleteUser(parsedStatement);
return { entityId }; return { entityId };
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [entityId, providerInputs.accessKeyId, providerInputs.secretAccessKey, providerInputs.clusterName]
});
throw new BadRequestError({
message: `Failed to revoke lease from provider: ${sanitizedErrorMessage}`
});
}
}; };
const renew = async (_inputs: unknown, entityId: string) => { const renew = async (_inputs: unknown, entityId: string) => {
@@ -23,6 +23,7 @@ import { CustomAWSHasher } from "@app/lib/aws/hashing";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto/cryptography"; import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, UnauthorizedError } from "@app/lib/errors"; import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
import { sanitizeString } from "@app/lib/fn";
import { alphaNumericNanoId } from "@app/lib/nanoid"; import { alphaNumericNanoId } from "@app/lib/nanoid";
import { AwsIamAuthType, DynamicSecretAwsIamSchema, TDynamicProviderFns } from "./models"; import { AwsIamAuthType, DynamicSecretAwsIamSchema, TDynamicProviderFns } from "./models";
@@ -118,6 +119,7 @@ export const AwsIamProvider = (): TDynamicProviderFns => {
const validateConnection = async (inputs: unknown, { projectId }: { projectId: string }) => { const validateConnection = async (inputs: unknown, { projectId }: { projectId: string }) => {
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
try {
const client = await $getClient(providerInputs, projectId); const client = await $getClient(providerInputs, projectId);
const isConnected = await client const isConnected = await client
.send(new GetUserCommand({})) .send(new GetUserCommand({}))
@@ -134,6 +136,22 @@ export const AwsIamProvider = (): TDynamicProviderFns => {
throw err; throw err;
}); });
return isConnected; return isConnected;
} catch (err) {
const sensitiveTokens = [];
if (providerInputs.method === AwsIamAuthType.AccessKey) {
sensitiveTokens.push(providerInputs.accessKey, providerInputs.secretAccessKey);
}
if (providerInputs.method === AwsIamAuthType.AssumeRole) {
sensitiveTokens.push(providerInputs.roleArn);
}
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: sensitiveTokens
});
throw new BadRequestError({
message: `Failed to connect with provider: ${sanitizedErrorMessage}`
});
}
}; };
const create = async (data: { const create = async (data: {
@@ -162,6 +180,7 @@ export const AwsIamProvider = (): TDynamicProviderFns => {
awsTags.push(...additionalTags); awsTags.push(...additionalTags);
} }
try {
const createUserRes = await client.send( const createUserRes = await client.send(
new CreateUserCommand({ new CreateUserCommand({
Path: awsPath, Path: awsPath,
@@ -188,7 +207,9 @@ export const AwsIamProvider = (): TDynamicProviderFns => {
.split(",") .split(",")
.filter(Boolean) .filter(Boolean)
.map((policyArn) => .map((policyArn) =>
client.send(new AttachUserPolicyCommand({ UserName: createUserRes?.User?.UserName, PolicyArn: policyArn })) client.send(
new AttachUserPolicyCommand({ UserName: createUserRes?.User?.UserName, PolicyArn: policyArn })
)
) )
); );
} }
@@ -218,6 +239,22 @@ export const AwsIamProvider = (): TDynamicProviderFns => {
USERNAME: username USERNAME: username
} }
}; };
} catch (err) {
const sensitiveTokens = [username];
if (providerInputs.method === AwsIamAuthType.AccessKey) {
sensitiveTokens.push(providerInputs.accessKey, providerInputs.secretAccessKey);
}
if (providerInputs.method === AwsIamAuthType.AssumeRole) {
sensitiveTokens.push(providerInputs.roleArn);
}
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: sensitiveTokens
});
throw new BadRequestError({
message: `Failed to create lease from provider: ${sanitizedErrorMessage}`
});
}
}; };
const revoke = async (inputs: unknown, entityId: string, metadata: { projectId: string }) => { const revoke = async (inputs: unknown, entityId: string, metadata: { projectId: string }) => {
@@ -278,8 +315,25 @@ export const AwsIamProvider = (): TDynamicProviderFns => {
) )
); );
try {
await client.send(new DeleteUserCommand({ UserName: username })); await client.send(new DeleteUserCommand({ UserName: username }));
return { entityId: username }; return { entityId: username };
} catch (err) {
const sensitiveTokens = [username];
if (providerInputs.method === AwsIamAuthType.AccessKey) {
sensitiveTokens.push(providerInputs.accessKey, providerInputs.secretAccessKey);
}
if (providerInputs.method === AwsIamAuthType.AssumeRole) {
sensitiveTokens.push(providerInputs.roleArn);
}
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: sensitiveTokens
});
throw new BadRequestError({
message: `Failed to revoke lease from provider: ${sanitizedErrorMessage}`
});
}
}; };
const renew = async (_inputs: unknown, entityId: string) => { const renew = async (_inputs: unknown, entityId: string) => {
@@ -2,6 +2,7 @@ import axios from "axios";
import { customAlphabet } from "nanoid"; import { customAlphabet } from "nanoid";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { sanitizeString } from "@app/lib/fn";
import { AzureEntraIDSchema, TDynamicProviderFns } from "./models"; import { AzureEntraIDSchema, TDynamicProviderFns } from "./models";
@@ -51,19 +52,30 @@ export const AzureEntraIDProvider = (): TDynamicProviderFns & {
const validateConnection = async (inputs: unknown) => { const validateConnection = async (inputs: unknown) => {
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
try {
const data = await $getToken(providerInputs.tenantId, providerInputs.applicationId, providerInputs.clientSecret); const data = await $getToken(providerInputs.tenantId, providerInputs.applicationId, providerInputs.clientSecret);
return data.success; return data.success;
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [providerInputs.clientSecret, providerInputs.applicationId, providerInputs.tenantId]
});
throw new BadRequestError({
message: `Failed to connect with provider: ${sanitizedErrorMessage}`
});
}
}; };
const create = async ({ inputs }: { inputs: unknown }) => { const create = async ({ inputs }: { inputs: unknown }) => {
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
const password = generatePassword();
try {
const data = await $getToken(providerInputs.tenantId, providerInputs.applicationId, providerInputs.clientSecret); const data = await $getToken(providerInputs.tenantId, providerInputs.applicationId, providerInputs.clientSecret);
if (!data.success) { if (!data.success) {
throw new BadRequestError({ message: "Failed to authorize to Microsoft Entra ID" }); throw new BadRequestError({ message: "Failed to authorize to Microsoft Entra ID" });
} }
const password = generatePassword();
const response = await axios.patch( const response = await axios.patch(
`${MSFT_GRAPH_API_URL}/users/${providerInputs.userId}`, `${MSFT_GRAPH_API_URL}/users/${providerInputs.userId}`,
{ {
@@ -84,12 +96,38 @@ export const AzureEntraIDProvider = (): TDynamicProviderFns & {
} }
return { entityId: providerInputs.userId, data: { email: providerInputs.email, password } }; return { entityId: providerInputs.userId, data: { email: providerInputs.email, password } };
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [
providerInputs.clientSecret,
providerInputs.applicationId,
providerInputs.userId,
providerInputs.email,
password
]
});
throw new BadRequestError({
message: `Failed to create lease from provider: ${sanitizedErrorMessage}`
});
}
}; };
const revoke = async (inputs: unknown, entityId: string) => { const revoke = async (inputs: unknown, entityId: string) => {
const providerInputs = await validateProviderInputs(inputs);
try {
// Creates a new password // Creates a new password
await create({ inputs }); await create({ inputs });
return { entityId }; return { entityId };
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [providerInputs.clientSecret, providerInputs.applicationId, entityId]
});
throw new BadRequestError({
message: `Failed to revoke lease from provider: ${sanitizedErrorMessage}`
});
}
}; };
const fetchAzureEntraIdUsers = async (tenantId: string, applicationId: string, clientSecret: string) => { const fetchAzureEntraIdUsers = async (tenantId: string, applicationId: string, clientSecret: string) => {
@@ -3,6 +3,8 @@ import handlebars from "handlebars";
import { customAlphabet } from "nanoid"; import { customAlphabet } from "nanoid";
import { z } from "zod"; import { z } from "zod";
import { BadRequestError } from "@app/lib/errors";
import { sanitizeString } from "@app/lib/fn";
import { alphaNumericNanoId } from "@app/lib/nanoid"; import { alphaNumericNanoId } from "@app/lib/nanoid";
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars"; import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
@@ -71,9 +73,24 @@ export const CassandraProvider = (): TDynamicProviderFns => {
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
const client = await $getClient(providerInputs); const client = await $getClient(providerInputs);
try {
const isConnected = await client.execute("SELECT * FROM system_schema.keyspaces").then(() => true); const isConnected = await client.execute("SELECT * FROM system_schema.keyspaces").then(() => true);
await client.shutdown(); await client.shutdown();
return isConnected; return isConnected;
} catch (err) {
const tokens = [providerInputs.password, providerInputs.username];
if (providerInputs.keyspace) {
tokens.push(providerInputs.keyspace);
}
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens
});
await client.shutdown();
throw new BadRequestError({
message: `Failed to connect with provider: ${sanitizedErrorMessage}`
});
}
}; };
const create = async (data: { const create = async (data: {
@@ -89,6 +106,8 @@ export const CassandraProvider = (): TDynamicProviderFns => {
const username = generateUsername(usernameTemplate, identity); const username = generateUsername(usernameTemplate, identity);
const password = generatePassword(); const password = generatePassword();
const { keyspace } = providerInputs; const { keyspace } = providerInputs;
try {
const expiration = new Date(expireAt).toISOString(); const expiration = new Date(expireAt).toISOString();
const creationStatement = handlebars.compile(providerInputs.creationStatement, { noEscape: true })({ const creationStatement = handlebars.compile(providerInputs.creationStatement, { noEscape: true })({
@@ -106,6 +125,20 @@ export const CassandraProvider = (): TDynamicProviderFns => {
await client.shutdown(); await client.shutdown();
return { entityId: username, data: { DB_USERNAME: username, DB_PASSWORD: password } }; return { entityId: username, data: { DB_USERNAME: username, DB_PASSWORD: password } };
} catch (err) {
const tokens = [username, password];
if (keyspace) {
tokens.push(keyspace);
}
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens
});
await client.shutdown();
throw new BadRequestError({
message: `Failed to create lease from provider: ${sanitizedErrorMessage}`
});
}
}; };
const revoke = async (inputs: unknown, entityId: string) => { const revoke = async (inputs: unknown, entityId: string) => {
@@ -115,6 +148,7 @@ export const CassandraProvider = (): TDynamicProviderFns => {
const username = entityId; const username = entityId;
const { keyspace } = providerInputs; const { keyspace } = providerInputs;
try {
const revokeStatement = handlebars.compile(providerInputs.revocationStatement)({ username, keyspace }); const revokeStatement = handlebars.compile(providerInputs.revocationStatement)({ username, keyspace });
const queries = revokeStatement.toString().split(";").filter(Boolean); const queries = revokeStatement.toString().split(";").filter(Boolean);
for (const query of queries) { for (const query of queries) {
@@ -123,6 +157,20 @@ export const CassandraProvider = (): TDynamicProviderFns => {
} }
await client.shutdown(); await client.shutdown();
return { entityId: username }; return { entityId: username };
} catch (err) {
const tokens = [username];
if (keyspace) {
tokens.push(keyspace);
}
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens
});
await client.shutdown();
throw new BadRequestError({
message: `Failed to revoke lease from provider: ${sanitizedErrorMessage}`
});
}
}; };
const renew = async (inputs: unknown, entityId: string, expireAt: number) => { const renew = async (inputs: unknown, entityId: string, expireAt: number) => {
@@ -130,10 +178,11 @@ export const CassandraProvider = (): TDynamicProviderFns => {
if (!providerInputs.renewStatement) return { entityId }; if (!providerInputs.renewStatement) return { entityId };
const client = await $getClient(providerInputs); const client = await $getClient(providerInputs);
const expiration = new Date(expireAt).toISOString();
const { keyspace } = providerInputs; const { keyspace } = providerInputs;
try {
const expiration = new Date(expireAt).toISOString();
const renewStatement = handlebars.compile(providerInputs.renewStatement)({ const renewStatement = handlebars.compile(providerInputs.renewStatement)({
username: entityId, username: entityId,
keyspace, keyspace,
@@ -145,6 +194,20 @@ export const CassandraProvider = (): TDynamicProviderFns => {
} }
await client.shutdown(); await client.shutdown();
return { entityId }; return { entityId };
} catch (err) {
const tokens = [entityId];
if (keyspace) {
tokens.push(keyspace);
}
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens
});
await client.shutdown();
throw new BadRequestError({
message: `Failed to renew lease from provider: ${sanitizedErrorMessage}`
});
}
}; };
return { return {
@@ -2,6 +2,8 @@ import { Client as ElasticSearchClient } from "@elastic/elasticsearch";
import { customAlphabet } from "nanoid"; import { customAlphabet } from "nanoid";
import { z } from "zod"; import { z } from "zod";
import { BadRequestError } from "@app/lib/errors";
import { sanitizeString } from "@app/lib/fn";
import { alphaNumericNanoId } from "@app/lib/nanoid"; import { alphaNumericNanoId } from "@app/lib/nanoid";
import { verifyHostInputValidity } from "../dynamic-secret-fns"; import { verifyHostInputValidity } from "../dynamic-secret-fns";
@@ -63,12 +65,24 @@ export const ElasticSearchProvider = (): TDynamicProviderFns => {
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
const connection = await $getClient(providerInputs); const connection = await $getClient(providerInputs);
const infoResponse = await connection try {
.info() const infoResponse = await connection.info().then(() => true);
.then(() => true)
.catch(() => false);
return infoResponse; return infoResponse;
} catch (err) {
const tokens = [];
if (providerInputs.auth.type === ElasticSearchAuthTypes.ApiKey) {
tokens.push(providerInputs.auth.apiKey, providerInputs.auth.apiKeyId);
} else {
tokens.push(providerInputs.auth.username, providerInputs.auth.password);
}
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens
});
throw new BadRequestError({
message: `Failed to connect with provider: ${sanitizedErrorMessage}`
});
}
}; };
const create = async (data: { inputs: unknown; usernameTemplate?: string | null; identity?: { name: string } }) => { const create = async (data: { inputs: unknown; usernameTemplate?: string | null; identity?: { name: string } }) => {
@@ -79,6 +93,7 @@ export const ElasticSearchProvider = (): TDynamicProviderFns => {
const username = generateUsername(usernameTemplate, identity); const username = generateUsername(usernameTemplate, identity);
const password = generatePassword(); const password = generatePassword();
try {
await connection.security.putUser({ await connection.security.putUser({
username, username,
password, password,
@@ -88,18 +103,39 @@ export const ElasticSearchProvider = (): TDynamicProviderFns => {
await connection.close(); await connection.close();
return { entityId: username, data: { DB_USERNAME: username, DB_PASSWORD: password } }; return { entityId: username, data: { DB_USERNAME: username, DB_PASSWORD: password } };
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [username, password]
});
await connection.close();
throw new BadRequestError({
message: `Failed to create lease from provider: ${sanitizedErrorMessage}`
});
}
}; };
const revoke = async (inputs: unknown, entityId: string) => { const revoke = async (inputs: unknown, entityId: string) => {
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
const connection = await $getClient(providerInputs); const connection = await $getClient(providerInputs);
try {
await connection.security.deleteUser({ await connection.security.deleteUser({
username: entityId username: entityId
}); });
await connection.close(); await connection.close();
return { entityId }; return { entityId };
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [entityId]
});
await connection.close();
throw new BadRequestError({
message: `Failed to revoke lease from provider: ${sanitizedErrorMessage}`
});
}
}; };
const renew = async (_inputs: unknown, entityId: string) => { const renew = async (_inputs: unknown, entityId: string) => {
@@ -3,6 +3,7 @@ import { GetAccessTokenResponse } from "google-auth-library/build/src/auth/oauth
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { BadRequestError, InternalServerError } from "@app/lib/errors"; import { BadRequestError, InternalServerError } from "@app/lib/errors";
import { sanitizeString } from "@app/lib/fn";
import { alphaNumericNanoId } from "@app/lib/nanoid"; import { alphaNumericNanoId } from "@app/lib/nanoid";
import { DynamicSecretGcpIamSchema, TDynamicProviderFns } from "./models"; import { DynamicSecretGcpIamSchema, TDynamicProviderFns } from "./models";
@@ -65,8 +66,18 @@ export const GcpIamProvider = (): TDynamicProviderFns => {
const validateConnection = async (inputs: unknown) => { const validateConnection = async (inputs: unknown) => {
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
try {
await $getToken(providerInputs.serviceAccountEmail, 10); await $getToken(providerInputs.serviceAccountEmail, 10);
return true; return true;
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [providerInputs.serviceAccountEmail]
});
throw new BadRequestError({
message: `Failed to connect with provider: ${sanitizedErrorMessage}`
});
}
}; };
const create = async (data: { inputs: unknown; expireAt: number }) => { const create = async (data: { inputs: unknown; expireAt: number }) => {
@@ -74,6 +85,7 @@ export const GcpIamProvider = (): TDynamicProviderFns => {
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
try {
const now = Math.floor(Date.now() / 1000); const now = Math.floor(Date.now() / 1000);
const ttl = Math.max(Math.floor(expireAt / 1000) - now, 0); const ttl = Math.max(Math.floor(expireAt / 1000) - now, 0);
@@ -81,6 +93,15 @@ export const GcpIamProvider = (): TDynamicProviderFns => {
const entityId = alphaNumericNanoId(32); const entityId = alphaNumericNanoId(32);
return { entityId, data: { SERVICE_ACCOUNT_EMAIL: providerInputs.serviceAccountEmail, TOKEN: token } }; return { entityId, data: { SERVICE_ACCOUNT_EMAIL: providerInputs.serviceAccountEmail, TOKEN: token } };
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [providerInputs.serviceAccountEmail]
});
throw new BadRequestError({
message: `Failed to create lease from provider: ${sanitizedErrorMessage}`
});
}
}; };
const revoke = async (_inputs: unknown, entityId: string) => { const revoke = async (_inputs: unknown, entityId: string) => {
@@ -89,10 +110,21 @@ export const GcpIamProvider = (): TDynamicProviderFns => {
}; };
const renew = async (inputs: unknown, entityId: string, expireAt: number) => { const renew = async (inputs: unknown, entityId: string, expireAt: number) => {
try {
// To renew a token it must be re-created // To renew a token it must be re-created
const data = await create({ inputs, expireAt }); const data = await create({ inputs, expireAt });
return { ...data, entityId }; return { ...data, entityId };
} catch (err) {
const providerInputs = await validateProviderInputs(inputs);
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [providerInputs.serviceAccountEmail]
});
throw new BadRequestError({
message: `Failed to renew lease from provider: ${sanitizedErrorMessage}`
});
}
}; };
return { return {
@@ -3,6 +3,7 @@ import jwt from "jsonwebtoken";
import { crypto } from "@app/lib/crypto"; import { crypto } from "@app/lib/crypto";
import { BadRequestError, InternalServerError } from "@app/lib/errors"; import { BadRequestError, InternalServerError } from "@app/lib/errors";
import { sanitizeString } from "@app/lib/fn";
import { alphaNumericNanoId } from "@app/lib/nanoid"; import { alphaNumericNanoId } from "@app/lib/nanoid";
import { IntegrationUrls } from "@app/services/integration-auth/integration-list"; import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
@@ -89,14 +90,25 @@ export const GithubProvider = (): TDynamicProviderFns => {
const validateConnection = async (inputs: unknown) => { const validateConnection = async (inputs: unknown) => {
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
try {
await $generateGitHubInstallationAccessToken(providerInputs); await $generateGitHubInstallationAccessToken(providerInputs);
return true; return true;
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [providerInputs.privateKey, String(providerInputs.appId), String(providerInputs.installationId)]
});
throw new BadRequestError({
message: `Failed to connect with provider: ${sanitizedErrorMessage}`
});
}
}; };
const create = async (data: { inputs: unknown }) => { const create = async (data: { inputs: unknown }) => {
const { inputs } = data; const { inputs } = data;
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
try {
const ghTokenData = await $generateGitHubInstallationAccessToken(providerInputs); const ghTokenData = await $generateGitHubInstallationAccessToken(providerInputs);
const entityId = alphaNumericNanoId(32); const entityId = alphaNumericNanoId(32);
@@ -109,6 +121,15 @@ export const GithubProvider = (): TDynamicProviderFns => {
REPOSITORY_SELECTION: ghTokenData.repository_selection REPOSITORY_SELECTION: ghTokenData.repository_selection
} }
}; };
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [providerInputs.privateKey, String(providerInputs.appId), String(providerInputs.installationId)]
});
throw new BadRequestError({
message: `Failed to create lease from provider: ${sanitizedErrorMessage}`
});
}
}; };
const revoke = async () => { const revoke = async () => {
@@ -2,7 +2,8 @@ import axios, { AxiosError } from "axios";
import handlebars from "handlebars"; import handlebars from "handlebars";
import https from "https"; import https from "https";
import { BadRequestError, InternalServerError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { sanitizeString } from "@app/lib/fn";
import { GatewayHttpProxyActions, GatewayProxyProtocol, withGatewayProxy } from "@app/lib/gateway"; import { GatewayHttpProxyActions, GatewayProxyProtocol, withGatewayProxy } from "@app/lib/gateway";
import { alphaNumericNanoId } from "@app/lib/nanoid"; import { alphaNumericNanoId } from "@app/lib/nanoid";
import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator"; import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator";
@@ -356,8 +357,12 @@ export const KubernetesProvider = ({ gatewayService }: TKubernetesProviderDTO):
errorMessage = (error.response?.data as { message: string }).message; errorMessage = (error.response?.data as { message: string }).message;
} }
throw new InternalServerError({ const sanitizedErrorMessage = sanitizeString({
message: `Failed to validate connection: ${errorMessage}` unsanitizedString: errorMessage,
tokens: [providerInputs.clusterToken || ""]
});
throw new BadRequestError({
message: `Failed to connect with provider: ${sanitizedErrorMessage}`
}); });
} }
}; };
@@ -602,8 +607,12 @@ export const KubernetesProvider = ({ gatewayService }: TKubernetesProviderDTO):
errorMessage = (error.response?.data as { message: string }).message; errorMessage = (error.response?.data as { message: string }).message;
} }
throw new InternalServerError({ const sanitizedErrorMessage = sanitizeString({
message: `Failed to create dynamic secret: ${errorMessage}` unsanitizedString: errorMessage,
tokens: [providerInputs.clusterToken || ""]
});
throw new BadRequestError({
message: `Failed to create lease from provider: ${sanitizedErrorMessage}`
}); });
} }
}; };
@@ -683,6 +692,7 @@ export const KubernetesProvider = ({ gatewayService }: TKubernetesProviderDTO):
}; };
if (providerInputs.credentialType === KubernetesCredentialType.Dynamic) { if (providerInputs.credentialType === KubernetesCredentialType.Dynamic) {
try {
const rawUrl = const rawUrl =
providerInputs.authMethod === KubernetesAuthMethod.Gateway providerInputs.authMethod === KubernetesAuthMethod.Gateway
? GATEWAY_AUTH_DEFAULT_URL ? GATEWAY_AUTH_DEFAULT_URL
@@ -728,6 +738,20 @@ export const KubernetesProvider = ({ gatewayService }: TKubernetesProviderDTO):
} else { } else {
await serviceAccountDynamicCallback(k8sHost, k8sPort, httpsAgent); await serviceAccountDynamicCallback(k8sHost, k8sPort, httpsAgent);
} }
} catch (error) {
let errorMessage = error instanceof Error ? error.message : "Unknown error";
if (axios.isAxiosError(error) && (error.response?.data as { message: string })?.message) {
errorMessage = (error.response?.data as { message: string }).message;
}
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: errorMessage,
tokens: [entityId, providerInputs.clusterToken || ""]
});
throw new BadRequestError({
message: `Failed to revoke lease from provider: ${sanitizedErrorMessage}`
});
}
} }
return { entityId }; return { entityId };
@@ -6,6 +6,7 @@ import RE2 from "re2";
import { z } from "zod"; import { z } from "zod";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { sanitizeString } from "@app/lib/fn";
import { alphaNumericNanoId } from "@app/lib/nanoid"; import { alphaNumericNanoId } from "@app/lib/nanoid";
import { LdapCredentialType, LdapSchema, TDynamicProviderFns } from "./models"; import { LdapCredentialType, LdapSchema, TDynamicProviderFns } from "./models";
@@ -91,8 +92,18 @@ export const LdapProvider = (): TDynamicProviderFns => {
const validateConnection = async (inputs: unknown) => { const validateConnection = async (inputs: unknown) => {
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
try {
const client = await $getClient(providerInputs); const client = await $getClient(providerInputs);
return client.connected; return client.connected;
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [providerInputs.bindpass, providerInputs.binddn]
});
throw new BadRequestError({
message: `Failed to connect with provider: ${sanitizedErrorMessage}`
});
}
}; };
const executeLdif = async (client: ldapjs.Client, ldif_file: string) => { const executeLdif = async (client: ldapjs.Client, ldif_file: string) => {
@@ -205,11 +216,11 @@ export const LdapProvider = (): TDynamicProviderFns => {
if (providerInputs.credentialType === LdapCredentialType.Static) { if (providerInputs.credentialType === LdapCredentialType.Static) {
const dnRegex = new RE2("^dn:\\s*(.+)", "m"); const dnRegex = new RE2("^dn:\\s*(.+)", "m");
const dnMatch = dnRegex.exec(providerInputs.rotationLdif); const dnMatch = dnRegex.exec(providerInputs.rotationLdif);
const username = dnMatch?.[1];
if (dnMatch) { if (!username) throw new BadRequestError({ message: "Username not found from Ldif" });
const username = dnMatch[1];
const password = generatePassword(); const password = generatePassword();
if (dnMatch) {
const generatedLdif = generateLDIF({ username, password, ldifTemplate: providerInputs.rotationLdif }); const generatedLdif = generateLDIF({ username, password, ldifTemplate: providerInputs.rotationLdif });
try { try {
@@ -217,7 +228,11 @@ export const LdapProvider = (): TDynamicProviderFns => {
return { entityId: username, data: { DN_ARRAY: dnArray, USERNAME: username, PASSWORD: password } }; return { entityId: username, data: { DN_ARRAY: dnArray, USERNAME: username, PASSWORD: password } };
} catch (err) { } catch (err) {
throw new BadRequestError({ message: (err as Error).message }); const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [username, password, providerInputs.binddn, providerInputs.bindpass]
});
throw new BadRequestError({ message: sanitizedErrorMessage });
} }
} else { } else {
throw new BadRequestError({ throw new BadRequestError({
@@ -238,7 +253,11 @@ export const LdapProvider = (): TDynamicProviderFns => {
const rollbackLdif = generateLDIF({ username, password, ldifTemplate: providerInputs.rollbackLdif }); const rollbackLdif = generateLDIF({ username, password, ldifTemplate: providerInputs.rollbackLdif });
await executeLdif(client, rollbackLdif); await executeLdif(client, rollbackLdif);
} }
throw new BadRequestError({ message: (err as Error).message }); const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [username, password, providerInputs.binddn, providerInputs.bindpass]
});
throw new BadRequestError({ message: sanitizedErrorMessage });
} }
} }
}; };
@@ -262,7 +281,11 @@ export const LdapProvider = (): TDynamicProviderFns => {
return { entityId: username, data: { DN_ARRAY: dnArray, USERNAME: username, PASSWORD: password } }; return { entityId: username, data: { DN_ARRAY: dnArray, USERNAME: username, PASSWORD: password } };
} catch (err) { } catch (err) {
throw new BadRequestError({ message: (err as Error).message }); const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [username, password, providerInputs.binddn, providerInputs.bindpass]
});
throw new BadRequestError({ message: sanitizedErrorMessage });
} }
} else { } else {
throw new BadRequestError({ throw new BadRequestError({
@@ -278,7 +301,7 @@ export const LdapProvider = (): TDynamicProviderFns => {
return { entityId }; return { entityId };
}; };
const renew = async (inputs: unknown, entityId: string) => { const renew = async (_inputs: unknown, entityId: string) => {
// No renewal necessary // No renewal necessary
return { entityId }; return { entityId };
}; };
@@ -3,6 +3,8 @@ import { customAlphabet } from "nanoid";
import { z } from "zod"; import { z } from "zod";
import { createDigestAuthRequestInterceptor } from "@app/lib/axios/digest-auth"; import { createDigestAuthRequestInterceptor } from "@app/lib/axios/digest-auth";
import { BadRequestError } from "@app/lib/errors";
import { sanitizeString } from "@app/lib/fn";
import { alphaNumericNanoId } from "@app/lib/nanoid"; import { alphaNumericNanoId } from "@app/lib/nanoid";
import { DynamicSecretMongoAtlasSchema, TDynamicProviderFns } from "./models"; import { DynamicSecretMongoAtlasSchema, TDynamicProviderFns } from "./models";
@@ -49,19 +51,25 @@ export const MongoAtlasProvider = (): TDynamicProviderFns => {
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
const client = await $getClient(providerInputs); const client = await $getClient(providerInputs);
try {
const isConnected = await client({ const isConnected = await client({
method: "GET", method: "GET",
url: `v2/groups/${providerInputs.groupId}/databaseUsers`, url: `v2/groups/${providerInputs.groupId}/databaseUsers`,
params: { itemsPerPage: 1 } params: { itemsPerPage: 1 }
}) }).then(() => true);
.then(() => true)
.catch((error) => {
if ((error as AxiosError).response) {
throw new Error(JSON.stringify((error as AxiosError).response?.data));
}
throw error;
});
return isConnected; return isConnected;
} catch (error) {
const errorMessage = (error as AxiosError).response
? JSON.stringify((error as AxiosError).response?.data)
: (error as Error)?.message;
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: errorMessage,
tokens: [providerInputs.adminPublicKey, providerInputs.adminPrivateKey, providerInputs.groupId]
});
throw new BadRequestError({
message: `Failed to connect with provider: ${sanitizedErrorMessage}`
});
}
}; };
const create = async (data: { const create = async (data: {
@@ -77,6 +85,7 @@ export const MongoAtlasProvider = (): TDynamicProviderFns => {
const username = generateUsername(usernameTemplate, identity); const username = generateUsername(usernameTemplate, identity);
const password = generatePassword(); const password = generatePassword();
const expiration = new Date(expireAt).toISOString(); const expiration = new Date(expireAt).toISOString();
try {
await client({ await client({
method: "POST", method: "POST",
url: `/v2/groups/${providerInputs.groupId}/databaseUsers`, url: `/v2/groups/${providerInputs.groupId}/databaseUsers`,
@@ -89,13 +98,26 @@ export const MongoAtlasProvider = (): TDynamicProviderFns => {
databaseName: "admin", databaseName: "admin",
groupId: providerInputs.groupId groupId: providerInputs.groupId
} }
}).catch((error) => {
if ((error as AxiosError).response) {
throw new Error(JSON.stringify((error as AxiosError).response?.data));
}
throw error;
}); });
return { entityId: username, data: { DB_USERNAME: username, DB_PASSWORD: password } }; return { entityId: username, data: { DB_USERNAME: username, DB_PASSWORD: password } };
} catch (error) {
const errorMessage = (error as AxiosError).response
? JSON.stringify((error as AxiosError).response?.data)
: (error as Error)?.message;
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: errorMessage,
tokens: [
username,
password,
providerInputs.adminPublicKey,
providerInputs.adminPrivateKey,
providerInputs.groupId
]
});
throw new BadRequestError({
message: `Failed to create lease from provider: ${sanitizedErrorMessage}`
});
}
}; };
const revoke = async (inputs: unknown, entityId: string) => { const revoke = async (inputs: unknown, entityId: string) => {
@@ -111,15 +133,23 @@ export const MongoAtlasProvider = (): TDynamicProviderFns => {
throw err; throw err;
}); });
if (isExisting) { if (isExisting) {
try {
await client({ await client({
method: "DELETE", method: "DELETE",
url: `/v2/groups/${providerInputs.groupId}/databaseUsers/admin/${username}` url: `/v2/groups/${providerInputs.groupId}/databaseUsers/admin/${username}`
}).catch((error) => {
if ((error as AxiosError).response) {
throw new Error(JSON.stringify((error as AxiosError).response?.data));
}
throw error;
}); });
} catch (error) {
const errorMessage = (error as AxiosError).response
? JSON.stringify((error as AxiosError).response?.data)
: (error as Error)?.message;
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: errorMessage,
tokens: [username, providerInputs.adminPublicKey, providerInputs.adminPrivateKey, providerInputs.groupId]
});
throw new BadRequestError({
message: `Failed to revoke lease from provider: ${sanitizedErrorMessage}`
});
}
} }
return { entityId: username }; return { entityId: username };
@@ -132,6 +162,7 @@ export const MongoAtlasProvider = (): TDynamicProviderFns => {
const username = entityId; const username = entityId;
const expiration = new Date(expireAt).toISOString(); const expiration = new Date(expireAt).toISOString();
try {
await client({ await client({
method: "PATCH", method: "PATCH",
url: `/v2/groups/${providerInputs.groupId}/databaseUsers/admin/${username}`, url: `/v2/groups/${providerInputs.groupId}/databaseUsers/admin/${username}`,
@@ -140,13 +171,20 @@ export const MongoAtlasProvider = (): TDynamicProviderFns => {
databaseName: "admin", databaseName: "admin",
groupId: providerInputs.groupId groupId: providerInputs.groupId
} }
}).catch((error) => {
if ((error as AxiosError).response) {
throw new Error(JSON.stringify((error as AxiosError).response?.data));
}
throw error;
}); });
return { entityId: username }; return { entityId: username };
} catch (error) {
const errorMessage = (error as AxiosError).response
? JSON.stringify((error as AxiosError).response?.data)
: (error as Error)?.message;
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: errorMessage,
tokens: [username, providerInputs.adminPublicKey, providerInputs.adminPrivateKey, providerInputs.groupId]
});
throw new BadRequestError({
message: `Failed to renew lease from provider: ${sanitizedErrorMessage}`
});
}
}; };
return { return {
@@ -2,6 +2,8 @@ import { MongoClient } from "mongodb";
import { customAlphabet } from "nanoid"; import { customAlphabet } from "nanoid";
import { z } from "zod"; import { z } from "zod";
import { BadRequestError } from "@app/lib/errors";
import { sanitizeString } from "@app/lib/fn";
import { alphaNumericNanoId } from "@app/lib/nanoid"; import { alphaNumericNanoId } from "@app/lib/nanoid";
import { verifyHostInputValidity } from "../dynamic-secret-fns"; import { verifyHostInputValidity } from "../dynamic-secret-fns";
@@ -51,6 +53,7 @@ export const MongoDBProvider = (): TDynamicProviderFns => {
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
const client = await $getClient(providerInputs); const client = await $getClient(providerInputs);
try {
const isConnected = await client const isConnected = await client
.db(providerInputs.database) .db(providerInputs.database)
.command({ ping: 1 }) .command({ ping: 1 })
@@ -58,6 +61,16 @@ export const MongoDBProvider = (): TDynamicProviderFns => {
await client.close(); await client.close();
return isConnected; return isConnected;
} catch (err) {
await client.close();
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [providerInputs.password, providerInputs.username, providerInputs.database, providerInputs.host]
});
throw new BadRequestError({
message: `Failed to connect with provider: ${sanitizedErrorMessage}`
});
}
}; };
const create = async (data: { inputs: unknown; usernameTemplate?: string | null; identity?: { name: string } }) => { const create = async (data: { inputs: unknown; usernameTemplate?: string | null; identity?: { name: string } }) => {
@@ -68,6 +81,7 @@ export const MongoDBProvider = (): TDynamicProviderFns => {
const username = generateUsername(usernameTemplate, identity); const username = generateUsername(usernameTemplate, identity);
const password = generatePassword(); const password = generatePassword();
try {
const db = client.db(providerInputs.database); const db = client.db(providerInputs.database);
await db.command({ await db.command({
@@ -78,6 +92,16 @@ export const MongoDBProvider = (): TDynamicProviderFns => {
await client.close(); await client.close();
return { entityId: username, data: { DB_USERNAME: username, DB_PASSWORD: password } }; return { entityId: username, data: { DB_USERNAME: username, DB_PASSWORD: password } };
} catch (err) {
await client.close();
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [username, password, providerInputs.password, providerInputs.username, providerInputs.database]
});
throw new BadRequestError({
message: `Failed to create lease from provider: ${sanitizedErrorMessage}`
});
}
}; };
const revoke = async (inputs: unknown, entityId: string) => { const revoke = async (inputs: unknown, entityId: string) => {
@@ -86,6 +110,7 @@ export const MongoDBProvider = (): TDynamicProviderFns => {
const username = entityId; const username = entityId;
try {
const db = client.db(providerInputs.database); const db = client.db(providerInputs.database);
await db.command({ await db.command({
dropUser: username dropUser: username
@@ -93,6 +118,16 @@ export const MongoDBProvider = (): TDynamicProviderFns => {
await client.close(); await client.close();
return { entityId: username }; return { entityId: username };
} catch (err) {
await client.close();
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [username, providerInputs.password, providerInputs.username, providerInputs.database]
});
throw new BadRequestError({
message: `Failed to revoke lease from provider: ${sanitizedErrorMessage}`
});
}
}; };
const renew = async (_inputs: unknown, entityId: string) => { const renew = async (_inputs: unknown, entityId: string) => {
@@ -3,6 +3,8 @@ import https from "https";
import { customAlphabet } from "nanoid"; import { customAlphabet } from "nanoid";
import { z } from "zod"; import { z } from "zod";
import { BadRequestError } from "@app/lib/errors";
import { sanitizeString } from "@app/lib/fn";
import { logger } from "@app/lib/logger"; import { logger } from "@app/lib/logger";
import { alphaNumericNanoId } from "@app/lib/nanoid"; import { alphaNumericNanoId } from "@app/lib/nanoid";
@@ -110,11 +112,19 @@ export const RabbitMqProvider = (): TDynamicProviderFns => {
const validateConnection = async (inputs: unknown) => { const validateConnection = async (inputs: unknown) => {
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
try {
const connection = await $getClient(providerInputs); const connection = await $getClient(providerInputs);
const infoResponse = await connection.get("/whoami").then(() => true); const infoResponse = await connection.get("/whoami").then(() => true);
return infoResponse; return infoResponse;
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [providerInputs.password, providerInputs.username, providerInputs.host]
});
throw new BadRequestError({
message: `Failed to connect with provider: ${sanitizedErrorMessage}`
});
}
}; };
const create = async (data: { inputs: unknown; usernameTemplate?: string | null; identity?: { name: string } }) => { const create = async (data: { inputs: unknown; usernameTemplate?: string | null; identity?: { name: string } }) => {
@@ -125,6 +135,7 @@ export const RabbitMqProvider = (): TDynamicProviderFns => {
const username = generateUsername(usernameTemplate, identity); const username = generateUsername(usernameTemplate, identity);
const password = generatePassword(); const password = generatePassword();
try {
await createRabbitMqUser({ await createRabbitMqUser({
axiosInstance: connection, axiosInstance: connection,
virtualHost: providerInputs.virtualHost, virtualHost: providerInputs.virtualHost,
@@ -134,17 +145,34 @@ export const RabbitMqProvider = (): TDynamicProviderFns => {
tags: [...(providerInputs.tags ?? []), "infisical-user"] tags: [...(providerInputs.tags ?? []), "infisical-user"]
} }
}); });
return { entityId: username, data: { DB_USERNAME: username, DB_PASSWORD: password } }; return { entityId: username, data: { DB_USERNAME: username, DB_PASSWORD: password } };
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [username, password, providerInputs.password, providerInputs.username]
});
throw new BadRequestError({
message: `Failed to create lease from provider: ${sanitizedErrorMessage}`
});
}
}; };
const revoke = async (inputs: unknown, entityId: string) => { const revoke = async (inputs: unknown, entityId: string) => {
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
const connection = await $getClient(providerInputs); const connection = await $getClient(providerInputs);
try {
await deleteRabbitMqUser({ axiosInstance: connection, usernameToDelete: entityId }); await deleteRabbitMqUser({ axiosInstance: connection, usernameToDelete: entityId });
return { entityId }; return { entityId };
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [entityId, providerInputs.password, providerInputs.username]
});
throw new BadRequestError({
message: `Failed to revoke lease from provider: ${sanitizedErrorMessage}`
});
}
}; };
const renew = async (_inputs: unknown, entityId: string) => { const renew = async (_inputs: unknown, entityId: string) => {
@@ -4,6 +4,7 @@ import { customAlphabet } from "nanoid";
import { z } from "zod"; import { z } from "zod";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { sanitizeString } from "@app/lib/fn";
import { alphaNumericNanoId } from "@app/lib/nanoid"; import { alphaNumericNanoId } from "@app/lib/nanoid";
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars"; import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
@@ -112,14 +113,27 @@ export const RedisDatabaseProvider = (): TDynamicProviderFns => {
const validateConnection = async (inputs: unknown) => { const validateConnection = async (inputs: unknown) => {
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
const connection = await $getClient(providerInputs); let connection;
try {
const pingResponse = await connection connection = await $getClient(providerInputs);
.ping() const pingResponse = await connection.ping().then(() => true);
.then(() => true) await connection.quit();
.catch(() => false);
return pingResponse; return pingResponse;
} catch (err) {
if (connection) await connection.quit();
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [
providerInputs.password || "",
providerInputs.username,
providerInputs.host,
String(providerInputs.port)
]
});
throw new BadRequestError({
message: `Failed to connect with provider: ${sanitizedErrorMessage}`
});
}
}; };
const create = async (data: { const create = async (data: {
@@ -144,10 +158,20 @@ export const RedisDatabaseProvider = (): TDynamicProviderFns => {
const queries = creationStatement.toString().split(";").filter(Boolean); const queries = creationStatement.toString().split(";").filter(Boolean);
try {
await executeTransactions(connection, queries); await executeTransactions(connection, queries);
await connection.quit(); await connection.quit();
return { entityId: username, data: { DB_USERNAME: username, DB_PASSWORD: password } }; return { entityId: username, data: { DB_USERNAME: username, DB_PASSWORD: password } };
} catch (err) {
await connection.quit();
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [username, password, providerInputs.password || "", providerInputs.username]
});
throw new BadRequestError({
message: `Failed to create lease from provider: ${sanitizedErrorMessage}`
});
}
}; };
const revoke = async (inputs: unknown, entityId: string) => { const revoke = async (inputs: unknown, entityId: string) => {
@@ -159,10 +183,20 @@ export const RedisDatabaseProvider = (): TDynamicProviderFns => {
const revokeStatement = handlebars.compile(providerInputs.revocationStatement)({ username }); const revokeStatement = handlebars.compile(providerInputs.revocationStatement)({ username });
const queries = revokeStatement.toString().split(";").filter(Boolean); const queries = revokeStatement.toString().split(";").filter(Boolean);
try {
await executeTransactions(connection, queries); await executeTransactions(connection, queries);
await connection.quit(); await connection.quit();
return { entityId: username }; return { entityId: username };
} catch (err) {
await connection.quit();
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [username, providerInputs.password || "", providerInputs.username]
});
throw new BadRequestError({
message: `Failed to revoke lease from provider: ${sanitizedErrorMessage}`
});
}
}; };
const renew = async (inputs: unknown, entityId: string, expireAt: number) => { const renew = async (inputs: unknown, entityId: string, expireAt: number) => {
@@ -176,13 +210,23 @@ export const RedisDatabaseProvider = (): TDynamicProviderFns => {
const renewStatement = handlebars.compile(providerInputs.renewStatement)({ username, expiration }); const renewStatement = handlebars.compile(providerInputs.renewStatement)({ username, expiration });
try {
if (renewStatement) { if (renewStatement) {
const queries = renewStatement.toString().split(";").filter(Boolean); const queries = renewStatement.toString().split(";").filter(Boolean);
await executeTransactions(connection, queries); await executeTransactions(connection, queries);
} }
await connection.quit(); await connection.quit();
return { entityId: username }; return { entityId: username };
} catch (err) {
await connection.quit();
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [username, providerInputs.password || "", providerInputs.username]
});
throw new BadRequestError({
message: `Failed to renew lease from provider: ${sanitizedErrorMessage}`
});
}
}; };
return { return {
@@ -4,6 +4,7 @@ import odbc from "odbc";
import { z } from "zod"; import { z } from "zod";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { sanitizeString } from "@app/lib/fn";
import { alphaNumericNanoId } from "@app/lib/nanoid"; import { alphaNumericNanoId } from "@app/lib/nanoid";
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars"; import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
@@ -67,8 +68,11 @@ export const SapAseProvider = (): TDynamicProviderFns => {
const validateConnection = async (inputs: unknown) => { const validateConnection = async (inputs: unknown) => {
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
const masterClient = await $getClient(providerInputs, true); let masterClient;
const client = await $getClient(providerInputs); let client;
try {
masterClient = await $getClient(providerInputs, true);
client = await $getClient(providerInputs);
const [resultFromMasterDatabase] = await masterClient.query<{ version: string }>("SELECT @@VERSION AS version"); const [resultFromMasterDatabase] = await masterClient.query<{ version: string }>("SELECT @@VERSION AS version");
const [resultFromSelectedDatabase] = await client.query<{ version: string }>("SELECT @@VERSION AS version"); const [resultFromSelectedDatabase] = await client.query<{ version: string }>("SELECT @@VERSION AS version");
@@ -85,7 +89,20 @@ export const SapAseProvider = (): TDynamicProviderFns => {
}); });
} }
await masterClient.close();
await client.close();
return true; return true;
} catch (err) {
if (masterClient) await masterClient.close();
if (client) await client.close();
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [providerInputs.password, providerInputs.username, providerInputs.host, providerInputs.database]
});
throw new BadRequestError({
message: `Failed to connect with provider: ${sanitizedErrorMessage}`
});
}
}; };
const create = async (data: { inputs: unknown; usernameTemplate?: string | null; identity?: { name: string } }) => { const create = async (data: { inputs: unknown; usernameTemplate?: string | null; identity?: { name: string } }) => {
@@ -105,16 +122,26 @@ export const SapAseProvider = (): TDynamicProviderFns => {
const queries = creationStatement.trim().replaceAll("\n", "").split(";").filter(Boolean); const queries = creationStatement.trim().replaceAll("\n", "").split(";").filter(Boolean);
try {
for await (const query of queries) { for await (const query of queries) {
// If it's an adduser query, we need to first call sp_addlogin on the MASTER database. // If it's an adduser query, we need to first call sp_addlogin on the MASTER database.
// If not done, then the newly created user won't be able to authenticate. // If not done, then the newly created user won't be able to authenticate.
await (query.startsWith(SapCommands.CreateLogin) ? masterClient : client).query(query); await (query.startsWith(SapCommands.CreateLogin) ? masterClient : client).query(query);
} }
await masterClient.close(); await masterClient.close();
await client.close(); await client.close();
return { entityId: username, data: { DB_USERNAME: username, DB_PASSWORD: password } }; return { entityId: username, data: { DB_USERNAME: username, DB_PASSWORD: password } };
} catch (err) {
await masterClient.close();
await client.close();
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [username, password, providerInputs.password, providerInputs.username, providerInputs.database]
});
throw new BadRequestError({
message: `Failed to create lease from provider: ${sanitizedErrorMessage}`
});
}
}; };
const revoke = async (inputs: unknown, username: string) => { const revoke = async (inputs: unknown, username: string) => {
@@ -140,14 +167,24 @@ export const SapAseProvider = (): TDynamicProviderFns => {
} }
} }
try {
for await (const query of queries) { for await (const query of queries) {
await (query.startsWith(SapCommands.DropLogin) ? masterClient : client).query(query); await (query.startsWith(SapCommands.DropLogin) ? masterClient : client).query(query);
} }
await masterClient.close(); await masterClient.close();
await client.close(); await client.close();
return { entityId: username }; return { entityId: username };
} catch (err) {
await masterClient.close();
await client.close();
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [username, providerInputs.password, providerInputs.username, providerInputs.database]
});
throw new BadRequestError({
message: `Failed to revoke lease from provider: ${sanitizedErrorMessage}`
});
}
}; };
const renew = async (_: unknown, username: string) => { const renew = async (_: unknown, username: string) => {
@@ -10,6 +10,7 @@ import { customAlphabet } from "nanoid";
import { z } from "zod"; import { z } from "zod";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { sanitizeString } from "@app/lib/fn";
import { alphaNumericNanoId } from "@app/lib/nanoid"; import { alphaNumericNanoId } from "@app/lib/nanoid";
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars"; import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
@@ -83,19 +84,26 @@ export const SapHanaProvider = (): TDynamicProviderFns => {
const validateConnection = async (inputs: unknown) => { const validateConnection = async (inputs: unknown) => {
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
try {
const client = await $getClient(providerInputs); const client = await $getClient(providerInputs);
const testResult = await new Promise<boolean>((resolve, reject) => { const testResult = await new Promise<boolean>((resolve, reject) => {
client.exec("SELECT 1 FROM DUMMY;", (err: any) => { client.exec("SELECT 1 FROM DUMMY;", (err: any) => {
if (err) { if (err) {
reject(); return reject(err);
} }
resolve(true); resolve(true);
}); });
}); });
return testResult; return testResult;
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [providerInputs.password, providerInputs.username, providerInputs.host]
});
throw new BadRequestError({
message: `Failed to connect with provider: ${sanitizedErrorMessage}`
});
}
}; };
const create = async (data: { const create = async (data: {
@@ -119,20 +127,24 @@ export const SapHanaProvider = (): TDynamicProviderFns => {
}); });
const queries = creationStatement.toString().split(";").filter(Boolean); const queries = creationStatement.toString().split(";").filter(Boolean);
try {
for await (const query of queries) { for await (const query of queries) {
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
client.exec(query, (err: any) => { client.exec(query, (err: any) => {
if (err) { if (err) return reject(err);
reject(
new BadRequestError({
message: err.message
})
);
}
resolve(true); resolve(true);
}); });
}); });
} }
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [username, password, providerInputs.password, providerInputs.username]
});
throw new BadRequestError({
message: `Failed to create lease from provider: ${sanitizedErrorMessage}`
});
}
return { entityId: username, data: { DB_USERNAME: username, DB_PASSWORD: password } }; return { entityId: username, data: { DB_USERNAME: username, DB_PASSWORD: password } };
}; };
@@ -142,20 +154,26 @@ export const SapHanaProvider = (): TDynamicProviderFns => {
const client = await $getClient(providerInputs); const client = await $getClient(providerInputs);
const revokeStatement = handlebars.compile(providerInputs.revocationStatement)({ username }); const revokeStatement = handlebars.compile(providerInputs.revocationStatement)({ username });
const queries = revokeStatement.toString().split(";").filter(Boolean); const queries = revokeStatement.toString().split(";").filter(Boolean);
try {
for await (const query of queries) { for await (const query of queries) {
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
client.exec(query, (err: any) => { client.exec(query, (err: any) => {
if (err) { if (err) {
reject( reject(err);
new BadRequestError({
message: err.message
})
);
} }
resolve(true); resolve(true);
}); });
}); });
} }
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [username, providerInputs.password, providerInputs.username]
});
throw new BadRequestError({
message: `Failed to revoke lease from provider: ${sanitizedErrorMessage}`
});
}
return { entityId: username }; return { entityId: username };
}; };
@@ -174,16 +192,20 @@ export const SapHanaProvider = (): TDynamicProviderFns => {
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
client.exec(query, (err: any) => { client.exec(query, (err: any) => {
if (err) { if (err) {
reject( reject(err);
new BadRequestError({
message: err.message
})
);
} }
resolve(true); resolve(true);
}); });
}); });
} }
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [entityId, providerInputs.password, providerInputs.username]
});
throw new BadRequestError({
message: `Failed to renew lease from provider: ${sanitizedErrorMessage}`
});
} finally { } finally {
client.disconnect(); client.disconnect();
} }
@@ -4,6 +4,7 @@ import snowflake from "snowflake-sdk";
import { z } from "zod"; import { z } from "zod";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { sanitizeString } from "@app/lib/fn";
import { alphaNumericNanoId } from "@app/lib/nanoid"; import { alphaNumericNanoId } from "@app/lib/nanoid";
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars"; import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
@@ -69,12 +70,10 @@ export const SnowflakeProvider = (): TDynamicProviderFns => {
const validateConnection = async (inputs: unknown) => { const validateConnection = async (inputs: unknown) => {
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
const client = await $getClient(providerInputs); let client;
let isValidConnection: boolean;
try { try {
isValidConnection = await Promise.race([ client = await $getClient(providerInputs);
const isValidConnection = await Promise.race([
client.isValidAsync(), client.isValidAsync(),
new Promise((resolve) => { new Promise((resolve) => {
setTimeout(resolve, 10000); setTimeout(resolve, 10000);
@@ -82,11 +81,18 @@ export const SnowflakeProvider = (): TDynamicProviderFns => {
throw new BadRequestError({ message: "Unable to establish connection - verify credentials" }); throw new BadRequestError({ message: "Unable to establish connection - verify credentials" });
}) })
]); ]);
} finally {
client.destroy(noop);
}
return isValidConnection; return isValidConnection;
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [providerInputs.password, providerInputs.username, providerInputs.accountId, providerInputs.orgId]
});
throw new BadRequestError({
message: `Failed to connect with provider: ${sanitizedErrorMessage}`
});
} finally {
if (client) client.destroy(noop);
}
}; };
const create = async (data: { const create = async (data: {
@@ -116,13 +122,19 @@ export const SnowflakeProvider = (): TDynamicProviderFns => {
sqlText: creationStatement, sqlText: creationStatement,
complete(err) { complete(err) {
if (err) { if (err) {
return reject(new BadRequestError({ name: "CreateLease", message: err.message })); return reject(err);
} }
return resolve(true); return resolve(true);
} }
}); });
}); });
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error).message,
tokens: [username, password, providerInputs.password, providerInputs.username]
});
throw new BadRequestError({ message: `Failed to create lease from provider: ${sanitizedErrorMessage}` });
} finally { } finally {
client.destroy(noop); client.destroy(noop);
} }
@@ -143,13 +155,19 @@ export const SnowflakeProvider = (): TDynamicProviderFns => {
sqlText: revokeStatement, sqlText: revokeStatement,
complete(err) { complete(err) {
if (err) { if (err) {
return reject(new BadRequestError({ name: "RevokeLease", message: err.message })); return reject(err);
} }
return resolve(true); return resolve(true);
} }
}); });
}); });
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error).message,
tokens: [username, providerInputs.password, providerInputs.username]
});
throw new BadRequestError({ message: `Failed to revoke lease from provider: ${sanitizedErrorMessage}` });
} finally { } finally {
client.destroy(noop); client.destroy(noop);
} }
@@ -175,13 +193,19 @@ export const SnowflakeProvider = (): TDynamicProviderFns => {
sqlText: renewStatement, sqlText: renewStatement,
complete(err) { complete(err) {
if (err) { if (err) {
return reject(new BadRequestError({ name: "RenewLease", message: err.message })); return reject(err);
} }
return resolve(true); return resolve(true);
} }
}); });
}); });
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error).message,
tokens: [entityId, providerInputs.password, providerInputs.username]
});
throw new BadRequestError({ message: `Failed to renew lease from provider: ${sanitizedErrorMessage}` });
} finally { } finally {
client.destroy(noop); client.destroy(noop);
} }
@@ -3,6 +3,8 @@ import knex from "knex";
import { z } from "zod"; import { z } from "zod";
import { crypto } from "@app/lib/crypto/cryptography"; import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError } from "@app/lib/errors";
import { sanitizeString } from "@app/lib/fn";
import { GatewayProxyProtocol, withGatewayProxy } from "@app/lib/gateway"; import { GatewayProxyProtocol, withGatewayProxy } from "@app/lib/gateway";
import { alphaNumericNanoId } from "@app/lib/nanoid"; import { alphaNumericNanoId } from "@app/lib/nanoid";
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars"; import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
@@ -212,8 +214,19 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
// oracle needs from keyword // oracle needs from keyword
const testStatement = providerInputs.client === SqlProviders.Oracle ? "SELECT 1 FROM DUAL" : "SELECT 1"; const testStatement = providerInputs.client === SqlProviders.Oracle ? "SELECT 1 FROM DUAL" : "SELECT 1";
try {
isConnected = await db.raw(testStatement).then(() => true); isConnected = await db.raw(testStatement).then(() => true);
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [providerInputs.username]
});
throw new BadRequestError({
message: `Failed to connect with provider: ${sanitizedErrorMessage}`
});
} finally {
await db.destroy(); await db.destroy();
}
}; };
if (providerInputs.gatewayId) { if (providerInputs.gatewayId) {
@@ -233,13 +246,13 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
const { inputs, expireAt, usernameTemplate, identity } = data; const { inputs, expireAt, usernameTemplate, identity } = data;
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
const { database } = providerInputs;
const username = generateUsername(providerInputs.client, usernameTemplate, identity); const username = generateUsername(providerInputs.client, usernameTemplate, identity);
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) => {
const db = await $getClient({ ...providerInputs, port, host }); const db = await $getClient({ ...providerInputs, port, host });
try { try {
const { database } = providerInputs;
const expiration = new Date(expireAt).toISOString(); const expiration = new Date(expireAt).toISOString();
const creationStatement = handlebars.compile(providerInputs.creationStatement, { noEscape: true })({ const creationStatement = handlebars.compile(providerInputs.creationStatement, { noEscape: true })({
@@ -256,6 +269,14 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
await tx.raw(query); await tx.raw(query);
} }
}); });
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [username, password, database]
});
throw new BadRequestError({
message: `Failed to create lease from provider: ${sanitizedErrorMessage}`
});
} finally { } finally {
await db.destroy(); await db.destroy();
} }
@@ -283,6 +304,14 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
await tx.raw(query); await tx.raw(query);
} }
}); });
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [username, database]
});
throw new BadRequestError({
message: `Failed to revoke lease from provider: ${sanitizedErrorMessage}`
});
} finally { } finally {
await db.destroy(); await db.destroy();
} }
@@ -319,6 +348,14 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
} }
}); });
} }
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [database]
});
throw new BadRequestError({
message: `Failed to renew lease from provider: ${sanitizedErrorMessage}`
});
} finally { } finally {
await db.destroy(); await db.destroy();
} }
@@ -1,6 +1,8 @@
import { authenticator } from "otplib"; import { authenticator } from "otplib";
import { HashAlgorithms } from "otplib/core"; import { HashAlgorithms } from "otplib/core";
import { BadRequestError } from "@app/lib/errors";
import { sanitizeString } from "@app/lib/fn";
import { alphaNumericNanoId } from "@app/lib/nanoid"; import { alphaNumericNanoId } from "@app/lib/nanoid";
import { DynamicSecretTotpSchema, TDynamicProviderFns, TotpConfigType } from "./models"; import { DynamicSecretTotpSchema, TDynamicProviderFns, TotpConfigType } from "./models";
@@ -12,11 +14,24 @@ export const TotpProvider = (): TDynamicProviderFns => {
return providerInputs; return providerInputs;
}; };
const validateConnection = async () => { const validateConnection = async (inputs: unknown) => {
try {
await validateProviderInputs(inputs);
return true; return true;
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: []
});
throw new BadRequestError({
message: `Failed to connect with provider: ${sanitizedErrorMessage}`
});
}
}; };
const create = async (inputs: unknown) => { const create = async (data: { inputs: unknown }) => {
const { inputs } = data;
try {
const providerInputs = await validateProviderInputs(inputs); const providerInputs = await validateProviderInputs(inputs);
const entityId = alphaNumericNanoId(32); const entityId = alphaNumericNanoId(32);
@@ -68,6 +83,15 @@ export const TotpProvider = (): TDynamicProviderFns => {
entityId, entityId,
data: { TOTP: authenticatorInstance.generate(secret), TIME_REMAINING: authenticatorInstance.timeRemaining() } data: { TOTP: authenticatorInstance.generate(secret), TIME_REMAINING: authenticatorInstance.timeRemaining() }
}; };
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: []
});
throw new BadRequestError({
message: `Failed to create lease from provider: ${sanitizedErrorMessage}`
});
}
}; };
const revoke = async (_inputs: unknown, entityId: string) => { const revoke = async (_inputs: unknown, entityId: string) => {
@@ -4,6 +4,7 @@ import { z } from "zod";
import { crypto } from "@app/lib/crypto/cryptography"; import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { sanitizeString } from "@app/lib/fn";
import { GatewayProxyProtocol, withGatewayProxy } from "@app/lib/gateway"; import { GatewayProxyProtocol, withGatewayProxy } from "@app/lib/gateway";
import { logger } from "@app/lib/logger"; import { logger } from "@app/lib/logger";
import { alphaNumericNanoId } from "@app/lib/nanoid"; import { alphaNumericNanoId } from "@app/lib/nanoid";
@@ -275,6 +276,14 @@ export const VerticaProvider = ({ gatewayService }: TVerticaProviderDTO): TDynam
await client.raw(trimmedQuery); await client.raw(trimmedQuery);
} }
} }
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [username, password, providerInputs.username, providerInputs.password]
});
throw new BadRequestError({
message: `Failed to create lease from provider: ${sanitizedErrorMessage}`
});
} finally { } finally {
if (client) await client.destroy(); if (client) await client.destroy();
} }
@@ -339,6 +348,14 @@ export const VerticaProvider = ({ gatewayService }: TVerticaProviderDTO): TDynam
await client.raw(trimmedQuery); await client.raw(trimmedQuery);
} }
} }
} catch (err) {
const sanitizedErrorMessage = sanitizeString({
unsanitizedString: (err as Error)?.message,
tokens: [username, providerInputs.username, providerInputs.password]
});
throw new BadRequestError({
message: `Failed to revoke lease from provider: ${sanitizedErrorMessage}`
});
} finally { } finally {
if (client) await client.destroy(); if (client) await client.destroy();
} }
+14
View File
@@ -19,3 +19,17 @@ export const prefixWithSlash = (str: string) => {
const vowelRegex = new RE2(/^[aeiou]/i); const vowelRegex = new RE2(/^[aeiou]/i);
export const startsWithVowel = (str: string) => vowelRegex.test(str); export const startsWithVowel = (str: string) => vowelRegex.test(str);
const pickWordsRegex = new RE2(/(\W+)/);
export const sanitizeString = (dto: { unsanitizedString: string; tokens: string[] }) => {
const words = dto.unsanitizedString.split(pickWordsRegex);
const redactionSet = new Set(dto.tokens.filter(Boolean));
const sanitizedWords = words.map((el) => {
if (redactionSet.has(el)) {
return "[REDACTED]";
}
return el;
});
return sanitizedWords.join("");
};