Skip to content

Commit eee2fe3

Browse files
committed
Baseline for changes
1 parent 0f04025 commit eee2fe3

File tree

8 files changed

+521
-61
lines changed

8 files changed

+521
-61
lines changed

driver/src/main/java/org/neo4j/driver/Config.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,11 @@ public class Config
8787

8888
/** Indicator for encrypted traffic */
8989
private final boolean encrypted;
90+
private final boolean isEncryptedUserConfigured;
9091

9192
/** Strategy for how to trust encryption certificate */
9293
private final TrustStrategy trustStrategy;
94+
private final boolean isTrustStrategyUserConfigured;
9395

9496
private final int routingFailureLimit;
9597
private final long routingRetryDelayMillis;
@@ -114,7 +116,9 @@ private Config( ConfigBuilder builder )
114116
this.connectionAcquisitionTimeoutMillis = builder.connectionAcquisitionTimeoutMillis;
115117

116118
this.encrypted = builder.encrypted;
119+
this.isEncryptedUserConfigured = builder.isEncryptedUserConfigured;
117120
this.trustStrategy = builder.trustStrategy;
121+
this.isTrustStrategyUserConfigured = builder.isTrustStrategyUserConfigured;
118122
this.routingFailureLimit = builder.routingFailureLimit;
119123
this.routingRetryDelayMillis = builder.routingRetryDelayMillis;
120124
this.connectionTimeoutMillis = builder.connectionTimeoutMillis;
@@ -193,6 +197,11 @@ public boolean encrypted()
193197
return encrypted;
194198
}
195199

200+
public boolean isEncryptedUserConfigured()
201+
{
202+
return isEncryptedUserConfigured;
203+
}
204+
196205
/**
197206
* @return the strategy to use to determine the authenticity of an encryption certificate provided by the Neo4j instance we are connecting to.
198207
*/
@@ -201,6 +210,11 @@ public TrustStrategy trustStrategy()
201210
return trustStrategy;
202211
}
203212

