Skip to content

Extended URI Schemes #685

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 18 additions & 15 deletions driver/src/main/java/org/neo4j/driver/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.neo4j.driver.exceptions.ServiceUnavailableException;
import org.neo4j.driver.exceptions.SessionExpiredException;
import org.neo4j.driver.exceptions.TransientException;
import org.neo4j.driver.internal.SecuritySettings;
import org.neo4j.driver.internal.async.pool.PoolSettings;
import org.neo4j.driver.internal.cluster.RoutingSettings;
import org.neo4j.driver.internal.handlers.pulln.FetchSizeUtil;
Expand All @@ -39,7 +40,6 @@
import org.neo4j.driver.util.Immutable;
import org.neo4j.driver.util.Resource;

import static org.neo4j.driver.Config.TrustStrategy.trustSystemCertificates;
import static org.neo4j.driver.Logging.javaUtilLogging;

/**
Expand Down Expand Up @@ -85,11 +85,7 @@ public class Config
private final long maxConnectionLifetimeMillis;
private final long connectionAcquisitionTimeoutMillis;

/** Indicator for encrypted traffic */
private final boolean encrypted;

/** Strategy for how to trust encryption certificate */
private final TrustStrategy trustStrategy;
private final SecuritySettings securitySettings;

private final int routingFailureLimit;
private final long routingRetryDelayMillis;
Expand All @@ -113,8 +109,8 @@ private Config( ConfigBuilder builder )
this.maxConnectionPoolSize = builder.maxConnectionPoolSize;
this.connectionAcquisitionTimeoutMillis = builder.connectionAcquisitionTimeoutMillis;

this.encrypted = builder.encrypted;
this.trustStrategy = builder.trustStrategy;
this.securitySettings = builder.securitySettingsBuilder.build();

this.routingFailureLimit = builder.routingFailureLimit;
this.routingRetryDelayMillis = builder.routingRetryDelayMillis;
this.connectionTimeoutMillis = builder.connectionTimeoutMillis;
Expand Down Expand Up @@ -190,15 +186,15 @@ public long connectionAcquisitionTimeoutMillis()
*/
public boolean encrypted()
{
return encrypted;
return securitySettings.encrypted();
}

/**
* @return the strategy to use to determine the authenticity of an encryption certificate provided by the Neo4j instance we are connecting to.
*/
public TrustStrategy trustStrategy()
{
return trustStrategy;
return securitySettings.trustStrategy();
}

/**
Expand Down Expand Up @@ -229,6 +225,14 @@ public static Config defaultConfig()
return EMPTY;
}

/**
* @return the security setting to use when creating connections.
*/
SecuritySettings securitySettings()
{
return securitySettings;
}

