Skip to content

Commit

Permalink
Add example of authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
pontusmelke committed Feb 25, 2016
1 parent 55c6fad commit 8f28d2c
Show file tree
Hide file tree
Showing 6 changed files with 315 additions and 96 deletions.
55 changes: 55 additions & 0 deletions community/bolt/src/docs/dev/examples.asciidoc
Expand Up @@ -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: <connect>
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: <disconnect>
Client: <connect>
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.
Expand Down
@@ -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 <http://www.gnu.org/licenses/>.
*/
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<Connection> client;

@Parameterized.Parameter( 3 )
public HostnamePort address;

@Parameterized.Parameters( name = "{0}" )
public static Collection<Object[]> documentedFullProtocolExamples()
{
Collection<Object[]> 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<Connection>) SecureSocketConnection::new, address});
mappings.add( new Object[]{"WebSocket - " + ex.name(), ex,
(Supplier<Connection>) SecureWebSocketConnection::new , address} );
}
return mappings;
}

@Override
protected Connection createClient()
{
return client.get();
}

@Override
protected HostnamePort address()
{
return address;
}

@Override
protected DocExchangeExample example()
{
return example;
}
}
@@ -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 <http://www.gnu.org/licenses/>.
*/
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, " " );
}
}

0 comments on commit 8f28d2c

Please sign in to comment.