Skip to content

Commit a693322

Browse files
committed
Accept SSL certificates by providing a URL to use cert from within a jar
[resolves pgjdbc#313]
1 parent a66f620 commit a693322

File tree

5 files changed

+64
-40
lines changed

5 files changed

+64
-40
lines changed

src/main/java/io/r2dbc/postgresql/PostgresqlConnectionConfiguration.java

+25-15
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@
3434

3535
import javax.net.ssl.HostnameVerifier;
3636
import java.io.File;
37+
import java.net.MalformedURLException;
3738
import java.net.Socket;
39+
import java.net.URL;
3840
import java.time.Duration;
3941
import java.util.ArrayList;
4042
import java.util.Collections;
@@ -331,20 +333,20 @@ public static final class Builder {
331333
private String socket;
332334

333335
@Nullable
334-
private String sslCert = null;
336+
private URL sslCert = null;
335337

336338
private HostnameVerifier sslHostnameVerifier = DefaultHostnameVerifier.INSTANCE;
337339

338340
@Nullable
339-
private String sslKey = null;
341+
private URL sslKey = null;
340342

341343
private SSLMode sslMode = SSLMode.DISABLE;
342344

343345
@Nullable
344346
private CharSequence sslPassword = null;
345347

346348
@Nullable
347-
private String sslRootCert = null;
349+
private URL sslRootCert = null;
348350

349351
private Function<SslContextBuilder, SslContextBuilder> sslContextBuilderCustomizer = Function.identity();
350352

@@ -637,8 +639,8 @@ public Builder sslContextBuilderCustomizer(Function<SslContextBuilder, SslContex
637639
* @param sslCert an X.509 certificate chain file in PEM format
638640
* @return this {@link Builder}
639641
*/
640-
public Builder sslCert(String sslCert) {
641-
this.sslCert = Assert.requireFileExistsOrNull(sslCert, "sslCert must not be null and must exist");
642+
public Builder sslCert(URL sslCert) {
643+
this.sslCert = Assert.requireUrlExistsOrNull(sslCert, "sslCert must not be null and must exist");
642644
return this;
643645
}
644646

@@ -659,8 +661,8 @@ public Builder sslHostnameVerifier(HostnameVerifier sslHostnameVerifier) {
659661
* @param sslKey a PKCS#8 private key file in PEM format
660662
* @return this {@link Builder}
661663
*/
662-
public Builder sslKey(String sslKey) {
663-
this.sslKey = Assert.requireFileExistsOrNull(sslKey, "sslKey must not be null and must exist");
664+
public Builder sslKey(URL sslKey) {
665+
this.sslKey = Assert.requireUrlExistsOrNull(sslKey, "sslKey must not be null and must exist");
664666
return this;
665667
}
666668

@@ -692,8 +694,8 @@ public Builder sslPassword(@Nullable CharSequence sslPassword) {
692694
* @param sslRootCert an X.509 certificate chain file in PEM format
693695
* @return this {@link Builder}
694696
*/
695-
public Builder sslRootCert(String sslRootCert) {
696-
this.sslRootCert = Assert.requireFileExistsOrNull(sslRootCert, "sslRootCert must not be null and must exist");
697+
public Builder sslRootCert(URL sslRootCert) {
698+
this.sslRootCert = Assert.requireUrlExistsOrNull(sslRootCert, "sslRootCert must not be null and must exist");
697699
return this;
698700
}
699701

@@ -779,14 +781,14 @@ private Supplier<SslProvider> createSslProvider() {
779781
SslContextBuilder sslContextBuilder = SslContextBuilder.forClient();
780782
if (this.sslMode.verifyCertificate()) {
781783
if (this.sslRootCert != null) {
782-
sslContextBuilder.trustManager(new File(this.sslRootCert));
784+
sslContextBuilder.trustManager(new File(this.sslRootCert.getFile()));
783785
}
784786
} else {
785787
sslContextBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE);
786788
}
787789

788-
String sslKey = this.sslKey;
789-
String sslCert = this.sslCert;
790+
URL sslKey = this.sslKey;
791+
URL sslCert = this.sslCert;
790792

791793
// Emulate Libpq behavior
792794
// Determining the default file location
@@ -801,20 +803,28 @@ private Supplier<SslProvider> createSslProvider() {
801803
if (sslCert == null) {
802804
String pathname = defaultDir + "postgresql.crt";
803805
if (new File(pathname).exists()) {
804-
sslCert = pathname;
806+
try {
807+
sslCert = new URL(pathname);
808+
} catch (MalformedURLException e) {
809+
throw new RuntimeException(e);
810+
}
805811
}
806812
}
807813

808814
if (sslKey == null) {
809815
String pathname = defaultDir + "postgresql.pk8";
810816
if (new File(pathname).exists()) {
811-
sslKey = pathname;
817+
try {
818+
sslKey = new URL(pathname);
819+
} catch (MalformedURLException e) {
820+
throw new RuntimeException(e);
821+
}
812822
}
813823
}
814824

815825
if (sslKey != null && sslCert != null) {
816826
String sslPassword = this.sslPassword == null ? null : this.sslPassword.toString();
817-
sslContextBuilder.keyManager(new File(sslCert), new File(sslKey), sslPassword);
827+
sslContextBuilder.keyManager(new File(sslCert.getFile()), new File(sslKey.getFile()), sslPassword);
818828
}
819829

820830
return () -> SslProvider.builder()

src/main/java/io/r2dbc/postgresql/PostgresqlConnectionFactoryProvider.java

+6-5
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import io.r2dbc.spi.Option;
2727

2828
import javax.net.ssl.HostnameVerifier;
29+
import java.net.URL;
2930
import java.util.LinkedHashMap;
3031
import java.util.Map;
3132
import java.util.function.Function;
@@ -107,9 +108,9 @@ public final class PostgresqlConnectionFactoryProvider implements ConnectionFact
107108
public static final Option<Function<SslContextBuilder, SslContextBuilder>> SSL_CONTEXT_BUILDER_CUSTOMIZER = Option.valueOf("sslContextBuilderCustomizer");
108109

109110
/**
110-
* Full path for the certificate file.
111+
* File path for the certificate file.
111112
*/
112-
public static final Option<String> SSL_CERT = Option.valueOf("sslCert");
113+
public static final Option<URL> SSL_CERT = Option.valueOf("sslCert");
113114

114115
/**
115116
* Class name of hostname verifier. Defaults to {@link DefaultHostnameVerifier}.
@@ -119,7 +120,7 @@ public final class PostgresqlConnectionFactoryProvider implements ConnectionFact
119120
/**
120121
* Full path for the key file.
121122
*/
122-
public static final Option<String> SSL_KEY = Option.valueOf("sslKey");
123+
public static final Option<URL> SSL_KEY = Option.valueOf("sslKey");
123124

124125
/**
125126
* Ssl mode. Default: disabled
@@ -132,9 +133,9 @@ public final class PostgresqlConnectionFactoryProvider implements ConnectionFact
132133
public static final Option<String> SSL_PASSWORD = Option.valueOf("sslPassword");
133134

134135
/**
135-
* File name of the SSL root certificate.
136+
* File path of the SSL root certificate.
136137
*/
137-
public static final Option<String> SSL_ROOT_CERT = Option.valueOf("sslRootCert");
138+
public static final Option<URL> SSL_ROOT_CERT = Option.valueOf("sslRootCert");
138139

139140
/**
140141
* Enable TCP KeepAlive.

src/main/java/io/r2dbc/postgresql/util/Assert.java

+20
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import reactor.util.annotation.Nullable;
2020

2121
import java.io.File;
22+
import java.net.URL;
2223

2324
/**
2425
* Assertion library for the implementation.
@@ -147,4 +148,23 @@ public static void isTrue(boolean expression, String message) {
147148
}
148149
}
149150

151+
/**
152+
* Checks that the provided URL exists or null.
153+
*
154+
* @param url url to check
155+
* @param message the message to use in exception if type is not as required
156+
* @return existing url
157+
* @throws IllegalArgumentException if {@code value} is not of the required type
158+
* @throws IllegalArgumentException if {@code value}, {@code type}, or {@code message} is {@code null}
159+
*/
160+
public static URL requireUrlExistsOrNull(@Nullable URL url, String message) {
161+
if (url == null) {
162+
throw new IllegalArgumentException(message);
163+
}
164+
if (!new File(url.getFile()).exists()) {
165+
throw new IllegalArgumentException(message);
166+
}
167+
return url;
168+
}
169+
150170
}

src/test/java/io/r2dbc/postgresql/client/ReactorNettyClientIntegrationTests.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -727,8 +727,8 @@ void verifyCa() {
727727
void verifyCaWithCustomizer() {
728728
client(
729729
c -> c.sslContextBuilderCustomizer(sslContextBuilder -> {
730-
return sslContextBuilder.trustManager(new File(SERVER.getServerCrt()))
731-
.keyManager(new File(SERVER.getClientCrt()), new File(SERVER.getClientKey()));
730+
return sslContextBuilder.trustManager(new File(SERVER.getServerCrt().getFile()))
731+
.keyManager(new File(SERVER.getClientCrt().getFile()), new File(SERVER.getClientKey().getFile()));
732732
})
733733
.sslMode(SSLMode.VERIFY_CA),
734734
c -> c

src/test/java/io/r2dbc/postgresql/util/PostgresqlServerExtension.java

+11-18
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,7 @@
3333
import java.io.File;
3434
import java.io.FileReader;
3535
import java.io.IOException;
36-
import java.net.URISyntaxException;
3736
import java.net.URL;
38-
import java.nio.file.Path;
39-
import java.nio.file.Paths;
4037
import java.util.Properties;
4138
import java.util.function.Supplier;
4239

@@ -134,12 +131,12 @@ private void initializeConnectors() {
134131
this.jdbcOperations = new JdbcTemplate(this.dataSource);
135132
}
136133

137-
public String getClientCrt() {
138-
return getResourcePath("client.crt").toAbsolutePath().toString();
134+
public URL getClientCrt() {
135+
return getResourceUrl("client.crt");
139136
}
140137

141-
public String getClientKey() {
142-
return getResourcePath("client.key").toAbsolutePath().toString();
138+
public URL getClientKey() {
139+
return getResourceUrl("client.key");
143140
}
144141

145142
public PostgresqlConnectionConfiguration.Builder configBuilder() {
@@ -171,12 +168,12 @@ public int getPort() {
171168
return this.postgres.getPort();
172169
}
173170

174-
public String getServerCrt() {
175-
return getResourcePath("server.crt").toAbsolutePath().toString();
171+
public URL getServerCrt() {
172+
return getResourceUrl("server.crt");
176173
}
177174

178-
public String getServerKey() {
179-
return getResourcePath("server.key").toAbsolutePath().toString();
175+
public URL getServerKey() {
176+
return getResourceUrl("server.key");
180177
}
181178

182179
public String getUsername() {
@@ -207,22 +204,18 @@ private <T extends PostgreSQLContainer<T>> T container() {
207204
return container;
208205
}
209206

210-
private Path getResourcePath(String name) {
207+
private URL getResourceUrl(String name) {
211208

212209
URL resource = getClass().getClassLoader().getResource(name);
213210
if (resource == null) {
214211
throw new IllegalStateException("Resource not found: " + name);
215212
}
216213

217-
try {
218-
return Paths.get(resource.toURI());
219-
} catch (URISyntaxException e) {
220-
throw new IllegalStateException("Cannot convert to path for: " + name, e);
221-
}
214+
return resource;
222215
}
223216

224217
private MountableFile getHostPath(String name, int mode) {
225-
return forHostPath(getResourcePath(name), mode);
218+
return forHostPath(getResourceUrl(name).getPath(), mode);
226219
}
227220

228221
/**

0 commit comments

Comments
 (0)