RoutingSettings routingSettings()
{
return new RoutingSettings( routingFailureLimit, routingRetryDelayMillis, routingTablePurgeDelayMillis );
Expand Down Expand Up @@ -268,8 +272,7 @@ public static class ConfigBuilder
private long idleTimeBeforeConnectionTest = PoolSettings.DEFAULT_IDLE_TIME_BEFORE_CONNECTION_TEST;
private long maxConnectionLifetimeMillis = PoolSettings.DEFAULT_MAX_CONNECTION_LIFETIME;
private long connectionAcquisitionTimeoutMillis = PoolSettings.DEFAULT_CONNECTION_ACQUISITION_TIMEOUT;
private boolean encrypted = false;
private TrustStrategy trustStrategy = trustSystemCertificates();
private final SecuritySettings.SecuritySettingsBuilder securitySettingsBuilder = new SecuritySettings.SecuritySettingsBuilder();
private int routingFailureLimit = RoutingSettings.DEFAULT.maxRoutingFailures();
private long routingRetryDelayMillis = RoutingSettings.DEFAULT.retryTimeoutDelay();
private long routingTablePurgeDelayMillis = RoutingSettings.DEFAULT.routingTablePurgeDelayMs();
Expand Down Expand Up @@ -443,7 +446,7 @@ public ConfigBuilder withConnectionAcquisitionTimeout( long value, TimeUnit unit
*/
public ConfigBuilder withEncryption()
{
this.encrypted = true;
securitySettingsBuilder.withEncryption();
return this;
}

Expand All @@ -453,7 +456,7 @@ public ConfigBuilder withEncryption()
*/
public ConfigBuilder withoutEncryption()
{
this.encrypted = false;
securitySettingsBuilder.withoutEncryption();
return this;
}

Expand All @@ -473,7 +476,7 @@ public ConfigBuilder withoutEncryption()
*/
public ConfigBuilder withTrustStrategy( TrustStrategy trustStrategy )
{
this.trustStrategy = trustStrategy;
securitySettingsBuilder.withTrustStrategy( trustStrategy );
return this;
}

Expand Down
15 changes: 10 additions & 5 deletions driver/src/main/java/org/neo4j/driver/GraphDatabase.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,13 @@

import org.neo4j.driver.exceptions.ServiceUnavailableException;
import org.neo4j.driver.internal.DriverFactory;
import org.neo4j.driver.internal.Scheme;
import org.neo4j.driver.internal.SecuritySettings;
import org.neo4j.driver.internal.cluster.RoutingSettings;
import org.neo4j.driver.internal.retry.RetrySettings;
import org.neo4j.driver.internal.security.SecurityPlan;

import static org.neo4j.driver.internal.DriverFactory.BOLT_ROUTING_URI_SCHEME;
import static org.neo4j.driver.internal.Scheme.NEO4J_URI_SCHEME;

/**
* Creates {@link Driver drivers}, optionally letting you {@link #driver(URI, Config)} to configure them.
Expand Down Expand Up @@ -132,8 +135,10 @@ public static Driver driver( URI uri, AuthToken authToken, Config config )
config = getOrDefault( config );
RoutingSettings routingSettings = config.routingSettings();
RetrySettings retrySettings = config.retrySettings();

return new DriverFactory().newInstance( uri, authToken, routingSettings, retrySettings, config );
SecuritySettings securitySettings = config.securitySettings();
Scheme.validateScheme( uri.getScheme() );
SecurityPlan securityPlan = securitySettings.createSecurityPlan( uri.getScheme() );
return new DriverFactory().newInstance( uri, authToken, routingSettings, retrySettings, config, securityPlan );
}

/**
Expand Down Expand Up @@ -192,10 +197,10 @@ private static void assertRoutingUris( Iterable<URI> uris )
{
for ( URI uri : uris )
{
if ( !BOLT_ROUTING_URI_SCHEME.equals( uri.getScheme() ) )
if ( !NEO4J_URI_SCHEME.equals( uri.getScheme() ) )
{
throw new IllegalArgumentException(
"Illegal URI scheme, expected '" + BOLT_ROUTING_URI_SCHEME + "' in '" + uri + "'" );
"Illegal URI scheme, expected '" + NEO4J_URI_SCHEME + "' in '" + uri + "'" );
}
}
}
Expand Down
79 changes: 12 additions & 67 deletions driver/src/main/java/org/neo4j/driver/internal/DriverFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@
import io.netty.util.concurrent.EventExecutorGroup;
import io.netty.util.internal.logging.InternalLoggerFactory;

import java.io.IOException;
import java.net.URI;
import java.security.GeneralSecurityException;

import org.neo4j.driver.AuthToken;
import org.neo4j.driver.AuthTokens;
Expand All @@ -50,7 +48,6 @@
import org.neo4j.driver.internal.retry.ExponentialBackoffRetryLogic;
import org.neo4j.driver.internal.retry.RetryLogic;
import org.neo4j.driver.internal.retry.RetrySettings;
import org.neo4j.driver.internal.security.SecurityPlanImpl;
import org.neo4j.driver.internal.security.SecurityPlan;
import org.neo4j.driver.internal.spi.ConnectionPool;
import org.neo4j.driver.internal.spi.ConnectionProvider;
Expand All @@ -59,24 +56,22 @@
import org.neo4j.driver.net.ServerAddressResolver;

import static java.lang.String.format;
import static org.neo4j.driver.internal.Scheme.isRoutingScheme;
import static org.neo4j.driver.internal.cluster.IdentityResolver.IDENTITY_RESOLVER;
import static org.neo4j.driver.internal.metrics.MetricsProvider.METRICS_DISABLED_PROVIDER;
import static org.neo4j.driver.internal.security.SecurityPlanImpl.insecure;
import static org.neo4j.driver.internal.util.ErrorUtil.addSuppressed;

public class DriverFactory
{
public static final String BOLT_URI_SCHEME = "bolt";
public static final String BOLT_ROUTING_URI_SCHEME = "neo4j";

public final Driver newInstance( URI uri, AuthToken authToken, RoutingSettings routingSettings,
RetrySettings retrySettings, Config config )
RetrySettings retrySettings, Config config, SecurityPlan securityPlan )
{
return newInstance( uri, authToken, routingSettings, retrySettings, config, null, null );
return newInstance( uri, authToken, routingSettings, retrySettings, config, null, securityPlan );
}

public final Driver newInstance ( URI uri, AuthToken authToken, RoutingSettings routingSettings,
RetrySettings retrySettings, Config config, EventLoopGroup eventLoopGroup, SecurityPlan customSecurityPlan )
RetrySettings retrySettings, Config config, EventLoopGroup eventLoopGroup, SecurityPlan securityPlan )
{
Bootstrap bootstrap;
boolean ownsEventLoopGroup;
Expand All @@ -96,16 +91,6 @@ public final Driver newInstance ( URI uri, AuthToken authToken, RoutingSettings
BoltServerAddress address = new BoltServerAddress( uri );
RoutingSettings newRoutingSettings = routingSettings.withRoutingContext( new RoutingContext( uri ) );

SecurityPlan securityPlan;
if ( customSecurityPlan != null )
{
securityPlan = customSecurityPlan;
}
else
{
securityPlan = createSecurityPlan( address, config );
}

InternalLoggerFactory.setDefaultFactory( new NettyLogging( config.logging() ) );
EventExecutorGroup eventExecutorGroup = bootstrap.config().group();
RetryLogic retryLogic = createRetryLogic( retrySettings, eventExecutorGroup, config.logging() );
Expand Down Expand Up @@ -148,20 +133,21 @@ protected ChannelConnector createConnector( ConnectionSettings settings, Securit
}

private InternalDriver createDriver( URI uri, SecurityPlan securityPlan, BoltServerAddress address, ConnectionPool connectionPool,
EventExecutorGroup eventExecutorGroup, RoutingSettings routingSettings, RetryLogic retryLogic, MetricsProvider metricsProvider, Config config )
EventExecutorGroup eventExecutorGroup, RoutingSettings routingSettings, RetryLogic retryLogic,
MetricsProvider metricsProvider, Config config )
{
try
{
String scheme = uri.getScheme().toLowerCase();
switch ( scheme )

if ( isRoutingScheme( scheme ) )
{
return createRoutingDriver( securityPlan, address, connectionPool, eventExecutorGroup, routingSettings, retryLogic, metricsProvider, config );
}
else
{
case BOLT_URI_SCHEME:
assertNoRoutingContext( uri, routingSettings );
return createDirectDriver( securityPlan, address, connectionPool, retryLogic, metricsProvider, config );
case BOLT_ROUTING_URI_SCHEME:
return createRoutingDriver( securityPlan, address, connectionPool, eventExecutorGroup, routingSettings, retryLogic, metricsProvider, config );
default:
throw new ClientException( format( "Unsupported URI scheme: %s", scheme ) );
}
}
catch ( Throwable driverError )
Expand Down Expand Up @@ -287,47 +273,6 @@ protected Bootstrap createBootstrap( EventLoopGroup eventLoopGroup )
return BootstrapFactory.newBootstrap( eventLoopGroup );
}

private static SecurityPlan createSecurityPlan( BoltServerAddress address, Config config )
{
try
{
return createSecurityPlanImpl( config );
}
catch ( GeneralSecurityException | IOException ex )
{
throw new ClientException( "Unable to establish SSL parameters", ex );
}
}

/*
* Establish a complete SecurityPlan based on the details provided for
* driver construction.
*/
private static SecurityPlan createSecurityPlanImpl( Config config )
throws GeneralSecurityException, IOException
{
if ( config.encrypted() )
{
Config.TrustStrategy trustStrategy = config.trustStrategy();
boolean hostnameVerificationEnabled = trustStrategy.isHostnameVerificationEnabled();
switch ( trustStrategy.strategy() )
{
case TRUST_CUSTOM_CA_SIGNED_CERTIFICATES:
return SecurityPlanImpl.forCustomCASignedCertificates( trustStrategy.certFile(), hostnameVerificationEnabled );
case TRUST_SYSTEM_CA_SIGNED_CERTIFICATES:
return SecurityPlanImpl.forSystemCASignedCertificates( hostnameVerificationEnabled );
case TRUST_ALL_CERTIFICATES:
return SecurityPlanImpl.forAllCertificates( hostnameVerificationEnabled );
default:
throw new ClientException(
"Unknown TLS authentication strategy: " + trustStrategy.strategy().name() );
}
}
else
{
return insecure();
}
}

private static void assertNoRoutingContext( URI uri, RoutingSettings routingSettings )
{
Expand Down
71 changes: 71 additions & 0 deletions driver/src/main/java/org/neo4j/driver/internal/Scheme.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright (c) 2002-2020 "Neo4j,"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.neo4j.driver.internal;

public class Scheme
{
public static final String BOLT_URI_SCHEME = "bolt";
public static final String BOLT_HIGH_TRUST_URI_SCHEME = "bolt+s";
public static final String BOLT_LOW_TRUST_URI_SCHEME = "bolt+ssc";
public static final String NEO4J_URI_SCHEME = "neo4j";
public static final String NEO4J_HIGH_TRUST_URI_SCHEME = "neo4j+s";
public static final String NEO4J_LOW_TRUST_URI_SCHEME = "neo4j+ssc";

public static void validateScheme( String scheme )
{
if ( scheme == null )
{
throw new IllegalArgumentException( "Scheme must not be null" );
}
switch ( scheme )
{
case BOLT_URI_SCHEME:
case BOLT_LOW_TRUST_URI_SCHEME:
case BOLT_HIGH_TRUST_URI_SCHEME:
case NEO4J_URI_SCHEME:
case NEO4J_LOW_TRUST_URI_SCHEME:
case NEO4J_HIGH_TRUST_URI_SCHEME:
return;
default:
throw new IllegalArgumentException( "Invalid address format " + scheme );
}
}

public static boolean isHighTrustScheme( String scheme )
{
return scheme.equals( BOLT_HIGH_TRUST_URI_SCHEME ) || scheme.equals( NEO4J_HIGH_TRUST_URI_SCHEME );
}

public static boolean isLowTrustScheme( String scheme )
{
return scheme.equals( BOLT_LOW_TRUST_URI_SCHEME ) || scheme.equals( NEO4J_LOW_TRUST_URI_SCHEME );
}

public static boolean isSecurityScheme( String scheme )
{
return scheme.equals( BOLT_LOW_TRUST_URI_SCHEME ) || scheme.equals( NEO4J_LOW_TRUST_URI_SCHEME )
|| scheme.equals( BOLT_HIGH_TRUST_URI_SCHEME ) || scheme.equals( NEO4J_HIGH_TRUST_URI_SCHEME );
}

public static boolean isRoutingScheme( String scheme )
{
return scheme.equals( NEO4J_LOW_TRUST_URI_SCHEME ) || scheme.equals( NEO4J_HIGH_TRUST_URI_SCHEME )
|| scheme.equals( NEO4J_URI_SCHEME );
}
}
Loading