Skip to content

Commit b25a122

Browse files
authored
Merge pull request #1577 from marklogic/release/6.2.2
Merging release/6.2.2 into master
2 parents e8100a1 + 55d832e commit b25a122

File tree

12 files changed

+137
-28
lines changed

12 files changed

+137
-28
lines changed

Diff for: README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,13 @@ To use the API in your [Maven](https://maven.apache.org/) project, include the f
4141
<dependency>
4242
<groupId>com.marklogic</groupId>
4343
<artifactId>marklogic-client-api</artifactId>
44-
<version>6.2.1</version>
44+
<version>6.2.2</version>
4545
</dependency>
4646

4747
To use the API in your [Gradle](https://gradle.org/) project, include the following in your build.gradle file:
4848

4949
dependencies {
50-
implementation "com.marklogic:marklogic-client-api:6.2.1"
50+
implementation "com.marklogic:marklogic-client-api:6.2.2"
5151
}
5252

5353
Next, read [The Java API in Five Minutes](http://developer.marklogic.com/try/java/index) to get started.
@@ -130,7 +130,7 @@ https://developer.marklogic.com/free-developer
130130

131131
To obtain verified downloads signed with MarkLogic's PGP key, use maven tools or directly download
132132
the .jar and .asc files from
133-
[maven central](https://repo1.maven.org/maven2/com/marklogic/marklogic-client-api/5.3.0/). MarkLogic's
133+
[maven central](https://repo1.maven.org/maven2/com/marklogic/marklogic-client-api/). MarkLogic's
134134
pgp key ID is 48D4B86E and it is available from pgp.mit.edu by installing gnupg and running the command:
135135

136136
$ gpg --keyserver pgp.mit.edu --recv-key 48D4B86E

Diff for: gradle.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
group=com.marklogic
2-
version=6.2.1
2+
version=6.2.2
33
describedName=MarkLogic Java Client API
44
publishUrl=file:../marklogic-java/releases
55

Diff for: marklogic-client-api/src/main/java/com/marklogic/client/DatabaseClientBuilder.java

+13
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,19 @@ public DatabaseClientBuilder withCertificateAuth(String file, String password) {
158158
.withCertificatePassword(password);
159159
}
160160

161+
/**
162+
*
163+
* @param sslContext
164+
* @param trustManager
165+
* @return
166+
* @since 6.2.2
167+
*/
168+
public DatabaseClientBuilder withCertificateAuth(SSLContext sslContext, X509TrustManager trustManager) {
169+
return withAuthType(AUTH_TYPE_CERTIFICATE)
170+
.withSSLContext(sslContext)
171+
.withTrustManager(trustManager);
172+
}
173+
161174
public DatabaseClientBuilder withSAMLAuth(String token) {
162175
return withAuthType(AUTH_TYPE_SAML)
163176
.withSAMLToken(token);

Diff for: marklogic-client-api/src/main/java/com/marklogic/client/DatabaseClientFactory.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -1238,8 +1238,8 @@ public String getCertificatePassword() {
12381238
* "kerberos", "certificate", or "saml"</li>
12391239
* <li>marklogic.client.username = must be a String; required for basic and digest authentication</li>
12401240
* <li>marklogic.client.password = must be a String; required for basic and digest authentication</li>
1241-
* <li>marklogic.client.certificate.file = must be a String; required for certificate authentication</li>
1242-
* <li>marklogic.client.certificate.password = must be a String; required for certificate authentication</li>
1241+
* <li>marklogic.client.certificate.file = must be a String; optional for certificate authentication</li>
1242+
* <li>marklogic.client.certificate.password = must be a String; optional for certificate authentication</li>
12431243
* <li>marklogic.client.cloud.apiKey = must be a String; required for cloud authentication</li>
12441244
* <li>marklogic.client.kerberos.principal = must be a String; required for Kerberos authentication</li>
12451245
* <li>marklogic.client.saml.token = must be a String; required for SAML authentication</li>

Diff for: marklogic-client-api/src/main/java/com/marklogic/client/impl/DatabaseClientPropertySource.java

+12-8
Original file line numberDiff line numberDiff line change
@@ -210,15 +210,19 @@ private DatabaseClientFactory.SecurityContext newCloudAuthContext() {
210210
}
211211

212212
private DatabaseClientFactory.SecurityContext newCertificateAuthContext(SSLInputs sslInputs) {
213-
try {
214-
return new DatabaseClientFactory.CertificateAuthContext(
215-
getRequiredStringValue("certificate.file"),
216-
getRequiredStringValue("certificate.password"),
217-
sslInputs.getTrustManager()
218-
);
219-
} catch (Exception e) {
220-
throw new RuntimeException("Unable to create CertificateAuthContext; cause " + e.getMessage(), e);
213+
String file = getNullableStringValue("certificate.file");
214+
String password = getNullableStringValue("certificate.password");
215+
if (file != null && file.trim().length() > 0) {
216+
try {
217+
if (password != null && password.trim().length() > 0) {
218+
return new DatabaseClientFactory.CertificateAuthContext(file, password, sslInputs.getTrustManager());
219+
}
220+
return new DatabaseClientFactory.CertificateAuthContext(file, sslInputs.getTrustManager());
221+
} catch (Exception e) {
222+
throw new RuntimeException("Unable to create CertificateAuthContext; cause " + e.getMessage(), e);
223+
}
221224
}
225+
return new DatabaseClientFactory.CertificateAuthContext(sslInputs.getSslContext(), sslInputs.getTrustManager());
222226
}
223227

224228
private DatabaseClientFactory.SecurityContext newKerberosAuthContext() {

Diff for: marklogic-client-api/src/main/java/com/marklogic/client/io/DocumentMetadataHandle.java

+2-6
Original file line numberDiff line numberDiff line change
@@ -787,12 +787,8 @@ private void sendMetadataImpl(OutputStream out) {
787787

788788
serializer.flush();
789789
serializer.close();
790-
} catch (XMLStreamException e) {
791-
throw new MarkLogicIOException("Failed to serialize metadata", e);
792-
} catch (TransformerFactoryConfigurationError e) {
793-
throw new MarkLogicIOException("Failed to serialize metadata", e);
794-
} catch (TransformerException e) {
795-
throw new MarkLogicIOException("Failed to serialize metadata", e);
790+
} catch (Exception e) {
791+
throw new MarkLogicIOException("Failed to serialize metadata: cause: " + e.getMessage(), e);
796792
}
797793
}
798794

Diff for: marklogic-client-api/src/test/java/com/marklogic/client/test/CheckSSLConnectionTest.java

+21-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
import javax.net.ssl.SSLContext;
1212
import javax.net.ssl.SSLException;
13-
import javax.net.ssl.SSLHandshakeException;
1413
import javax.net.ssl.TrustManager;
1514

1615
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -54,6 +53,27 @@ void trustAllManager() throws Exception {
5453
assertNull(result.getErrorMessage());
5554
}
5655

56+
/**
57+
* Demonstrates using a custom X509TrustManager that only accepts the issuer of the public certificate associated
58+
* with the certificate template created via RequireSSLExtension.
59+
*/
60+
@Test
61+
void customTrustManager() {
62+
if (Common.USE_REVERSE_PROXY_SERVER) {
63+
return;
64+
}
65+
66+
DatabaseClient client = Common.newClientBuilder()
67+
.withSSLProtocol("TLSv1.2")
68+
.withTrustManager(RequireSSLExtension.newTrustManager())
69+
.withSSLHostnameVerifier(DatabaseClientFactory.SSLHostnameVerifier.ANY)
70+
.build();
71+
72+
DatabaseClient.ConnectionResult result = client.checkConnection();
73+
assertEquals(0, result.getStatusCode());
74+
assertNull(result.getErrorMessage());
75+
}
76+
5777
@Test
5878
void defaultSslContext() throws Exception {
5979
DatabaseClient client = Common.newClientBuilder()

Diff for: marklogic-client-api/src/test/java/com/marklogic/client/test/DatabaseClientBuilderTest.java

+40-4
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,18 @@
33
import com.marklogic.client.DatabaseClient;
44
import com.marklogic.client.DatabaseClientBuilder;
55
import com.marklogic.client.DatabaseClientFactory;
6+
import com.marklogic.client.ext.modulesloader.ssl.SimpleX509TrustManager;
67
import org.junit.jupiter.api.Test;
78

89
import javax.net.ssl.SSLContext;
10+
import javax.net.ssl.X509TrustManager;
11+
12+
import java.security.NoSuchAlgorithmException;
913

1014
import static org.junit.jupiter.api.Assertions.assertEquals;
1115
import static org.junit.jupiter.api.Assertions.assertNotNull;
1216
import static org.junit.jupiter.api.Assertions.assertNull;
17+
import static org.junit.jupiter.api.Assertions.assertSame;
1318
import static org.junit.jupiter.api.Assertions.assertThrows;
1419
import static org.junit.jupiter.api.Assertions.assertTrue;
1520

@@ -138,14 +143,45 @@ void kerberos() {
138143
}
139144

140145
@Test
141-
void certificate() {
146+
void certificateValidFile() {
147+
DatabaseClient client = Common.newClientBuilder()
148+
.withCertificateAuth("src/test/resources/test_certificate.p12", "abc")
149+
.build();
150+
151+
assertNotNull(client);
152+
assertNotNull(client.getSecurityContext().getSSLContext(), "An SSLContext should have been created based " +
153+
"on the test keystore.");
154+
}
155+
156+
@Test
157+
void certificateInvalidFile() {
142158
DatabaseClientBuilder builder = Common.newClientBuilder()
143159
.withCertificateAuth("not.found", "passwd");
144160

145161
Exception ex = assertThrows(Exception.class, () -> builder.buildBean());
146-
assertTrue(ex.getMessage().contains("Unable to create CertificateAuthContext"),
147-
"We don't yet have a real test for certificate authentication, so there's not yet a certificate store " +
148-
"to test against; just making sure that an attempt is made to create a CertificateAuthContext");
162+
assertEquals("Unable to create CertificateAuthContext; cause not.found (No such file or directory)",
163+
ex.getMessage(), "Should fail because the certificate file path is not valid, and thus a keystore " +
164+
"cannot be created from it.");
165+
}
166+
167+
@Test
168+
void certificateWithNoFile() throws NoSuchAlgorithmException {
169+
SSLContext defaultContext = SSLContext.getDefault();
170+
X509TrustManager trustManager = new SimpleX509TrustManager();
171+
DatabaseClientBuilder builder = Common.newClientBuilder()
172+
.withCertificateAuth(defaultContext, trustManager)
173+
.withSSLHostnameVerifier(DatabaseClientFactory.SSLHostnameVerifier.STRICT);
174+
175+
// Verify the SSL-related objects are the same ones passed in above.
176+
DatabaseClientFactory.Bean bean = builder.buildBean();
177+
assertSame(defaultContext, bean.getSecurityContext().getSSLContext());
178+
assertSame(trustManager, bean.getSecurityContext().getTrustManager());
179+
assertSame(DatabaseClientFactory.SSLHostnameVerifier.STRICT, bean.getSecurityContext().getSSLHostnameVerifier());
180+
181+
DatabaseClient client = bean.newClient();
182+
assertNotNull(client, "The client can be instantiated because a certificate file and password aren't " +
183+
"required. In this scenario, it's expected that a user will provide their own SSLContext to use for " +
184+
"certificate authentication.");
149185
}
150186

151187
@Test

Diff for: marklogic-client-api/src/test/java/com/marklogic/client/test/junit5/RequireSSLExtension.java

+40
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,17 @@
55
import com.marklogic.mgmt.ManageClient;
66
import com.marklogic.mgmt.resource.appservers.ServerManager;
77
import com.marklogic.mgmt.resource.security.CertificateTemplateManager;
8+
import com.marklogic.rest.util.Fragment;
89
import org.junit.jupiter.api.extension.AfterAllCallback;
910
import org.junit.jupiter.api.extension.BeforeAllCallback;
1011
import org.junit.jupiter.api.extension.ExtensionContext;
1112

13+
import javax.net.ssl.X509TrustManager;
14+
import java.io.ByteArrayInputStream;
15+
import java.security.cert.CertificateException;
16+
import java.security.cert.CertificateFactory;
17+
import java.security.cert.X509Certificate;
18+
1219
/**
1320
* Use this on tests that require an app server to require SSL connections. The app server will be modified to require
1421
* SSL connections before any test runs and will then be restored back to normal after all tests in the test class run.
@@ -52,6 +59,39 @@ public void afterAll(ExtensionContext context) {
5259
new CertificateTemplateManager(manageClient).delete(TEMPLATE);
5360
}
5461

62+
/**
63+
* @return a trust manager that accepts the public certificate associated with the certificate template created
64+
* by this class.
65+
*/
66+
public static X509TrustManager newTrustManager() {
67+
return new X509TrustManager() {
68+
@Override
69+
public void checkClientTrusted(X509Certificate[] chain, String authType) {
70+
}
71+
72+
@Override
73+
public void checkServerTrusted(X509Certificate[] chain, String authType) {
74+
}
75+
76+
@Override
77+
public X509Certificate[] getAcceptedIssuers() {
78+
return new X509Certificate[]{getCertificate()};
79+
}
80+
};
81+
}
82+
83+
private static X509Certificate getCertificate() {
84+
CertificateTemplateManager mgr = new CertificateTemplateManager(Common.newManageClient());
85+
86+
Fragment response = mgr.getCertificatesForTemplate(TEMPLATE_NAME);
87+
String cert = response.getElementValue("/msec:certificate-list/msec:certificate/msec:pem");
88+
try {
89+
return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(new ByteArrayInputStream(cert.getBytes()));
90+
} catch (CertificateException e) {
91+
throw new RuntimeException("Unable to generate X509Certificate: " + e.getMessage(), e);
92+
}
93+
}
94+
5595
private void setSslCertificateTemplate(String templateName) {
5696
new ServerManager(manageClient).save(
5797
Common.newServerPayload()
2.34 KB
Binary file not shown.

Diff for: pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ It is not intended to be used to build this project.
1111
<modelVersion>4.0.0</modelVersion>
1212
<groupId>com.marklogic</groupId>
1313
<artifactId>marklogic-client-api</artifactId>
14-
<version>6.2.1</version>
14+
<version>6.2.2</version>
1515
<dependencies>
1616
<dependency>
1717
<groupId>javax.xml.bind</groupId>

Diff for: test-app/build.gradle

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ plugins {
55
}
66

77
dependencies {
8-
implementation "io.undertow:undertow-core:2.2.21.Final"
9-
implementation "io.undertow:undertow-servlet:2.2.21.Final"
8+
implementation "io.undertow:undertow-core:2.2.24.Final"
9+
implementation "io.undertow:undertow-servlet:2.2.24.Final"
1010
implementation "com.marklogic:ml-javaclient-util:4.4.0"
1111
implementation 'org.slf4j:slf4j-api:1.7.36'
1212
implementation 'ch.qos.logback:logback-classic:1.3.5'

0 commit comments

Comments
 (0)