From cb453cbb969b0ea52a3b73318990f389a118d801 Mon Sep 17 00:00:00 2001
From: Rea Rustagi <85902999+rustagir@users.noreply.github.com>
Date: Mon, 24 Jun 2024 15:44:56 -0400
Subject: [PATCH] DOCSP-40133: custom auth provider (#163)

* DOCSP-40133: custom auth provider

* wip

* vale

* fixes

* updates

* add to wn

* wip RW tech comments

* RW comments

* fix

* fix

* RW comments

(cherry picked from commit e21986b2c6d24cd98c6ca7e4cca2b74cce56b49d)
---
 .../AwsAssumeRoleCredentialProvider.java      | 54 ++++++++++++
 source/security-and-authentication.txt        |  7 +-
 .../custom-auth.txt                           | 83 +++++++++++++++++++
 .../mongodb-aws-auth.txt                      |  5 ++
 source/whats-new.txt                          |  4 +-
 5 files changed, 147 insertions(+), 6 deletions(-)
 create mode 100644 source/includes/security/AwsAssumeRoleCredentialProvider.java
 create mode 100644 source/security-and-authentication/custom-auth.txt

diff --git a/source/includes/security/AwsAssumeRoleCredentialProvider.java b/source/includes/security/AwsAssumeRoleCredentialProvider.java
new file mode 100644
index 00000000..f72ae758
--- /dev/null
+++ b/source/includes/security/AwsAssumeRoleCredentialProvider.java
@@ -0,0 +1,54 @@
+package com.mongodb;
+
+import java.util.Map;
+import java.util.function.Supplier;
+
+import com.mongodb.kafka.connect.util.custom.credentials.CustomCredentialProvider;
+
+import com.amazonaws.auth.AWSCredentialsProvider;
+import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
+import com.amazonaws.services.securitytoken.AWSSecurityTokenService;
+import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceAsyncClientBuilder;
+import com.amazonaws.services.securitytoken.model.AssumeRoleRequest;
+import com.amazonaws.services.securitytoken.model.AssumeRoleResult;
+import com.amazonaws.services.securitytoken.model.Credentials;
+import com.amazonaws.util.StringUtils;
+
+public class SampleAssumeRoleCredential implements CustomCredentialProvider {
+
+  public SampleAssumeRoleCredential() {}
+  @Override
+  public MongoCredential getCustomCredential(Map<?, ?> map) {
+    AWSCredentialsProvider provider = new DefaultAWSCredentialsProviderChain();
+    Supplier<AwsCredential> awsFreshCredentialSupplier = () -> {
+      AWSSecurityTokenService stsClient = AWSSecurityTokenServiceAsyncClientBuilder.standard()
+          .withCredentials(provider)
+          .withRegion("us-east-1")
+          .build();
+      AssumeRoleRequest assumeRoleRequest = new AssumeRoleRequest().withDurationSeconds(3600)
+          .withRoleArn((String)map.get("mongodbaws.auth.mechanism.roleArn"))
+          .withRoleSessionName("Test_Session");
+      AssumeRoleResult assumeRoleResult = stsClient.assumeRole(assumeRoleRequest);
+      Credentials creds = assumeRoleResult.getCredentials();
+      // Add your code to fetch new credentials
+      return new AwsCredential(creds.getAccessKeyId(), creds.getSecretAccessKey(), creds.getSessionToken());
+    };
+    return MongoCredential.createAwsCredential(null, null)
+        .withMechanismProperty(MongoCredential.AWS_CREDENTIAL_PROVIDER_KEY, awsFreshCredentialSupplier);
+  }
+  
+  // Validates presence of an ARN
+  @Override
+  public void validate(Map<?, ?> map) {
+    String roleArn = (String) map.get("mongodbaws.auth.mechanism.roleArn");
+    if (StringUtils.isNullOrEmpty(roleArn)) {
+      throw new RuntimeException("Invalid value set for customProperty");
+    }
+  }
+
+  // Initializes the custom provider
+  @Override
+  public void init(Map<?, ?> map) {
+
+  }
+}
\ No newline at end of file
diff --git a/source/security-and-authentication.txt b/source/security-and-authentication.txt
index d140a9f1..a8f7cbf8 100644
--- a/source/security-and-authentication.txt
+++ b/source/security-and-authentication.txt
@@ -10,10 +10,11 @@ Security and Authentication
 
    SSL/TLS and X.509 Certificates </security-and-authentication/tls-and-x509>
    MongoDB AWS-based Authentication </security-and-authentication/mongodb-aws-auth>
+   Custom Authentication Provider </security-and-authentication/custom-auth>
 
 Read the following sections to learn how to secure communications between MongoDB 
 and the {+connector+}:
 
-- :doc:`Encrypt the Messages Your Connector Sends with SSL/TLS </security-and-authentication/tls-and-x509>`
-- :doc:`Authenticate Your Connector with MongoDB using Amazon Web Services </security-and-authentication/mongodb-aws-auth>`
-
+- :ref:`Encrypt the Messages Your Connector Sends with SSL/TLS <kafka-configure-ssl>`
+- :ref:`Authenticate Your Connector with MongoDB using Amazon Web Services <kafka-mongodb-aws>`
+- :ref:`Implement a Custom Authentication Provider <kafka-custom-auth>`
diff --git a/source/security-and-authentication/custom-auth.txt b/source/security-and-authentication/custom-auth.txt
new file mode 100644
index 00000000..bc24ae1f
--- /dev/null
+++ b/source/security-and-authentication/custom-auth.txt
@@ -0,0 +1,83 @@
+.. _kafka-custom-auth:
+
+==============================
+Custom Authentication Provider
+==============================
+
+.. facet::
+   :name: genre
+   :values: reference
+
+.. meta::
+   :keywords: credentials, implementation class, custom class
+
+.. contents:: On this page
+   :local:
+   :backlinks: none
+   :depth: 2
+   :class: singlecol
+
+Overview
+--------
+
+You can add a custom authentication provider by implementing the
+``com.mongodb.kafka.connect.util.custom.credentials.CustomCredentialProvider``
+interface. You must place your custom class JAR file in the ``lib`` folder
+in your {+kafka-connect+} deployment.
+
+Set following authentication properties to configure the authentication
+provider:
+
+- ``mongo.custom.auth.mechanism.enable``: set to ``true``
+- ``mongo.custom.auth.mechanism.providerClass``: set to the qualified
+  class name of the implementation class
+- *(Optional)* ``mongodbaws.auth.mechanism.roleArn``: set to an Amazon Resource Name (ARN)
+
+AWS IAM Authentication Example
+------------------------------
+
+This example provides a custom authentication provider that supports AWS
+IAM. The following code shows the custom authentication
+provider JAR file:
+
+.. literalinclude:: /includes/security/AwsAssumeRoleCredentialProvider.java
+   :language: java
+
+Compile the JAR file and place it in the ``lib`` folder in your
+deployment. 
+
+.. note::
+   
+   To view an example of a ``pom.xml`` file that can build the complete JAR containing
+   the implementation class, see the `Kafka Connector GitHub repository
+   README file
+   <https://github.com/mongodb/mongo-kafka/blob/master/README.md#pom-file-to-build-the-sample-customroleprovider-into-a-jar>`__.
+
+Next, configure your source or sink connector to include the custom
+authentication method. The following configuration properties define a
+sink connector that connects the {+connector-short+} to MongoDB Atlas
+by using AWS IAM authentication:
+
+.. code-block:: json
+   :emphasize-lines: 12-14
+
+   {
+      "name": "mongo-tutorial-sink",
+      "config": {
+        "connector.class": "com.mongodb.kafka.connect.MongoSinkConnector",
+        "topics": "<topic>",
+        "connection.uri": "<connection string>?authSource=%24external&authMechanism=MONGODB-AWS&retryWrites=true&w=majority",
+        "key.converter": "org.apache.kafka.connect.storage.StringConverter",
+        "value.converter": "org.apache.kafka.connect.json.JsonConverter",
+        "value.converter.schemas.enable": false,
+        "database": "<db>",
+        "collection": "<collection>",
+        "mongo.custom.auth.mechanism.enable": "true",
+        "mongo.custom.auth.mechanism.providerClass": "com.mongodb.SampleAssumeRoleCredential",
+        "mongodbaws.auth.mechanism.roleArn": "<AWS IAM ARN>"
+      }
+   }
+
+In this example, the ``roleArn`` value is the IAM Role of the user group that has
+access to MongoDB Atlas. In the AWS IAM console, the IAM account that is
+running {+kafka-connect+} has ``AssumeRole`` permissions to the Atlas User Group.
diff --git a/source/security-and-authentication/mongodb-aws-auth.txt b/source/security-and-authentication/mongodb-aws-auth.txt
index 74d55a7b..888ee5b3 100644
--- a/source/security-and-authentication/mongodb-aws-auth.txt
+++ b/source/security-and-authentication/mongodb-aws-auth.txt
@@ -79,3 +79,8 @@ replace:
          and placeholder value.
        | *Optional*
 
+.. tip:: Custom Authentication Provider
+
+   You can create and use a custom authentication provider to support
+   AWS IAM authentication. To learn more, see the
+   :ref:`kafka-custom-auth` guide.
diff --git a/source/whats-new.txt b/source/whats-new.txt
index da670365..1e9f4557 100644
--- a/source/whats-new.txt
+++ b/source/whats-new.txt
@@ -47,9 +47,7 @@ What's New in 1.13
 
 - Added a custom authentication provider interface for Source and Sink
   Connectors. This feature enables you to write and use a custom implementation
-  class in your connector.
-
-.. TODO add link To learn more, see the :ref:`` guide.
+  class in your connector. To learn more, see the :ref:`kafka-custom-auth` guide.
 
 - Fixed an issue that occurred when validating configuration for Source
   and Sink Connectors if the configuration contained secrets and used