16
16
17
17
package io .r2dbc .mssql ;
18
18
19
+ import io .netty .handler .ssl .SslContextBuilder ;
19
20
import io .r2dbc .mssql .client .ClientConfiguration ;
21
+ import io .r2dbc .mssql .client .ssl .ExpectedHostnameX509TrustManager ;
22
+ import io .r2dbc .mssql .client .ssl .TrustAllTrustManager ;
20
23
import io .r2dbc .mssql .codec .DefaultCodecs ;
21
24
import io .r2dbc .mssql .message .tds .Redirect ;
22
25
import io .r2dbc .mssql .util .Assert ;
23
26
import io .r2dbc .mssql .util .StringUtils ;
24
27
import reactor .netty .resources .ConnectionProvider ;
28
+ import reactor .netty .tcp .SslProvider ;
25
29
import reactor .util .annotation .Nullable ;
26
30
31
+ import javax .net .ssl .TrustManager ;
32
+ import javax .net .ssl .TrustManagerFactory ;
33
+ import javax .net .ssl .X509TrustManager ;
27
34
import java .net .InetAddress ;
28
35
import java .net .UnknownHostException ;
36
+ import java .security .GeneralSecurityException ;
37
+ import java .security .KeyStore ;
29
38
import java .time .Duration ;
30
39
import java .util .Optional ;
31
40
import java .util .UUID ;
41
+ import java .util .function .Function ;
32
42
import java .util .function .Predicate ;
33
43
44
+ import static reactor .netty .tcp .SslProvider .DefaultConfigurationType .TCP ;
45
+
34
46
/**
35
47
* Connection configuration information for connecting to a Microsoft SQL database.
36
48
* Allows configuration of the connection endpoint, login credentials, database and trace details such as application name and connection Id.
@@ -73,10 +85,13 @@ public final class MssqlConnectionConfiguration {
73
85
74
86
private final boolean ssl ;
75
87
88
+ private final Function <SslContextBuilder , SslContextBuilder > sslContextBuilderCustomizer ;
89
+
76
90
private final String username ;
77
91
78
92
private MssqlConnectionConfiguration (@ Nullable String applicationName , @ Nullable UUID connectionId , Duration connectTimeout , @ Nullable String database , String host , String hostNameInCertificate
79
- , CharSequence password , Predicate <String > preferCursoredExecution , int port , boolean sendStringParametersAsUnicode , boolean ssl , String username ) {
93
+ , CharSequence password , Predicate <String > preferCursoredExecution , int port , boolean sendStringParametersAsUnicode , boolean ssl ,
94
+ Function <SslContextBuilder , SslContextBuilder > sslContextBuilderCustomizer , String username ) {
80
95
81
96
this .applicationName = applicationName ;
82
97
this .connectionId = connectionId ;
@@ -89,6 +104,7 @@ private MssqlConnectionConfiguration(@Nullable String applicationName, @Nullable
89
104
this .port = port ;
90
105
this .sendStringParametersAsUnicode = sendStringParametersAsUnicode ;
91
106
this .ssl = ssl ;
107
+ this .sslContextBuilderCustomizer = sslContextBuilderCustomizer ;
92
108
this .username = Assert .requireNonNull (username , "username must not be null" );
93
109
}
94
110
@@ -126,11 +142,11 @@ MssqlConnectionConfiguration withRedirect(Redirect redirect) {
126
142
}
127
143
128
144
return new MssqlConnectionConfiguration (this .applicationName , this .connectionId , this .connectTimeout , this .database , redirectServerName , hostNameInCertificate , this .password ,
129
- this .preferCursoredExecution , redirect .getPort (), this .sendStringParametersAsUnicode , this .ssl , this .username );
145
+ this .preferCursoredExecution , redirect .getPort (), this .sendStringParametersAsUnicode , this .ssl , this .sslContextBuilderCustomizer , this . username );
130
146
}
131
147
132
148
ClientConfiguration toClientConfiguration () {
133
- return new DefaultClientConfiguration (this .connectTimeout , this .host , this .hostNameInCertificate , this .port , this .ssl );
149
+ return new DefaultClientConfiguration (this .connectTimeout , this .host , this .hostNameInCertificate , this .port , this .ssl , sslContextBuilderCustomizer );
134
150
}
135
151
136
152
ConnectionOptions toConnectionOptions () {
@@ -152,6 +168,7 @@ public String toString() {
152
168
sb .append (", port=" ).append (this .port );
153
169
sb .append (", sendStringParametersAsUnicode=" ).append (this .sendStringParametersAsUnicode );
154
170
sb .append (", ssl=" ).append (this .ssl );
171
+ sb .append (", sslContextBuilderCustomizer=" ).append (this .sslContextBuilderCustomizer );
155
172
sb .append (", username=\" " ).append (this .username ).append ('\"' );
156
173
sb .append (']' );
157
174
return sb .toString ();
@@ -280,6 +297,8 @@ public static final class Builder {
280
297
281
298
private boolean ssl ;
282
299
300
+ private Function <SslContextBuilder , SslContextBuilder > sslContextBuilderCustomizer = Function .identity ();
301
+
283
302
private String username ;
284
303
285
304
private Builder () {
@@ -428,6 +447,21 @@ public Builder sendStringParametersAsUnicode(boolean sendStringParametersAsUnico
428
447
return this ;
429
448
}
430
449
450
+ /**
451
+ * Configure a {@link SslContextBuilder} customizer. The customizer gets applied on each SSL connection attempt to allow for just-in-time configuration updates. The {@link Function} gets
452
+ * called with the prepared {@link SslContextBuilder} that has all configuration options applied. The customizer may return the same builder or return a new builder instance to be used to
453
+ * build the SSL context.
454
+ *
455
+ * @param sslContextBuilderCustomizer customizer function
456
+ * @return this {@link Builder}
457
+ * @throws IllegalArgumentException if {@code sslContextBuilderCustomizer} is {@code null}
458
+ * @since 0.8.3
459
+ */
460
+ public Builder sslContextBuilderCustomizer (Function <SslContextBuilder , SslContextBuilder > sslContextBuilderCustomizer ) {
461
+ this .sslContextBuilderCustomizer = Assert .requireNonNull (sslContextBuilderCustomizer , "sslContextBuilderCustomizer must not be null" );
462
+ return this ;
463
+ }
464
+
431
465
/**
432
466
* Configure the username.
433
467
*
@@ -453,7 +487,7 @@ public MssqlConnectionConfiguration build() {
453
487
454
488
return new MssqlConnectionConfiguration (this .applicationName , this .connectionId , this .connectTimeout , this .database , this .host , this .hostNameInCertificate , this .password ,
455
489
this .preferCursoredExecution , this .port ,
456
- this .sendStringParametersAsUnicode , this .ssl , this .username );
490
+ this .sendStringParametersAsUnicode , this .ssl , this .sslContextBuilderCustomizer , this . username );
457
491
}
458
492
}
459
493
@@ -469,13 +503,17 @@ private static class DefaultClientConfiguration implements ClientConfiguration {
469
503
470
504
private final boolean ssl ;
471
505
472
- DefaultClientConfiguration (Duration connectTimeout , String host , String hostNameInCertificate , int port , boolean ssl ) {
506
+ private final Function <SslContextBuilder , SslContextBuilder > sslContextBuilderCustomizer ;
507
+
508
+ DefaultClientConfiguration (Duration connectTimeout , String host , String hostNameInCertificate , int port , boolean ssl ,
509
+ Function <SslContextBuilder , SslContextBuilder > sslContextBuilderCustomizer ) {
473
510
474
511
this .connectTimeout = connectTimeout ;
475
512
this .host = host ;
476
513
this .hostNameInCertificate = hostNameInCertificate ;
477
514
this .port = port ;
478
515
this .ssl = ssl ;
516
+ this .sslContextBuilderCustomizer = sslContextBuilderCustomizer ;
479
517
}
480
518
481
519
@ Override
@@ -504,8 +542,29 @@ public boolean isSslEnabled() {
504
542
}
505
543
506
544
@ Override
507
- public String getHostNameInCertificate () {
508
- return this .hostNameInCertificate ;
545
+ public SslProvider getSslProvider () throws GeneralSecurityException {
546
+
547
+ SslContextBuilder sslContextBuilder = SslContextBuilder .forClient ();
548
+
549
+ TrustManagerFactory tmf = TrustManagerFactory .getInstance (TrustManagerFactory .getDefaultAlgorithm ());
550
+ KeyStore ks = null ;
551
+ tmf .init (ks );
552
+
553
+ TrustManager [] trustManagers = tmf .getTrustManagers ();
554
+ TrustManager result ;
555
+
556
+ if (isSslEnabled ()) {
557
+ result = new ExpectedHostnameX509TrustManager ((X509TrustManager ) trustManagers [0 ], this .hostNameInCertificate );
558
+ } else {
559
+ result = TrustAllTrustManager .INSTANCE ;
560
+ }
561
+
562
+ sslContextBuilder .trustManager (result );
563
+
564
+ return SslProvider .builder ()
565
+ .sslContext (this .sslContextBuilderCustomizer .apply (sslContextBuilder ))
566
+ .defaultConfiguration (TCP )
567
+ .build ();
509
568
}
510
569
}
511
570
}
0 commit comments