diff --git a/community/bolt/src/docs/dev/examples.asciidoc b/community/bolt/src/docs/dev/examples.asciidoc index d2d42874c3825..418c64425dd28 100644 --- a/community/bolt/src/docs/dev/examples.asciidoc +++ b/community/bolt/src/docs/dev/examples.asciidoc @@ -3,6 +3,61 @@ This section contains concrete examples showing how to perform tasks using the full Bolt protocol stack. +=== Authentication + +The first time you connect to neo4j with the default credentials you will be asked to update the password. + +.Run query +[source,bolt_auth] +---- +# Handshake +Client: +Client: 60 60 B0 17 +Client: 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 +Server: 00 00 00 01 + +Client: INIT "MyClient/1.0" { "scheme": "basic", "principal": "neo4j", "credentials": "neo4j"} + + 00 3F B1 01 8C 4D 79 43 6C 69 65 6E 74 2F 31 2E + 30 A3 86 73 63 68 65 6D 65 85 62 61 73 69 63 89 + 70 72 69 6E 63 69 70 61 6C 85 6E 65 6F 34 6A 8B + 63 72 65 64 65 6E 74 69 61 6C 73 85 6E 65 6F 34 + 6A 00 00 + +Server: FAILURE { "code": "Neo.ClientError.Security.CredentialsExpired", + "message": "The credentials have expired and needs to be updated."} + + 00 74 B1 7F A2 84 63 6F 64 65 D0 2B 4E 65 6F 2E + 43 6C 69 65 6E 74 45 72 72 6F 72 2E 53 65 63 75 + 72 69 74 79 2E 43 72 65 64 65 6E 74 69 61 6C 73 + 45 78 70 69 72 65 64 87 6D 65 73 73 61 67 65 D0 + 35 54 68 65 20 63 72 65 64 65 6E 74 69 61 6C 73 + 20 68 61 76 65 20 65 78 70 69 72 65 64 20 61 6E + 64 20 6E 65 65 64 73 20 74 6F 20 62 65 20 75 70 + 64 61 74 65 64 2E 00 00 + +Server: +Client: +Client: 60 60 B0 17 +Client: 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 +Server: 00 00 00 01 + +Client: INIT "MyClient/1.0" { "scheme": "basic", "principal": "neo4j", "credentials": "neo4j", "new-credentials": "secret"} + + 00 40 B1 01 8C 4D 79 43 6C 69 65 6E 74 2F 31 2E + 30 A4 86 73 63 68 65 6D 65 85 62 61 73 69 63 89 + 70 72 69 6E 63 69 70 61 6C 85 6E 65 6F 34 6A 8B + 63 72 65 64 65 6E 74 69 61 6C 73 85 6E 65 6F 34 + 6A 8F 00 16 6E 65 77 2D 63 72 65 64 65 6E 74 69 + 61 6C 73 86 73 65 63 72 65 74 00 00 + + +Server: SUCCESS { } + + 00 03 b1 70 a0 00 00 + +---- + === Running a Cypher query This illustrates running a simple Cypher query without parameters, and retrieving the results. diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/docs/BoltAuthDocTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/docs/BoltAuthDocTest.java new file mode 100644 index 0000000000000..00e2160d7fe3d --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/docs/BoltAuthDocTest.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2002-2016 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.docs; + +import org.junit.Rule; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.function.Supplier; + +import org.neo4j.bolt.v1.transport.integration.Neo4jWithSocket; +import org.neo4j.bolt.v1.transport.socket.client.Connection; +import org.neo4j.bolt.v1.transport.socket.client.SecureSocketConnection; +import org.neo4j.bolt.v1.transport.socket.client.SecureWebSocketConnection; +import org.neo4j.graphdb.factory.GraphDatabaseSettings; +import org.neo4j.helpers.HostnamePort; + +@RunWith( Parameterized.class ) +public class BoltAuthDocTest extends BoltFullDocTest +{ + @Rule + public Neo4jWithSocket server = new Neo4jWithSocket( settings -> { + settings.put( GraphDatabaseSettings.auth_enabled, "true" ); + } ); + + @Parameterized.Parameter( 0 ) + public String testName; + + @Parameterized.Parameter( 1 ) + public DocExchangeExample example; + + @Parameterized.Parameter( 2 ) + public Supplier client; + + @Parameterized.Parameter( 3 ) + public HostnamePort address; + + @Parameterized.Parameters( name = "{0}" ) + public static Collection documentedFullProtocolExamples() + { + Collection mappings = new ArrayList<>(); + + // Load the documented mappings + HostnamePort address = new HostnamePort( "localhost:7687" ); + + for ( DocExchangeExample ex : DocsRepository.docs().read( + "dev/examples.asciidoc", + "code[data-lang=\"bolt_auth\"]", + DocExchangeExample.exchange_example ) ) + { + mappings.add( new Object[]{"Socket - " + ex.name(), ex, + (Supplier) SecureSocketConnection::new, address}); + mappings.add( new Object[]{"WebSocket - " + ex.name(), ex, + (Supplier) SecureWebSocketConnection::new , address} ); + } + return mappings; + } + + @Override + protected Connection createClient() + { + return client.get(); + } + + @Override + protected HostnamePort address() + { + return address; + } + + @Override + protected DocExchangeExample example() + { + return example; + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/docs/BoltFullDocTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/docs/BoltFullDocTest.java new file mode 100644 index 0000000000000..2b1e403bf4f46 --- /dev/null +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/docs/BoltFullDocTest.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2002-2016 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.bolt.v1.docs; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import org.neo4j.bolt.v1.messaging.message.Message; +import org.neo4j.bolt.v1.transport.socket.client.Connection; +import org.neo4j.helpers.HostnamePort; +import org.neo4j.kernel.impl.util.HexPrinter; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.message; +import static org.neo4j.bolt.v1.transport.integration.TransportTestUtil.dechunk; +import static org.neo4j.bolt.v1.transport.integration.TransportTestUtil.recvOneMessage; + +public abstract class BoltFullDocTest +{ + private Connection client; + + @Test + public void serverShouldBehaveAsDocumented() throws Throwable + { + for ( DocExchangeExample.Event event : example() ) + { + if ( event.from().equalsIgnoreCase( "client" ) ) + { + // Play out a client action + switch ( event.type() ) + { + case CONNECT: + client.connect( address() ); + break; + case DISCONNECT: + client.disconnect(); + break; + case SEND: + // Ensure the documented binary representation matches the human-readable version in the docs + if ( event.hasHumanReadableValue() ) + { + assertThat( "'" + event.humanReadableMessage() + "' should serialize to the documented " + + "binary data.", + hex( event.payload() ), + equalTo( hex( DocSerialization.packAndChunk( event.humanReadableMessage(), 64 ) ) ) ); + } + client.send( event.payload() ); + break; + default: + throw new RuntimeException( "Unknown client event: " + event.type() ); + } + } + else if ( event.from().equalsIgnoreCase( "server" ) ) + { + // Assert that the server does what the docs say + switch ( event.type() ) + { + case DISCONNECT: + // There's not really a good way to verify that the remote connection is closed, we can read and + // time out, or write perhaps, but that's buggy and racy.. not sure how to test this on this + // level. + client.disconnect(); + client = createClient(); + break; + case SEND: + if ( event.hasHumanReadableValue() ) + { + // Ensure the documented binary representation matches the human-readable version in the docs + assertThat( "'" + event.humanReadableMessage() + "' should serialize to the documented " + + "binary data.", + hex( event.payload() ), + equalTo( hex( DocSerialization.packAndChunk( event.humanReadableMessage(), 1024 * 8 ) ) ) ); + + // Ensure that the server replies as documented + Message serverMessage = recvOneMessage( client ); + assertThat( + "The message recieved from the server should match the documented binary representation. " + + "Human-readable message is <" + event.humanReadableMessage() + ">, received message was: " + serverMessage, + serverMessage, + equalTo( message( dechunk( event.payload() ) ) ) ); + } + else + { + // Raw data assertions - used for documenting the version negotiation, for instance + assertThat( "The data recieved from the server should match the documented binary representation.", + hex( client.recv( event.payload().length ) ), + equalTo( hex( event.payload() ) ) ); + } + + break; + default: + throw new RuntimeException( "Unknown server event: " + event.type() ); + } + } + } + } + + @Before + public void setUp() + { + client = createClient(); + } + + @After + public void shutdown() throws Exception + { + if ( client != null ) + { + client.disconnect(); + } + } + + protected abstract Connection createClient(); + protected abstract HostnamePort address(); + protected abstract DocExchangeExample example(); + + private static String hex( byte[] payload ) + { + return HexPrinter.hex( payload, 4, " " ); + } +} diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/docs/BoltFullExchangesDocTest.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/docs/BoltFullExchangesDocTest.java index c3e612e99693e..0abf55b901dab 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/docs/BoltFullExchangesDocTest.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/docs/BoltFullExchangesDocTest.java @@ -19,32 +19,23 @@ */ package org.neo4j.bolt.v1.docs; -import org.junit.After; import org.junit.Rule; -import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import java.util.ArrayList; import java.util.Collection; +import java.util.function.Supplier; -import org.neo4j.bolt.v1.messaging.message.Message; import org.neo4j.bolt.v1.transport.integration.Neo4jWithSocket; import org.neo4j.bolt.v1.transport.socket.client.Connection; import org.neo4j.bolt.v1.transport.socket.client.SecureSocketConnection; import org.neo4j.bolt.v1.transport.socket.client.SecureWebSocketConnection; import org.neo4j.graphdb.factory.GraphDatabaseSettings; import org.neo4j.helpers.HostnamePort; -import org.neo4j.kernel.impl.util.HexPrinter; - -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.neo4j.bolt.v1.messaging.util.MessageMatchers.message; -import static org.neo4j.bolt.v1.transport.integration.TransportTestUtil.dechunk; -import static org.neo4j.bolt.v1.transport.integration.TransportTestUtil.recvOneMessage; @RunWith( Parameterized.class ) -public class BoltFullExchangesDocTest +public class BoltFullExchangesDocTest extends BoltFullDocTest { @Rule public Neo4jWithSocket server = new Neo4jWithSocket( settings -> { @@ -59,7 +50,7 @@ public class BoltFullExchangesDocTest public DocExchangeExample example; @Parameterized.Parameter( 2 ) - public Connection client; + public Supplier client; @Parameterized.Parameter( 3 ) public HostnamePort address; @@ -76,8 +67,10 @@ public static Collection documentedFullProtocolExamples() "code[data-lang=\"bolt_exchange\"]", DocExchangeExample.exchange_example ) ) { - mappings.add( new Object[]{"Socket - "+ex.name(), ex, new SecureSocketConnection(), address} ); - mappings.add( new Object[]{"WebSocket - "+ex.name(), ex, new SecureWebSocketConnection(), address} ); + mappings.add( new Object[]{"Socket - " + ex.name(), ex, + (Supplier) SecureSocketConnection::new, address}); + mappings.add( new Object[]{"WebSocket - " + ex.name(), ex, + (Supplier) SecureWebSocketConnection::new , address} ); } for ( DocExchangeExample ex : DocsRepository.docs().read( @@ -85,95 +78,30 @@ public static Collection documentedFullProtocolExamples() "code[data-lang=\"bolt_exchange\"]", DocExchangeExample.exchange_example ) ) { - mappings.add( new Object[]{"Socket - "+ex.name(), ex, new SecureSocketConnection(), address} ); - mappings.add( new Object[]{"WebSocket - "+ex.name(), ex, new SecureWebSocketConnection(), address} ); + mappings.add( new Object[]{"Socket - " + ex.name(), ex, + (Supplier) SecureSocketConnection::new, address}); + mappings.add( new Object[]{"WebSocket - " + ex.name(), ex, + (Supplier) SecureWebSocketConnection::new , address} ); } return mappings; } - @After - public void shutdown() throws Exception + @Override + protected Connection createClient() { - client.disconnect(); + return client.get(); } - @Test - public void serverShouldBehaveAsDocumented() throws Throwable + @Override + protected HostnamePort address() { - for ( DocExchangeExample.Event event : example ) - { - if ( event.from().equalsIgnoreCase( "client" ) ) - { - // Play out a client action - switch ( event.type() ) - { - case CONNECT: - client.connect( address ); - break; - case DISCONNECT: - client.disconnect(); - break; - case SEND: - // Ensure the documented binary representation matches the human-readable version in the docs - if ( event.hasHumanReadableValue() ) - { - assertThat( "'" + event.humanReadableMessage() + "' should serialize to the documented " + - "binary data.", - hex( event.payload() ), - equalTo( hex( DocSerialization.packAndChunk( event.humanReadableMessage(), 64 ) ) ) ); - } - client.send( event.payload() ); - break; - default: - throw new RuntimeException( "Unknown client event: " + event.type() ); - } - } - else if ( event.from().equalsIgnoreCase( "server" ) ) - { - // Assert that the server does what the docs say - switch ( event.type() ) - { - case DISCONNECT: - // There's not really a good way to verify that the remote connection is closed, we can read and - // time out, or write perhaps, but that's buggy and racy.. not sure how to test this on this - // level. - break; - case SEND: - if ( event.hasHumanReadableValue() ) - { - // Ensure the documented binary representation matches the human-readable version in the docs - assertThat( "'" + event.humanReadableMessage() + "' should serialize to the documented " + - "binary data.", - hex( event.payload() ), - equalTo( hex( DocSerialization.packAndChunk( event.humanReadableMessage(), 1024 * 8 ) ) ) ); - - // Ensure that the server replies as documented - Message serverMessage = recvOneMessage( client ); - assertThat( - "The message recieved from the server should match the documented binary representation. " + - "Human-readable message is <" + event.humanReadableMessage() + ">, received message was: " + serverMessage, - serverMessage, - equalTo( message( dechunk( event.payload() ) ) ) ); - } - else - { - // Raw data assertions - used for documenting the version negotiation, for instance - assertThat( "The data recieved from the server should match the documented binary representation.", - hex( client.recv( event.payload().length ) ), - equalTo( hex( event.payload() ) ) ); - } - - break; - default: - throw new RuntimeException( "Unknown server event: " + event.type() ); - } - } - } + return address; } - private static String hex( byte[] payload ) + @Override + protected DocExchangeExample example() { - return HexPrinter.hex( payload, 4, " " ); + return example; } } diff --git a/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/socket/client/WebSocketConnection.java b/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/socket/client/WebSocketConnection.java index 727904b77da3d..eb99168b72306 100644 --- a/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/socket/client/WebSocketConnection.java +++ b/community/bolt/src/test/java/org/neo4j/bolt/v1/transport/socket/client/WebSocketConnection.java @@ -82,7 +82,7 @@ public Connection connect( HostnamePort address ) throws Exception client = clientSupplier.get(); client.start(); - Session session = null; + Session session; try { session = client.connect( this, target ).get( 10, SECONDS ); @@ -156,7 +156,10 @@ private void waitForRecievedData( int length, int remaining, byte[] target ) @Override public void disconnect() throws Exception { - client.stop(); + if (client != null) + { + client.stop(); + } } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/factory/CommunityEditionModule.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/factory/CommunityEditionModule.java index ba0a03820ed64..5591e873f74f2 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/factory/CommunityEditionModule.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/factory/CommunityEditionModule.java @@ -31,8 +31,6 @@ import org.neo4j.kernel.internal.KernelData; import org.neo4j.kernel.NeoStoreDataSource; import org.neo4j.kernel.internal.Version; -import org.neo4j.kernel.api.exceptions.InvalidTransactionTypeKernelException; -import org.neo4j.kernel.Version; import org.neo4j.kernel.configuration.Config; import org.neo4j.kernel.impl.api.SchemaWriteGuard; import org.neo4j.kernel.impl.api.index.RemoveOrphanConstraintIndexesOnStartup;