mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-11 05:28:51 +00:00
Merge pull request #4328 from Infisical/feat/error-log
feat: better error notification for dynamic secret
This commit is contained in:
@@ -15,6 +15,7 @@ import { z } from "zod";
|
||||
import { CustomAWSHasher } from "@app/lib/aws/hashing";
|
||||
import { crypto } from "@app/lib/crypto";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { sanitizeString } from "@app/lib/fn";
|
||||
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
|
||||
|
||||
import { DynamicSecretAwsElastiCacheSchema, TDynamicProviderFns } from "./models";
|
||||
@@ -170,6 +171,7 @@ export const AwsElastiCacheDatabaseProvider = (): TDynamicProviderFns => {
|
||||
};
|
||||
const validateConnection = async (inputs: unknown) => {
|
||||
const providerInputs = await validateProviderInputs(inputs);
|
||||
try {
|
||||
await ElastiCacheUserManager(
|
||||
{
|
||||
accessKeyId: providerInputs.accessKeyId,
|
||||
@@ -178,6 +180,20 @@ export const AwsElastiCacheDatabaseProvider = (): TDynamicProviderFns => {
|
||||
providerInputs.region
|
||||
).verifyCredentials(providerInputs.clusterName);
|
||||
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: {
|
||||
@@ -206,6 +222,7 @@ export const AwsElastiCacheDatabaseProvider = (): TDynamicProviderFns => {
|
||||
|
||||
const parsedStatement = CreateElastiCacheUserSchema.parse(JSON.parse(creationStatement));
|
||||
|
||||
try {
|
||||
await ElastiCacheUserManager(
|
||||
{
|
||||
accessKeyId: providerInputs.accessKeyId,
|
||||
@@ -221,6 +238,21 @@ export const AwsElastiCacheDatabaseProvider = (): TDynamicProviderFns => {
|
||||
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) => {
|
||||
@@ -229,6 +261,7 @@ export const AwsElastiCacheDatabaseProvider = (): TDynamicProviderFns => {
|
||||
const revokeStatement = handlebars.compile(providerInputs.revocationStatement)({ username: entityId });
|
||||
const parsedStatement = DeleteElasticCacheUserSchema.parse(JSON.parse(revokeStatement));
|
||||
|
||||
try {
|
||||
await ElastiCacheUserManager(
|
||||
{
|
||||
accessKeyId: providerInputs.accessKeyId,
|
||||
@@ -238,6 +271,15 @@ export const AwsElastiCacheDatabaseProvider = (): TDynamicProviderFns => {
|
||||
).deleteUser(parsedStatement);
|
||||
|
||||
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) => {
|
||||
|
||||
@@ -23,6 +23,7 @@ import { CustomAWSHasher } from "@app/lib/aws/hashing";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { crypto } from "@app/lib/crypto/cryptography";
|
||||
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
|
||||
import { sanitizeString } from "@app/lib/fn";
|
||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
|
||||
import { AwsIamAuthType, DynamicSecretAwsIamSchema, TDynamicProviderFns } from "./models";
|
||||
@@ -118,6 +119,7 @@ export const AwsIamProvider = (): TDynamicProviderFns => {
|
||||
|
||||
const validateConnection = async (inputs: unknown, { projectId }: { projectId: string }) => {
|
||||
const providerInputs = await validateProviderInputs(inputs);
|
||||
try {
|
||||
const client = await $getClient(providerInputs, projectId);
|
||||
const isConnected = await client
|
||||
.send(new GetUserCommand({}))
|
||||
@@ -134,6 +136,22 @@ export const AwsIamProvider = (): TDynamicProviderFns => {
|
||||
throw err;
|
||||
});
|
||||
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: {
|
||||
@@ -162,6 +180,7 @@ export const AwsIamProvider = (): TDynamicProviderFns => {
|
||||
awsTags.push(...additionalTags);
|
||||
}
|
||||
|
||||
try {
|
||||
const createUserRes = await client.send(
|
||||
new CreateUserCommand({
|
||||
Path: awsPath,
|
||||
@@ -188,7 +207,9 @@ export const AwsIamProvider = (): TDynamicProviderFns => {
|
||||
.split(",")
|
||||
.filter(Boolean)
|
||||
.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
|
||||
}
|
||||
};
|
||||
} 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 }) => {
|
||||
@@ -278,8 +315,25 @@ export const AwsIamProvider = (): TDynamicProviderFns => {
|
||||
)
|
||||
);
|
||||
|
||||
try {
|
||||
await client.send(new DeleteUserCommand({ UserName: 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) => {
|
||||
|
||||
@@ -2,6 +2,7 @@ import axios from "axios";
|
||||
import { customAlphabet } from "nanoid";
|
||||
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { sanitizeString } from "@app/lib/fn";
|
||||
|
||||
import { AzureEntraIDSchema, TDynamicProviderFns } from "./models";
|
||||
|
||||
@@ -51,19 +52,30 @@ export const AzureEntraIDProvider = (): TDynamicProviderFns & {
|
||||
|
||||
const validateConnection = async (inputs: unknown) => {
|
||||
const providerInputs = await validateProviderInputs(inputs);
|
||||
try {
|
||||
const data = await $getToken(providerInputs.tenantId, providerInputs.applicationId, providerInputs.clientSecret);
|
||||
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 providerInputs = await validateProviderInputs(inputs);
|
||||
|
||||
const password = generatePassword();
|
||||
try {
|
||||
const data = await $getToken(providerInputs.tenantId, providerInputs.applicationId, providerInputs.clientSecret);
|
||||
if (!data.success) {
|
||||
throw new BadRequestError({ message: "Failed to authorize to Microsoft Entra ID" });
|
||||
}
|
||||
|
||||
const password = generatePassword();
|
||||
|
||||
const response = await axios.patch(
|
||||
`${MSFT_GRAPH_API_URL}/users/${providerInputs.userId}`,
|
||||
{
|
||||
@@ -84,12 +96,38 @@ export const AzureEntraIDProvider = (): TDynamicProviderFns & {
|
||||
}
|
||||
|
||||
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 providerInputs = await validateProviderInputs(inputs);
|
||||
try {
|
||||
// Creates a new password
|
||||
await create({ inputs });
|
||||
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) => {
|
||||
|
||||
@@ -3,6 +3,8 @@ import handlebars from "handlebars";
|
||||
import { customAlphabet } from "nanoid";
|
||||
import { z } from "zod";
|
||||
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { sanitizeString } from "@app/lib/fn";
|
||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
|
||||
|
||||
@@ -71,9 +73,24 @@ export const CassandraProvider = (): TDynamicProviderFns => {
|
||||
const providerInputs = await validateProviderInputs(inputs);
|
||||
const client = await $getClient(providerInputs);
|
||||
|
||||
try {
|
||||
const isConnected = await client.execute("SELECT * FROM system_schema.keyspaces").then(() => true);
|
||||
await client.shutdown();
|
||||
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: {
|
||||
@@ -89,6 +106,8 @@ export const CassandraProvider = (): TDynamicProviderFns => {
|
||||
const username = generateUsername(usernameTemplate, identity);
|
||||
const password = generatePassword();
|
||||
const { keyspace } = providerInputs;
|
||||
|
||||
try {
|
||||
const expiration = new Date(expireAt).toISOString();
|
||||
|
||||
const creationStatement = handlebars.compile(providerInputs.creationStatement, { noEscape: true })({
|
||||
@@ -106,6 +125,20 @@ export const CassandraProvider = (): TDynamicProviderFns => {
|
||||
await client.shutdown();
|
||||
|
||||
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) => {
|
||||
@@ -115,6 +148,7 @@ export const CassandraProvider = (): TDynamicProviderFns => {
|
||||
const username = entityId;
|
||||
const { keyspace } = providerInputs;
|
||||
|
||||
try {
|
||||
const revokeStatement = handlebars.compile(providerInputs.revocationStatement)({ username, keyspace });
|
||||
const queries = revokeStatement.toString().split(";").filter(Boolean);
|
||||
for (const query of queries) {
|
||||
@@ -123,6 +157,20 @@ export const CassandraProvider = (): TDynamicProviderFns => {
|
||||
}
|
||||
await client.shutdown();
|
||||
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) => {
|
||||
@@ -130,10 +178,11 @@ export const CassandraProvider = (): TDynamicProviderFns => {
|
||||
if (!providerInputs.renewStatement) return { entityId };
|
||||
|
||||
const client = await $getClient(providerInputs);
|
||||
|
||||
const expiration = new Date(expireAt).toISOString();
|
||||
const { keyspace } = providerInputs;
|
||||
|
||||
try {
|
||||
const expiration = new Date(expireAt).toISOString();
|
||||
|
||||
const renewStatement = handlebars.compile(providerInputs.renewStatement)({
|
||||
username: entityId,
|
||||
keyspace,
|
||||
@@ -145,6 +194,20 @@ export const CassandraProvider = (): TDynamicProviderFns => {
|
||||
}
|
||||
await client.shutdown();
|
||||
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 {
|
||||
|
||||
@@ -2,6 +2,8 @@ import { Client as ElasticSearchClient } from "@elastic/elasticsearch";
|
||||
import { customAlphabet } from "nanoid";
|
||||
import { z } from "zod";
|
||||
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { sanitizeString } from "@app/lib/fn";
|
||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
|
||||
import { verifyHostInputValidity } from "../dynamic-secret-fns";
|
||||
@@ -63,12 +65,24 @@ export const ElasticSearchProvider = (): TDynamicProviderFns => {
|
||||
const providerInputs = await validateProviderInputs(inputs);
|
||||
const connection = await $getClient(providerInputs);
|
||||
|
||||
const infoResponse = await connection
|
||||
.info()
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
|
||||
try {
|
||||
const infoResponse = await connection.info().then(() => true);
|
||||
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 } }) => {
|
||||
@@ -79,6 +93,7 @@ export const ElasticSearchProvider = (): TDynamicProviderFns => {
|
||||
const username = generateUsername(usernameTemplate, identity);
|
||||
const password = generatePassword();
|
||||
|
||||
try {
|
||||
await connection.security.putUser({
|
||||
username,
|
||||
password,
|
||||
@@ -88,18 +103,39 @@ export const ElasticSearchProvider = (): TDynamicProviderFns => {
|
||||
|
||||
await connection.close();
|
||||
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 providerInputs = await validateProviderInputs(inputs);
|
||||
const connection = await $getClient(providerInputs);
|
||||
|
||||
try {
|
||||
await connection.security.deleteUser({
|
||||
username: entityId
|
||||
});
|
||||
|
||||
await connection.close();
|
||||
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) => {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { GetAccessTokenResponse } from "google-auth-library/build/src/auth/oauth
|
||||
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { BadRequestError, InternalServerError } from "@app/lib/errors";
|
||||
import { sanitizeString } from "@app/lib/fn";
|
||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
|
||||
import { DynamicSecretGcpIamSchema, TDynamicProviderFns } from "./models";
|
||||
@@ -65,8 +66,18 @@ export const GcpIamProvider = (): TDynamicProviderFns => {
|
||||
|
||||
const validateConnection = async (inputs: unknown) => {
|
||||
const providerInputs = await validateProviderInputs(inputs);
|
||||
try {
|
||||
await $getToken(providerInputs.serviceAccountEmail, 10);
|
||||
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 }) => {
|
||||
@@ -74,6 +85,7 @@ export const GcpIamProvider = (): TDynamicProviderFns => {
|
||||
|
||||
const providerInputs = await validateProviderInputs(inputs);
|
||||
|
||||
try {
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const ttl = Math.max(Math.floor(expireAt / 1000) - now, 0);
|
||||
|
||||
@@ -81,6 +93,15 @@ export const GcpIamProvider = (): TDynamicProviderFns => {
|
||||
const entityId = alphaNumericNanoId(32);
|
||||
|
||||
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) => {
|
||||
@@ -89,10 +110,21 @@ export const GcpIamProvider = (): TDynamicProviderFns => {
|
||||
};
|
||||
|
||||
const renew = async (inputs: unknown, entityId: string, expireAt: number) => {
|
||||
try {
|
||||
// To renew a token it must be re-created
|
||||
const data = await create({ inputs, expireAt });
|
||||
|
||||
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 {
|
||||
|
||||
@@ -3,6 +3,7 @@ import jwt from "jsonwebtoken";
|
||||
|
||||
import { crypto } from "@app/lib/crypto";
|
||||
import { BadRequestError, InternalServerError } from "@app/lib/errors";
|
||||
import { sanitizeString } from "@app/lib/fn";
|
||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
||||
|
||||
@@ -89,14 +90,25 @@ export const GithubProvider = (): TDynamicProviderFns => {
|
||||
|
||||
const validateConnection = async (inputs: unknown) => {
|
||||
const providerInputs = await validateProviderInputs(inputs);
|
||||
try {
|
||||
await $generateGitHubInstallationAccessToken(providerInputs);
|
||||
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 { inputs } = data;
|
||||
const providerInputs = await validateProviderInputs(inputs);
|
||||
|
||||
try {
|
||||
const ghTokenData = await $generateGitHubInstallationAccessToken(providerInputs);
|
||||
const entityId = alphaNumericNanoId(32);
|
||||
|
||||
@@ -109,6 +121,15 @@ export const GithubProvider = (): TDynamicProviderFns => {
|
||||
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 () => {
|
||||
|
||||
@@ -2,7 +2,8 @@ import axios, { AxiosError } from "axios";
|
||||
import handlebars from "handlebars";
|
||||
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 { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator";
|
||||
@@ -356,8 +357,12 @@ export const KubernetesProvider = ({ gatewayService }: TKubernetesProviderDTO):
|
||||
errorMessage = (error.response?.data as { message: string }).message;
|
||||
}
|
||||
|
||||
throw new InternalServerError({
|
||||
message: `Failed to validate connection: ${errorMessage}`
|
||||
const sanitizedErrorMessage = sanitizeString({
|
||||
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;
|
||||
}
|
||||
|
||||
throw new InternalServerError({
|
||||
message: `Failed to create dynamic secret: ${errorMessage}`
|
||||
const sanitizedErrorMessage = sanitizeString({
|
||||
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) {
|
||||
try {
|
||||
const rawUrl =
|
||||
providerInputs.authMethod === KubernetesAuthMethod.Gateway
|
||||
? GATEWAY_AUTH_DEFAULT_URL
|
||||
@@ -728,6 +738,20 @@ export const KubernetesProvider = ({ gatewayService }: TKubernetesProviderDTO):
|
||||
} else {
|
||||
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 };
|
||||
|
||||
@@ -6,6 +6,7 @@ import RE2 from "re2";
|
||||
import { z } from "zod";
|
||||
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { sanitizeString } from "@app/lib/fn";
|
||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
|
||||
import { LdapCredentialType, LdapSchema, TDynamicProviderFns } from "./models";
|
||||
@@ -91,8 +92,18 @@ export const LdapProvider = (): TDynamicProviderFns => {
|
||||
|
||||
const validateConnection = async (inputs: unknown) => {
|
||||
const providerInputs = await validateProviderInputs(inputs);
|
||||
try {
|
||||
const client = await $getClient(providerInputs);
|
||||
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) => {
|
||||
@@ -205,11 +216,11 @@ export const LdapProvider = (): TDynamicProviderFns => {
|
||||
if (providerInputs.credentialType === LdapCredentialType.Static) {
|
||||
const dnRegex = new RE2("^dn:\\s*(.+)", "m");
|
||||
const dnMatch = dnRegex.exec(providerInputs.rotationLdif);
|
||||
|
||||
if (dnMatch) {
|
||||
const username = dnMatch[1];
|
||||
const username = dnMatch?.[1];
|
||||
if (!username) throw new BadRequestError({ message: "Username not found from Ldif" });
|
||||
const password = generatePassword();
|
||||
|
||||
if (dnMatch) {
|
||||
const generatedLdif = generateLDIF({ username, password, ldifTemplate: providerInputs.rotationLdif });
|
||||
|
||||
try {
|
||||
@@ -217,7 +228,11 @@ export const LdapProvider = (): TDynamicProviderFns => {
|
||||
|
||||
return { entityId: username, data: { DN_ARRAY: dnArray, USERNAME: username, PASSWORD: password } };
|
||||
} 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 {
|
||||
throw new BadRequestError({
|
||||
@@ -238,7 +253,11 @@ export const LdapProvider = (): TDynamicProviderFns => {
|
||||
const rollbackLdif = generateLDIF({ username, password, ldifTemplate: providerInputs.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 } };
|
||||
} 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 {
|
||||
throw new BadRequestError({
|
||||
@@ -278,7 +301,7 @@ export const LdapProvider = (): TDynamicProviderFns => {
|
||||
return { entityId };
|
||||
};
|
||||
|
||||
const renew = async (inputs: unknown, entityId: string) => {
|
||||
const renew = async (_inputs: unknown, entityId: string) => {
|
||||
// No renewal necessary
|
||||
return { entityId };
|
||||
};
|
||||
|
||||
@@ -3,6 +3,8 @@ import { customAlphabet } from "nanoid";
|
||||
import { z } from "zod";
|
||||
|
||||
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 { DynamicSecretMongoAtlasSchema, TDynamicProviderFns } from "./models";
|
||||
@@ -49,19 +51,25 @@ export const MongoAtlasProvider = (): TDynamicProviderFns => {
|
||||
const providerInputs = await validateProviderInputs(inputs);
|
||||
const client = await $getClient(providerInputs);
|
||||
|
||||
try {
|
||||
const isConnected = await client({
|
||||
method: "GET",
|
||||
url: `v2/groups/${providerInputs.groupId}/databaseUsers`,
|
||||
params: { itemsPerPage: 1 }
|
||||
})
|
||||
.then(() => true)
|
||||
.catch((error) => {
|
||||
if ((error as AxiosError).response) {
|
||||
throw new Error(JSON.stringify((error as AxiosError).response?.data));
|
||||
}
|
||||
throw error;
|
||||
});
|
||||
}).then(() => true);
|
||||
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: {
|
||||
@@ -77,6 +85,7 @@ export const MongoAtlasProvider = (): TDynamicProviderFns => {
|
||||
const username = generateUsername(usernameTemplate, identity);
|
||||
const password = generatePassword();
|
||||
const expiration = new Date(expireAt).toISOString();
|
||||
try {
|
||||
await client({
|
||||
method: "POST",
|
||||
url: `/v2/groups/${providerInputs.groupId}/databaseUsers`,
|
||||
@@ -89,13 +98,26 @@ export const MongoAtlasProvider = (): TDynamicProviderFns => {
|
||||
databaseName: "admin",
|
||||
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 } };
|
||||
} 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) => {
|
||||
@@ -111,15 +133,23 @@ export const MongoAtlasProvider = (): TDynamicProviderFns => {
|
||||
throw err;
|
||||
});
|
||||
if (isExisting) {
|
||||
try {
|
||||
await client({
|
||||
method: "DELETE",
|
||||
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 };
|
||||
@@ -132,6 +162,7 @@ export const MongoAtlasProvider = (): TDynamicProviderFns => {
|
||||
const username = entityId;
|
||||
const expiration = new Date(expireAt).toISOString();
|
||||
|
||||
try {
|
||||
await client({
|
||||
method: "PATCH",
|
||||
url: `/v2/groups/${providerInputs.groupId}/databaseUsers/admin/${username}`,
|
||||
@@ -140,13 +171,20 @@ export const MongoAtlasProvider = (): TDynamicProviderFns => {
|
||||
databaseName: "admin",
|
||||
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 };
|
||||
} 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 {
|
||||
|
||||
@@ -2,6 +2,8 @@ import { MongoClient } from "mongodb";
|
||||
import { customAlphabet } from "nanoid";
|
||||
import { z } from "zod";
|
||||
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { sanitizeString } from "@app/lib/fn";
|
||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
|
||||
import { verifyHostInputValidity } from "../dynamic-secret-fns";
|
||||
@@ -51,6 +53,7 @@ export const MongoDBProvider = (): TDynamicProviderFns => {
|
||||
const providerInputs = await validateProviderInputs(inputs);
|
||||
const client = await $getClient(providerInputs);
|
||||
|
||||
try {
|
||||
const isConnected = await client
|
||||
.db(providerInputs.database)
|
||||
.command({ ping: 1 })
|
||||
@@ -58,6 +61,16 @@ export const MongoDBProvider = (): TDynamicProviderFns => {
|
||||
|
||||
await client.close();
|
||||
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 } }) => {
|
||||
@@ -68,6 +81,7 @@ export const MongoDBProvider = (): TDynamicProviderFns => {
|
||||
const username = generateUsername(usernameTemplate, identity);
|
||||
const password = generatePassword();
|
||||
|
||||
try {
|
||||
const db = client.db(providerInputs.database);
|
||||
|
||||
await db.command({
|
||||
@@ -78,6 +92,16 @@ export const MongoDBProvider = (): TDynamicProviderFns => {
|
||||
await client.close();
|
||||
|
||||
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) => {
|
||||
@@ -86,6 +110,7 @@ export const MongoDBProvider = (): TDynamicProviderFns => {
|
||||
|
||||
const username = entityId;
|
||||
|
||||
try {
|
||||
const db = client.db(providerInputs.database);
|
||||
await db.command({
|
||||
dropUser: username
|
||||
@@ -93,6 +118,16 @@ export const MongoDBProvider = (): TDynamicProviderFns => {
|
||||
await client.close();
|
||||
|
||||
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) => {
|
||||
|
||||
@@ -3,6 +3,8 @@ import https from "https";
|
||||
import { customAlphabet } from "nanoid";
|
||||
import { z } from "zod";
|
||||
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { sanitizeString } from "@app/lib/fn";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
|
||||
@@ -110,11 +112,19 @@ export const RabbitMqProvider = (): TDynamicProviderFns => {
|
||||
|
||||
const validateConnection = async (inputs: unknown) => {
|
||||
const providerInputs = await validateProviderInputs(inputs);
|
||||
try {
|
||||
const connection = await $getClient(providerInputs);
|
||||
|
||||
const infoResponse = await connection.get("/whoami").then(() => true);
|
||||
|
||||
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 } }) => {
|
||||
@@ -125,6 +135,7 @@ export const RabbitMqProvider = (): TDynamicProviderFns => {
|
||||
const username = generateUsername(usernameTemplate, identity);
|
||||
const password = generatePassword();
|
||||
|
||||
try {
|
||||
await createRabbitMqUser({
|
||||
axiosInstance: connection,
|
||||
virtualHost: providerInputs.virtualHost,
|
||||
@@ -134,17 +145,34 @@ export const RabbitMqProvider = (): TDynamicProviderFns => {
|
||||
tags: [...(providerInputs.tags ?? []), "infisical-user"]
|
||||
}
|
||||
});
|
||||
|
||||
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 providerInputs = await validateProviderInputs(inputs);
|
||||
const connection = await $getClient(providerInputs);
|
||||
|
||||
try {
|
||||
await deleteRabbitMqUser({ axiosInstance: connection, usernameToDelete: 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) => {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { customAlphabet } from "nanoid";
|
||||
import { z } from "zod";
|
||||
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { sanitizeString } from "@app/lib/fn";
|
||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
|
||||
|
||||
@@ -112,14 +113,27 @@ export const RedisDatabaseProvider = (): TDynamicProviderFns => {
|
||||
|
||||
const validateConnection = async (inputs: unknown) => {
|
||||
const providerInputs = await validateProviderInputs(inputs);
|
||||
const connection = await $getClient(providerInputs);
|
||||
|
||||
const pingResponse = await connection
|
||||
.ping()
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
|
||||
let connection;
|
||||
try {
|
||||
connection = await $getClient(providerInputs);
|
||||
const pingResponse = await connection.ping().then(() => true);
|
||||
await connection.quit();
|
||||
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: {
|
||||
@@ -144,10 +158,20 @@ export const RedisDatabaseProvider = (): TDynamicProviderFns => {
|
||||
|
||||
const queries = creationStatement.toString().split(";").filter(Boolean);
|
||||
|
||||
try {
|
||||
await executeTransactions(connection, queries);
|
||||
|
||||
await connection.quit();
|
||||
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) => {
|
||||
@@ -159,10 +183,20 @@ export const RedisDatabaseProvider = (): TDynamicProviderFns => {
|
||||
const revokeStatement = handlebars.compile(providerInputs.revocationStatement)({ username });
|
||||
const queries = revokeStatement.toString().split(";").filter(Boolean);
|
||||
|
||||
try {
|
||||
await executeTransactions(connection, queries);
|
||||
|
||||
await connection.quit();
|
||||
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) => {
|
||||
@@ -176,13 +210,23 @@ export const RedisDatabaseProvider = (): TDynamicProviderFns => {
|
||||
|
||||
const renewStatement = handlebars.compile(providerInputs.renewStatement)({ username, expiration });
|
||||
|
||||
try {
|
||||
if (renewStatement) {
|
||||
const queries = renewStatement.toString().split(";").filter(Boolean);
|
||||
await executeTransactions(connection, queries);
|
||||
}
|
||||
|
||||
await connection.quit();
|
||||
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 {
|
||||
|
||||
@@ -4,6 +4,7 @@ import odbc from "odbc";
|
||||
import { z } from "zod";
|
||||
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { sanitizeString } from "@app/lib/fn";
|
||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
|
||||
|
||||
@@ -67,8 +68,11 @@ export const SapAseProvider = (): TDynamicProviderFns => {
|
||||
|
||||
const validateConnection = async (inputs: unknown) => {
|
||||
const providerInputs = await validateProviderInputs(inputs);
|
||||
const masterClient = await $getClient(providerInputs, true);
|
||||
const client = await $getClient(providerInputs);
|
||||
let masterClient;
|
||||
let client;
|
||||
try {
|
||||
masterClient = await $getClient(providerInputs, true);
|
||||
client = await $getClient(providerInputs);
|
||||
|
||||
const [resultFromMasterDatabase] = await masterClient.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;
|
||||
} 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 } }) => {
|
||||
@@ -105,16 +122,26 @@ export const SapAseProvider = (): TDynamicProviderFns => {
|
||||
|
||||
const queries = creationStatement.trim().replaceAll("\n", "").split(";").filter(Boolean);
|
||||
|
||||
try {
|
||||
for await (const query of queries) {
|
||||
// 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.
|
||||
await (query.startsWith(SapCommands.CreateLogin) ? masterClient : client).query(query);
|
||||
}
|
||||
|
||||
await masterClient.close();
|
||||
await client.close();
|
||||
|
||||
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) => {
|
||||
@@ -140,14 +167,24 @@ export const SapAseProvider = (): TDynamicProviderFns => {
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
for await (const query of queries) {
|
||||
await (query.startsWith(SapCommands.DropLogin) ? masterClient : client).query(query);
|
||||
}
|
||||
|
||||
await masterClient.close();
|
||||
await client.close();
|
||||
|
||||
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) => {
|
||||
|
||||
@@ -10,6 +10,7 @@ import { customAlphabet } from "nanoid";
|
||||
import { z } from "zod";
|
||||
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { sanitizeString } from "@app/lib/fn";
|
||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
|
||||
|
||||
@@ -83,19 +84,26 @@ export const SapHanaProvider = (): TDynamicProviderFns => {
|
||||
|
||||
const validateConnection = async (inputs: unknown) => {
|
||||
const providerInputs = await validateProviderInputs(inputs);
|
||||
try {
|
||||
const client = await $getClient(providerInputs);
|
||||
|
||||
const testResult = await new Promise<boolean>((resolve, reject) => {
|
||||
client.exec("SELECT 1 FROM DUMMY;", (err: any) => {
|
||||
if (err) {
|
||||
reject();
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
resolve(true);
|
||||
});
|
||||
});
|
||||
|
||||
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: {
|
||||
@@ -119,20 +127,24 @@ export const SapHanaProvider = (): TDynamicProviderFns => {
|
||||
});
|
||||
|
||||
const queries = creationStatement.toString().split(";").filter(Boolean);
|
||||
try {
|
||||
for await (const query of queries) {
|
||||
await new Promise((resolve, reject) => {
|
||||
client.exec(query, (err: any) => {
|
||||
if (err) {
|
||||
reject(
|
||||
new BadRequestError({
|
||||
message: err.message
|
||||
})
|
||||
);
|
||||
}
|
||||
if (err) return reject(err);
|
||||
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 } };
|
||||
};
|
||||
@@ -142,20 +154,26 @@ export const SapHanaProvider = (): TDynamicProviderFns => {
|
||||
const client = await $getClient(providerInputs);
|
||||
const revokeStatement = handlebars.compile(providerInputs.revocationStatement)({ username });
|
||||
const queries = revokeStatement.toString().split(";").filter(Boolean);
|
||||
try {
|
||||
for await (const query of queries) {
|
||||
await new Promise((resolve, reject) => {
|
||||
client.exec(query, (err: any) => {
|
||||
if (err) {
|
||||
reject(
|
||||
new BadRequestError({
|
||||
message: err.message
|
||||
})
|
||||
);
|
||||
reject(err);
|
||||
}
|
||||
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 };
|
||||
};
|
||||
@@ -174,16 +192,20 @@ export const SapHanaProvider = (): TDynamicProviderFns => {
|
||||
await new Promise((resolve, reject) => {
|
||||
client.exec(query, (err: any) => {
|
||||
if (err) {
|
||||
reject(
|
||||
new BadRequestError({
|
||||
message: err.message
|
||||
})
|
||||
);
|
||||
reject(err);
|
||||
}
|
||||
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 {
|
||||
client.disconnect();
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import snowflake from "snowflake-sdk";
|
||||
import { z } from "zod";
|
||||
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { sanitizeString } from "@app/lib/fn";
|
||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
|
||||
|
||||
@@ -69,12 +70,10 @@ export const SnowflakeProvider = (): TDynamicProviderFns => {
|
||||
|
||||
const validateConnection = async (inputs: unknown) => {
|
||||
const providerInputs = await validateProviderInputs(inputs);
|
||||
const client = await $getClient(providerInputs);
|
||||
|
||||
let isValidConnection: boolean;
|
||||
|
||||
let client;
|
||||
try {
|
||||
isValidConnection = await Promise.race([
|
||||
client = await $getClient(providerInputs);
|
||||
const isValidConnection = await Promise.race([
|
||||
client.isValidAsync(),
|
||||
new Promise((resolve) => {
|
||||
setTimeout(resolve, 10000);
|
||||
@@ -82,11 +81,18 @@ export const SnowflakeProvider = (): TDynamicProviderFns => {
|
||||
throw new BadRequestError({ message: "Unable to establish connection - verify credentials" });
|
||||
})
|
||||
]);
|
||||
} finally {
|
||||
client.destroy(noop);
|
||||
}
|
||||
|
||||
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: {
|
||||
@@ -116,13 +122,19 @@ export const SnowflakeProvider = (): TDynamicProviderFns => {
|
||||
sqlText: creationStatement,
|
||||
complete(err) {
|
||||
if (err) {
|
||||
return reject(new BadRequestError({ name: "CreateLease", message: err.message }));
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
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 {
|
||||
client.destroy(noop);
|
||||
}
|
||||
@@ -143,13 +155,19 @@ export const SnowflakeProvider = (): TDynamicProviderFns => {
|
||||
sqlText: revokeStatement,
|
||||
complete(err) {
|
||||
if (err) {
|
||||
return reject(new BadRequestError({ name: "RevokeLease", message: err.message }));
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
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 {
|
||||
client.destroy(noop);
|
||||
}
|
||||
@@ -175,13 +193,19 @@ export const SnowflakeProvider = (): TDynamicProviderFns => {
|
||||
sqlText: renewStatement,
|
||||
complete(err) {
|
||||
if (err) {
|
||||
return reject(new BadRequestError({ name: "RenewLease", message: err.message }));
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
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 {
|
||||
client.destroy(noop);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ import knex from "knex";
|
||||
import { z } from "zod";
|
||||
|
||||
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 { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
|
||||
@@ -212,8 +214,19 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
|
||||
// oracle needs from keyword
|
||||
const testStatement = providerInputs.client === SqlProviders.Oracle ? "SELECT 1 FROM DUAL" : "SELECT 1";
|
||||
|
||||
try {
|
||||
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();
|
||||
}
|
||||
};
|
||||
|
||||
if (providerInputs.gatewayId) {
|
||||
@@ -233,13 +246,13 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
|
||||
const { inputs, expireAt, usernameTemplate, identity } = data;
|
||||
|
||||
const providerInputs = await validateProviderInputs(inputs);
|
||||
const { database } = providerInputs;
|
||||
const username = generateUsername(providerInputs.client, usernameTemplate, identity);
|
||||
|
||||
const password = generatePassword(providerInputs.client, providerInputs.passwordRequirements);
|
||||
const gatewayCallback = async (host = providerInputs.host, port = providerInputs.port) => {
|
||||
const db = await $getClient({ ...providerInputs, port, host });
|
||||
try {
|
||||
const { database } = providerInputs;
|
||||
const expiration = new Date(expireAt).toISOString();
|
||||
|
||||
const creationStatement = handlebars.compile(providerInputs.creationStatement, { noEscape: true })({
|
||||
@@ -256,6 +269,14 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
|
||||
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 {
|
||||
await db.destroy();
|
||||
}
|
||||
@@ -283,6 +304,14 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
|
||||
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 {
|
||||
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 {
|
||||
await db.destroy();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { authenticator } from "otplib";
|
||||
import { HashAlgorithms } from "otplib/core";
|
||||
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { sanitizeString } from "@app/lib/fn";
|
||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
|
||||
import { DynamicSecretTotpSchema, TDynamicProviderFns, TotpConfigType } from "./models";
|
||||
@@ -12,11 +14,24 @@ export const TotpProvider = (): TDynamicProviderFns => {
|
||||
return providerInputs;
|
||||
};
|
||||
|
||||
const validateConnection = async () => {
|
||||
const validateConnection = async (inputs: unknown) => {
|
||||
try {
|
||||
await validateProviderInputs(inputs);
|
||||
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 entityId = alphaNumericNanoId(32);
|
||||
@@ -68,6 +83,15 @@ export const TotpProvider = (): TDynamicProviderFns => {
|
||||
entityId,
|
||||
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) => {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { z } from "zod";
|
||||
|
||||
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 { logger } from "@app/lib/logger";
|
||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
@@ -275,6 +276,14 @@ export const VerticaProvider = ({ gatewayService }: TVerticaProviderDTO): TDynam
|
||||
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 {
|
||||
if (client) await client.destroy();
|
||||
}
|
||||
@@ -339,6 +348,14 @@ export const VerticaProvider = ({ gatewayService }: TVerticaProviderDTO): TDynam
|
||||
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 {
|
||||
if (client) await client.destroy();
|
||||
}
|
||||
|
||||
@@ -19,3 +19,17 @@ export const prefixWithSlash = (str: string) => {
|
||||
const vowelRegex = new RE2(/^[aeiou]/i);
|
||||
|
||||
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("");
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user