diff --git a/backend/src/ee/routes/v2/secret-rotation-v2-routers/index.ts b/backend/src/ee/routes/v2/secret-rotation-v2-routers/index.ts
index 8d1702850..d58f3c2f7 100644
--- a/backend/src/ee/routes/v2/secret-rotation-v2-routers/index.ts
+++ b/backend/src/ee/routes/v2/secret-rotation-v2-routers/index.ts
@@ -4,6 +4,7 @@ import { registerAuth0ClientSecretRotationRouter } from "./auth0-client-secret-r
import { registerAwsIamUserSecretRotationRouter } from "./aws-iam-user-secret-rotation-router";
import { registerAzureClientSecretRotationRouter } from "./azure-client-secret-rotation-router";
import { registerLdapPasswordRotationRouter } from "./ldap-password-rotation-router";
+import { registerMongoDBCredentialsRotationRouter } from "./mongodb-credentials-rotation-router";
import { registerMsSqlCredentialsRotationRouter } from "./mssql-credentials-rotation-router";
import { registerMySqlCredentialsRotationRouter } from "./mysql-credentials-rotation-router";
import { registerOktaClientSecretRotationRouter } from "./okta-client-secret-rotation-router";
@@ -26,5 +27,6 @@ export const SECRET_ROTATION_REGISTER_ROUTER_MAP: Record<
[SecretRotation.AwsIamUserSecret]: registerAwsIamUserSecretRotationRouter,
[SecretRotation.LdapPassword]: registerLdapPasswordRotationRouter,
[SecretRotation.OktaClientSecret]: registerOktaClientSecretRotationRouter,
- [SecretRotation.RedisCredentials]: registerRedisCredentialsRotationRouter
+ [SecretRotation.RedisCredentials]: registerRedisCredentialsRotationRouter,
+ [SecretRotation.MongoDBCredentials]: registerMongoDBCredentialsRotationRouter
};
diff --git a/backend/src/ee/routes/v2/secret-rotation-v2-routers/mongodb-credentials-rotation-router.ts b/backend/src/ee/routes/v2/secret-rotation-v2-routers/mongodb-credentials-rotation-router.ts
new file mode 100644
index 000000000..0b41f24f9
--- /dev/null
+++ b/backend/src/ee/routes/v2/secret-rotation-v2-routers/mongodb-credentials-rotation-router.ts
@@ -0,0 +1,19 @@
+import {
+ CreateMongoDBCredentialsRotationSchema,
+ MongoDBCredentialsRotationGeneratedCredentialsSchema,
+ MongoDBCredentialsRotationSchema,
+ UpdateMongoDBCredentialsRotationSchema
+} from "@app/ee/services/secret-rotation-v2/mongodb-credentials";
+import { SecretRotation } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-enums";
+
+import { registerSecretRotationEndpoints } from "./secret-rotation-v2-endpoints";
+
+export const registerMongoDBCredentialsRotationRouter = async (server: FastifyZodProvider) =>
+ registerSecretRotationEndpoints({
+ type: SecretRotation.MongoDBCredentials,
+ server,
+ responseSchema: MongoDBCredentialsRotationSchema,
+ createSchema: CreateMongoDBCredentialsRotationSchema,
+ updateSchema: UpdateMongoDBCredentialsRotationSchema,
+ generatedCredentialsSchema: MongoDBCredentialsRotationGeneratedCredentialsSchema
+ });
diff --git a/backend/src/ee/routes/v2/secret-rotation-v2-routers/secret-rotation-v2-router.ts b/backend/src/ee/routes/v2/secret-rotation-v2-routers/secret-rotation-v2-router.ts
index 6ea6497e4..53346657b 100644
--- a/backend/src/ee/routes/v2/secret-rotation-v2-routers/secret-rotation-v2-router.ts
+++ b/backend/src/ee/routes/v2/secret-rotation-v2-routers/secret-rotation-v2-router.ts
@@ -5,6 +5,7 @@ import { Auth0ClientSecretRotationListItemSchema } from "@app/ee/services/secret
import { AwsIamUserSecretRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/aws-iam-user-secret";
import { AzureClientSecretRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/azure-client-secret";
import { LdapPasswordRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/ldap-password";
+import { MongoDBCredentialsRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/mongodb-credentials";
import { MsSqlCredentialsRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/mssql-credentials";
import { MySqlCredentialsRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/mysql-credentials";
import { OktaClientSecretRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/okta-client-secret";
@@ -27,7 +28,8 @@ const SecretRotationV2OptionsSchema = z.discriminatedUnion("type", [
AwsIamUserSecretRotationListItemSchema,
LdapPasswordRotationListItemSchema,
OktaClientSecretRotationListItemSchema,
- RedisCredentialsRotationListItemSchema
+ RedisCredentialsRotationListItemSchema,
+ MongoDBCredentialsRotationListItemSchema
]);
export const registerSecretRotationV2Router = async (server: FastifyZodProvider) => {
diff --git a/backend/src/ee/services/secret-rotation-v2/mongodb-credentials/index.ts b/backend/src/ee/services/secret-rotation-v2/mongodb-credentials/index.ts
new file mode 100644
index 000000000..876ab836d
--- /dev/null
+++ b/backend/src/ee/services/secret-rotation-v2/mongodb-credentials/index.ts
@@ -0,0 +1,4 @@
+export * from "./mongodb-credentials-rotation-constants";
+export * from "./mongodb-credentials-rotation-fns";
+export * from "./mongodb-credentials-rotation-schemas";
+export * from "./mongodb-credentials-rotation-types";
diff --git a/backend/src/ee/services/secret-rotation-v2/mongodb-credentials/mongodb-credentials-rotation-constants.ts b/backend/src/ee/services/secret-rotation-v2/mongodb-credentials/mongodb-credentials-rotation-constants.ts
new file mode 100644
index 000000000..82b43f45a
--- /dev/null
+++ b/backend/src/ee/services/secret-rotation-v2/mongodb-credentials/mongodb-credentials-rotation-constants.ts
@@ -0,0 +1,27 @@
+import { SecretRotation } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-enums";
+import { TSecretRotationV2ListItem } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-types";
+import { AppConnection } from "@app/services/app-connection/app-connection-enums";
+
+export const MONGODB_CREDENTIALS_ROTATION_LIST_OPTION: TSecretRotationV2ListItem = {
+ name: "MongoDB Credentials",
+ type: SecretRotation.MongoDBCredentials,
+ connection: AppConnection.MongoDB,
+ template: {
+ createUserStatement: `use [DATABASE_NAME]
+db.createUser({
+ user: "infisical_user_1",
+ pwd: "temporary_password",
+ roles: [{ role: "readWrite", db: "[DATABASE_NAME]" }]
+})
+
+db.createUser({
+ user: "infisical_user_2",
+ pwd: "temporary_password",
+ roles: [{ role: "readWrite", db: "[DATABASE_NAME]" }]
+})`,
+ secretsMapping: {
+ username: "MONGODB_DB_USERNAME",
+ password: "MONGODB_DB_PASSWORD"
+ }
+ }
+};
diff --git a/backend/src/ee/services/secret-rotation-v2/mongodb-credentials/mongodb-credentials-rotation-fns.ts b/backend/src/ee/services/secret-rotation-v2/mongodb-credentials/mongodb-credentials-rotation-fns.ts
new file mode 100644
index 000000000..aad286ecc
--- /dev/null
+++ b/backend/src/ee/services/secret-rotation-v2/mongodb-credentials/mongodb-credentials-rotation-fns.ts
@@ -0,0 +1,191 @@
+/* eslint-disable no-await-in-loop */
+import { MongoClient } from "mongodb";
+
+import {
+ TRotationFactory,
+ TRotationFactoryGetSecretsPayload,
+ TRotationFactoryIssueCredentials,
+ 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 {
+ TMongoDBCredentialsRotationGeneratedCredentials,
+ TMongoDBCredentialsRotationWithConnection
+} from "./mongodb-credentials-rotation-types";
+
+const redactPasswords = (e: unknown, credentials: TMongoDBCredentialsRotationGeneratedCredentials) => {
+ const error = e as Error;
+
+ if (!error?.message) return "Unknown error";
+
+ let redactedMessage = error.message;
+
+ credentials.forEach(({ password }) => {
+ redactedMessage = redactedMessage.replaceAll(password, "*******************");
+ });
+
+ return redactedMessage;
+};
+
+export const mongodbCredentialsRotationFactory: TRotationFactory<
+ TMongoDBCredentialsRotationWithConnection,
+ TMongoDBCredentialsRotationGeneratedCredentials
+> = (secretRotation) => {
+ const {
+ connection,
+ parameters: { username1, username2 },
+ activeIndex,
+ secretsMapping
+ } = secretRotation;
+
+ const passwordRequirement = DEFAULT_PASSWORD_REQUIREMENTS;
+
+ const $getClient = async () => {
+ let client: MongoClient | null = null;
+ try {
+ client = await createMongoClient(connection.credentials, { validateConnection: true });
+ return client;
+ } catch (err) {
+ if (client) await client.close();
+ throw err;
+ }
+ };
+
+ const $validateCredentials = async (credentials: TMongoDBCredentialsRotationGeneratedCredentials[number]) => {
+ let client: MongoClient | null = null;
+ try {
+ client = await createMongoClient(connection.credentials, {
+ authCredentials: {
+ username: credentials.username,
+ password: credentials.password
+ },
+ validateConnection: true
+ });
+ } catch (error) {
+ throw new Error(redactPasswords(error, [credentials]));
+ } finally {
+ if (client) await client.close();
+ }
+ };
+
+ const issueCredentials: TRotationFactoryIssueCredentials = async (
+ callback
+ ) => {
+ // For MongoDB, since we get existing users, we change both their passwords
+ // on issue to invalidate their existing passwords
+ const credentialsSet = [
+ { username: username1, password: generatePassword(passwordRequirement) },
+ { username: username2, password: generatePassword(passwordRequirement) }
+ ];
+
+ let client: MongoClient | null = null;
+ try {
+ client = await $getClient();
+ const db = client.db(connection.credentials.database);
+
+ for (const credentials of credentialsSet) {
+ await db.command({
+ updateUser: credentials.username,
+ pwd: credentials.password
+ });
+ }
+ } catch (error) {
+ throw new Error(redactPasswords(error, credentialsSet));
+ } finally {
+ if (client) await client.close();
+ }
+
+ for (const credentials of credentialsSet) {
+ await $validateCredentials(credentials);
+ }
+
+ return callback(credentialsSet[0]);
+ };
+
+ const revokeCredentials: TRotationFactoryRevokeCredentials = async (
+ credentialsToRevoke,
+ callback
+ ) => {
+ const revokedCredentials = credentialsToRevoke.map(({ username }) => ({
+ username,
+ password: generatePassword(passwordRequirement)
+ }));
+
+ let client: MongoClient | null = null;
+ try {
+ client = await $getClient();
+ const db = client.db(connection.credentials.database);
+
+ for (const credentials of revokedCredentials) {
+ await db.command({
+ updateUser: credentials.username,
+ pwd: credentials.password
+ });
+ }
+ } catch (error) {
+ throw new Error(redactPasswords(error, revokedCredentials));
+ } finally {
+ if (client) await client.close();
+ }
+
+ return callback();
+ };
+
+ const rotateCredentials: TRotationFactoryRotateCredentials = async (
+ _,
+ callback
+ ) => {
+ const credentials = {
+ username: activeIndex === 0 ? username2 : username1,
+ password: generatePassword(passwordRequirement)
+ };
+
+ let client: MongoClient | null = null;
+ try {
+ client = await $getClient();
+ const db = client.db(connection.credentials.database);
+
+ await db.command({
+ updateUser: credentials.username,
+ pwd: credentials.password
+ });
+ } catch (error) {
+ throw new Error(redactPasswords(error, [credentials]));
+ } finally {
+ if (client) await client.close();
+ }
+
+ await $validateCredentials(credentials);
+
+ return callback(credentials);
+ };
+
+ const getSecretsPayload: TRotationFactoryGetSecretsPayload = (
+ generatedCredentials
+ ) => {
+ const { username, password } = secretsMapping;
+
+ const secrets = [
+ {
+ key: username,
+ value: generatedCredentials.username
+ },
+ {
+ key: password,
+ value: generatedCredentials.password
+ }
+ ];
+
+ return secrets;
+ };
+
+ return {
+ issueCredentials,
+ revokeCredentials,
+ rotateCredentials,
+ getSecretsPayload
+ };
+};
diff --git a/backend/src/ee/services/secret-rotation-v2/mongodb-credentials/mongodb-credentials-rotation-schemas.ts b/backend/src/ee/services/secret-rotation-v2/mongodb-credentials/mongodb-credentials-rotation-schemas.ts
new file mode 100644
index 000000000..9a5335f5f
--- /dev/null
+++ b/backend/src/ee/services/secret-rotation-v2/mongodb-credentials/mongodb-credentials-rotation-schemas.ts
@@ -0,0 +1,52 @@
+import { z } from "zod";
+
+import { SecretRotation } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-enums";
+import {
+ BaseCreateSecretRotationSchema,
+ BaseSecretRotationSchema,
+ BaseUpdateSecretRotationSchema
+} from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-schemas";
+import {
+ SqlCredentialsRotationGeneratedCredentialsSchema,
+ SqlCredentialsRotationParametersSchema,
+ SqlCredentialsRotationTemplateSchema
+} from "@app/ee/services/secret-rotation-v2/shared/sql-credentials/sql-credentials-rotation-schemas";
+import { SecretRotations } from "@app/lib/api-docs";
+import { SecretNameSchema } from "@app/server/lib/schemas";
+import { AppConnection } from "@app/services/app-connection/app-connection-enums";
+
+export const MongoDBCredentialsRotationGeneratedCredentialsSchema = SqlCredentialsRotationGeneratedCredentialsSchema;
+export const MongoDBCredentialsRotationParametersSchema = SqlCredentialsRotationParametersSchema;
+export const MongoDBCredentialsRotationTemplateSchema = SqlCredentialsRotationTemplateSchema;
+
+const MongoDBCredentialsRotationSecretsMappingSchema = z.object({
+ username: SecretNameSchema.describe(SecretRotations.SECRETS_MAPPING.MONGODB_CREDENTIALS.username),
+ password: SecretNameSchema.describe(SecretRotations.SECRETS_MAPPING.MONGODB_CREDENTIALS.password)
+});
+
+export const MongoDBCredentialsRotationSchema = BaseSecretRotationSchema(SecretRotation.MongoDBCredentials).extend({
+ type: z.literal(SecretRotation.MongoDBCredentials),
+ parameters: MongoDBCredentialsRotationParametersSchema,
+ secretsMapping: MongoDBCredentialsRotationSecretsMappingSchema
+});
+
+export const CreateMongoDBCredentialsRotationSchema = BaseCreateSecretRotationSchema(
+ SecretRotation.MongoDBCredentials
+).extend({
+ parameters: MongoDBCredentialsRotationParametersSchema,
+ secretsMapping: MongoDBCredentialsRotationSecretsMappingSchema
+});
+
+export const UpdateMongoDBCredentialsRotationSchema = BaseUpdateSecretRotationSchema(
+ SecretRotation.MongoDBCredentials
+).extend({
+ parameters: MongoDBCredentialsRotationParametersSchema.optional(),
+ secretsMapping: MongoDBCredentialsRotationSecretsMappingSchema.optional()
+});
+
+export const MongoDBCredentialsRotationListItemSchema = z.object({
+ name: z.literal("MongoDB Credentials"),
+ connection: z.literal(AppConnection.MongoDB),
+ type: z.literal(SecretRotation.MongoDBCredentials),
+ template: MongoDBCredentialsRotationTemplateSchema
+});
diff --git a/backend/src/ee/services/secret-rotation-v2/mongodb-credentials/mongodb-credentials-rotation-types.ts b/backend/src/ee/services/secret-rotation-v2/mongodb-credentials/mongodb-credentials-rotation-types.ts
new file mode 100644
index 000000000..3a53a8cc5
--- /dev/null
+++ b/backend/src/ee/services/secret-rotation-v2/mongodb-credentials/mongodb-credentials-rotation-types.ts
@@ -0,0 +1,24 @@
+import { z } from "zod";
+
+import { TMongoDBConnection } from "@app/services/app-connection/mongodb";
+
+import {
+ CreateMongoDBCredentialsRotationSchema,
+ MongoDBCredentialsRotationGeneratedCredentialsSchema,
+ MongoDBCredentialsRotationListItemSchema,
+ MongoDBCredentialsRotationSchema
+} from "./mongodb-credentials-rotation-schemas";
+
+export type TMongoDBCredentialsRotation = z.infer;
+
+export type TMongoDBCredentialsRotationInput = z.infer;
+
+export type TMongoDBCredentialsRotationListItem = z.infer;
+
+export type TMongoDBCredentialsRotationWithConnection = TMongoDBCredentialsRotation & {
+ connection: TMongoDBConnection;
+};
+
+export type TMongoDBCredentialsRotationGeneratedCredentials = z.infer<
+ typeof MongoDBCredentialsRotationGeneratedCredentialsSchema
+>;
diff --git a/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-enums.ts b/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-enums.ts
index 661a2399a..470a63849 100644
--- a/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-enums.ts
+++ b/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-enums.ts
@@ -8,7 +8,8 @@ export enum SecretRotation {
AwsIamUserSecret = "aws-iam-user-secret",
LdapPassword = "ldap-password",
OktaClientSecret = "okta-client-secret",
- RedisCredentials = "redis-credentials"
+ RedisCredentials = "redis-credentials",
+ MongoDBCredentials = "mongodb-credentials"
}
export enum SecretRotationStatus {
diff --git a/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-fns.ts b/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-fns.ts
index e4e6a8531..bb774c4be 100644
--- a/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-fns.ts
+++ b/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-fns.ts
@@ -9,6 +9,7 @@ import { AUTH0_CLIENT_SECRET_ROTATION_LIST_OPTION } from "./auth0-client-secret"
import { AWS_IAM_USER_SECRET_ROTATION_LIST_OPTION } from "./aws-iam-user-secret";
import { AZURE_CLIENT_SECRET_ROTATION_LIST_OPTION } from "./azure-client-secret";
import { LDAP_PASSWORD_ROTATION_LIST_OPTION, TLdapPasswordRotation } from "./ldap-password";
+import { MONGODB_CREDENTIALS_ROTATION_LIST_OPTION } from "./mongodb-credentials";
import { MSSQL_CREDENTIALS_ROTATION_LIST_OPTION } from "./mssql-credentials";
import { MYSQL_CREDENTIALS_ROTATION_LIST_OPTION } from "./mysql-credentials";
import { OKTA_CLIENT_SECRET_ROTATION_LIST_OPTION } from "./okta-client-secret";
@@ -37,7 +38,8 @@ const SECRET_ROTATION_LIST_OPTIONS: Record {
diff --git a/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-maps.ts b/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-maps.ts
index 2087fa195..c2b0714ab 100644
--- a/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-maps.ts
+++ b/backend/src/ee/services/secret-rotation-v2/secret-rotation-v2-maps.ts
@@ -11,7 +11,8 @@ export const SECRET_ROTATION_NAME_MAP: Record = {
[SecretRotation.AwsIamUserSecret]: "AWS IAM User Secret",
[SecretRotation.LdapPassword]: "LDAP Password",
[SecretRotation.OktaClientSecret]: "Okta Client Secret",
- [SecretRotation.RedisCredentials]: "Redis Credentials"
+ [SecretRotation.RedisCredentials]: "Redis Credentials",
+ [SecretRotation.MongoDBCredentials]: "MongoDB Credentials"
};
export const SECRET_ROTATION_CONNECTION_MAP: Record = {
@@ -24,5 +25,6 @@ export const SECRET_ROTATION_CONNECTION_MAP: Record = async (
callback
) => {
- // For SQL, since we get existing users, we change both their passwords
- // on issue to invalidate their existing passwords
// For SQL, since we get existing users, we change both their passwords
// on issue to invalidate their existing passwords
const credentialsSet = [
diff --git a/backend/src/lib/api-docs/constants.ts b/backend/src/lib/api-docs/constants.ts
index 3c8972248..99fb1a63a 100644
--- a/backend/src/lib/api-docs/constants.ts
+++ b/backend/src/lib/api-docs/constants.ts
@@ -2860,6 +2860,12 @@ export const SecretRotations = {
},
REDIS_CREDENTIALS: {
permissionScope: "The ACL permission scope to assign to the issued Redis users."
+ },
+ MONGODB_CREDENTIALS: {
+ username1:
+ "The username of the first MongoDB user to rotate passwords for. This user must already exist in your database.",
+ username2:
+ "The username of the second MongoDB user to rotate passwords for. This user must already exist in your database."
}
},
SECRETS_MAPPING: {
@@ -2890,6 +2896,10 @@ export const SecretRotations = {
OKTA_CLIENT_SECRET: {
clientId: "The name of the secret that the client ID will be mapped to.",
clientSecret: "The name of the secret that the rotated client secret will be mapped to."
+ },
+ MONGODB_CREDENTIALS: {
+ username: "The name of the secret that the active username will be mapped to.",
+ password: "The name of the secret that the generated password will be mapped to."
}
}
};
diff --git a/backend/src/server/routes/v1/app-connection-routers/app-connection-router.ts b/backend/src/server/routes/v1/app-connection-routers/app-connection-router.ts
index 48fdc7c38..072abbadb 100644
--- a/backend/src/server/routes/v1/app-connection-routers/app-connection-router.ts
+++ b/backend/src/server/routes/v1/app-connection-routers/app-connection-router.ts
@@ -87,6 +87,10 @@ import {
SanitizedLaravelForgeConnectionSchema
} from "@app/services/app-connection/laravel-forge";
import { LdapConnectionListItemSchema, SanitizedLdapConnectionSchema } from "@app/services/app-connection/ldap";
+import {
+ MongoDBConnectionListItemSchema,
+ SanitizedMongoDBConnectionSchema
+} from "@app/services/app-connection/mongodb";
import { MsSqlConnectionListItemSchema, SanitizedMsSqlConnectionSchema } from "@app/services/app-connection/mssql";
import { MySqlConnectionListItemSchema, SanitizedMySqlConnectionSchema } from "@app/services/app-connection/mysql";
import {
@@ -173,6 +177,7 @@ const SanitizedAppConnectionSchema = z.union([
...SanitizedOktaConnectionSchema.options,
...SanitizedAzureADCSConnectionSchema.options,
...SanitizedRedisConnectionSchema.options,
+ ...SanitizedMongoDBConnectionSchema.options,
...SanitizedLaravelForgeConnectionSchema.options,
...SanitizedChefConnectionSchema.options,
...SanitizedDNSMadeEasyConnectionSchema.options
@@ -219,6 +224,7 @@ const AppConnectionOptionsSchema = z.discriminatedUnion("app", [
OktaConnectionListItemSchema,
AzureADCSConnectionListItemSchema,
RedisConnectionListItemSchema,
+ MongoDBConnectionListItemSchema,
LaravelForgeConnectionListItemSchema,
ChefConnectionListItemSchema,
DNSMadeEasyConnectionListItemSchema
diff --git a/backend/src/server/routes/v1/app-connection-routers/index.ts b/backend/src/server/routes/v1/app-connection-routers/index.ts
index 4eef89128..0738f0407 100644
--- a/backend/src/server/routes/v1/app-connection-routers/index.ts
+++ b/backend/src/server/routes/v1/app-connection-routers/index.ts
@@ -28,6 +28,7 @@ import { registerHerokuConnectionRouter } from "./heroku-connection-router";
import { registerHumanitecConnectionRouter } from "./humanitec-connection-router";
import { registerLaravelForgeConnectionRouter } from "./laravel-forge-connection-router";
import { registerLdapConnectionRouter } from "./ldap-connection-router";
+import { registerMongoDBConnectionRouter } from "./mongodb-connection-router";
import { registerMsSqlConnectionRouter } from "./mssql-connection-router";
import { registerMySqlConnectionRouter } from "./mysql-connection-router";
import { registerNetlifyConnectionRouter } from "./netlify-connection-router";
@@ -90,5 +91,6 @@ export const APP_CONNECTION_REGISTER_ROUTER_MAP: Record {
+ registerAppConnectionEndpoints({
+ app: AppConnection.MongoDB,
+ server,
+ sanitizedResponseSchema: SanitizedMongoDBConnectionSchema,
+ createSchema: CreateMongoDBConnectionSchema,
+ updateSchema: UpdateMongoDBConnectionSchema
+ });
+};
diff --git a/backend/src/services/app-connection/app-connection-enums.ts b/backend/src/services/app-connection/app-connection-enums.ts
index 8e0260c01..e7e2bca76 100644
--- a/backend/src/services/app-connection/app-connection-enums.ts
+++ b/backend/src/services/app-connection/app-connection-enums.ts
@@ -39,6 +39,7 @@ export enum AppConnection {
Netlify = "netlify",
Okta = "okta",
Redis = "redis",
+ MongoDB = "mongodb",
LaravelForge = "laravel-forge",
Chef = "chef",
Northflank = "northflank"
diff --git a/backend/src/services/app-connection/app-connection-fns.ts b/backend/src/services/app-connection/app-connection-fns.ts
index d8af3773b..f28508efb 100644
--- a/backend/src/services/app-connection/app-connection-fns.ts
+++ b/backend/src/services/app-connection/app-connection-fns.ts
@@ -119,6 +119,7 @@ import {
validateLaravelForgeConnectionCredentials
} from "./laravel-forge";
import { getLdapConnectionListItem, LdapConnectionMethod, validateLdapConnectionCredentials } from "./ldap";
+import { getMongoDBConnectionListItem, MongoDBConnectionMethod, validateMongoDBConnectionCredentials } from "./mongodb";
import { getMsSqlConnectionListItem, MsSqlConnectionMethod } from "./mssql";
import { MySqlConnectionMethod } from "./mysql/mysql-connection-enums";
import { getMySqlConnectionListItem } from "./mysql/mysql-connection-fns";
@@ -224,6 +225,7 @@ export const listAppConnectionOptions = (projectType?: ProjectType) => {
getNorthflankConnectionListItem(),
getOktaConnectionListItem(),
getRedisConnectionListItem(),
+ getMongoDBConnectionListItem(),
getChefConnectionListItem()
]
.filter((option) => {
@@ -357,7 +359,8 @@ export const validateAppConnectionCredentials = async (
[AppConnection.Northflank]: validateNorthflankConnectionCredentials as TAppConnectionCredentialsValidator,
[AppConnection.Okta]: validateOktaConnectionCredentials as TAppConnectionCredentialsValidator,
[AppConnection.Chef]: validateChefConnectionCredentials as TAppConnectionCredentialsValidator,
- [AppConnection.Redis]: validateRedisConnectionCredentials as TAppConnectionCredentialsValidator
+ [AppConnection.Redis]: validateRedisConnectionCredentials as TAppConnectionCredentialsValidator,
+ [AppConnection.MongoDB]: validateMongoDBConnectionCredentials as TAppConnectionCredentialsValidator
};
return VALIDATE_APP_CONNECTION_CREDENTIALS_MAP[appConnection.app](appConnection, gatewayService, gatewayV2Service);
@@ -411,6 +414,7 @@ export const getAppConnectionMethodName = (method: TAppConnection["method"]) =>
case OracleDBConnectionMethod.UsernameAndPassword:
case AzureADCSConnectionMethod.UsernamePassword:
case RedisConnectionMethod.UsernameAndPassword:
+ case MongoDBConnectionMethod.UsernameAndPassword:
return "Username & Password";
case WindmillConnectionMethod.AccessToken:
case HCVaultConnectionMethod.AccessToken:
@@ -504,6 +508,7 @@ export const TRANSITION_CONNECTION_CREDENTIALS_TO_PLATFORM: Record<
[AppConnection.Northflank]: platformManagedCredentialsNotSupported,
[AppConnection.Okta]: platformManagedCredentialsNotSupported,
[AppConnection.Redis]: platformManagedCredentialsNotSupported,
+ [AppConnection.MongoDB]: platformManagedCredentialsNotSupported,
[AppConnection.LaravelForge]: platformManagedCredentialsNotSupported,
[AppConnection.Chef]: platformManagedCredentialsNotSupported
};
diff --git a/backend/src/services/app-connection/app-connection-maps.ts b/backend/src/services/app-connection/app-connection-maps.ts
index 27d6a27a8..a41589d12 100644
--- a/backend/src/services/app-connection/app-connection-maps.ts
+++ b/backend/src/services/app-connection/app-connection-maps.ts
@@ -42,6 +42,7 @@ export const APP_CONNECTION_NAME_MAP: Record = {
[AppConnection.Netlify]: "Netlify",
[AppConnection.Okta]: "Okta",
[AppConnection.Redis]: "Redis",
+ [AppConnection.MongoDB]: "MongoDB",
[AppConnection.Chef]: "Chef",
[AppConnection.Northflank]: "Northflank"
};
@@ -88,6 +89,7 @@ export const APP_CONNECTION_PLAN_MAP: Record {
+ return {
+ name: "MongoDB" as const,
+ app: AppConnection.MongoDB as const,
+ methods: Object.values(MongoDBConnectionMethod) as [MongoDBConnectionMethod.UsernameAndPassword],
+ supportsPlatformManagement: false as const
+ };
+};
+
+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 => {
+ const srvRegex = new RE2("^mongodb\\+srv:\\/\\/");
+ const protocolRegex = new RE2("^mongodb:\\/\\/");
+
+ let normalizedHost = credentials.host.trim();
+ 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 = !credentials.port || isSrvFromHost;
+ const uri = isSrv ? `mongodb+srv://${hostIp}` : `mongodb://${hostIp}:${credentials.port}`;
+
+ const authCredentials = options?.authCredentials ?? {
+ username: credentials.username,
+ password: credentials.password
+ };
+
+ 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;
+ }
+ }
+
+ const client = new MongoClient(uri, clientOptions);
+
+ if (options?.validateConnection) {
+ await client
+ .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();
+
+ return config.credentials;
+ } catch (err) {
+ if (err instanceof BadRequestError) {
+ throw err;
+ }
+ throw new BadRequestError({
+ message: `Unable to validate connection: ${(err as Error)?.message || "verify credentials"}`
+ });
+ } finally {
+ if (client) await client.close();
+ }
+};
diff --git a/backend/src/services/app-connection/mongodb/mongodb-connection-schemas.ts b/backend/src/services/app-connection/mongodb/mongodb-connection-schemas.ts
new file mode 100644
index 000000000..c934a0741
--- /dev/null
+++ b/backend/src/services/app-connection/mongodb/mongodb-connection-schemas.ts
@@ -0,0 +1,89 @@
+import z from "zod";
+
+import { AppConnections } from "@app/lib/api-docs";
+import {
+ BaseAppConnectionSchema,
+ GenericCreateAppConnectionFieldsSchema,
+ GenericUpdateAppConnectionFieldsSchema
+} from "@app/services/app-connection/app-connection-schemas";
+
+import { AppConnection } from "../app-connection-enums";
+import { MongoDBConnectionMethod } from "./mongodb-connection-enums";
+
+export const BaseMongoDBUsernameAndPasswordConnectionSchema = z.object({
+ host: z.string().toLowerCase().min(1),
+ port: z.coerce.number(),
+ username: z.string().min(1),
+ password: z.string().min(1),
+ database: z.string().min(1).trim(),
+
+ tlsRejectUnauthorized: z.boolean(),
+ tlsEnabled: z.boolean(),
+ tlsCertificate: z
+ .string()
+ .trim()
+ .transform((value) => value || undefined)
+ .optional()
+});
+
+export const MongoDBConnectionAccessTokenCredentialsSchema = BaseMongoDBUsernameAndPasswordConnectionSchema;
+
+const BaseMongoDBConnectionSchema = BaseAppConnectionSchema.extend({ app: z.literal(AppConnection.MongoDB) });
+
+export const MongoDBConnectionSchema = BaseMongoDBConnectionSchema.extend({
+ method: z.literal(MongoDBConnectionMethod.UsernameAndPassword),
+ credentials: MongoDBConnectionAccessTokenCredentialsSchema
+});
+
+export const SanitizedMongoDBConnectionSchema = z.discriminatedUnion("method", [
+ BaseMongoDBConnectionSchema.extend({
+ method: z.literal(MongoDBConnectionMethod.UsernameAndPassword),
+ credentials: MongoDBConnectionAccessTokenCredentialsSchema.pick({
+ host: true,
+ port: true,
+ username: true,
+ database: true,
+ tlsEnabled: true,
+ tlsRejectUnauthorized: true,
+ tlsCertificate: true
+ })
+ })
+]);
+
+export const ValidateMongoDBConnectionCredentialsSchema = z.discriminatedUnion("method", [
+ z.object({
+ method: z
+ .literal(MongoDBConnectionMethod.UsernameAndPassword)
+ .describe(AppConnections.CREATE(AppConnection.MongoDB).method),
+ credentials: MongoDBConnectionAccessTokenCredentialsSchema.describe(
+ AppConnections.CREATE(AppConnection.MongoDB).credentials
+ )
+ })
+]);
+
+export const CreateMongoDBConnectionSchema = ValidateMongoDBConnectionCredentialsSchema.and(
+ GenericCreateAppConnectionFieldsSchema(AppConnection.MongoDB, {
+ supportsPlatformManagedCredentials: false,
+ supportsGateways: false
+ })
+);
+
+export const UpdateMongoDBConnectionSchema = z
+ .object({
+ credentials: MongoDBConnectionAccessTokenCredentialsSchema.optional().describe(
+ AppConnections.UPDATE(AppConnection.MongoDB).credentials
+ )
+ })
+ .and(
+ GenericUpdateAppConnectionFieldsSchema(AppConnection.MongoDB, {
+ supportsPlatformManagedCredentials: false,
+ supportsGateways: false
+ })
+ );
+
+export const MongoDBConnectionListItemSchema = z.object({
+ name: z.literal("MongoDB"),
+ app: z.literal(AppConnection.MongoDB),
+ methods: z.nativeEnum(MongoDBConnectionMethod).array(),
+ supportsPlatformManagement: z.literal(false)
+});
diff --git a/backend/src/services/app-connection/mongodb/mongodb-connection-types.ts b/backend/src/services/app-connection/mongodb/mongodb-connection-types.ts
new file mode 100644
index 000000000..52212545a
--- /dev/null
+++ b/backend/src/services/app-connection/mongodb/mongodb-connection-types.ts
@@ -0,0 +1,22 @@
+import z from "zod";
+
+import { DiscriminativePick } from "@app/lib/types";
+
+import { AppConnection } from "../app-connection-enums";
+import {
+ CreateMongoDBConnectionSchema,
+ MongoDBConnectionSchema,
+ ValidateMongoDBConnectionCredentialsSchema
+} from "./mongodb-connection-schemas";
+
+export type TMongoDBConnection = z.infer;
+
+export type TMongoDBConnectionInput = z.infer & {
+ app: AppConnection.MongoDB;
+};
+
+export type TValidateMongoDBConnectionCredentialsSchema = typeof ValidateMongoDBConnectionCredentialsSchema;
+
+export type TMongoDBConnectionConfig = DiscriminativePick & {
+ orgId: string;
+};
diff --git a/docs/docs.json b/docs/docs.json
index 5a3d965fd..39ebf31a9 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -130,6 +130,7 @@
"integrations/app-connections/laravel-forge",
"integrations/app-connections/ldap",
"integrations/app-connections/mssql",
+ "integrations/app-connections/mongodb",
"integrations/app-connections/mysql",
"integrations/app-connections/netlify",
"integrations/app-connections/northflank",
@@ -444,6 +445,7 @@
"documentation/platform/secret-rotation/aws-iam-user-secret",
"documentation/platform/secret-rotation/azure-client-secret",
"documentation/platform/secret-rotation/ldap-password",
+ "documentation/platform/secret-rotation/mongodb-credentials",
"documentation/platform/secret-rotation/mssql-credentials",
"documentation/platform/secret-rotation/mysql-credentials",
"documentation/platform/secret-rotation/okta-client-secret",
@@ -1393,6 +1395,18 @@
"api-reference/endpoints/app-connections/mssql/delete"
]
},
+ {
+ "group": "MongoDB",
+ "pages": [
+ "api-reference/endpoints/app-connections/mongodb/list",
+ "api-reference/endpoints/app-connections/mongodb/available",
+ "api-reference/endpoints/app-connections/mongodb/get-by-id",
+ "api-reference/endpoints/app-connections/mongodb/get-by-name",
+ "api-reference/endpoints/app-connections/mongodb/create",
+ "api-reference/endpoints/app-connections/mongodb/update",
+ "api-reference/endpoints/app-connections/mongodb/delete"
+ ]
+ },
{
"group": "MySQL",
"pages": [
@@ -1929,6 +1943,19 @@
"api-reference/endpoints/secret-rotations/mssql-credentials/update"
]
},
+ {
+ "group": "MongoDB Credentials",
+ "pages": [
+ "api-reference/endpoints/secret-rotations/mongodb-credentials/create",
+ "api-reference/endpoints/secret-rotations/mongodb-credentials/delete",
+ "api-reference/endpoints/secret-rotations/mongodb-credentials/get-by-id",
+ "api-reference/endpoints/secret-rotations/mongodb-credentials/get-by-name",
+ "api-reference/endpoints/secret-rotations/mongodb-credentials/get-generated-credentials-by-id",
+ "api-reference/endpoints/secret-rotations/mongodb-credentials/list",
+ "api-reference/endpoints/secret-rotations/mongodb-credentials/rotate-secrets",
+ "api-reference/endpoints/secret-rotations/mongodb-credentials/update"
+ ]
+ },
{
"group": "MySQL Credentials",
"pages": [
diff --git a/docs/documentation/platform/secret-rotation/mongodb-credentials.mdx b/docs/documentation/platform/secret-rotation/mongodb-credentials.mdx
new file mode 100644
index 000000000..5b3808126
--- /dev/null
+++ b/docs/documentation/platform/secret-rotation/mongodb-credentials.mdx
@@ -0,0 +1,177 @@
+---
+title: "MongoDB Credentials Rotation"
+description: "Learn how to automatically rotate MongoDB credentials."
+---
+
+## Prerequisites
+
+1. Create a [MongoDB Connection](/integrations/app-connections/mongodb) with the required **Secret Rotation** permissions
+2. Create two designated database users for Infisical to rotate the credentials for. Be sure to grant each user login permissions for the desired database with the necessary privileges their use case will require.
+
+ An example creation statement might look like:
+ ```bash
+ // Switch to the target database
+ use my_database
+
+ // Create first user
+ db.createUser({
+ user: "infisical_user_1",
+ pwd: "temporary_password",
+ roles: []
+ })
+
+ // Create second user
+ db.createUser({
+ user: "infisical_user_2",
+ pwd: "temporary_password",
+ roles: []
+ })
+
+ // Grant necessary permissions to both users
+ db.grantRolesToUser("infisical_user_1", [
+ { role: "readWrite", db: "my_database" }
+ ])
+
+ db.grantRolesToUser("infisical_user_2", [
+ { role: "readWrite", db: "my_database" }
+ ])
+ ```
+
+
+ To learn more about MongoDB's permission system, please visit their [documentation](https://www.mongodb.com/docs/manual/core/security-built-in-roles/).
+
+
+3. Ensure your network security policies allow incoming requests from Infisical to this rotation provider, if network restrictions apply.
+
+## Create a MongoDB Credentials Rotation in Infisical
+
+
+
+ 1. Navigate to your Secret Manager Project's Dashboard and select **Add Secret Rotation** from the actions dropdown.
+ 
+
+ 2. Select the **MongoDB Credentials** option.
+ 
+
+ 3. Select the **MongoDB Connection** to use and configure the rotation behavior. Then click **Next**.
+ 
+
+ - **MongoDB Connection** - the connection that will perform the rotation of the configured database user credentials.
+ - **Rotation Interval** - the interval, in days, that once elapsed will trigger a rotation.
+ - **Rotate At** - the local time of day when rotation should occur once the interval has elapsed.
+ - **Auto-Rotation Enabled** - whether secrets should automatically be rotated once the rotation interval has elapsed. Disable this option to manually rotate secrets or pause secret rotation.
+
+ 4. Input the usernames of the database users created above that will be used for rotation. Then click **Next**.
+ 
+
+ - **Database Username 1** - the username of the first user that will be used for rotation.
+ - **Database Username 2** - the username of the second user that will be used for rotation.
+
+ 5. Specify the secret names that the active credentials should be mapped to. Then click **Next**.
+ 
+
+ - **Username** - the name of the secret that the active username will be mapped to.
+ - **Password** - the name of the secret that the active password will be mapped to.
+
+ 6. Give your rotation a name and description (optional). Then click **Next**.
+ 
+
+ - **Name** - the name of the secret rotation configuration. Must be slug-friendly.
+ - **Description** (optional) - a description of this rotation configuration.
+
+ 7. Review your configuration, then click **Create Secret Rotation**.
+ 
+
+ 8. Your **MongoDB Credentials** are now available for use via the mapped secrets.
+ 
+
+
+ To create a MongoDB Credentials Rotation, make an API request to the [Create MongoDB
+ Credentials Rotation](/api-reference/endpoints/secret-rotations/mongodb-credentials/create) API endpoint.
+
+ ### Sample request
+
+ ```bash Request
+ curl --request POST \
+ --url https://us.infisical.com/api/v2/secret-rotations/mongodb-credentials \
+ --header 'Content-Type: application/json' \
+ --data '{
+ "name": "my-mongodb-rotation",
+ "projectId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
+ "description": "my database credentials rotation",
+ "connectionId": "11c76f38-cd13-4137-b1a3-ecd6a429952c",
+ "environment": "dev",
+ "secretPath": "/",
+ "isAutoRotationEnabled": true,
+ "rotationInterval": 30,
+ "rotateAtUtc": {
+ "hours": 0,
+ "minutes": 0
+ },
+ "parameters": {
+ "username1": "infisical_user_1",
+ "username2": "infisical_user_2"
+ },
+ "secretsMapping": {
+ "username": "MONGODB_DB_USERNAME",
+ "password": "MONGODB_DB_PASSWORD"
+ }
+ }'
+ ```
+
+ ### Sample response
+
+ ```bash Response
+ {
+ "secretRotation": {
+ "id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
+ "name": "my-mongodb-rotation",
+ "description": "my database credentials rotation",
+ "secretsMapping": {
+ "username": "MONGODB_DB_USERNAME",
+ "password": "MONGODB_DB_PASSWORD"
+ },
+ "isAutoRotationEnabled": true,
+ "activeIndex": 0,
+ "folderId": "b3257e1f-8d32-4e86-8bfd-b1f1bc1bf2c3",
+ "connectionId": "11c76f38-cd13-4137-b1a3-ecd6a429952c",
+ "createdAt": "2023-11-07T05:31:56Z",
+ "updatedAt": "2023-11-07T05:31:56Z",
+ "rotationInterval": 30,
+ "rotationStatus": "success",
+ "lastRotationAttemptedAt": "2023-11-07T05:31:56Z",
+ "lastRotatedAt": "2023-11-07T05:31:56Z",
+ "lastRotationJobId": null,
+ "nextRotationAt": "2023-11-07T05:31:56Z",
+ "isLastRotationManual": true,
+ "connection": {
+ "app": "mongodb",
+ "name": "my-mongodb-connection",
+ "id": "11c76f38-cd13-4137-b1a3-ecd6a429952c"
+ },
+ "environment": {
+ "slug": "dev",
+ "name": "Development",
+ "id": "170a40f1-1b48-4cc7-addf-e563aa9fbe37"
+ },
+ "projectId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
+ "folder": {
+ "id": "b3257e1f-8d32-4e86-8bfd-b1f1bc1bf2c3",
+ "path": "/"
+ },
+ "rotateAtUtc": {
+ "hours": 0,
+ "minutes": 0
+ },
+ "lastRotationMessage": null,
+ "type": "mongodb-credentials",
+ "parameters": {
+ "username1": "infisical_user_1",
+ "username2": "infisical_user_2"
+ }
+ }
+ }
+ ```
+
+
+
diff --git a/docs/images/app-connections/general/add-connection.png b/docs/images/app-connections/general/add-connection.png
index b6dce69ac..5ebadae09 100644
Binary files a/docs/images/app-connections/general/add-connection.png and b/docs/images/app-connections/general/add-connection.png differ
diff --git a/docs/images/app-connections/mongodb/mongodb-app-connection-form.png b/docs/images/app-connections/mongodb/mongodb-app-connection-form.png
new file mode 100644
index 000000000..f57ca1aa5
Binary files /dev/null and b/docs/images/app-connections/mongodb/mongodb-app-connection-form.png differ
diff --git a/docs/images/app-connections/mongodb/mongodb-app-connection-generated.png b/docs/images/app-connections/mongodb/mongodb-app-connection-generated.png
new file mode 100644
index 000000000..eed7d01af
Binary files /dev/null and b/docs/images/app-connections/mongodb/mongodb-app-connection-generated.png differ
diff --git a/docs/images/app-connections/mongodb/mongodb-app-connection-option.png b/docs/images/app-connections/mongodb/mongodb-app-connection-option.png
new file mode 100644
index 000000000..0999decb3
Binary files /dev/null and b/docs/images/app-connections/mongodb/mongodb-app-connection-option.png differ
diff --git a/docs/images/secret-rotations-v2/generic/add-secret-rotation.png b/docs/images/secret-rotations-v2/generic/add-secret-rotation.png
index 86b84001b..4b1b626ae 100644
Binary files a/docs/images/secret-rotations-v2/generic/add-secret-rotation.png and b/docs/images/secret-rotations-v2/generic/add-secret-rotation.png differ
diff --git a/docs/images/secret-rotations-v2/mongodb-credentials/mongodb-credentials-configuration.png b/docs/images/secret-rotations-v2/mongodb-credentials/mongodb-credentials-configuration.png
new file mode 100644
index 000000000..8d0db7d03
Binary files /dev/null and b/docs/images/secret-rotations-v2/mongodb-credentials/mongodb-credentials-configuration.png differ
diff --git a/docs/images/secret-rotations-v2/mongodb-credentials/mongodb-credentials-confirm.png b/docs/images/secret-rotations-v2/mongodb-credentials/mongodb-credentials-confirm.png
new file mode 100644
index 000000000..d568e2d5d
Binary files /dev/null and b/docs/images/secret-rotations-v2/mongodb-credentials/mongodb-credentials-confirm.png differ
diff --git a/docs/images/secret-rotations-v2/mongodb-credentials/mongodb-credentials-created.png b/docs/images/secret-rotations-v2/mongodb-credentials/mongodb-credentials-created.png
new file mode 100644
index 000000000..e18ef7b0f
Binary files /dev/null and b/docs/images/secret-rotations-v2/mongodb-credentials/mongodb-credentials-created.png differ
diff --git a/docs/images/secret-rotations-v2/mongodb-credentials/mongodb-credentials-details.png b/docs/images/secret-rotations-v2/mongodb-credentials/mongodb-credentials-details.png
new file mode 100644
index 000000000..949ff40be
Binary files /dev/null and b/docs/images/secret-rotations-v2/mongodb-credentials/mongodb-credentials-details.png differ
diff --git a/docs/images/secret-rotations-v2/mongodb-credentials/mongodb-credentials-parameters.png b/docs/images/secret-rotations-v2/mongodb-credentials/mongodb-credentials-parameters.png
new file mode 100644
index 000000000..14723c0a6
Binary files /dev/null and b/docs/images/secret-rotations-v2/mongodb-credentials/mongodb-credentials-parameters.png differ
diff --git a/docs/images/secret-rotations-v2/mongodb-credentials/mongodb-credentials-secrets-mapping.png b/docs/images/secret-rotations-v2/mongodb-credentials/mongodb-credentials-secrets-mapping.png
new file mode 100644
index 000000000..aceb38a8d
Binary files /dev/null and b/docs/images/secret-rotations-v2/mongodb-credentials/mongodb-credentials-secrets-mapping.png differ
diff --git a/docs/images/secret-rotations-v2/mongodb-credentials/select-mongodb-credentials-option.png b/docs/images/secret-rotations-v2/mongodb-credentials/select-mongodb-credentials-option.png
new file mode 100644
index 000000000..4de950114
Binary files /dev/null and b/docs/images/secret-rotations-v2/mongodb-credentials/select-mongodb-credentials-option.png differ
diff --git a/docs/integrations/app-connections/mongodb.mdx b/docs/integrations/app-connections/mongodb.mdx
new file mode 100644
index 000000000..baaff7127
--- /dev/null
+++ b/docs/integrations/app-connections/mongodb.mdx
@@ -0,0 +1,141 @@
+---
+title: "MongoDB Connection"
+description: "Learn how to configure a MongoDB Connection for Infisical."
+---
+
+Infisical supports the use of Username & Password authentication to connect with MongoDB databases.
+
+## Configure a MongoDB user for Infisical
+
+
+
+ Infisical recommends creating a designated user in your MongoDB database for your connection.
+
+ ```bash
+ use [TARGET-DATABASE]
+ db.createUser({
+ user: "infisical_manager",
+ pwd: "[ENTER-YOUR-USER-PASSWORD]",
+ roles: []
+ })
+ ```
+
+
+
+ Depending on how you intend to use your MongoDB connection, you'll need to grant one or more of the following permissions.
+
+
+ To learn more about MongoDB's permission system, please visit their [documentation](https://www.mongodb.com/docs/manual/core/security-built-in-roles/).
+
+
+
+
+ For Secret Rotations, your Infisical user will require the ability to create, update, and delete users in the target database:
+
+ ```bash
+ use [TARGET-DATABASE]
+ db.grantRolesToUser("infisical_manager", [
+ { role: "userAdmin", db: "[TARGET-DATABASE]" }
+ ])
+ ```
+
+
+ The `userAdmin` role allows managing users (create, update passwords, delete) within the specified database.
+
+
+
+
+
+
+
+## Create MongoDB Connection in Infisical
+
+
+
+
+
+ In your Infisical dashboard, navigate to the **App Connections** page in the desired project.
+
+ 
+
+
+ Click the **+ Add Connection** button and select the **MongoDB Connection** option from the available integrations.
+
+ 
+
+
+ Complete the MongoDB Connection form by entering:
+ - A descriptive name for the connection
+ - An optional description for future reference
+ - The MongoDB host URL for your database
+ - The MongoDB port for your database
+ - The MongoDB username for your database
+ - The MongoDB password for your database
+ - The MongoDB database name to connect to
+
+ You can optionally configure SSL/TLS for your MongoDB connection in the **SSL** section.
+
+ 
+
+
+ After clicking Create, your **MongoDB Connection** is established and ready to use with your Infisical project.
+
+ 
+
+
+
+
+ To create a MongoDB Connection, make an API request to the [Create MongoDB Connection](/api-reference/endpoints/app-connections/mongodb/create) API endpoint.
+
+ ### Sample request
+
+ ```bash Request
+ curl --request POST \
+ --url https://app.infisical.com/api/v1/app-connections/mongodb \
+ --header 'Content-Type: application/json' \
+ --data '{
+ "name": "my-mongodb-connection",
+ "method": "username-and-password",
+ "projectId": "7ffbb072-2575-495a-b5b0-127f88caef78",
+ "credentials": {
+ "host": "[MONGODB HOST]",
+ "port": 27017,
+ "username": "[MONGODB USERNAME]",
+ "password": "[MONGODB PASSWORD]",
+ "database": "[MONGODB DATABASE]"
+ }
+ }'
+ ```
+
+ ### Sample response
+
+ ```bash Response
+ {
+ "appConnection": {
+ "id": "e5d18aca-86f7-4026-a95e-efb8aeb0d8e6",
+ "name": "my-mongodb-connection",
+ "projectId": "7ffbb072-2575-495a-b5b0-127f88caef78",
+ "description": null,
+ "version": 1,
+ "orgId": "6f03caa1-a5de-43ce-b127-95a145d3464c",
+ "createdAt": "2025-04-23T19:46:34.831Z",
+ "updatedAt": "2025-04-23T19:46:34.831Z",
+ "isPlatformManagedCredentials": false,
+ "credentialsHash": "7c2d371dec195f82a6a0d5b41c970a229cfcaf88e894a5b6395e2dbd0280661f",
+ "app": "mongodb",
+ "method": "username-and-password",
+ "credentials": {
+ "host": "[MONGODB HOST]",
+ "port": 27017,
+ "username": "[MONGODB USERNAME]",
+ "database": "[MONGODB DATABASE]",
+ "sslEnabled": false,
+ "sslRejectUnauthorized": false,
+ "sslCertificate": ""
+ }
+ }
+ }
+ ```
+
+
+
diff --git a/docs/snippets/AppConnectionsBrowser.jsx b/docs/snippets/AppConnectionsBrowser.jsx
index d7001d6eb..c79ef366b 100644
--- a/docs/snippets/AppConnectionsBrowser.jsx
+++ b/docs/snippets/AppConnectionsBrowser.jsx
@@ -362,6 +362,13 @@ export const AppConnectionsBrowser = () => {
"Learn how to connect your Northflank projects to pull secrets from Infisical.",
category: "Hosting",
},
+ {
+ name: "MongoDB",
+ slug: "mongodb",
+ path: "/integrations/app-connections/mongodb",
+ description: "Learn how to connect your MongoDB to pull secrets from Infisical.",
+ category: "Databases"
+ }
].sort(function (a, b) {
return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
});
diff --git a/docs/snippets/RotationsBrowser.jsx b/docs/snippets/RotationsBrowser.jsx
index 3dbede698..0666237b3 100644
--- a/docs/snippets/RotationsBrowser.jsx
+++ b/docs/snippets/RotationsBrowser.jsx
@@ -16,7 +16,8 @@ export const RotationsBrowser = () => {
{"name": "PostgreSQL", "slug": "postgres-credentials", "path": "/documentation/platform/secret-rotation/postgres-credentials", "description": "Learn how to automatically rotate PostgreSQL database credentials.", "category": "Databases"},
{"name": "Redis", "slug": "redis-credentials", "path": "/documentation/platform/secret-rotation/redis-credentials", "description": "Learn how to automatically rotate Redis database credentials.", "category": "Databases"},
{"name": "Microsoft SQL Server", "slug": "mssql-credentials", "path": "/documentation/platform/secret-rotation/mssql-credentials", "description": "Learn how to automatically rotate Microsoft SQL Server credentials.", "category": "Databases"},
- {"name": "Oracle Database", "slug": "oracledb-credentials", "path": "/documentation/platform/secret-rotation/oracledb-credentials", "description": "Learn how to automatically rotate Oracle Database credentials.", "category": "Databases"}
+ {"name": "Oracle Database", "slug": "oracledb-credentials", "path": "/documentation/platform/secret-rotation/oracledb-credentials", "description": "Learn how to automatically rotate Oracle Database credentials.", "category": "Databases"},
+ {"name": "MongoDB Credentials", "slug": "mongodb-credentials", "path": "/documentation/platform/secret-rotation/mongodb-credentials", "description": "Learn how to automatically rotate MongoDB credentials.", "category": "Databases"}
].sort(function(a, b) {
return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
});
diff --git a/frontend/src/components/secret-rotations-v2/ViewSecretRotationV2GeneratedCredentials/ViewSecretRotationV2GeneratedCredentials.tsx b/frontend/src/components/secret-rotations-v2/ViewSecretRotationV2GeneratedCredentials/ViewSecretRotationV2GeneratedCredentials.tsx
index e8553f6d9..01904b852 100644
--- a/frontend/src/components/secret-rotations-v2/ViewSecretRotationV2GeneratedCredentials/ViewSecretRotationV2GeneratedCredentials.tsx
+++ b/frontend/src/components/secret-rotations-v2/ViewSecretRotationV2GeneratedCredentials/ViewSecretRotationV2GeneratedCredentials.tsx
@@ -67,6 +67,7 @@ const Content = ({ secretRotation }: ContentProps) => {
case SecretRotation.MySqlCredentials:
case SecretRotation.MsSqlCredentials:
case SecretRotation.OracleDBCredentials:
+ case SecretRotation.MongoDBCredentials:
Component = (
= {
[SecretRotation.LdapPassword]: LdapPasswordRotationParametersFields,
[SecretRotation.AwsIamUserSecret]: AwsIamUserSecretRotationParametersFields,
[SecretRotation.OktaClientSecret]: OktaClientSecretRotationParametersFields,
- [SecretRotation.RedisCredentials]: RedisCredentialsRotationParametersFields
+ [SecretRotation.RedisCredentials]: RedisCredentialsRotationParametersFields,
+ [SecretRotation.MongoDBCredentials]: SqlCredentialsRotationParametersFields
};
export const SecretRotationV2ParametersFields = () => {
diff --git a/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ReviewFields/SecretRotationReviewFields.tsx b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ReviewFields/SecretRotationReviewFields.tsx
index e484a64b1..d48c69cca 100644
--- a/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ReviewFields/SecretRotationReviewFields.tsx
+++ b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2ReviewFields/SecretRotationReviewFields.tsx
@@ -24,7 +24,8 @@ const COMPONENT_MAP: Record = {
[SecretRotation.LdapPassword]: LdapPasswordRotationReviewFields,
[SecretRotation.AwsIamUserSecret]: AwsIamUserSecretRotationReviewFields,
[SecretRotation.OktaClientSecret]: OktaClientSecretRotationReviewFields,
- [SecretRotation.RedisCredentials]: RedisCredentialsRotationReviewFields
+ [SecretRotation.RedisCredentials]: RedisCredentialsRotationReviewFields,
+ [SecretRotation.MongoDBCredentials]: SqlCredentialsRotationReviewFields
};
export const SecretRotationV2ReviewFields = () => {
diff --git a/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2SecretsMappingFields/SecretRotationV2SecretsMappingFields.tsx b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2SecretsMappingFields/SecretRotationV2SecretsMappingFields.tsx
index e05fd31f5..a211abff8 100644
--- a/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2SecretsMappingFields/SecretRotationV2SecretsMappingFields.tsx
+++ b/frontend/src/components/secret-rotations-v2/forms/SecretRotationV2SecretsMappingFields/SecretRotationV2SecretsMappingFields.tsx
@@ -21,7 +21,8 @@ const COMPONENT_MAP: Record = {
[SecretRotation.LdapPassword]: LdapPasswordRotationSecretsMappingFields,
[SecretRotation.AwsIamUserSecret]: AwsIamUserSecretRotationSecretsMappingFields,
[SecretRotation.OktaClientSecret]: OktaClientSecretRotationSecretsMappingFields,
- [SecretRotation.RedisCredentials]: RedisCredentialsRotationSecretsMappingFields
+ [SecretRotation.RedisCredentials]: RedisCredentialsRotationSecretsMappingFields,
+ [SecretRotation.MongoDBCredentials]: SqlCredentialsRotationSecretsMappingFields
};
export const SecretRotationV2SecretsMappingFields = () => {
diff --git a/frontend/src/components/secret-rotations-v2/forms/schemas/index.ts b/frontend/src/components/secret-rotations-v2/forms/schemas/index.ts
index 199036a8f..3ea0f75ed 100644
--- a/frontend/src/components/secret-rotations-v2/forms/schemas/index.ts
+++ b/frontend/src/components/secret-rotations-v2/forms/schemas/index.ts
@@ -4,6 +4,7 @@ import { Auth0ClientSecretRotationSchema } from "@app/components/secret-rotation
import { AwsIamUserSecretRotationSchema } from "@app/components/secret-rotations-v2/forms/schemas/aws-iam-user-secret-rotation-schema";
import { AzureClientSecretRotationSchema } from "@app/components/secret-rotations-v2/forms/schemas/azure-client-secret-rotation-schema";
import { LdapPasswordRotationSchema } from "@app/components/secret-rotations-v2/forms/schemas/ldap-password-rotation-schema";
+import { MongoDBCredentialsRotationSchema } from "@app/components/secret-rotations-v2/forms/schemas/mongodb-credentials-rotation-schema";
import { MsSqlCredentialsRotationSchema } from "@app/components/secret-rotations-v2/forms/schemas/mssql-credentials-rotation-schema";
import { MySqlCredentialsRotationSchema } from "@app/components/secret-rotations-v2/forms/schemas/mysql-credentials-rotation-schema";
import { PostgresCredentialsRotationSchema } from "@app/components/secret-rotations-v2/forms/schemas/postgres-credentials-rotation-schema";
@@ -27,7 +28,8 @@ export const SecretRotationV2FormSchema = (isUpdate: boolean) =>
LdapPasswordRotationSchema,
AwsIamUserSecretRotationSchema,
OktaClientSecretRotationSchema,
- RedisCredentialsRotationSchema
+ RedisCredentialsRotationSchema,
+ MongoDBCredentialsRotationSchema
]),
z.object({ id: z.string().optional() })
)
diff --git a/frontend/src/components/secret-rotations-v2/forms/schemas/mongodb-credentials-rotation-schema.ts b/frontend/src/components/secret-rotations-v2/forms/schemas/mongodb-credentials-rotation-schema.ts
new file mode 100644
index 000000000..24c22cfed
--- /dev/null
+++ b/frontend/src/components/secret-rotations-v2/forms/schemas/mongodb-credentials-rotation-schema.ts
@@ -0,0 +1,12 @@
+import { z } from "zod";
+
+import { BaseSecretRotationSchema } from "@app/components/secret-rotations-v2/forms/schemas/base-secret-rotation-v2-schema";
+import { SqlCredentialsRotationSchema } from "@app/components/secret-rotations-v2/forms/schemas/shared";
+import { SecretRotation } from "@app/hooks/api/secretRotationsV2";
+
+export const MongoDBCredentialsRotationSchema = z
+ .object({
+ type: z.literal(SecretRotation.MongoDBCredentials)
+ })
+ .merge(SqlCredentialsRotationSchema)
+ .merge(BaseSecretRotationSchema);
diff --git a/frontend/src/helpers/appConnections.ts b/frontend/src/helpers/appConnections.ts
index 7f02bd64e..e8857e022 100644
--- a/frontend/src/helpers/appConnections.ts
+++ b/frontend/src/helpers/appConnections.ts
@@ -31,6 +31,7 @@ import {
HCVaultConnectionMethod,
HumanitecConnectionMethod,
LdapConnectionMethod,
+ MongoDBConnectionMethod,
MsSqlConnectionMethod,
MySqlConnectionMethod,
OktaConnectionMethod,
@@ -129,6 +130,7 @@ export const APP_CONNECTION_MAP: Record<
[AppConnection.Northflank]: { name: "Northflank", image: "Northflank.png" },
[AppConnection.Okta]: { name: "Okta", image: "Okta.png" },
[AppConnection.Redis]: { name: "Redis", image: "Redis.png" },
+ [AppConnection.MongoDB]: { name: "MongoDB", image: "MongoDB.png" },
[AppConnection.LaravelForge]: {
name: "Laravel Forge",
image: "Laravel Forge.png",
@@ -181,6 +183,7 @@ export const getAppConnectionMethodDetails = (method: TAppConnection["method"])
case OracleDBConnectionMethod.UsernameAndPassword:
case AzureADCSConnectionMethod.UsernamePassword:
case RedisConnectionMethod.UsernameAndPassword:
+ case MongoDBConnectionMethod.UsernameAndPassword:
return { name: "Username & Password", icon: faLock };
case HCVaultConnectionMethod.AccessToken:
case TeamCityConnectionMethod.AccessToken:
diff --git a/frontend/src/helpers/secretRotationsV2.ts b/frontend/src/helpers/secretRotationsV2.ts
index d3bb83f19..187177c68 100644
--- a/frontend/src/helpers/secretRotationsV2.ts
+++ b/frontend/src/helpers/secretRotationsV2.ts
@@ -54,6 +54,11 @@ export const SECRET_ROTATION_MAP: Record<
name: "Redis Credentials",
image: "Redis.png",
size: 50
+ },
+ [SecretRotation.MongoDBCredentials]: {
+ name: "MongoDB Credentials",
+ image: "MongoDB.png",
+ size: 50
}
};
@@ -67,7 +72,8 @@ export const SECRET_ROTATION_CONNECTION_MAP: Record = {
[SecretRotation.LdapPassword]: false,
[SecretRotation.AwsIamUserSecret]: true,
[SecretRotation.OktaClientSecret]: true,
- [SecretRotation.RedisCredentials]: true
+ [SecretRotation.RedisCredentials]: true,
+ [SecretRotation.MongoDBCredentials]: true
};
export const getRotateAtLocal = ({ hours, minutes }: TSecretRotationV2["rotateAtUtc"]) => {
diff --git a/frontend/src/hooks/api/appConnections/enums.ts b/frontend/src/hooks/api/appConnections/enums.ts
index 9535e8348..dbe6c7367 100644
--- a/frontend/src/hooks/api/appConnections/enums.ts
+++ b/frontend/src/hooks/api/appConnections/enums.ts
@@ -40,6 +40,7 @@ export enum AppConnection {
Northflank = "northflank",
Okta = "okta",
Redis = "redis",
+ MongoDB = "mongodb",
LaravelForge = "laravel-forge",
Chef = "chef"
}
diff --git a/frontend/src/hooks/api/appConnections/types/app-options.ts b/frontend/src/hooks/api/appConnections/types/app-options.ts
index d1c991f34..9c0f78a0c 100644
--- a/frontend/src/hooks/api/appConnections/types/app-options.ts
+++ b/frontend/src/hooks/api/appConnections/types/app-options.ts
@@ -184,6 +184,10 @@ export type TRedisConnectionOption = TAppConnectionOptionBase & {
app: AppConnection.Redis;
};
+export type TMongoDBConnectionOption = TAppConnectionOptionBase & {
+ app: AppConnection.MongoDB;
+};
+
export type TDNSMadeEasyConnectionOption = TAppConnectionOptionBase & {
app: AppConnection.DNSMadeEasy;
};
@@ -229,6 +233,8 @@ export type TAppConnectionOption =
| TOktaConnectionOption
| TAzureAdCsConnectionOption
| TLaravelForgeConnectionOption
+ | TRedisConnectionOption
+ | TMongoDBConnectionOption
| TChefConnectionOption
| TDNSMadeEasyConnectionOption;
@@ -274,6 +280,7 @@ export type TAppConnectionOptionMap = {
[AppConnection.Okta]: TOktaConnectionOption;
[AppConnection.AzureADCS]: TAzureAdCsConnectionOption;
[AppConnection.Redis]: TRedisConnectionOption;
+ [AppConnection.MongoDB]: TMongoDBConnectionOption;
[AppConnection.LaravelForge]: TLaravelForgeConnectionOption;
[AppConnection.Chef]: TChefConnectionOption;
};
diff --git a/frontend/src/hooks/api/appConnections/types/index.ts b/frontend/src/hooks/api/appConnections/types/index.ts
index a272b48cd..c78d2aed2 100644
--- a/frontend/src/hooks/api/appConnections/types/index.ts
+++ b/frontend/src/hooks/api/appConnections/types/index.ts
@@ -26,6 +26,7 @@ import { THerokuConnection } from "./heroku-connection";
import { THumanitecConnection } from "./humanitec-connection";
import { TLaravelForgeConnection } from "./laravel-forge-connection";
import { TLdapConnection } from "./ldap-connection";
+import { TMongoDBConnection } from "./mongodb-connection";
import { TMsSqlConnection } from "./mssql-connection";
import { TMySqlConnection } from "./mysql-connection";
import { TNetlifyConnection } from "./netlify-connection";
@@ -69,6 +70,7 @@ export * from "./heroku-connection";
export * from "./humanitec-connection";
export * from "./laravel-forge-connection";
export * from "./ldap-connection";
+export * from "./mongodb-connection";
export * from "./mssql-connection";
export * from "./mysql-connection";
export * from "./netlify-connection";
@@ -129,6 +131,7 @@ export type TAppConnection =
| TNorthflankConnection
| TOktaConnection
| TRedisConnection
+ | TMongoDBConnection
| TChefConnection
| TDNSMadeEasyConnection;
diff --git a/frontend/src/hooks/api/appConnections/types/mongodb-connection.ts b/frontend/src/hooks/api/appConnections/types/mongodb-connection.ts
new file mode 100644
index 000000000..5c8e72cb5
--- /dev/null
+++ b/frontend/src/hooks/api/appConnections/types/mongodb-connection.ts
@@ -0,0 +1,22 @@
+import { AppConnection } from "@app/hooks/api/appConnections/enums";
+import { TRootAppConnection } from "@app/hooks/api/appConnections/types/root-connection";
+
+export enum MongoDBConnectionMethod {
+ UsernameAndPassword = "username-and-password"
+}
+
+export type TMongoDBConnectionCredentials = {
+ host: string;
+ port: number;
+ username: string;
+ password: string;
+ database: string;
+ tlsEnabled: boolean;
+ tlsRejectUnauthorized: boolean;
+ tlsCertificate?: string;
+};
+
+export type TMongoDBConnection = TRootAppConnection & { app: AppConnection.MongoDB } & {
+ method: MongoDBConnectionMethod.UsernameAndPassword;
+ credentials: TMongoDBConnectionCredentials;
+};
diff --git a/frontend/src/hooks/api/secretRotationsV2/enums.ts b/frontend/src/hooks/api/secretRotationsV2/enums.ts
index 264a6a4a4..d52de16fc 100644
--- a/frontend/src/hooks/api/secretRotationsV2/enums.ts
+++ b/frontend/src/hooks/api/secretRotationsV2/enums.ts
@@ -8,7 +8,8 @@ export enum SecretRotation {
LdapPassword = "ldap-password",
AwsIamUserSecret = "aws-iam-user-secret",
OktaClientSecret = "okta-client-secret",
- RedisCredentials = "redis-credentials"
+ RedisCredentials = "redis-credentials",
+ MongoDBCredentials = "mongodb-credentials"
}
export enum SecretRotationStatus {
diff --git a/frontend/src/hooks/api/secretRotationsV2/types/index.ts b/frontend/src/hooks/api/secretRotationsV2/types/index.ts
index a04b0e020..cc938ae05 100644
--- a/frontend/src/hooks/api/secretRotationsV2/types/index.ts
+++ b/frontend/src/hooks/api/secretRotationsV2/types/index.ts
@@ -31,6 +31,11 @@ import { TSqlCredentialsRotationOption } from "@app/hooks/api/secretRotationsV2/
import { SecretV3RawSanitized } from "@app/hooks/api/secrets/types";
import { DiscriminativePick } from "@app/types";
+import {
+ TMongoDBCredentialsRotation,
+ TMongoDBCredentialsRotationGeneratedCredentialsResponse,
+ TMongoDBCredentialsRotationOption
+} from "./mongodb-credentials-rotation";
import {
TMySqlCredentialsRotation,
TMySqlCredentialsRotationGeneratedCredentialsResponse
@@ -61,6 +66,7 @@ export type TSecretRotationV2 = (
| TAwsIamUserSecretRotation
| TOktaClientSecretRotation
| TRedisCredentialsRotation
+ | TMongoDBCredentialsRotation
) & {
secrets: (SecretV3RawSanitized | null)[];
};
@@ -72,7 +78,8 @@ export type TSecretRotationV2Option =
| TLdapPasswordRotationOption
| TAwsIamUserSecretRotationOption
| TOktaClientSecretRotationOption
- | TRedisCredentialsRotationOption;
+ | TRedisCredentialsRotationOption
+ | TMongoDBCredentialsRotationOption;
export type TListSecretRotationV2Options = { secretRotationOptions: TSecretRotationV2Option[] };
@@ -88,7 +95,8 @@ export type TViewSecretRotationGeneratedCredentialsResponse =
| TLdapPasswordRotationGeneratedCredentialsResponse
| TAwsIamUserSecretRotationGeneratedCredentialsResponse
| TOktaClientSecretRotationGeneratedCredentialsResponse
- | TRedisCredentialsRotationGeneratedCredentialsResponse;
+ | TRedisCredentialsRotationGeneratedCredentialsResponse
+ | TMongoDBCredentialsRotationGeneratedCredentialsResponse;
export type TCreateSecretRotationV2DTO = DiscriminativePick<
TSecretRotationV2,
@@ -142,6 +150,7 @@ export type TSecretRotationOptionMap = {
[SecretRotation.AwsIamUserSecret]: TAwsIamUserSecretRotationOption;
[SecretRotation.OktaClientSecret]: TOktaClientSecretRotationOption;
[SecretRotation.RedisCredentials]: TRedisCredentialsRotationOption;
+ [SecretRotation.MongoDBCredentials]: TMongoDBCredentialsRotationOption;
};
export type TSecretRotationGeneratedCredentialsResponseMap = {
@@ -155,4 +164,5 @@ export type TSecretRotationGeneratedCredentialsResponseMap = {
[SecretRotation.AwsIamUserSecret]: TAwsIamUserSecretRotationGeneratedCredentialsResponse;
[SecretRotation.OktaClientSecret]: TOktaClientSecretRotationGeneratedCredentialsResponse;
[SecretRotation.RedisCredentials]: TRedisCredentialsRotationGeneratedCredentialsResponse;
+ [SecretRotation.MongoDBCredentials]: TMongoDBCredentialsRotationGeneratedCredentialsResponse;
};
diff --git a/frontend/src/hooks/api/secretRotationsV2/types/mongodb-credentials-rotation.ts b/frontend/src/hooks/api/secretRotationsV2/types/mongodb-credentials-rotation.ts
new file mode 100644
index 000000000..425357d54
--- /dev/null
+++ b/frontend/src/hooks/api/secretRotationsV2/types/mongodb-credentials-rotation.ts
@@ -0,0 +1,28 @@
+import { AppConnection } from "@app/hooks/api/appConnections/enums";
+import { SecretRotation } from "@app/hooks/api/secretRotationsV2";
+import {
+ TSecretRotationV2Base,
+ TSecretRotationV2GeneratedCredentialsResponseBase,
+ TSqlCredentialsRotationGeneratedCredentials,
+ TSqlCredentialsRotationProperties
+} from "@app/hooks/api/secretRotationsV2/types/shared";
+
+export type TMongoDBCredentialsRotation = TSecretRotationV2Base & {
+ type: SecretRotation.MongoDBCredentials;
+} & TSqlCredentialsRotationProperties;
+
+export type TMongoDBCredentialsRotationGeneratedCredentialsResponse =
+ TSecretRotationV2GeneratedCredentialsResponseBase<
+ SecretRotation.MongoDBCredentials,
+ TSqlCredentialsRotationGeneratedCredentials
+ >;
+
+export type TMongoDBCredentialsRotationOption = {
+ name: string;
+ type: SecretRotation.MongoDBCredentials;
+ connection: AppConnection.MongoDB;
+ template: {
+ createUserStatement: string;
+ secretsMapping: TMongoDBCredentialsRotation["secretsMapping"];
+ };
+};
diff --git a/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/AppConnectionForm.tsx b/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/AppConnectionForm.tsx
index af26a940d..6fa3d8854 100644
--- a/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/AppConnectionForm.tsx
+++ b/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/AppConnectionForm.tsx
@@ -35,6 +35,7 @@ import { HerokuConnectionForm } from "./HerokuAppConnectionForm";
import { HumanitecConnectionForm } from "./HumanitecConnectionForm";
import { LaravelForgeConnectionForm } from "./LaravelForgeConnectionForm";
import { LdapConnectionForm } from "./LdapConnectionForm";
+import { MongoDBConnectionForm } from "./MongoDBConnectionForm";
import { MsSqlConnectionForm } from "./MsSqlConnectionForm";
import { MySqlConnectionForm } from "./MySqlConnectionForm";
import { NetlifyConnectionForm } from "./NetlifyConnectionForm";
@@ -173,6 +174,8 @@ const CreateForm = ({ app, onComplete, projectId }: CreateFormProps) => {
return ;
case AppConnection.Redis:
return ;
+ case AppConnection.MongoDB:
+ return ;
default:
throw new Error(`Unhandled App ${app}`);
}
@@ -331,6 +334,8 @@ const UpdateForm = ({ appConnection, onComplete }: UpdateFormProps) => {
return ;
case AppConnection.Redis:
return ;
+ case AppConnection.MongoDB:
+ return ;
default:
throw new Error(`Unhandled App ${(appConnection as TAppConnection).app}`);
}
diff --git a/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/MongoDBConnectionForm.tsx b/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/MongoDBConnectionForm.tsx
new file mode 100644
index 000000000..72e359e0a
--- /dev/null
+++ b/frontend/src/pages/organization/AppConnections/AppConnectionsPage/components/AppConnectionForm/MongoDBConnectionForm.tsx
@@ -0,0 +1,326 @@
+import { useState } from "react";
+import { Controller, FormProvider, useForm } from "react-hook-form";
+import { faQuestionCircle } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { Tab } from "@headlessui/react";
+import { zodResolver } from "@hookform/resolvers/zod";
+import { z } from "zod";
+
+import {
+ Button,
+ FormControl,
+ Input,
+ ModalClose,
+ SecretInput,
+ Select,
+ SelectItem,
+ Switch,
+ TextArea,
+ Tooltip
+} from "@app/components/v2";
+import { APP_CONNECTION_MAP, getAppConnectionMethodDetails } from "@app/helpers/appConnections";
+import { MongoDBConnectionMethod, TMongoDBConnection } from "@app/hooks/api/appConnections";
+import { AppConnection } from "@app/hooks/api/appConnections/enums";
+
+import {
+ genericAppConnectionFieldsSchema,
+ GenericAppConnectionsFields
+} from "./GenericAppConnectionFields";
+
+type Props = {
+ appConnection?: TMongoDBConnection;
+ onSubmit: (formData: FormData) => Promise;
+};
+
+const rootSchema = genericAppConnectionFieldsSchema.extend({
+ app: z.literal(AppConnection.MongoDB)
+});
+
+const formSchema = z.discriminatedUnion("method", [
+ rootSchema.extend({
+ method: z.literal(MongoDBConnectionMethod.UsernameAndPassword),
+ credentials: z.object({
+ host: z.string().trim().min(1, "Host required"),
+ port: z.coerce.number().default(27017),
+ username: z.string().trim().min(1, "Username required"),
+ password: z.string().trim().min(1, "Password required"),
+ database: z.string().trim().min(1, "Database required"),
+ tlsEnabled: z.boolean().default(false),
+ tlsRejectUnauthorized: z.boolean().default(true),
+ tlsCertificate: z
+ .string()
+ .trim()
+ .transform((value) => value || undefined)
+ .optional()
+ })
+ })
+]);
+
+type FormData = z.infer;
+
+export const MongoDBConnectionForm = ({ appConnection, onSubmit }: Props) => {
+ const isUpdate = Boolean(appConnection);
+ const [selectedTabIndex, setSelectedTabIndex] = useState(0);
+
+ const form = useForm({
+ resolver: zodResolver(formSchema),
+ defaultValues: appConnection ?? {
+ app: AppConnection.MongoDB,
+ method: MongoDBConnectionMethod.UsernameAndPassword,
+ credentials: {
+ host: "",
+ port: 27017,
+ username: "",
+ password: "",
+ database: "",
+ tlsEnabled: false,
+ tlsRejectUnauthorized: true,
+ tlsCertificate: undefined
+ }
+ }
+ });
+
+ const {
+ handleSubmit,
+ watch,
+ control,
+ formState: { isSubmitting, isDirty }
+ } = form;
+
+ const tlsEnabled = watch("credentials.tlsEnabled");
+
+ return (
+
+
+
+
+ )}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};