Skip to content

Commit 5975ce8

Browse files
authored
Merge pull request #1571 from marklogic/feature/508-certificate-bug
DEVEXP-508 Certificate auth no longer requires a file path
2 parents 2475274 + 5ba9519 commit 5975ce8

File tree

5 files changed

+60
-14
lines changed

5 files changed

+60
-14
lines changed

marklogic-client-api/src/main/java/com/marklogic/client/DatabaseClientBuilder.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,12 @@ public DatabaseClientBuilder withCertificateAuth(String file, String password) {
158158
.withCertificatePassword(password);
159159
}
160160

161+
public DatabaseClientBuilder withCertificateAuth(SSLContext sslContext, X509TrustManager trustManager) {
162+
return withAuthType(AUTH_TYPE_CERTIFICATE)
163+
.withSSLContext(sslContext)
164+
.withTrustManager(trustManager);
165+
}
166+
161167
public DatabaseClientBuilder withSAMLAuth(String token) {
162168
return withAuthType(AUTH_TYPE_SAML)
163169
.withSAMLToken(token);

marklogic-client-api/src/main/java/com/marklogic/client/DatabaseClientFactory.java

Lines changed: 2 additions & 2 deletions
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>

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

Lines changed: 12 additions & 8 deletions
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() {

marklogic-client-api/src/test/java/com/marklogic/client/test/DatabaseClientBuilderTest.java

Lines changed: 40 additions & 4 deletions
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
Binary file not shown.

0 commit comments

Comments
 (0)