Skip to content

Throw AuthenticationException if failed to conn due to auth errors #315

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 2 commits into from
Feb 6, 2017
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
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.neo4j.driver.internal.util.Clock;
import org.neo4j.driver.v1.Logger;
import org.neo4j.driver.v1.exceptions.ServiceUnavailableException;
import org.neo4j.driver.v1.exceptions.SecurityException;

import static java.lang.String.format;

Expand Down Expand Up @@ -75,7 +76,11 @@ public ClusterComposition lookupRoutingTable( ConnectionPool connections, Routin
{
response = provider.getClusterComposition( connection );
}
catch ( Throwable e )
catch( SecurityException e )
{
throw e; // terminate the discovery immediately
}
catch ( Exception e )
{
// the connection breaks
logger.error( format( "Failed to connect to routing server '%s'.", address ), e );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.neo4j.driver.v1.exceptions.AuthenticationException;
import org.neo4j.driver.internal.messaging.MessageHandler;
import org.neo4j.driver.internal.spi.Collector;
import org.neo4j.driver.internal.summary.InternalNotification;
Expand Down Expand Up @@ -65,7 +66,14 @@ public void handleFailureMessage( String code, String message )
switch ( classification )
{
case "ClientError":
error = new ClientException( code, message );
if( code.equalsIgnoreCase( "Neo.ClientError.Security.Unauthorized" ) )
{
error = new AuthenticationException( code, message );
}
else
{
error = new ClientException( code, message );
}
break;
case "TransientError":
error = new TransientException( code, message );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.neo4j.driver.internal.util.BytePrinter;
import org.neo4j.driver.v1.exceptions.ClientException;
import org.neo4j.driver.v1.exceptions.ServiceUnavailableException;
import org.neo4j.driver.v1.exceptions.SecurityException;

import static java.lang.String.format;
import static javax.net.ssl.SSLEngineResult.HandshakeStatus.FINISHED;
Expand Down Expand Up @@ -81,7 +82,7 @@ public static TLSSocketChannel create( ByteChannel channel, Logger logger, SSLEn
}
catch ( SSLHandshakeException e )
{
throw new ClientException( "Failed to establish secured connection with the server: " + e.getMessage(), e );
throw new SecurityException( "Failed to establish secured connection with the server: " + e.getMessage(), e );
}
return tlsChannel;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright (c) 2002-2017 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.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.v1.exceptions;

/**
* Failed to authenticate the driver to the server due to bad credentials provided.
* When this error happens, the error could be recovered by closing the current driver and restart a new driver with
* the correct credentials.
*
* @since 1.1
*/
public class AuthenticationException extends SecurityException
{
public AuthenticationException( String code, String message )
{
super( code, message );
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2002-2017 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.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.v1.exceptions;

/**
* Failed to communicate with the server due to security errors.
* When this type of error happens, the security cause of the error should be fixed to ensure the safety of your data.
* Restart of server/driver/cluster might be required to recover from this error.
* @since 1.1
*/
public class SecurityException extends Neo4jException
{
public SecurityException( String code, String message )
{
super( code, message );
}

public SecurityException( String message, Throwable t )
{
super( message, t );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLSession;

import org.neo4j.driver.v1.exceptions.ClientException;
import org.neo4j.driver.v1.exceptions.ServiceUnavailableException;
import org.neo4j.driver.v1.exceptions.SecurityException;

import static junit.framework.TestCase.fail;
import static org.hamcrest.MatcherAssert.assertThat;
Expand Down Expand Up @@ -107,7 +107,7 @@ public void shouldCloseConnectionIfFailedToWrite() throws Throwable
}

@Test
public void shouldThrowClientErrorIfFailedToHandshake() throws Throwable
public void shouldThrowUnauthorizedIfFailedToHandshake() throws Throwable
{
// Given
ByteChannel mockedChannel = mock( ByteChannel.class );
Expand All @@ -128,7 +128,7 @@ public void shouldThrowClientErrorIfFailedToHandshake() throws Throwable
}
catch( Exception e )
{
assertThat( e, instanceOf( ClientException.class ) );
assertThat( e, instanceOf( SecurityException.class ) );
assertThat( e.getMessage(), startsWith( "Failed to establish secured connection with the server: Failed handshake!" ) );
}
verify( mockedChannel, never() ).close();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
import org.neo4j.driver.v1.GraphDatabase;
import org.neo4j.driver.v1.Session;
import org.neo4j.driver.v1.Value;
import org.neo4j.driver.v1.exceptions.ClientException;
import org.neo4j.driver.v1.exceptions.SecurityException;
import org.neo4j.driver.v1.util.Neo4jSettings;
import org.neo4j.driver.v1.util.TestNeo4j;

Expand Down Expand Up @@ -79,7 +79,7 @@ public void shouldGetHelpfulErrorOnInvalidCredentials() throws Throwable
}
catch( Throwable e )
{
assertThat( e, instanceOf( ClientException.class ) );
assertThat( e, instanceOf( SecurityException.class ) );
assertThat( e.getMessage(), containsString( "The client is unauthorized due to authentication failure." ) );
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLHandshakeException;

import org.neo4j.driver.internal.net.BoltServerAddress;
import org.neo4j.driver.internal.security.SecurityPlan;
Expand All @@ -43,7 +42,7 @@
import org.neo4j.driver.v1.Logging;
import org.neo4j.driver.v1.Session;
import org.neo4j.driver.v1.StatementResult;
import org.neo4j.driver.v1.exceptions.ClientException;
import org.neo4j.driver.v1.exceptions.SecurityException;
import org.neo4j.driver.v1.util.CertificateToolTest;
import org.neo4j.driver.v1.util.Neo4jRunner;
import org.neo4j.driver.v1.util.Neo4jSettings;
Expand All @@ -53,6 +52,7 @@
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
Expand Down Expand Up @@ -150,18 +150,25 @@ public void shouldNotPerformTLSHandshakeWithNonSystemCert() throws Throwable
SecurityPlan securityPlan = SecurityPlan.forSystemCASignedCertificates();

// When
TLSSocketChannel sslChannel = null;
try
{
TLSSocketChannel sslChannel =
TLSSocketChannel.create(address, securityPlan, channel, logger);
sslChannel.close();
sslChannel = TLSSocketChannel.create(address, securityPlan, channel, logger);
fail( "Should have thrown exception" );
}
catch ( ClientException e )
catch ( SecurityException e )
{
assertThat( e.getMessage(), containsString( "General SSLEngine problem" ) );
assertThat( getRootCause( e ).getMessage(),
containsString( "unable to find valid certification path to requested target" ) );
}
finally
{
if( sslChannel != null )
{
sslChannel.close();
}
}
}
finally
{
Expand All @@ -188,12 +195,12 @@ public void shouldFailTLSHandshakeDueToWrongCertInKnownCertsFile() throws Throwa
TLSSocketChannel sslChannel = null;
try
{
sslChannel = TLSSocketChannel.create( address, securityPlan, channel, mock( Logger.class ) );
sslChannel.close();
sslChannel = TLSSocketChannel.create( address, securityPlan, channel, DEV_NULL_LOGGER );
fail( "Should have thrown exception" );
}
catch ( SSLHandshakeException e )
catch ( SecurityException e )
{
assertEquals( "General SSLEngine problem", e.getMessage() );
assertThat( e.getMessage(), containsString( "General SSLEngine problem" ) );
assertThat( getRootCause( e ).getMessage(), containsString(
"If you trust the certificate the server uses now, simply remove the line that starts with" ) );
}
Expand All @@ -209,14 +216,13 @@ public void shouldFailTLSHandshakeDueToWrongCertInKnownCertsFile() throws Throwa
private void createFakeServerCertPairInKnownCerts( BoltServerAddress address, File knownCerts )
throws Throwable
{
address = address.resolve(); // localhost -> 127.0.0.1
String serverId = address.toString();

X509Certificate cert = CertificateToolTest.generateSelfSignedCertificate();
String certStr = fingerprint(cert);

BufferedWriter writer = new BufferedWriter( new FileWriter( knownCerts, true ) );
writer.write( serverId + "," + certStr );
writer.write( serverId + " " + certStr );
writer.newLine();
writer.close();
}
Expand All @@ -241,9 +247,9 @@ public void shouldFailTLSHandshakeDueToServerCertNotSignedByKnownCA() throws Thr
try
{
sslChannel = TLSSocketChannel.create( neo4j.address(), securityPlan, channel, mock( Logger.class ) );
sslChannel.close();
fail( "Should have thrown exception" );
}
catch ( ClientException e )
catch ( SecurityException e )
{
assertThat( e.getMessage(), containsString( "General SSLEngine problem" ) );
assertThat( getRootCause( e ).getMessage(), containsString( "No trusted certificate found" ) );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
import org.neo4j.driver.v1.StatementResult;
import org.neo4j.driver.v1.Transaction;
import org.neo4j.driver.v1.exceptions.ClientException;
import org.neo4j.driver.v1.exceptions.ServiceUnavailableException;
import org.neo4j.driver.v1.exceptions.SecurityException;
import org.neo4j.driver.v1.types.Node;
import org.neo4j.driver.v1.util.DaemonThreadFactory;
import org.neo4j.driver.v1.util.cc.LocalOrRemoteClusterRule;
Expand Down Expand Up @@ -411,14 +411,12 @@ public void execute()
}
catch ( Exception e )
{
assertThat( e, instanceOf( ServiceUnavailableException.class ) );
assertThat( e, instanceOf( SecurityException.class ) );
assertThat( e.getMessage(), containsString( "authentication failure" ) );

ArgumentCaptor<Throwable> captor = ArgumentCaptor.forClass( Throwable.class );
verify( logger ).error( startsWith( "Failed to connect to routing server" ), captor.capture() );

Throwable loggedThrowable = captor.getValue();
assertThat( loggedThrowable, instanceOf( ClientException.class ) );
assertThat( loggedThrowable.getMessage().toLowerCase(), containsString( "authentication failure" ) );
verify( logger ).debug( startsWith( "~~ [CLOSED SECURE CHANNEL]" ), captor.capture() );
verify( logger ).debug( startsWith( "~~ [DISCONNECT]" ), captor.capture() );
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
import org.neo4j.driver.v1.GraphDatabase;
import org.neo4j.driver.v1.Session;
import org.neo4j.driver.v1.StatementResult;
import org.neo4j.driver.v1.exceptions.ClientException;
import org.neo4j.driver.v1.exceptions.SecurityException;
import org.neo4j.driver.v1.util.CertificateToolTest.CertificateSigningRequestGenerator;
import org.neo4j.driver.v1.util.CertificateToolTest.SelfSignedCertificateGenerator;
import org.neo4j.driver.v1.util.Neo4jRunner;
Expand Down Expand Up @@ -134,7 +134,7 @@ public void creatingSessionsShouldFail() throws Throwable
public void iShouldGetAHelpfulErrorExplainingThatCertificateChanged( String str ) throws Throwable
{
assertThat( exception, notNullValue() );
assertThat( exception, instanceOf( ClientException.class ) );
assertThat( exception, instanceOf( SecurityException.class ) );
Throwable rootCause = getRootCause( exception );
assertThat( rootCause.toString(), containsString(
"Unable to connect to neo4j at `localhost:7687`, because the certificate the server uses has changed. " +
Expand Down Expand Up @@ -244,7 +244,7 @@ public void aRunningNeo4jDatabaseUsingACertNotSignedByTheTrustedCertificates() t
public void iShouldGetAHelpfulErrorExplainingThatCertificatedNotSigned() throws Throwable
{
assertThat( exception, notNullValue() );
assertThat( exception, instanceOf( ClientException.class ) );
assertThat( exception, instanceOf( SecurityException.class ) );
Throwable rootCause = getRootCause( exception );
assertThat( rootCause.toString(), containsString( "Signature does not match.") );
}
Expand Down