213+
public boolean isTrustStrategyUserConfigured()
214+
{
215+
return isTrustStrategyUserConfigured;
216+
}
217+
204218
/**
205219
* Server address resolver.
206220
*
@@ -269,7 +283,9 @@ public static class ConfigBuilder
269283
private long maxConnectionLifetimeMillis = PoolSettings.DEFAULT_MAX_CONNECTION_LIFETIME;
270284
private long connectionAcquisitionTimeoutMillis = PoolSettings.DEFAULT_CONNECTION_ACQUISITION_TIMEOUT;
271285
private boolean encrypted = false;
286+
private boolean isEncryptedUserConfigured = false;
272287
private TrustStrategy trustStrategy = trustSystemCertificates();
288+
private boolean isTrustStrategyUserConfigured = false;
273289
private int routingFailureLimit = RoutingSettings.DEFAULT.maxRoutingFailures();
274290
private long routingRetryDelayMillis = RoutingSettings.DEFAULT.retryTimeoutDelay();
275291
private long routingTablePurgeDelayMillis = RoutingSettings.DEFAULT.routingTablePurgeDelayMs();
@@ -444,6 +460,7 @@ public ConfigBuilder withConnectionAcquisitionTimeout( long value, TimeUnit unit
444460
public ConfigBuilder withEncryption()
445461
{
446462
this.encrypted = true;
463+
this.isEncryptedUserConfigured = true;
447464
return this;
448465
}
449466

@@ -454,6 +471,7 @@ public ConfigBuilder withEncryption()
454471
public ConfigBuilder withoutEncryption()
455472
{
456473
this.encrypted = false;
474+
this.isEncryptedUserConfigured = true;
457475
return this;
458476
}
459477

@@ -474,6 +492,7 @@ public ConfigBuilder withoutEncryption()
474492
public ConfigBuilder withTrustStrategy( TrustStrategy trustStrategy )
475493
{
476494
this.trustStrategy = trustStrategy;
495+
this.isTrustStrategyUserConfigured = true;
477496
return this;
478497
}
479498

driver/src/main/java/org/neo4j/driver/internal/DriverFactory.java

Lines changed: 93 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.io.IOException;
2727
import java.net.URI;
2828
import java.security.GeneralSecurityException;
29+
import java.security.NoSuchAlgorithmException;
2930

3031
import org.neo4j.driver.AuthToken;
3132
import org.neo4j.driver.AuthTokens;
@@ -50,8 +51,8 @@
5051
import org.neo4j.driver.internal.retry.ExponentialBackoffRetryLogic;
5152
import org.neo4j.driver.internal.retry.RetryLogic;
5253
import org.neo4j.driver.internal.retry.RetrySettings;
53-
import org.neo4j.driver.internal.security.SecurityPlanImpl;
5454
import org.neo4j.driver.internal.security.SecurityPlan;
55+
import org.neo4j.driver.internal.security.SecurityPlanImpl;
5556
import org.neo4j.driver.internal.spi.ConnectionPool;
5657
import org.neo4j.driver.internal.spi.ConnectionProvider;
5758
import org.neo4j.driver.internal.util.Clock;
@@ -67,7 +68,12 @@
6768
public class DriverFactory
6869
{
6970
public static final String BOLT_URI_SCHEME = "bolt";
71+
public static final String BOLT_HIGH_TRUST_URI_SCHEME = "bolt+s";
72+
public static final String BOLT_LOW_TRUST_URI_SCHEME = "bolt+ssc";
7073
public static final String BOLT_ROUTING_URI_SCHEME = "neo4j";
74+
public static final String BOLT_ROUTING_HIGH_TRUST_URI_SCHEME = "neo4j+s";
75+
public static final String BOLT_ROUTING_LOW_TRUST_URI_SCHEME = "neo4j+ssc";
76+
7177

7278
public final Driver newInstance( URI uri, AuthToken authToken, RoutingSettings routingSettings,
7379
RetrySettings retrySettings, Config config )
@@ -103,7 +109,7 @@ public final Driver newInstance ( URI uri, AuthToken authToken, RoutingSettings
103109
}
104110
else
105111
{
106-
securityPlan = createSecurityPlan( address, config );
112+
securityPlan = createSecurityPlan( uri, config );
107113
}
108114

109115
InternalLoggerFactory.setDefaultFactory( new NettyLogging( config.logging() ) );
@@ -148,17 +154,22 @@ protected ChannelConnector createConnector( ConnectionSettings settings, Securit
148154
}
149155

150156
private InternalDriver createDriver( URI uri, SecurityPlan securityPlan, BoltServerAddress address, ConnectionPool connectionPool,
151-
EventExecutorGroup eventExecutorGroup, RoutingSettings routingSettings, RetryLogic retryLogic, MetricsProvider metricsProvider, Config config )
157+
EventExecutorGroup eventExecutorGroup, RoutingSettings routingSettings, RetryLogic retryLogic,
158+
MetricsProvider metricsProvider, Config config )
152159
{
153160
try
154161
{
155162
String scheme = uri.getScheme().toLowerCase();
156163
switch ( scheme )
157164
{
158165
case BOLT_URI_SCHEME:
166+
case BOLT_LOW_TRUST_URI_SCHEME:
167+
case BOLT_HIGH_TRUST_URI_SCHEME:
159168
assertNoRoutingContext( uri, routingSettings );
160169
return createDirectDriver( securityPlan, address, connectionPool, retryLogic, metricsProvider, config );
161170
case BOLT_ROUTING_URI_SCHEME:
171+
case BOLT_ROUTING_LOW_TRUST_URI_SCHEME:
172+
case BOLT_ROUTING_HIGH_TRUST_URI_SCHEME:
162173
return createRoutingDriver( securityPlan, address, connectionPool, eventExecutorGroup, routingSettings, retryLogic, metricsProvider, config );
163174
default:
164175
throw new ClientException( format( "Unsupported URI scheme: %s", scheme ) );
@@ -287,11 +298,11 @@ protected Bootstrap createBootstrap( EventLoopGroup eventLoopGroup )
287298
return BootstrapFactory.newBootstrap( eventLoopGroup );
288299
}
289300

290-
private static SecurityPlan createSecurityPlan( BoltServerAddress address, Config config )
301+
private static SecurityPlan createSecurityPlan( URI uri, Config config )
291302
{
292303
try
293304
{
294-
return createSecurityPlanImpl( config );
305+
return createSecurityPlanImpl( config, uri );
295306
}
296307
catch ( GeneralSecurityException | IOException ex )
297308
{
@@ -301,31 +312,50 @@ private static SecurityPlan createSecurityPlan( BoltServerAddress address, Confi
301312

302313
/*
303314
* Establish a complete SecurityPlan based on the details provided for
304-
* driver construction.
315+
* driver construction based off the scheme and configured properties.
305316
*/
306-
private static SecurityPlan createSecurityPlanImpl( Config config )
317+
private static SecurityPlan createSecurityPlanImpl( Config config, URI uri )
307318
throws GeneralSecurityException, IOException
308319
{
309-
if ( config.encrypted() )
320+
String scheme = uri.getScheme().toLowerCase();
321+
322+
if ( scheme.equals( BOLT_URI_SCHEME ) || scheme.equals( BOLT_ROUTING_URI_SCHEME ) )
310323
{
311-
Config.TrustStrategy trustStrategy = config.trustStrategy();
312-
boolean hostnameVerificationEnabled = trustStrategy.isHostnameVerificationEnabled();
313-
switch ( trustStrategy.strategy() )
324+
if ( config.encrypted() )
314325
{
315-
case TRUST_CUSTOM_CA_SIGNED_CERTIFICATES:
316-
return SecurityPlanImpl.forCustomCASignedCertificates( trustStrategy.certFile(), hostnameVerificationEnabled );
317-
case TRUST_SYSTEM_CA_SIGNED_CERTIFICATES:
318-
return SecurityPlanImpl.forSystemCASignedCertificates( hostnameVerificationEnabled );
319-
case TRUST_ALL_CERTIFICATES:
320-
return SecurityPlanImpl.forAllCertificates( hostnameVerificationEnabled );
321-
default:
322-
throw new ClientException(
323-
"Unknown TLS authentication strategy: " + trustStrategy.strategy().name() );
326+
Config.TrustStrategy trustStrategy = config.trustStrategy();
327+
boolean hostnameVerificationEnabled = trustStrategy.isHostnameVerificationEnabled();
328+
switch ( trustStrategy.strategy() )
329+
{
330+
case TRUST_CUSTOM_CA_SIGNED_CERTIFICATES:
331+
return SecurityPlanImpl.forCustomCASignedCertificates( trustStrategy.certFile(), hostnameVerificationEnabled );
332+
case TRUST_SYSTEM_CA_SIGNED_CERTIFICATES:
333+
return SecurityPlanImpl.forSystemCASignedCertificates( hostnameVerificationEnabled );
334+
case TRUST_ALL_CERTIFICATES:
335+
return SecurityPlanImpl.forAllCertificates( hostnameVerificationEnabled );
336+
default:
337+
throw new ClientException(
338+
"Unknown TLS authentication strategy: " + trustStrategy.strategy().name() );
339+
}
340+
}
341+
else
342+
{
343+
return insecure();
324344
}
325345
}
326346
else
327347
{
328-
return insecure();
348+
switch ( scheme )
349+
{
350+
case BOLT_HIGH_TRUST_URI_SCHEME:
351+
case BOLT_ROUTING_HIGH_TRUST_URI_SCHEME:
352+
return configureHighTrustSecurityPlan( config, scheme );
353+
case BOLT_LOW_TRUST_URI_SCHEME:
354+
case BOLT_ROUTING_LOW_TRUST_URI_SCHEME:
355+
return configureLowTrustSecurityPlan( config, scheme );
356+
default:
357+
throw new ClientException( format( "Unsupported URI scheme: %s", scheme ) );
358+
}
329359
}
330360
}
331361

