Skip to content

Commit d4faba1

Browse files
authored
feat: expose sslSocketFactory, sslParameters, and hostnameVerifier on RedisStoreBuilder (#182)
**Expose SSL configuration options on RedisStoreBuilder** The JedisPool constructor accepts sslSocketFactory, sslParameters, and hostnameVerifier for customizing TLS behaviour, but all three were hardcoded to null in RedisStoreImplBase, making them unreachable by callers. This PR exposes all three as builder options on RedisStoreBuilder. **Changes** - Added sslSocketFactory(SSLSocketFactory), sslParameters(SSLParameters), and hostnameVerifier(HostnameVerifier) methods to RedisStoreBuilder - Wired the new fields through to the JedisPool constructor in RedisStoreImplBase - All three are no-ops when TLS is not enabled <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Changes affect how TLS Redis connections are established; misconfigured custom SSL or hostname verification could weaken security, though behavior is unchanged unless callers opt in. > > **Overview** > **RedisStoreBuilder** now lets callers pass **SSLSocketFactory**, **SSLParameters**, and **HostnameVerifier** for TLS Redis connections, matching what **JedisPool** already supports. > > Previously **RedisStoreImplBase** always passed `null` for those three constructor arguments, so custom trust stores, cipher suites, or hostname checks were impossible without forking. The new builder fields are forwarded into **JedisPool**; when TLS is off they are documented as ignored, and unset values still defer to JVM defaults. > > Unit tests cover default nulls and that each setter stores the configured instance. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 86fa0cb. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 9a9941b commit d4faba1

3 files changed

Lines changed: 87 additions & 5 deletions

File tree

lib/java-server-sdk-redis-store/src/main/java/com/launchdarkly/sdk/server/integrations/RedisStoreBuilder.java

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
import java.net.URI;
1212
import java.time.Duration;
1313

14+
import javax.net.ssl.HostnameVerifier;
15+
import javax.net.ssl.SSLParameters;
16+
import javax.net.ssl.SSLSocketFactory;
17+
1418
import redis.clients.jedis.JedisPoolConfig;
1519
import redis.clients.jedis.Protocol;
1620

@@ -56,7 +60,7 @@
5660
* .build();
5761
* </code></pre>
5862
*
59-
* @param <T> the component type that this builder is being used for
63+
* @param <T> the component type that this builder is being used for
6064
*
6165
* @since 5.0.0
6266
*/
@@ -80,6 +84,9 @@ public abstract class RedisStoreBuilder<T> implements ComponentConfigurer<T>, Di
8084
String password = null;
8185
boolean tls = false;
8286
JedisPoolConfig poolConfig = null;
87+
SSLSocketFactory sslSocketFactory = null;
88+
SSLParameters sslParameters = null;
89+
HostnameVerifier hostnameVerifier = null;
8390

8491
// These constructors are called only from Implementations
8592
RedisStoreBuilder() {
@@ -146,7 +153,52 @@ public RedisStoreBuilder<T> tls(boolean tls) {
146153
this.tls = tls;
147154
return this;
148155
}
149-
156+
157+
/**
158+
* Optionally specifies a custom {@link SSLSocketFactory} for TLS connections.
159+
* <p>
160+
* This is only used when TLS is enabled (either via {@link #tls(boolean)} or by using a
161+
* {@code rediss:} URI). If TLS is not enabled this value is silently ignored. If not set,
162+
* the JVM default SSL socket factory is used.
163+
*
164+
* @param sslSocketFactory the SSL socket factory, or null to use the default
165+
* @return the builder
166+
*/
167+
public RedisStoreBuilder<T> sslSocketFactory(SSLSocketFactory sslSocketFactory) {
168+
this.sslSocketFactory = sslSocketFactory;
169+
return this;
170+
}
171+
172+
/**
173+
* Optionally specifies {@link SSLParameters} for TLS connections.
174+
* <p>
175+
* This is only used when TLS is enabled (either via {@link #tls(boolean)} or by using a
176+
* {@code rediss:} URI). If TLS is not enabled this value is silently ignored. If not set,
177+
* the JVM default SSL parameters are used.
178+
*
179+
* @param sslParameters the SSL parameters, or null to use the default
180+
* @return the builder
181+
*/
182+
public RedisStoreBuilder<T> sslParameters(SSLParameters sslParameters) {
183+
this.sslParameters = sslParameters;
184+
return this;
185+
}
186+
187+
/**
188+
* Optionally specifies a {@link HostnameVerifier} for TLS connections.
189+
* <p>
190+
* This is only used when TLS is enabled (either via {@link #tls(boolean)} or by using a
191+
* {@code rediss:} URI). If TLS is not enabled this value is silently ignored. If not set,
192+
* the JVM default hostname verifier is used.
193+
*
194+
* @param hostnameVerifier the hostname verifier, or null to use the default
195+
* @return the builder
196+
*/
197+
public RedisStoreBuilder<T> hostnameVerifier(HostnameVerifier hostnameVerifier) {
198+
this.hostnameVerifier = hostnameVerifier;
199+
return this;
200+
}
201+
150202
/**
151203
* Specifies a Redis host URI other than {@link #DEFAULT_URI}.
152204
*

lib/java-server-sdk-redis-store/src/main/java/com/launchdarkly/sdk/server/integrations/RedisStoreImplBase.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ protected RedisStoreImplBase(RedisStoreBuilder<?> builder, LDLogger logger) {
4040
this.prefix = (builder.prefix == null || builder.prefix.isEmpty()) ?
4141
RedisStoreBuilder.DEFAULT_PREFIX :
4242
builder.prefix;
43+
4344
this.pool = new JedisPool(poolConfig,
4445
host,
4546
port,
@@ -50,9 +51,9 @@ protected RedisStoreImplBase(RedisStoreBuilder<?> builder, LDLogger logger) {
5051
database,
5152
null, // clientName
5253
tls,
53-
null, // sslSocketFactory
54-
null, // sslParameters
55-
null // hostnameVerifier
54+
builder.sslSocketFactory,
55+
builder.sslParameters,
56+
builder.hostnameVerifier
5657
);
5758
}
5859

lib/java-server-sdk-redis-store/src/test/java/com/launchdarkly/sdk/server/integrations/RedisDataStoreBuilderTest.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111
import static org.junit.Assert.assertNull;
1212
import static org.junit.Assert.assertTrue;
1313

14+
import javax.net.ssl.HostnameVerifier;
15+
import javax.net.ssl.HttpsURLConnection;
16+
import javax.net.ssl.SSLParameters;
17+
import javax.net.ssl.SSLSocketFactory;
18+
1419
import redis.clients.jedis.JedisPoolConfig;
1520
import redis.clients.jedis.Protocol;
1621

@@ -23,6 +28,9 @@ public void testDefaultValues() {
2328
assertNull(conf.database);
2429
assertNull(conf.password);
2530
assertFalse(conf.tls);
31+
assertNull(conf.sslSocketFactory);
32+
assertNull(conf.sslParameters);
33+
assertNull(conf.hostnameVerifier);
2634
assertEquals(Duration.ofMillis(Protocol.DEFAULT_TIMEOUT), conf.connectTimeout);
2735
assertEquals(Duration.ofMillis(Protocol.DEFAULT_TIMEOUT), conf.socketTimeout);
2836
assertEquals(RedisStoreBuilder.DEFAULT_PREFIX, conf.prefix);
@@ -53,6 +61,27 @@ public void testTlsConfigured() {
5361
RedisStoreBuilder<?> conf = Redis.dataStore().tls(true);
5462
assertTrue(conf.tls);
5563
}
64+
65+
@Test
66+
public void testSslSocketFactoryConfigured() {
67+
SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
68+
RedisStoreBuilder<?> conf = Redis.dataStore().sslSocketFactory(factory);
69+
assertEquals(factory, conf.sslSocketFactory);
70+
}
71+
72+
@Test
73+
public void testSslParametersConfigured() {
74+
SSLParameters params = new SSLParameters();
75+
RedisStoreBuilder<?> conf = Redis.dataStore().sslParameters(params);
76+
assertEquals(params, conf.sslParameters);
77+
}
78+
79+
@Test
80+
public void testHostnameVerifierConfigured() {
81+
HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier();
82+
RedisStoreBuilder<?> conf = Redis.dataStore().hostnameVerifier(verifier);
83+
assertEquals(verifier, conf.hostnameVerifier);
84+
}
5685

5786
@Test
5887
public void testPrefixConfigured() throws URISyntaxException {

0 commit comments

Comments
 (0)