refactor(mongodb-credentials): replace SSL terminology with TLS and enhance MongoDB client creation logic

This commit is contained in:
Victor Santos
2025-12-05 19:35:23 -03:00
parent dfbc2ed033
commit 8c3b36f15c
5 changed files with 105 additions and 123 deletions
@@ -1,8 +1,6 @@
/* eslint-disable no-await-in-loop */ /* eslint-disable no-await-in-loop */
import { MongoClient } from "mongodb"; import { MongoClient } from "mongodb";
import RE2 from "re2";
import { verifyHostInputValidity } from "@app/ee/services/dynamic-secret/dynamic-secret-fns";
import { import {
TRotationFactory, TRotationFactory,
TRotationFactoryGetSecretsPayload, TRotationFactoryGetSecretsPayload,
@@ -10,6 +8,7 @@ import {
TRotationFactoryRevokeCredentials, TRotationFactoryRevokeCredentials,
TRotationFactoryRotateCredentials TRotationFactoryRotateCredentials
} from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-types"; } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-types";
import { createMongoClient } from "@app/services/app-connection/mongodb/mongodb-connection-fns";
import { DEFAULT_PASSWORD_REQUIREMENTS, generatePassword } from "../shared/utils"; import { DEFAULT_PASSWORD_REQUIREMENTS, generatePassword } from "../shared/utils";
import { import {
@@ -44,67 +43,10 @@ export const mongodbCredentialsRotationFactory: TRotationFactory<
const passwordRequirement = DEFAULT_PASSWORD_REQUIREMENTS; const passwordRequirement = DEFAULT_PASSWORD_REQUIREMENTS;
// Helper function to create MongoDB client with given credentials
const $createMongoClient = async (
authCredentials: { username: string; password: string },
options?: { validateConnection?: boolean }
): Promise<MongoClient> => {
let normalizedHost = connection.credentials.host.trim();
const srvRegex = new RE2("^mongodb\\+srv:\\/\\/");
const protocolRegex = new RE2("^mongodb:\\/\\/");
const isSrvFromHost = srvRegex.test(normalizedHost);
if (isSrvFromHost) {
normalizedHost = srvRegex.replace(normalizedHost, "");
} else if (protocolRegex.test(normalizedHost)) {
normalizedHost = protocolRegex.replace(normalizedHost, "");
}
const [hostIp] = await verifyHostInputValidity(normalizedHost);
const isSrv = !connection.credentials.port || isSrvFromHost;
const uri = isSrv ? `mongodb+srv://${hostIp}` : `mongodb://${hostIp}:${connection.credentials.port}`;
const clientOptions: {
auth?: { username: string; password?: string };
authSource?: string;
tls?: boolean;
tlsInsecure?: boolean;
ca?: string;
directConnection?: boolean;
} = {
auth: {
username: authCredentials.username,
password: authCredentials.password
},
authSource: isSrv ? undefined : connection.credentials.database,
directConnection: !isSrv
};
if (connection.credentials.sslCertificate) {
clientOptions.tls = true;
clientOptions.ca = connection.credentials.sslCertificate;
}
const client = new MongoClient(uri, clientOptions);
if (options?.validateConnection) {
await client.db(connection.credentials.database).command({ ping: 1 });
}
return client;
};
const $getClient = async () => { const $getClient = async () => {
let client: MongoClient | null = null; let client: MongoClient | null = null;
try { try {
client = await $createMongoClient( client = await createMongoClient(connection.credentials, { validateConnection: true });
{
username: connection.credentials.username,
password: connection.credentials.password
},
{ validateConnection: true }
);
return client; return client;
} catch (err) { } catch (err) {
if (client) await client.close(); if (client) await client.close();
@@ -115,13 +57,13 @@ export const mongodbCredentialsRotationFactory: TRotationFactory<
const $validateCredentials = async (credentials: TMongoDBCredentialsRotationGeneratedCredentials[number]) => { const $validateCredentials = async (credentials: TMongoDBCredentialsRotationGeneratedCredentials[number]) => {
let client: MongoClient | null = null; let client: MongoClient | null = null;
try { try {
client = await $createMongoClient( client = await createMongoClient(connection.credentials, {
{ authCredentials: {
username: credentials.username, username: credentials.username,
password: credentials.password password: credentials.password
}, },
{ validateConnection: true } validateConnection: true
); });
} catch (error) { } catch (error) {
throw new Error(redactPasswords(error, [credentials])); throw new Error(redactPasswords(error, [credentials]));
} finally { } finally {
@@ -17,11 +17,32 @@ export const getMongoDBConnectionListItem = () => {
}; };
}; };
export const validateMongoDBConnectionCredentials = async (config: TMongoDBConnectionConfig) => { export type TMongoDBConnectionCredentials = {
host: string;
port?: number;
database: string;
username: string;
password: string;
tlsEnabled?: boolean;
tlsRejectUnauthorized?: boolean;
tlsCertificate?: string;
};
export type TCreateMongoClientOptions = {
authCredentials?: { username: string; password: string };
validateConnection?: boolean;
};
const DEFAULT_CONNECTION_TIMEOUT_MS = 10_000;
export const createMongoClient = async (
credentials: TMongoDBConnectionCredentials,
options?: TCreateMongoClientOptions
): Promise<MongoClient> => {
const srvRegex = new RE2("^mongodb\\+srv:\\/\\/"); const srvRegex = new RE2("^mongodb\\+srv:\\/\\/");
const protocolRegex = new RE2("^mongodb:\\/\\/"); const protocolRegex = new RE2("^mongodb:\\/\\/");
let normalizedHost = config.credentials.host.trim(); let normalizedHost = credentials.host.trim();
const isSrvFromHost = srvRegex.test(normalizedHost); const isSrvFromHost = srvRegex.test(normalizedHost);
if (isSrvFromHost) { if (isSrvFromHost) {
normalizedHost = srvRegex.replace(normalizedHost, ""); normalizedHost = srvRegex.replace(normalizedHost, "");
@@ -31,41 +52,60 @@ export const validateMongoDBConnectionCredentials = async (config: TMongoDBConne
const [hostIp] = await verifyHostInputValidity(normalizedHost); const [hostIp] = await verifyHostInputValidity(normalizedHost);
let client: MongoClient | null = null; const isSrv = !credentials.port || isSrvFromHost;
try { const uri = isSrv ? `mongodb+srv://${hostIp}` : `mongodb://${hostIp}:${credentials.port}`;
const isSrv = !config.credentials.port || isSrvFromHost;
const uri = isSrv ? `mongodb+srv://${hostIp}` : `mongodb://${hostIp}:${config.credentials.port}`;
const clientOptions: { const authCredentials = options?.authCredentials ?? {
auth?: { username: string; password?: string }; username: credentials.username,
authSource?: string; password: credentials.password
tls?: boolean; };
tlsInsecure?: boolean;
ca?: string;
directConnection?: boolean;
} = {
auth: {
username: config.credentials.username,
password: config.credentials.password
},
authSource: isSrv ? undefined : config.credentials.database,
directConnection: !isSrv
};
if (config.credentials.sslEnabled) { const clientOptions: {
clientOptions.tls = true; auth?: { username: string; password?: string };
clientOptions.tlsInsecure = !config.credentials.sslRejectUnauthorized; authSource?: string;
if (config.credentials.sslCertificate) { tls?: boolean;
clientOptions.ca = config.credentials.sslCertificate; tlsInsecure?: boolean;
} ca?: string;
directConnection?: boolean;
connectTimeoutMS?: number;
serverSelectionTimeoutMS?: number;
socketTimeoutMS?: number;
} = {
auth: {
username: authCredentials.username,
password: authCredentials.password
},
authSource: isSrv ? undefined : credentials.database,
directConnection: !isSrv,
connectTimeoutMS: DEFAULT_CONNECTION_TIMEOUT_MS,
serverSelectionTimeoutMS: DEFAULT_CONNECTION_TIMEOUT_MS,
socketTimeoutMS: DEFAULT_CONNECTION_TIMEOUT_MS
};
if (credentials.tlsEnabled) {
clientOptions.tls = true;
clientOptions.tlsInsecure = !credentials.tlsRejectUnauthorized;
if (credentials.tlsCertificate) {
clientOptions.ca = credentials.tlsCertificate;
} }
}
client = new MongoClient(uri, clientOptions); const client = new MongoClient(uri, clientOptions);
if (options?.validateConnection) {
await client await client
.db(config.credentials.database) .db(credentials.database)
.command({ ping: 1 }) .command({ ping: 1 })
.then(() => true); .then(() => true);
}
return client;
};
export const validateMongoDBConnectionCredentials = async (config: TMongoDBConnectionConfig) => {
let client: MongoClient | null = null;
try {
client = await createMongoClient(config.credentials, { validateConnection: true });
if (client) await client.close(); if (client) await client.close();
@@ -17,9 +17,9 @@ export const BaseMongoDBUsernameAndPasswordConnectionSchema = z.object({
password: z.string().min(1), password: z.string().min(1),
database: z.string().min(1).trim(), database: z.string().min(1).trim(),
sslRejectUnauthorized: z.boolean(), tlsRejectUnauthorized: z.boolean(),
sslEnabled: z.boolean(), tlsEnabled: z.boolean(),
sslCertificate: z tlsCertificate: z
.string() .string()
.trim() .trim()
.transform((value) => value || undefined) .transform((value) => value || undefined)
@@ -43,9 +43,9 @@ export const SanitizedMongoDBConnectionSchema = z.discriminatedUnion("method", [
port: true, port: true,
username: true, username: true,
database: true, database: true,
sslEnabled: true, tlsEnabled: true,
sslRejectUnauthorized: true, tlsRejectUnauthorized: true,
sslCertificate: true tlsCertificate: true
}) })
}) })
]); ]);
@@ -11,9 +11,9 @@ export type TMongoDBConnectionCredentials = {
username: string; username: string;
password: string; password: string;
database: string; database: string;
sslEnabled: boolean; tlsEnabled: boolean;
sslRejectUnauthorized: boolean; tlsRejectUnauthorized: boolean;
sslCertificate?: string; tlsCertificate?: string;
}; };
export type TMongoDBConnection = TRootAppConnection & { app: AppConnection.MongoDB } & { export type TMongoDBConnection = TRootAppConnection & { app: AppConnection.MongoDB } & {
@@ -45,9 +45,9 @@ const formSchema = z.discriminatedUnion("method", [
username: z.string().trim().min(1, "Username required"), username: z.string().trim().min(1, "Username required"),
password: z.string().trim().min(1, "Password required"), password: z.string().trim().min(1, "Password required"),
database: z.string().trim().min(1, "Database required"), database: z.string().trim().min(1, "Database required"),
sslEnabled: z.boolean().default(false), tlsEnabled: z.boolean().default(false),
sslRejectUnauthorized: z.boolean().default(true), tlsRejectUnauthorized: z.boolean().default(true),
sslCertificate: z tlsCertificate: z
.string() .string()
.trim() .trim()
.transform((value) => value || undefined) .transform((value) => value || undefined)
@@ -73,9 +73,9 @@ export const MongoDBConnectionForm = ({ appConnection, onSubmit }: Props) => {
username: "", username: "",
password: "", password: "",
database: "", database: "",
sslEnabled: false, tlsEnabled: false,
sslRejectUnauthorized: true, tlsRejectUnauthorized: true,
sslCertificate: undefined tlsCertificate: undefined
} }
} }
}); });
@@ -87,7 +87,7 @@ export const MongoDBConnectionForm = ({ appConnection, onSubmit }: Props) => {
formState: { isSubmitting, isDirty } formState: { isSubmitting, isDirty }
} = form; } = form;
const sslEnabled = watch("credentials.sslEnabled"); const tlsEnabled = watch("credentials.tlsEnabled");
return ( return (
<FormProvider {...form}> <FormProvider {...form}>
@@ -147,7 +147,7 @@ export const MongoDBConnectionForm = ({ appConnection, onSubmit }: Props) => {
}` }`
} }
> >
SSL ({sslEnabled ? "Enabled" : "Disabled"}) TLS ({tlsEnabled ? "Enabled" : "Disabled"})
</Tab> </Tab>
</Tab.List> </Tab.List>
<Tab.Panels className="mb-4 rounded-sm border border-mineshaft-600 bg-mineshaft-700/70 p-3 pb-0"> <Tab.Panels className="mb-4 rounded-sm border border-mineshaft-600 bg-mineshaft-700/70 p-3 pb-0">
@@ -233,53 +233,53 @@ export const MongoDBConnectionForm = ({ appConnection, onSubmit }: Props) => {
</Tab.Panel> </Tab.Panel>
<Tab.Panel> <Tab.Panel>
<Controller <Controller
name="credentials.sslEnabled" name="credentials.tlsEnabled"
control={control} control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => ( render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl isError={Boolean(error?.message)} errorText={error?.message}> <FormControl isError={Boolean(error?.message)} errorText={error?.message}>
<Switch <Switch
className="bg-mineshaft-400/50 shadow-inner data-[state=checked]:bg-green/80" className="bg-mineshaft-400/50 shadow-inner data-[state=checked]:bg-green/80"
id="ssl-enabled" id="tls-enabled"
thumbClassName="bg-mineshaft-800" thumbClassName="bg-mineshaft-800"
isChecked={value} isChecked={value}
onCheckedChange={onChange} onCheckedChange={onChange}
> >
Enable SSL Enable TLS
</Switch> </Switch>
</FormControl> </FormControl>
)} )}
/> />
<Controller <Controller
name="credentials.sslCertificate" name="credentials.tlsCertificate"
control={control} control={control}
render={({ field, fieldState: { error } }) => ( render={({ field, fieldState: { error } }) => (
<FormControl <FormControl
errorText={error?.message} errorText={error?.message}
isError={Boolean(error?.message)} isError={Boolean(error?.message)}
className={sslEnabled ? "" : "opacity-50"} className={tlsEnabled ? "" : "opacity-50"}
label="SSL Certificate" label="TLS Certificate"
isOptional isOptional
> >
<TextArea className="h-14 resize-none!" {...field} isDisabled={!sslEnabled} /> <TextArea className="h-14 resize-none!" {...field} isDisabled={!tlsEnabled} />
</FormControl> </FormControl>
)} )}
/> />
<Controller <Controller
name="credentials.sslRejectUnauthorized" name="credentials.tlsRejectUnauthorized"
control={control} control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => ( render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl <FormControl
className={sslEnabled ? "" : "opacity-50"} className={tlsEnabled ? "" : "opacity-50"}
isError={Boolean(error?.message)} isError={Boolean(error?.message)}
errorText={error?.message} errorText={error?.message}
> >
<Switch <Switch
className="bg-mineshaft-400/50 shadow-inner data-[state=checked]:bg-green/80" className="bg-mineshaft-400/50 shadow-inner data-[state=checked]:bg-green/80"
id="ssl-reject-unauthorized" id="tls-reject-unauthorized"
thumbClassName="bg-mineshaft-800" thumbClassName="bg-mineshaft-800"
isChecked={sslEnabled ? value : false} isChecked={tlsEnabled ? value : false}
onCheckedChange={onChange} onCheckedChange={onChange}
isDisabled={!sslEnabled} isDisabled={!tlsEnabled}
> >
<p className="w-38"> <p className="w-38">
Reject Unauthorized Reject Unauthorized
@@ -288,7 +288,7 @@ export const MongoDBConnectionForm = ({ appConnection, onSubmit }: Props) => {
content={ content={
<p> <p>
If enabled, Infisical will only connect to the server if it has a If enabled, Infisical will only connect to the server if it has a
valid, trusted SSL certificate. valid, trusted TLS certificate.
</p> </p>
} }
> >