@@ -350,4 +380,46 @@ private static void closeConnectionPoolAndSuppressError( ConnectionPool connecti
350380
addSuppressed( mainError, closeError );
351381
}
352382
}
383+
384+
private static SecurityPlan configureHighTrustSecurityPlan( Config config, String scheme ) throws NoSuchAlgorithmException
385+
{
386+
if ( config.isEncryptedUserConfigured() && !config.encrypted() )
387+
{
388+
throw new IllegalArgumentException( "Encryption must be enabled for scheme: " + scheme );
389+
}
390+
else if ( config.isTrustStrategyUserConfigured() &&
391+
( config.trustStrategy().strategy().equals( Config.TrustStrategy.Strategy.TRUST_ALL_CERTIFICATES ) ||
392+
config.trustStrategy().strategy().equals( Config.TrustStrategy.Strategy.TRUST_CUSTOM_CA_SIGNED_CERTIFICATES )))
393+
{
394+
throw new IllegalArgumentException( "Scheme: " + scheme + " must use a higher level of trust configuration" );
395+
}
396+
397+
return SecurityPlanImpl.forSystemCASignedCertificates( config.trustStrategy().isHostnameVerificationEnabled() );
398+
}
399+
400+
private static SecurityPlan configureLowTrustSecurityPlan( Config config, String scheme ) throws GeneralSecurityException, IOException
401+
{
402+
if ( config.isEncryptedUserConfigured() && !config.encrypted() )
403+
{
404+
throw new IllegalArgumentException( "Encryption must be enabled for scheme: " + scheme );
405+
}
406+
else if ( config.isTrustStrategyUserConfigured() &&
407+
config.trustStrategy().strategy().equals( Config.TrustStrategy.Strategy.TRUST_SYSTEM_CA_SIGNED_CERTIFICATES ) )
408+
{
409+
throw new IllegalArgumentException( "Scheme: " + scheme + " must use a lower level of trust configuration" );
410+
}
411+
{
412+
if ( !config.isTrustStrategyUserConfigured() ||
413+
(config.isTrustStrategyUserConfigured() && config.trustStrategy().strategy().equals( Config.TrustStrategy.Strategy.TRUST_ALL_CERTIFICATES )) )
414+
415+
{
416+
return SecurityPlanImpl.forAllCertificates( config.trustStrategy().isHostnameVerificationEnabled() );
417+
}
418+
else
419+
{
420+
return SecurityPlanImpl.forCustomCASignedCertificates( config.trustStrategy().certFile(),
421+
config.trustStrategy().isHostnameVerificationEnabled() );
422+
}
423+
}
424+
}
353425
}

