mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 09:28:06 +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 { 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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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("");
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user