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

View File

@@ -1,8 +1,6 @@
/* eslint-disable no-await-in-loop */
import { MongoClient } from "mongodb";
import RE2 from "re2";
import { verifyHostInputValidity } from "@app/ee/services/dynamic-secret/dynamic-secret-fns";
import {
TRotationFactory,
TRotationFactoryGetSecretsPayload,
@@ -10,6 +8,7 @@ import {
TRotationFactoryRevokeCredentials,
TRotationFactoryRotateCredentials
} 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 {
@@ -44,67 +43,10 @@ export const mongodbCredentialsRotationFactory: TRotationFactory<
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 () => {
let client: MongoClient | null = null;
try {
client = await $createMongoClient(
{
username: connection.credentials.username,
password: connection.credentials.password
},
{ validateConnection: true }
);
client = await createMongoClient(connection.credentials, { validateConnection: true });
return client;
} catch (err) {
if (client) await client.close();
@@ -115,13 +57,13 @@ export const mongodbCredentialsRotationFactory: TRotationFactory<
const $validateCredentials = async (credentials: TMongoDBCredentialsRotationGeneratedCredentials[number]) => {
let client: MongoClient | null = null;
try {
client = await $createMongoClient(
{
client = await createMongoClient(connection.credentials, {
authCredentials: {
username: credentials.username,
password: credentials.password
},
{ validateConnection: true }
);
validateConnection: true
});
} catch (error) {
throw new Error(redactPasswords(error, [credentials]));
} finally {

View File

@@ -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 protocolRegex = new RE2("^mongodb:\\/\\/");
let normalizedHost = config.credentials.host.trim();
let normalizedHost = credentials.host.trim();
const isSrvFromHost = srvRegex.test(normalizedHost);
if (isSrvFromHost) {
normalizedHost = srvRegex.replace(normalizedHost, "");
@@ -31,41 +52,60 @@ export const validateMongoDBConnectionCredentials = async (config: TMongoDBConne
const [hostIp] = await verifyHostInputValidity(normalizedHost);
let client: MongoClient | null = null;
try {
const isSrv = !config.credentials.port || isSrvFromHost;
const uri = isSrv ? `mongodb+srv://${hostIp}` : `mongodb://${hostIp}:${config.credentials.port}`;
const isSrv = !credentials.port || isSrvFromHost;
const uri = isSrv ? `mongodb+srv://${hostIp}` : `mongodb://${hostIp}:${credentials.port}`;
const clientOptions: {
auth?: { username: string; password?: string };
authSource?: string;
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
};
const authCredentials = options?.authCredentials ?? {
username: credentials.username,
password: credentials.password
};
if (config.credentials.sslEnabled) {
clientOptions.tls = true;
clientOptions.tlsInsecure = !config.credentials.sslRejectUnauthorized;
if (config.credentials.sslCertificate) {
clientOptions.ca = config.credentials.sslCertificate;
}
const clientOptions: {
auth?: { username: string; password?: string };
authSource?: string;
tls?: boolean;
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
.db(config.credentials.database)
.db(credentials.database)
.command({ ping: 1 })
.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();

View File

@@ -17,9 +17,9 @@ export const BaseMongoDBUsernameAndPasswordConnectionSchema = z.object({
password: z.string().min(1),
database: z.string().min(1).trim(),
sslRejectUnauthorized: z.boolean(),
sslEnabled: z.boolean(),
sslCertificate: z
tlsRejectUnauthorized: z.boolean(),
tlsEnabled: z.boolean(),
tlsCertificate: z
.string()
.trim()
.transform((value) => value || undefined)
@@ -43,9 +43,9 @@ export const SanitizedMongoDBConnectionSchema = z.discriminatedUnion("method", [
port: true,
username: true,
database: true,
sslEnabled: true,
sslRejectUnauthorized: true,
sslCertificate: true
tlsEnabled: true,
tlsRejectUnauthorized: true,
tlsCertificate: true
})
})
]);

View File

@@ -11,9 +11,9 @@ export type TMongoDBConnectionCredentials = {
username: string;
password: string;
database: string;
sslEnabled: boolean;
sslRejectUnauthorized: boolean;
sslCertificate?: string;
tlsEnabled: boolean;
tlsRejectUnauthorized: boolean;
tlsCertificate?: string;
};
export type TMongoDBConnection = TRootAppConnection & { app: AppConnection.MongoDB } & {

View File

@@ -45,9 +45,9 @@ const formSchema = z.discriminatedUnion("method", [
username: z.string().trim().min(1, "Username required"),
password: z.string().trim().min(1, "Password required"),
database: z.string().trim().min(1, "Database required"),
sslEnabled: z.boolean().default(false),
sslRejectUnauthorized: z.boolean().default(true),
sslCertificate: z
tlsEnabled: z.boolean().default(false),
tlsRejectUnauthorized: z.boolean().default(true),
tlsCertificate: z
.string()
.trim()
.transform((value) => value || undefined)
@@ -73,9 +73,9 @@ export const MongoDBConnectionForm = ({ appConnection, onSubmit }: Props) => {
username: "",
password: "",
database: "",
sslEnabled: false,
sslRejectUnauthorized: true,
sslCertificate: undefined
tlsEnabled: false,
tlsRejectUnauthorized: true,
tlsCertificate: undefined
}
}
});
@@ -87,7 +87,7 @@ export const MongoDBConnectionForm = ({ appConnection, onSubmit }: Props) => {
formState: { isSubmitting, isDirty }
} = form;
const sslEnabled = watch("credentials.sslEnabled");
const tlsEnabled = watch("credentials.tlsEnabled");
return (
<FormProvider {...form}>
@@ -147,7 +147,7 @@ export const MongoDBConnectionForm = ({ appConnection, onSubmit }: Props) => {
}`
}
>
SSL ({sslEnabled ? "Enabled" : "Disabled"})
TLS ({tlsEnabled ? "Enabled" : "Disabled"})
</Tab>
</Tab.List>
<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>
<Controller
name="credentials.sslEnabled"
name="credentials.tlsEnabled"
control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl isError={Boolean(error?.message)} errorText={error?.message}>
<Switch
className="bg-mineshaft-400/50 shadow-inner data-[state=checked]:bg-green/80"
id="ssl-enabled"
id="tls-enabled"
thumbClassName="bg-mineshaft-800"
isChecked={value}
onCheckedChange={onChange}
>
Enable SSL
Enable TLS
</Switch>
</FormControl>
)}
/>
<Controller
name="credentials.sslCertificate"
name="credentials.tlsCertificate"
control={control}
render={({ field, fieldState: { error } }) => (
<FormControl
errorText={error?.message}
isError={Boolean(error?.message)}
className={sslEnabled ? "" : "opacity-50"}
label="SSL Certificate"
className={tlsEnabled ? "" : "opacity-50"}
label="TLS Certificate"
isOptional
>
<TextArea className="h-14 resize-none!" {...field} isDisabled={!sslEnabled} />
<TextArea className="h-14 resize-none!" {...field} isDisabled={!tlsEnabled} />
</FormControl>
)}
/>
<Controller
name="credentials.sslRejectUnauthorized"
name="credentials.tlsRejectUnauthorized"
control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
className={sslEnabled ? "" : "opacity-50"}
className={tlsEnabled ? "" : "opacity-50"}
isError={Boolean(error?.message)}
errorText={error?.message}
>
<Switch
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"
isChecked={sslEnabled ? value : false}
isChecked={tlsEnabled ? value : false}
onCheckedChange={onChange}
isDisabled={!sslEnabled}
isDisabled={!tlsEnabled}
>
<p className="w-38">
Reject Unauthorized
@@ -288,7 +288,7 @@ export const MongoDBConnectionForm = ({ appConnection, onSubmit }: Props) => {
content={
<p>
If enabled, Infisical will only connect to the server if it has a
valid, trusted SSL certificate.
valid, trusted TLS certificate.
</p>
}
>