driver/src/test/java/org/neo4j/driver/ConfigTest.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,4 +315,36 @@ void shouldErrorWithIllegalEventLoopThreadsSize( int value ) throws Throwable
315315
{
316316
assertThrows( IllegalArgumentException.class, () -> Config.builder().withEventLoopThreads( value ).build() );
317317
}
318+
319+
@Test
320+
void shouldFlagUserChangeOfDefaultEncryption()
321+
{
322+
Config config = Config.builder()
323+
.withoutEncryption()
324+
.build();
325+
assertTrue( config.isEncryptedUserConfigured() );
326+
}
327+
328+
@Test
329+
void shouldFlagDefaultEncryption()
330+
{
331+
Config config = Config.builder().build();
332+
assertFalse( config.isEncryptedUserConfigured() );
333+
}
334+
335+
@Test
336+
void shouldFlagUserChangeOfTestStrategy()
337+
{
338+
Config config = Config.builder()
339+
.withTrustStrategy( Config.TrustStrategy.trustAllCertificates() )
340+
.build();
341+
assertTrue( config.isTrustStrategyUserConfigured() );
342+
}
343+
344+
@Test
345+
void shouldFlagDefaultTustStrategy()
346+
{
347+
Config config = Config.builder().build();
348+
assertFalse( config.isEncryptedUserConfigured() );
349+
}
318350
}

