From 6946d3816d5d4be65fb972a9bab55e241e27d9e0 Mon Sep 17 00:00:00 2001 From: Rob Rudin Date: Wed, 28 Feb 2024 08:24:51 -0500 Subject: [PATCH] MLE-4126 Can now configure OAuth authentication --- .../client/DatabaseClientBuilder.java | 20 +++++++++++++++ .../client/DatabaseClientFactory.java | 19 ++++++++++++++ .../impl/DatabaseClientPropertySource.java | 6 +++++ .../okhttp/BasicAuthenticationConfigurer.java | 2 +- .../DigestAuthenticationConfigurer.java | 2 +- ...arkLogicCloudAuthenticationConfigurer.java | 4 +-- .../okhttp/OAuthAuthenticationConfigurer.java | 25 +++++++++++++++++++ .../client/impl/okhttp/OkHttpUtil.java | 2 ++ .../OAuthAuthenticationConfigurerTest.java | 20 +++++++++++++++ .../test/DatabaseClientBuilderTest.java | 10 ++++++++ 10 files changed, 106 insertions(+), 4 deletions(-) create mode 100644 marklogic-client-api/src/main/java/com/marklogic/client/impl/okhttp/OAuthAuthenticationConfigurer.java create mode 100644 marklogic-client-api/src/test/java/com/marklogic/client/impl/okhttp/OAuthAuthenticationConfigurerTest.java diff --git a/marklogic-client-api/src/main/java/com/marklogic/client/DatabaseClientBuilder.java b/marklogic-client-api/src/main/java/com/marklogic/client/DatabaseClientBuilder.java index 0bf7f5561..00eb83960 100644 --- a/marklogic-client-api/src/main/java/com/marklogic/client/DatabaseClientBuilder.java +++ b/marklogic-client-api/src/main/java/com/marklogic/client/DatabaseClientBuilder.java @@ -44,6 +44,7 @@ public class DatabaseClientBuilder { public final static String AUTH_TYPE_KERBEROS = "kerberos"; public final static String AUTH_TYPE_CERTIFICATE = "certificate"; public final static String AUTH_TYPE_SAML = "saml"; + public final static String AUTH_TYPE_OAUTH = "oauth"; private final Map props; @@ -189,6 +190,25 @@ public DatabaseClientBuilder withSAMLAuth(String token) { .withSAMLToken(token); } + /** + * @param token + * @return + * @since 6.6.0 + */ + public DatabaseClientBuilder withOAuth(String token) { + return withAuthType(AUTH_TYPE_OAUTH).withOAuthToken(token); + } + + /** + * @param token + * @return + * @since 6.6.0 + */ + public DatabaseClientBuilder withOAuthToken(String token) { + props.put(PREFIX + "oauth.token", token); + return this; + } + public DatabaseClientBuilder withConnectionType(DatabaseClient.ConnectionType type) { props.put(PREFIX + "connectionType", type); return this; diff --git a/marklogic-client-api/src/main/java/com/marklogic/client/DatabaseClientFactory.java b/marklogic-client-api/src/main/java/com/marklogic/client/DatabaseClientFactory.java index cd77c00b3..622805f08 100644 --- a/marklogic-client-api/src/main/java/com/marklogic/client/DatabaseClientFactory.java +++ b/marklogic-client-api/src/main/java/com/marklogic/client/DatabaseClientFactory.java @@ -529,6 +529,24 @@ public MarkLogicCloudAuthContext withSSLHostnameVerifier(SSLHostnameVerifier ver } } + /** + * @since 6.6.0 + */ + public static class OAuthContext extends AuthContext { + private String token; + + /** + * @param token the OAuth token to include in the Authorization header in each request sent to MarkLogic. + */ + public OAuthContext(String token) { + this.token = token; + } + + public String getToken() { + return token; + } + } + public static class BasicAuthContext extends AuthContext { String user; String password; @@ -1287,6 +1305,7 @@ public String getCertificatePassword() { * minutes for which an access token lasts; supported since 6.3.0. *
  • marklogic.client.kerberos.principal = must be a String; required for Kerberos authentication
  • *
  • marklogic.client.saml.token = must be a String; required for SAML authentication
  • + *
  • marklogic.client.oauth.token = must be a String; required for OAuth authentication; supported since 6.6.0.
  • *
  • marklogic.client.sslContext = must be an instance of {@code javax.net.ssl.SSLContext}
  • *
  • marklogic.client.sslProtocol = must be a String; if "default', then uses the JVM default SSL * context; else, the value is passed to the {@code getInstance} method in {@code javax.net.ssl.SSLContext}
  • diff --git a/marklogic-client-api/src/main/java/com/marklogic/client/impl/DatabaseClientPropertySource.java b/marklogic-client-api/src/main/java/com/marklogic/client/impl/DatabaseClientPropertySource.java index 8047f93bc..5125ba6b5 100644 --- a/marklogic-client-api/src/main/java/com/marklogic/client/impl/DatabaseClientPropertySource.java +++ b/marklogic-client-api/src/main/java/com/marklogic/client/impl/DatabaseClientPropertySource.java @@ -175,6 +175,8 @@ private DatabaseClientFactory.SecurityContext newSecurityContext(String type, SS return newCertificateAuthContext(sslInputs); case DatabaseClientBuilder.AUTH_TYPE_SAML: return newSAMLAuthContext(); + case DatabaseClientBuilder.AUTH_TYPE_OAUTH: + return newOAuthContext(); default: throw new IllegalArgumentException("Unrecognized auth type: " + type); } @@ -253,6 +255,10 @@ private DatabaseClientFactory.SecurityContext newSAMLAuthContext() { return new DatabaseClientFactory.SAMLAuthContext(getRequiredStringValue("saml.token")); } + private DatabaseClientFactory.SecurityContext newOAuthContext() { + return new DatabaseClientFactory.OAuthContext(getRequiredStringValue("oauth.token")); + } + private DatabaseClientFactory.SSLHostnameVerifier determineHostnameVerifier() { Object verifierObject = propertySource.apply(PREFIX + "sslHostnameVerifier"); if (verifierObject instanceof DatabaseClientFactory.SSLHostnameVerifier) { diff --git a/marklogic-client-api/src/main/java/com/marklogic/client/impl/okhttp/BasicAuthenticationConfigurer.java b/marklogic-client-api/src/main/java/com/marklogic/client/impl/okhttp/BasicAuthenticationConfigurer.java index c412053d4..beb31259d 100644 --- a/marklogic-client-api/src/main/java/com/marklogic/client/impl/okhttp/BasicAuthenticationConfigurer.java +++ b/marklogic-client-api/src/main/java/com/marklogic/client/impl/okhttp/BasicAuthenticationConfigurer.java @@ -25,7 +25,7 @@ import java.io.IOException; import java.nio.charset.Charset; -public class BasicAuthenticationConfigurer implements AuthenticationConfigurer { +class BasicAuthenticationConfigurer implements AuthenticationConfigurer { @Override public void configureAuthentication(OkHttpClient.Builder clientBuilder, DatabaseClientFactory.BasicAuthContext securityContext) { diff --git a/marklogic-client-api/src/main/java/com/marklogic/client/impl/okhttp/DigestAuthenticationConfigurer.java b/marklogic-client-api/src/main/java/com/marklogic/client/impl/okhttp/DigestAuthenticationConfigurer.java index 15c11b2db..a36ff8e2a 100644 --- a/marklogic-client-api/src/main/java/com/marklogic/client/impl/okhttp/DigestAuthenticationConfigurer.java +++ b/marklogic-client-api/src/main/java/com/marklogic/client/impl/okhttp/DigestAuthenticationConfigurer.java @@ -26,7 +26,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -public class DigestAuthenticationConfigurer implements AuthenticationConfigurer { +class DigestAuthenticationConfigurer implements AuthenticationConfigurer { @Override public void configureAuthentication(OkHttpClient.Builder clientBuilder, DatabaseClientFactory.DigestAuthContext securityContext) { diff --git a/marklogic-client-api/src/main/java/com/marklogic/client/impl/okhttp/MarkLogicCloudAuthenticationConfigurer.java b/marklogic-client-api/src/main/java/com/marklogic/client/impl/okhttp/MarkLogicCloudAuthenticationConfigurer.java index db7ce3849..3ee3a1897 100644 --- a/marklogic-client-api/src/main/java/com/marklogic/client/impl/okhttp/MarkLogicCloudAuthenticationConfigurer.java +++ b/marklogic-client-api/src/main/java/com/marklogic/client/impl/okhttp/MarkLogicCloudAuthenticationConfigurer.java @@ -30,11 +30,11 @@ import java.io.IOException; -public class MarkLogicCloudAuthenticationConfigurer implements AuthenticationConfigurer { +class MarkLogicCloudAuthenticationConfigurer implements AuthenticationConfigurer { private String host; - public MarkLogicCloudAuthenticationConfigurer(String host) { + MarkLogicCloudAuthenticationConfigurer(String host) { this.host = host; } diff --git a/marklogic-client-api/src/main/java/com/marklogic/client/impl/okhttp/OAuthAuthenticationConfigurer.java b/marklogic-client-api/src/main/java/com/marklogic/client/impl/okhttp/OAuthAuthenticationConfigurer.java new file mode 100644 index 000000000..95acb10f1 --- /dev/null +++ b/marklogic-client-api/src/main/java/com/marklogic/client/impl/okhttp/OAuthAuthenticationConfigurer.java @@ -0,0 +1,25 @@ +package com.marklogic.client.impl.okhttp; + +import com.marklogic.client.DatabaseClientFactory; +import okhttp3.OkHttpClient; +import okhttp3.Request; + +/** + * @since 6.6.0 + */ +class OAuthAuthenticationConfigurer implements AuthenticationConfigurer { + + @Override + public void configureAuthentication(OkHttpClient.Builder clientBuilder, DatabaseClientFactory.OAuthContext authContext) { + clientBuilder.addInterceptor(chain -> { + Request authenticatedRequest = makeAuthenticatedRequest(chain.request(), authContext); + return chain.proceed(authenticatedRequest); + }); + } + + Request makeAuthenticatedRequest(Request request, DatabaseClientFactory.OAuthContext authContext) { + String authValue = String.format("Bearer %s", authContext.getToken()); + return request.newBuilder().header("Authorization", authValue).build(); + } +} + diff --git a/marklogic-client-api/src/main/java/com/marklogic/client/impl/okhttp/OkHttpUtil.java b/marklogic-client-api/src/main/java/com/marklogic/client/impl/okhttp/OkHttpUtil.java index 54fab220b..00a8ca9c7 100644 --- a/marklogic-client-api/src/main/java/com/marklogic/client/impl/okhttp/OkHttpUtil.java +++ b/marklogic-client-api/src/main/java/com/marklogic/client/impl/okhttp/OkHttpUtil.java @@ -68,6 +68,8 @@ public static OkHttpClient.Builder newOkHttpClientBuilder(String host, DatabaseC configureSAMLAuth((DatabaseClientFactory.SAMLAuthContext) securityContext, clientBuilder); } else if (securityContext instanceof DatabaseClientFactory.MarkLogicCloudAuthContext) { authenticationConfigurer = new MarkLogicCloudAuthenticationConfigurer(host); + } else if (securityContext instanceof DatabaseClientFactory.OAuthContext) { + authenticationConfigurer = new OAuthAuthenticationConfigurer(); } else { throw new IllegalArgumentException("Unsupported security context: " + securityContext.getClass()); } diff --git a/marklogic-client-api/src/test/java/com/marklogic/client/impl/okhttp/OAuthAuthenticationConfigurerTest.java b/marklogic-client-api/src/test/java/com/marklogic/client/impl/okhttp/OAuthAuthenticationConfigurerTest.java new file mode 100644 index 000000000..dd09153c5 --- /dev/null +++ b/marklogic-client-api/src/test/java/com/marklogic/client/impl/okhttp/OAuthAuthenticationConfigurerTest.java @@ -0,0 +1,20 @@ +package com.marklogic.client.impl.okhttp; + +import com.marklogic.client.DatabaseClientFactory; +import okhttp3.Request; +import okhttp3.mockwebserver.MockWebServer; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class OAuthAuthenticationConfigurerTest { + + @Test + void test() { + DatabaseClientFactory.OAuthContext authContext = new DatabaseClientFactory.OAuthContext("abc123"); + Request request = new Request.Builder().url(new MockWebServer().url("/url-doesnt-matter")).build(); + + Request authenticatedRequest = new OAuthAuthenticationConfigurer().makeAuthenticatedRequest(request, authContext); + assertEquals("Bearer abc123", authenticatedRequest.header("Authorization")); + } +} diff --git a/marklogic-client-api/src/test/java/com/marklogic/client/test/DatabaseClientBuilderTest.java b/marklogic-client-api/src/test/java/com/marklogic/client/test/DatabaseClientBuilderTest.java index 1053000cd..93af54f29 100644 --- a/marklogic-client-api/src/test/java/com/marklogic/client/test/DatabaseClientBuilderTest.java +++ b/marklogic-client-api/src/test/java/com/marklogic/client/test/DatabaseClientBuilderTest.java @@ -204,6 +204,16 @@ void saml() { assertEquals("my-token", context.getToken()); } + @Test + void oauth() { + bean = Common.newClientBuilder() + .withOAuth("abc123") + .buildBean(); + + DatabaseClientFactory.OAuthContext context = (DatabaseClientFactory.OAuthContext) bean.getSecurityContext(); + assertEquals("abc123", context.getToken()); + } + @Test void defaultSslContext() throws Exception { bean = Common.newClientBuilder()