diff --git a/driver/src/main/java/org/neo4j/driver/internal/InternalDriver.java b/driver/src/main/java/org/neo4j/driver/internal/InternalDriver.java new file mode 100644 index 0000000000..1a4b861057 --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/InternalDriver.java @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2002-2016 "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.internal; + +import java.net.URI; + +import org.neo4j.driver.internal.pool.InternalConnectionPool; +import org.neo4j.driver.internal.spi.ConnectionPool; +import org.neo4j.driver.v1.AuthToken; +import org.neo4j.driver.v1.Config; +import org.neo4j.driver.v1.Driver; +import org.neo4j.driver.v1.Session; + +public class InternalDriver implements Driver +{ + private final ConnectionPool connections; + private final URI url; + private final Config config; + + public InternalDriver( URI url, AuthToken authToken, Config config ) + { + this.url = url; + this.connections = new InternalConnectionPool( config, authToken ); + this.config = config; + } + + /** + * Establish a session + * @return a session that could be used to run {@link Session#run(String) a statement} or + * {@link Session#beginTransaction() a transaction }. + */ + @Override + public Session session() + { + return new InternalSession( connections.acquire( url ), config.logging().getLog( "session" ) ); + } + + /** + * Close all the resources assigned to this driver + * @throws Exception any error that might happen when releasing all resources + */ + public void close() throws Exception + { + connections.close(); + } +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/auth/InternalAuthToken.java b/driver/src/main/java/org/neo4j/driver/internal/auth/InternalAuthToken.java new file mode 100644 index 0000000000..10f9e872c5 --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/internal/auth/InternalAuthToken.java @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2002-2016 "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.internal.auth; + +import java.util.Map; + +import org.neo4j.driver.v1.Value; +import org.neo4j.driver.v1.AuthToken; + +/** + * A simple common token for authentication schemes that easily convert to + * an auth token map + */ +public class InternalAuthToken implements AuthToken +{ + private final Map content; + + public InternalAuthToken( Map content ) + { + this.content = content; + } + + public Map toMap() + { + return content; + } + + @Override + public boolean equals( Object o ) + { + if ( this == o ) + { return true; } + if ( o == null || getClass() != o.getClass() ) + { return false; } + + InternalAuthToken that = (InternalAuthToken) o; + + return content != null ? content.equals( that.content ) : that.content == null; + + } + + @Override + public int hashCode() + { + return content != null ? content.hashCode() : 0; + } +} diff --git a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/LoggingResponseHandler.java b/driver/src/main/java/org/neo4j/driver/internal/connector/socket/LoggingResponseHandler.java index d108e00938..a08463a577 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/LoggingResponseHandler.java +++ b/driver/src/main/java/org/neo4j/driver/internal/connector/socket/LoggingResponseHandler.java @@ -40,9 +40,9 @@ public LoggingResponseHandler( Logger logger ) } @Override - public void handleInitMessage( String clientNameAndVersion ) + public void handleInitMessage( String clientNameAndVersion, Map authToken ) { - super.handleInitMessage( clientNameAndVersion ); + super.handleInitMessage( clientNameAndVersion, authToken ); logger.debug( "S: [INIT \"%s\"]", clientNameAndVersion ); } diff --git a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketConnection.java b/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketConnection.java index 07814dc7b8..1752a361c1 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketConnection.java +++ b/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketConnection.java @@ -66,9 +66,9 @@ public SocketConnection( String host, int port, Config config ) } @Override - public void init( String clientName ) + public void init( String clientName, Map authToken ) { - queueMessage( new InitMessage( clientName ), StreamCollector.NO_OP ); + queueMessage( new InitMessage( clientName, authToken ), StreamCollector.NO_OP ); } @Override diff --git a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketConnector.java b/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketConnector.java index 22f9f1de6e..9ef9408348 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketConnector.java +++ b/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketConnector.java @@ -21,11 +21,16 @@ import java.net.URI; import java.util.Collection; import java.util.Collections; +import java.util.Map; import org.neo4j.driver.internal.Version; +import org.neo4j.driver.internal.auth.InternalAuthToken; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.Connector; import org.neo4j.driver.v1.Config; +import org.neo4j.driver.v1.Value; +import org.neo4j.driver.v1.AuthToken; +import org.neo4j.driver.v1.AuthTokens; import org.neo4j.driver.v1.exceptions.ClientException; public class SocketConnector implements Connector @@ -40,11 +45,11 @@ public boolean supports( String scheme ) } @Override - public Connection connect( URI sessionURI, Config config ) throws ClientException + public Connection connect( URI sessionURI, Config config, AuthToken authToken ) throws ClientException { int port = sessionURI.getPort() == -1 ? DEFAULT_PORT : sessionURI.getPort(); SocketConnection conn = new SocketConnection( sessionURI.getHost(), port, config ); - conn.init( "bolt-java-driver/" + Version.driverVersion() ); + conn.init( "bolt-java-driver/" + Version.driverVersion(), tokenAsMap( authToken ) ); return conn; } @@ -53,4 +58,17 @@ public Collection supportedSchemes() { return Collections.singletonList( SCHEME ); } + + private Map tokenAsMap( AuthToken token ) + { + if( token instanceof InternalAuthToken ) + { + return ((InternalAuthToken) token).toMap(); + } + else + { + throw new ClientException( "Unknown authentication token, `" + token + "`. Please use one of the supported " + + "tokens from `" + AuthTokens.class.getSimpleName() + "`." ); + } + } } diff --git a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketResponseHandler.java b/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketResponseHandler.java index 42e3e5ab5a..8a871c6739 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketResponseHandler.java +++ b/driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketResponseHandler.java @@ -198,7 +198,7 @@ public void handlePullAllMessage() } @Override - public void handleInitMessage( String clientNameAndVersion ) + public void handleInitMessage( String clientNameAndVersion, Map authToken ) { } diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/InitMessage.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/InitMessage.java index a6b9964b4d..e2c1723c63 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/InitMessage.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/InitMessage.java @@ -19,6 +19,9 @@ package org.neo4j.driver.internal.messaging; import java.io.IOException; +import java.util.Map; + +import org.neo4j.driver.v1.Value; import static java.lang.String.format; @@ -30,16 +33,18 @@ public class InitMessage implements Message { private final String clientNameAndVersion; + private Map authToken; - public InitMessage( String clientNameAndVersion ) + public InitMessage( String clientNameAndVersion, Map authToken ) { this.clientNameAndVersion = clientNameAndVersion; + this.authToken = authToken; } @Override public void dispatch( MessageHandler handler ) throws IOException { - handler.handleInitMessage( clientNameAndVersion ); + handler.handleInitMessage( clientNameAndVersion, authToken ); } @Override diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/MessageHandler.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/MessageHandler.java index fe94d014e0..ec220838be 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/MessageHandler.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/MessageHandler.java @@ -26,7 +26,7 @@ public interface MessageHandler { // Requests - void handleInitMessage( String clientNameAndVersion ) throws IOException; + void handleInitMessage( String clientNameAndVersion, Map authToken ) throws IOException; void handleRunMessage( String statement, Map parameters ) throws IOException; diff --git a/driver/src/main/java/org/neo4j/driver/internal/messaging/PackStreamMessageFormatV1.java b/driver/src/main/java/org/neo4j/driver/internal/messaging/PackStreamMessageFormatV1.java index 91664edf71..7a090bbc3c 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/messaging/PackStreamMessageFormatV1.java +++ b/driver/src/main/java/org/neo4j/driver/internal/messaging/PackStreamMessageFormatV1.java @@ -34,7 +34,6 @@ import org.neo4j.driver.internal.InternalRelationship; import org.neo4j.driver.internal.connector.socket.ChunkedInput; import org.neo4j.driver.internal.connector.socket.ChunkedOutput; -import org.neo4j.driver.internal.packstream.BufferedChannelOutput; import org.neo4j.driver.internal.packstream.PackInput; import org.neo4j.driver.internal.packstream.PackOutput; import org.neo4j.driver.internal.packstream.PackStream; @@ -105,11 +104,6 @@ public static class Writer implements MessageFormat.Writer, MessageHandler private final PackStream.Packer packer; private final Runnable onMessageComplete; - public Writer() - { - this( new BufferedChannelOutput( 8192 ), new NoOpRunnable() ); - } - /** * @param output interface to write messages to * @param onMessageComplete invoked for each message, after it's done writing to the output @@ -121,10 +115,11 @@ public Writer( PackOutput output, Runnable onMessageComplete ) } @Override - public void handleInitMessage( String clientNameAndVersion ) throws IOException + public void handleInitMessage( String clientNameAndVersion, Map authToken ) throws IOException { packer.packStructHeader( 1, MSG_INIT ); packer.pack( clientNameAndVersion ); + packRawMap( authToken ); onMessageComplete.run(); } @@ -445,7 +440,7 @@ private void unpackResetMessage( MessageHandler handler ) throws IOException private void unpackInitMessage( MessageHandler handler ) throws IOException { - handler.handleInitMessage( unpacker.unpackString() ); + handler.handleInitMessage( unpacker.unpackString(), unpackMap() ); onMessageComplete.run(); } diff --git a/driver/src/main/java/org/neo4j/driver/internal/pool/InternalConnectionPool.java b/driver/src/main/java/org/neo4j/driver/internal/pool/InternalConnectionPool.java index c2ca2cf1a2..a67c759226 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/pool/InternalConnectionPool.java +++ b/driver/src/main/java/org/neo4j/driver/internal/pool/InternalConnectionPool.java @@ -33,6 +33,7 @@ import org.neo4j.driver.internal.spi.Connector; import org.neo4j.driver.internal.util.Clock; import org.neo4j.driver.internal.util.Consumer; +import org.neo4j.driver.v1.AuthToken; import org.neo4j.driver.v1.Config; import org.neo4j.driver.v1.exceptions.ClientException; @@ -65,6 +66,7 @@ public class InternalConnectionPool implements ConnectionPool */ private final ValidationStrategy connectionValidation; + private final AuthToken authToken; /** * Timeout in milliseconds if there are no available sessions. */ @@ -73,13 +75,16 @@ public class InternalConnectionPool implements ConnectionPool private final Clock clock; private final Config config; - public InternalConnectionPool( Config config ) + public InternalConnectionPool( Config config, AuthToken authToken ) { - this( loadConnectors(), Clock.SYSTEM, config, Long.getLong( "neo4j.driver.acquireSessionTimeout", 30_000 ) ); + this( loadConnectors(), Clock.SYSTEM, config, authToken, + Long.getLong( "neo4j.driver.acquireSessionTimeout", 30_000 ) ); } - public InternalConnectionPool( Collection conns, Clock clock, Config config, long acquireTimeout ) + public InternalConnectionPool( Collection conns, Clock clock, Config config, + AuthToken authToken, long acquireTimeout ) { + this.authToken = authToken; this.acquireSessionTimeout = acquireTimeout; this.config = config; this.clock = clock; @@ -180,7 +185,7 @@ public PooledConnection allocate( Consumer release ) "'" + uri.getScheme() + "' is not a supported transport (in '" + uri + "', available transports are: " + connectorSchemes() + "." ); } - Connection conn = connector.connect( uri, config ); + Connection conn = connector.connect( uri, config, authToken ); return new PooledConnection( conn, release ); } diff --git a/driver/src/main/java/org/neo4j/driver/internal/pool/PooledConnection.java b/driver/src/main/java/org/neo4j/driver/internal/pool/PooledConnection.java index 70b09938cf..0cfeff9623 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/pool/PooledConnection.java +++ b/driver/src/main/java/org/neo4j/driver/internal/pool/PooledConnection.java @@ -41,11 +41,11 @@ public PooledConnection( Connection delegate, Consumer release } @Override - public void init( String clientName ) + public void init( String clientName, Map authToken ) { try { - delegate.init( clientName ); + delegate.init( clientName, authToken ); } catch( RuntimeException e ) { diff --git a/driver/src/main/java/org/neo4j/driver/internal/spi/Connection.java b/driver/src/main/java/org/neo4j/driver/internal/spi/Connection.java index 8c8ada0564..d3e98a56fe 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/spi/Connection.java +++ b/driver/src/main/java/org/neo4j/driver/internal/spi/Connection.java @@ -31,8 +31,9 @@ public interface Connection extends AutoCloseable /** * Initialize the connection. This must be done before any other action is allowed. * @param clientName should be the driver name and version: "java-driver/1.0.0" + * @param authToken */ - void init( String clientName ); + void init( String clientName, Map authToken ); /** * Queue up a run action. The collector will value called with metadata about the stream that will become available diff --git a/driver/src/main/java/org/neo4j/driver/internal/spi/Connector.java b/driver/src/main/java/org/neo4j/driver/internal/spi/Connector.java index b17aa36046..cea1c7f55c 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/spi/Connector.java +++ b/driver/src/main/java/org/neo4j/driver/internal/spi/Connector.java @@ -21,6 +21,7 @@ import java.net.URI; import java.util.Collection; +import org.neo4j.driver.v1.AuthToken; import org.neo4j.driver.v1.Config; import org.neo4j.driver.v1.exceptions.ClientException; @@ -43,9 +44,10 @@ public interface Connector * * @param sessionURL a URL identifying a remote session * @param config a configuration for this connection + * @param authToken * @return a Connection object */ - Connection connect( URI sessionURL, Config config ) throws ClientException; + Connection connect( URI sessionURL, Config config, AuthToken authToken ) throws ClientException; /** List names of supported schemes, used for error messages and similar signaling to end users. */ Collection supportedSchemes(); diff --git a/driver/src/main/java/org/neo4j/driver/v1/AuthToken.java b/driver/src/main/java/org/neo4j/driver/v1/AuthToken.java new file mode 100644 index 0000000000..a126a9f214 --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/v1/AuthToken.java @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2002-2016 "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; + +/** + * This is a combination of a Principal, for instance a username, + * and one or more Credentials, for instance a password. It is used + * to authenticate with a Neo4j instance. See {@link AuthTokens} + * for available types of {@link AuthToken}. + * + * @see AuthTokens + * @see GraphDatabase#driver(String, AuthToken) + */ +public interface AuthToken +{ + +} diff --git a/driver/src/main/java/org/neo4j/driver/v1/AuthTokens.java b/driver/src/main/java/org/neo4j/driver/v1/AuthTokens.java new file mode 100644 index 0000000000..339a484d81 --- /dev/null +++ b/driver/src/main/java/org/neo4j/driver/v1/AuthTokens.java @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2002-2016 "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; + +import org.neo4j.driver.internal.auth.InternalAuthToken; + +import static org.neo4j.driver.v1.Values.parameters; + +/** + * This is a listing of the various methods of authentication supported by this + * driver. The scheme used must be supported by the Neo4j Instance you are connecting + * to. + * @see GraphDatabase#driver(String, AuthToken) + */ +public class AuthTokens +{ + /** + * The basic authentication scheme, using a username and a password. + * @param username this is the "principal", identifying who this token represents + * @param password this is the "credential", proving the identity of the user + * @return an authentication token that can be used to connect to Neo4j + * @see GraphDatabase#driver(String, AuthToken) + */ + public static AuthToken basic( String username, String password ) + { + return new InternalAuthToken( parameters( + "scheme", "basic", + "principal", username, + "credentials", password ) ); + } + + /** + * No authentication scheme. This will only work if authentication is disabled + * on the Neo4j Instance we are connecting to. + * @return an authentication token that can be used to connect to Neo4j instances with auth disabled + * @see GraphDatabase#driver(String, AuthToken) + */ + public static AuthToken none() + { + return new InternalAuthToken( parameters("scheme", "none" ) ); + } +} diff --git a/driver/src/main/java/org/neo4j/driver/v1/Driver.java b/driver/src/main/java/org/neo4j/driver/v1/Driver.java index 903bebb8cd..db13ebf2c3 100644 --- a/driver/src/main/java/org/neo4j/driver/v1/Driver.java +++ b/driver/src/main/java/org/neo4j/driver/v1/Driver.java @@ -20,10 +20,6 @@ import java.net.URI; -import org.neo4j.driver.internal.InternalSession; -import org.neo4j.driver.internal.pool.InternalConnectionPool; -import org.neo4j.driver.internal.spi.ConnectionPool; - /** * A Neo4j database driver, through which you can create {@link Session sessions} to run statements against the database. *

@@ -70,35 +66,18 @@ * to use the same driver instance across your application. You can control the connection pooling behavior when you * create the driver using the {@link Config} you pass into {@link GraphDatabase#driver(URI, Config)}. */ -public class Driver implements AutoCloseable +public interface Driver extends AutoCloseable { - private final ConnectionPool connections; - private final URI url; - private final Config config; - - public Driver( URI url, Config config ) - { - this.url = url; - this.connections = new InternalConnectionPool( config ); - this.config = config; - } - /** * Establish a session * @return a session that could be used to run {@link Session#run(String) a statement} or * {@link Session#beginTransaction() a transaction }. */ - public Session session() - { - return new InternalSession( connections.acquire( url ), config.logging().getLog( "session" ) ); - } + Session session(); /** * Close all the resources assigned to this driver * @throws Exception any error that might happen when releasing all resources */ - public void close() throws Exception - { - connections.close(); - } + void close() throws Exception; } diff --git a/driver/src/main/java/org/neo4j/driver/v1/GraphDatabase.java b/driver/src/main/java/org/neo4j/driver/v1/GraphDatabase.java index 399bcd0f2c..f0e285f757 100644 --- a/driver/src/main/java/org/neo4j/driver/v1/GraphDatabase.java +++ b/driver/src/main/java/org/neo4j/driver/v1/GraphDatabase.java @@ -20,6 +20,8 @@ import java.net.URI; +import org.neo4j.driver.internal.InternalDriver; + /** * Creates {@link Driver drivers}, optionally letting you {@link #driver(URI, Config)} to configure them. * @see Driver @@ -57,7 +59,7 @@ public static Driver driver( URI url ) */ public static Driver driver( URI url, Config config ) { - return new Driver( url, config ); + return driver( url, AuthTokens.none(), config ); } /** @@ -71,4 +73,54 @@ public static Driver driver( String url, Config config ) { return driver( URI.create( url ), config ); } + + /** + * Return a driver for a Neo4j instance with the default configuration settings + * + * @param url the URL to a Neo4j instance + * @param authToken authentication to use, see {@link AuthTokens} + * @return a new driver to the database instance specified by the URL + */ + public static Driver driver( String url, AuthToken authToken ) + { + return driver( url, authToken, Config.defaultConfig() ); + } + + /** + * Return a driver for a Neo4j instance with the default configuration settings + * + * @param url the URL to a Neo4j instance + * @param authToken authentication to use, see {@link AuthTokens} + * @return a new driver to the database instance specified by the URL + */ + public static Driver driver( URI url, AuthToken authToken ) + { + return driver( url, authToken, Config.defaultConfig() ); + } + + /** + * Return a driver for a Neo4j instance with custom configuration. + * + * @param url the URL to a Neo4j instance + * @param authToken authentication to use, see {@link AuthTokens} + * @param config user defined configuration + * @return a new driver to the database instance specified by the URL + */ + public static Driver driver( String url, AuthToken authToken, Config config ) + { + return driver( URI.create( url ), authToken, config ); + } + + /** + * Return a driver for a Neo4j instance with custom configuration. + * + * @param url the URL to a Neo4j instance + * @param authToken authentication to use, see {@link AuthTokens} + * @param config user defined configuration + * @return a new driver to the database instance specified by the URL + */ + public static Driver driver( URI url, AuthToken authToken, Config config ) + { + return new InternalDriver( url, authToken, config ); + } } diff --git a/driver/src/test/java/org/neo4j/driver/internal/connector/socket/LoggingResponseHandlerTest.java b/driver/src/test/java/org/neo4j/driver/internal/connector/socket/LoggingResponseHandlerTest.java index b79a21766c..7130fa282e 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/connector/socket/LoggingResponseHandlerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/connector/socket/LoggingResponseHandlerTest.java @@ -18,27 +18,25 @@ */ package org.neo4j.driver.internal.connector.socket; -import java.util.HashMap; - import org.junit.Test; +import java.util.HashMap; + import org.neo4j.driver.internal.logging.DevNullLogger; -import org.neo4j.driver.internal.messaging.ResetMessage; import org.neo4j.driver.internal.messaging.DiscardAllMessage; import org.neo4j.driver.internal.messaging.FailureMessage; import org.neo4j.driver.internal.messaging.IgnoredMessage; import org.neo4j.driver.internal.messaging.InitMessage; import org.neo4j.driver.internal.messaging.Message; -import org.neo4j.driver.internal.messaging.MessageHandler; import org.neo4j.driver.internal.messaging.PullAllMessage; import org.neo4j.driver.internal.messaging.RecordMessage; +import org.neo4j.driver.internal.messaging.ResetMessage; import org.neo4j.driver.internal.messaging.RunMessage; import org.neo4j.driver.internal.messaging.SuccessMessage; import org.neo4j.driver.internal.spi.StreamCollector; import org.neo4j.driver.v1.Value; import static org.junit.Assert.assertEquals; - import static org.neo4j.driver.v1.Values.parameters; public class LoggingResponseHandlerTest @@ -59,11 +57,11 @@ public void debug( String message, Object... params ) public void shouldLogInitMessage() throws Throwable { // When - handler.handleInitMessage( "client" ); + handler.handleInitMessage( "client", parameters()); // Then assertEquals( "S: [INIT \"client\"]", log ); - assertEquals( format( new InitMessage( "client" ) ), log ); + assertEquals( format( new InitMessage( "client", parameters() ) ), log ); } @Test diff --git a/driver/src/test/java/org/neo4j/driver/internal/messaging/MessageFormatTest.java b/driver/src/test/java/org/neo4j/driver/internal/messaging/MessageFormatTest.java index cdb5da1329..0ae77d4ba3 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/messaging/MessageFormatTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/messaging/MessageFormatTest.java @@ -64,7 +64,7 @@ public void shouldPackAllRequests() throws Throwable assertSerializes( new IgnoredMessage() ); assertSerializes( new FailureMessage( "Neo.Banana.Bork.Birk", "Hello, world!" ) ); assertSerializes( new ResetMessage() ); - assertSerializes( new InitMessage( "JavaDriver/1.0.0" ) ); + assertSerializes( new InitMessage( "JavaDriver/1.0.0", parameters() ) ); } @Test diff --git a/driver/src/test/java/org/neo4j/driver/internal/pool/InternalConnectionPoolTest.java b/driver/src/test/java/org/neo4j/driver/internal/pool/InternalConnectionPoolTest.java index 1694b36ab0..56af44c604 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/pool/InternalConnectionPoolTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/pool/InternalConnectionPoolTest.java @@ -28,6 +28,8 @@ import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.Connector; import org.neo4j.driver.internal.util.Clock; +import org.neo4j.driver.v1.AuthToken; +import org.neo4j.driver.v1.AuthTokens; import org.neo4j.driver.v1.Config; import org.neo4j.driver.v1.exceptions.ClientException; @@ -53,7 +55,7 @@ public void shouldThrowExceptionWhenConnectionPoolIsFull() throws Throwable Connector connector = connector( "bolt" ); Config config = Config.build().withMaxSessions( 1 ).toConfig(); InternalConnectionPool pool = new InternalConnectionPool( singletonList( connector ), - Clock.SYSTEM, config, 100 ); + Clock.SYSTEM, config, AuthTokens.none(), 100 ); // When & Then pool.acquire( uri ); @@ -74,7 +76,7 @@ public void shouldAcquireAndRelease() throws Throwable Connector connector = connector( "bolt" ); Config config = Config.defaultConfig(); InternalConnectionPool pool = new InternalConnectionPool( singletonList( connector ), - Clock.SYSTEM, config, 100 ); + Clock.SYSTEM, config, AuthTokens.none(), 100 ); Connection conn = pool.acquire( uri ); conn.close(); @@ -83,14 +85,16 @@ public void shouldAcquireAndRelease() throws Throwable pool.acquire( uri ); // Then - verify( connector, times( 1 ) ).connect( uri, config ); + verify( connector, times( 1 ) ).connect( uri, config, AuthTokens.none() ); } private Connector connector( String scheme ) { Connector mock = mock( Connector.class ); when( mock.supportedSchemes() ).thenReturn( Collections.singletonList( scheme ) ); - when( mock.connect( any( URI.class ), any( Config.class ) ) ).thenReturn( mock( Connection.class ) ); + when( mock.connect( any( URI.class ), any( Config.class ), any( AuthToken.class ) ) ).thenReturn( mock( + Connection.class + ) ); return mock; } } diff --git a/driver/src/test/java/org/neo4j/driver/v1/integration/CredentialsIT.java b/driver/src/test/java/org/neo4j/driver/v1/integration/CredentialsIT.java new file mode 100644 index 0000000000..75280ab629 --- /dev/null +++ b/driver/src/test/java/org/neo4j/driver/v1/integration/CredentialsIT.java @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2002-2016 "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.integration; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; + +import org.neo4j.driver.internal.auth.InternalAuthToken; +import org.neo4j.driver.v1.Driver; +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.util.Neo4jSettings; +import org.neo4j.driver.v1.util.TestNeo4j; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.neo4j.driver.v1.AuthTokens.basic; +import static org.neo4j.driver.v1.Values.parameters; + +public class CredentialsIT +{ + @Rule + public TemporaryFolder tempDir = new TemporaryFolder(); + + @Rule + public TestNeo4j neo4j = new TestNeo4j(); + + @Rule + public ExpectedException exception = ExpectedException.none(); + + @Test + public void basicCredentialsShouldWork() throws Throwable + { + // Given + String password = "secret"; + enableAuth( password ); + + Driver driver = GraphDatabase.driver( neo4j.address(), + basic("neo4j", password ) ); + + // When + Session session = driver.session(); + Value single = session.run( "RETURN 1" ).single( 0 ); + + // Then + assertThat( single.asLong(), equalTo(1l)); + } + + @Test + public void shouldGetHelpfulErrorOnInvalidCredentials() throws Throwable + { + // Given + String password = "secret"; + enableAuth( password ); + + Driver driver = GraphDatabase.driver( neo4j.address(), basic("thisisnotthepassword", password ) ); + Session session = driver.session(); + + // Expect + exception.expect( ClientException.class ); + exception.expectMessage( "The client provided an incorrect username and/or password." ); + + // When + session.run( "RETURN 1" ).single( 0 ); + } + + private void enableAuth( String password ) throws Exception + { + neo4j.restartServerOnEmptyDatabase( Neo4jSettings.DEFAULT + .updateWith( Neo4jSettings.AUTH_ENABLED, "true" ) + .updateWith( Neo4jSettings.AUTH_FILE, tempDir.newFile( "auth" ).getAbsolutePath() )); + + Driver setPassword = GraphDatabase.driver( neo4j.address(), new InternalAuthToken( + parameters( + "scheme", "basic", + "principal", "neo4j", + "credentials", "neo4j", + "new-credentials", password ) ) ); + Session sess = setPassword.session(); + sess.run( "RETURN 1" ).close(); + sess.close(); + setPassword.close(); + } +} diff --git a/driver/src/test/java/org/neo4j/driver/v1/integration/SocketClientIT.java b/driver/src/test/java/org/neo4j/driver/v1/integration/SocketClientIT.java index 11b4ab0b1a..b1ca7c01fb 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/integration/SocketClientIT.java +++ b/driver/src/test/java/org/neo4j/driver/v1/integration/SocketClientIT.java @@ -18,15 +18,15 @@ */ package org.neo4j.driver.v1.integration; -import java.net.URI; -import java.util.LinkedList; -import java.util.Queue; - import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import java.net.URI; +import java.util.LinkedList; +import java.util.Queue; + import org.neo4j.driver.internal.connector.socket.SocketClient; import org.neo4j.driver.internal.connector.socket.SocketResponseHandler; import org.neo4j.driver.internal.logging.DevNullLogger; @@ -44,6 +44,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.neo4j.driver.v1.Values.parameters; public class SocketClientIT { @@ -74,8 +75,8 @@ public void shouldCloseConnectionWhenReceivingProtocolViolationError() throws Ex { // Given Queue messages = new LinkedList<>(); - messages.add( new InitMessage( "EvilClientV1_Hello" ) ); - messages.add( new InitMessage( "EvilClientV1_World" ) ); + messages.add( new InitMessage( "EvilClientV1_Hello", parameters() ) ); + messages.add( new InitMessage( "EvilClientV1_World", parameters() ) ); SocketResponseHandler handler = mock( SocketResponseHandler.class ); when( handler.protocolViolationErrorOccurred() ).thenReturn( true ); diff --git a/driver/src/test/java/org/neo4j/driver/v1/util/DumpMessage.java b/driver/src/test/java/org/neo4j/driver/v1/util/DumpMessage.java index f8411a058d..ab26779efa 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/util/DumpMessage.java +++ b/driver/src/test/java/org/neo4j/driver/v1/util/DumpMessage.java @@ -314,9 +314,9 @@ public void handlePullAllMessage() } @Override - public void handleInitMessage( String clientNameAndVersion ) throws IOException + public void handleInitMessage( String clientNameAndVersion, Map authToken ) throws IOException { - outcome.add( new InitMessage( clientNameAndVersion ) ); + outcome.add( new InitMessage( clientNameAndVersion, authToken ) ); } @Override diff --git a/driver/src/test/java/org/neo4j/driver/v1/util/Neo4jRunner.java b/driver/src/test/java/org/neo4j/driver/v1/util/Neo4jRunner.java index 229d7d886d..f8056ad7b7 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/util/Neo4jRunner.java +++ b/driver/src/test/java/org/neo4j/driver/v1/util/Neo4jRunner.java @@ -27,10 +27,10 @@ import org.neo4j.driver.internal.logging.DevNullLogger; import org.neo4j.driver.v1.Config; import org.neo4j.driver.v1.Driver; +import org.neo4j.driver.v1.GraphDatabase; import org.neo4j.driver.v1.exceptions.ClientException; import static java.lang.String.format; - import static org.neo4j.driver.internal.ConfigTest.deleteDefaultKnownCertFileIfExists; import static org.neo4j.driver.v1.util.FileTools.deleteRecursively; import static org.neo4j.driver.v1.util.FileTools.updateProperties; @@ -157,7 +157,7 @@ private void clear( Neo4jSettings config ) throws Exception throw new IllegalStateException( "Failed to start server" ); } awaitServerStatusOrFail( ServerStatus.ONLINE ); - currentDriver = new Driver( serverURI(), testConfig ); + currentDriver = GraphDatabase.driver( serverURI(), testConfig ); } private boolean updateServerSettings( Neo4jSettings settingsUpdate ) diff --git a/driver/src/test/java/org/neo4j/driver/v1/util/Neo4jSettings.java b/driver/src/test/java/org/neo4j/driver/v1/util/Neo4jSettings.java index 8260ee2b0c..811b3ca867 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/util/Neo4jSettings.java +++ b/driver/src/test/java/org/neo4j/driver/v1/util/Neo4jSettings.java @@ -26,6 +26,9 @@ public class Neo4jSettings { + public static final String AUTH_ENABLED = "dbms.security.auth_enabled"; + public static final String AUTH_FILE = "dbms.security.auth_store.location"; + private static final String TLS_CERT_KEY = "dbms.security.tls_certificate_file"; private static final String TLS_KEY_KEY = "dbms.security.tls_key_file"; @@ -39,7 +42,8 @@ public class Neo4jSettings public static Neo4jSettings DEFAULT = new Neo4jSettings( map( TLS_CERT_KEY, DEFAULT_TLS_CERT_PATH, - TLS_KEY_KEY, DEFAULT_TLS_KEY_PATH) ); + TLS_KEY_KEY, DEFAULT_TLS_KEY_PATH, + AUTH_ENABLED, "false" ) ); private Neo4jSettings( Map settings ) { @@ -56,6 +60,11 @@ public Neo4jSettings updateWith( Neo4jSettings other ) return updateWith( other.settings ); } + public Neo4jSettings updateWith( String key, String value ) + { + return updateWith( map(key, value) ); + } + private Neo4jSettings updateWith( Map updates ) { HashMap newSettings = new HashMap<>( settings ); @@ -75,7 +84,6 @@ public boolean equals( Object o ) Neo4jSettings that = (Neo4jSettings) o; return settings.equals( that.settings ); - } public Neo4jSettings usingEncryptionKeyAndCert( File key, File cert ) diff --git a/driver/src/test/java/org/neo4j/driver/v1/util/TestNeo4j.java b/driver/src/test/java/org/neo4j/driver/v1/util/TestNeo4j.java index 99c0bca8a8..141214760f 100644 --- a/driver/src/test/java/org/neo4j/driver/v1/util/TestNeo4j.java +++ b/driver/src/test/java/org/neo4j/driver/v1/util/TestNeo4j.java @@ -18,21 +18,21 @@ */ package org.neo4j.driver.v1.util; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.net.URL; -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; - import org.neo4j.driver.v1.Driver; import org.neo4j.driver.v1.Session; public class TestNeo4j implements TestRule { - private final Neo4jSettings initialSettings = Neo4jSettings.DEFAULT; + private Neo4jSettings settings = Neo4jSettings.DEFAULT; private Neo4jRunner runner; @Override @@ -44,7 +44,7 @@ public Statement apply( final Statement base, final Description description ) public void evaluate() throws Throwable { runner = Neo4jRunner.getOrCreateGlobalRunner(); - if ( !runner.ensureRunning( initialSettings ) ) + if ( !runner.ensureRunning( settings ) ) { try ( Session session = driver().session() ) { @@ -96,4 +96,10 @@ public void restartServerOnEmptyDatabase( Neo4jSettings neo4jSettings ) throws E { runner.restart( neo4jSettings ); } + + public TestNeo4j withSettings( Neo4jSettings settings ) + { + this.settings = settings; + return this; + } }