driver/src/test/java/org/neo4j/driver/integration/EncryptionIT.java

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import org.junit.jupiter.api.Test;
2222
import org.junit.jupiter.api.extension.RegisterExtension;
2323

24+
import java.net.URI;
25+
2426
import org.neo4j.driver.Config;
2527
import org.neo4j.driver.Driver;
2628
import org.neo4j.driver.GraphDatabase;
@@ -48,13 +50,13 @@ class EncryptionIT
4850
@Test
4951
void shouldOperateWithNoEncryptionWhenItIsOptionalInTheDatabase()
5052
{
51-
testMatchingEncryption( BoltTlsLevel.OPTIONAL, false );
53+
testMatchingEncryption( BoltTlsLevel.OPTIONAL, false, neo4j.uri().getScheme() );
5254
}
5355

5456
@Test
5557
void shouldOperateWithEncryptionWhenItIsOptionalInTheDatabase()
5658
{
57-
testMatchingEncryption( BoltTlsLevel.OPTIONAL, true );
59+
testMatchingEncryption( BoltTlsLevel.OPTIONAL, true, neo4j.uri().getScheme() );
5860
}
5961

6062
@Test
@@ -66,7 +68,19 @@ void shouldFailWithoutEncryptionWhenItIsRequiredInTheDatabase()
6668
@Test
6769
void shouldOperateWithEncryptionWhenItIsAlsoRequiredInTheDatabase()
6870
{
69-
testMatchingEncryption( BoltTlsLevel.REQUIRED, true );
71+
testMatchingEncryption( BoltTlsLevel.REQUIRED, true, neo4j.uri().getScheme() );
72+
}
73+
74+
@Test
75+
void shouldOperateWithEncryptionWhenConfiguredUsingBoltSscURI()
76+
{
77+
testMatchingEncryption( BoltTlsLevel.REQUIRED, true, "bolt+ssc" );
78+
}
79+
80+
@Test
81+
void shouldOperateWithEncryptionWhenConfiguredUsingNeo4jSscURI()
82+
{
83+
testMatchingEncryption( BoltTlsLevel.REQUIRED, true, "neo4j+ssc" );
7084
}
7185

7286
@Test
@@ -78,15 +92,17 @@ void shouldFailWithEncryptionWhenItIsDisabledInTheDatabase()
7892
@Test
7993
void shouldOperateWithoutEncryptionWhenItIsAlsoDisabledInTheDatabase()
8094
{
81-
testMatchingEncryption( BoltTlsLevel.DISABLED, false );
95+
testMatchingEncryption( BoltTlsLevel.DISABLED, false, neo4j.uri().getScheme() );
8296
}
8397

84-
private void testMatchingEncryption( BoltTlsLevel tlsLevel, boolean driverEncrypted )
98+
private void testMatchingEncryption( BoltTlsLevel tlsLevel, boolean driverEncrypted, String scheme )
8599
{
86100
neo4j.restartDb( Neo4jSettings.TEST_SETTINGS.updateWith( Neo4jSettings.BOLT_TLS_LEVEL, tlsLevel.toString() ) );
87101
Config config = newConfig( driverEncrypted );
88102

89-
try ( Driver driver = GraphDatabase.driver( neo4j.uri(), neo4j.authToken(), config ) )
103+
URI uri = URI.create( String.format( "%s://%s:%s", scheme, neo4j.uri().getHost(), neo4j.uri().getPort()) );
104+
105+
try ( Driver driver = GraphDatabase.driver( uri, neo4j.authToken(), config ) )
90106
{
91107
assertThat( driver.isEncrypted(), equalTo( driverEncrypted ) );
92108

0 commit comments

Comments
 (0)