Skip to content
This repository was archived by the owner on Jul 6, 2023. It is now read-only.

Commit a1fb500

Browse files
authored
Merge pull request #216 from sherfert/4.0-new-protocols
Support +ssc and +s protocols
2 parents e78b0ab + ae4b9db commit a1fb500

File tree

13 files changed

+184
-57
lines changed

13 files changed

+184
-57
lines changed
Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package org.neo4j.shell.commands;
22

3-
import org.neo4j.driver.exceptions.ServiceUnavailableException;
43
import org.neo4j.shell.ConnectionConfig;
54
import org.neo4j.shell.CypherShell;
5+
import org.neo4j.shell.cli.Encryption;
66
import org.neo4j.shell.exception.CommandException;
77

88
import static org.neo4j.shell.DatabaseManager.ABSENT_DB_NAME;
@@ -12,15 +12,6 @@ abstract class CypherShellIntegrationTest
1212
CypherShell shell;
1313

1414
void connect(String password) throws CommandException {
15-
// Try with encryption off first, which is the default for 4.X
16-
try
17-
{
18-
shell.connect( new ConnectionConfig( "bolt://", "localhost", 7687, "neo4j", password, false, ABSENT_DB_NAME ) );
19-
}
20-
catch ( ServiceUnavailableException e )
21-
{
22-
// This means we are probably in 3.X, let's retry with encryption on
23-
shell.connect( new ConnectionConfig( "bolt://", "localhost", 7687, "neo4j", password, true, ABSENT_DB_NAME ) );
24-
}
15+
shell.connect( new ConnectionConfig( "bolt://", "localhost", 7687, "neo4j", password, Encryption.DEFAULT, ABSENT_DB_NAME ) );
2516
}
2617
}

cypher-shell/src/integration-test/java/org/neo4j/shell/commands/CypherShellMultiDatabaseIntegrationTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.neo4j.shell.CypherShell;
1212
import org.neo4j.shell.ShellParameterMap;
1313
import org.neo4j.shell.StringLinePrinter;
14+
import org.neo4j.shell.cli.Encryption;
1415
import org.neo4j.shell.cli.Format;
1516
import org.neo4j.shell.exception.CommandException;
1617
import org.neo4j.shell.prettyprint.PrettyConfig;
@@ -45,7 +46,7 @@ public void setUp() throws Exception
4546
beginCommand = new Begin( shell );
4647
rollbackCommand = new Rollback( shell );
4748

48-
shell.connect( new ConnectionConfig( "bolt://", "localhost", 7687, "neo4j", "neo", false, ABSENT_DB_NAME ) );
49+
shell.connect( new ConnectionConfig( "bolt://", "localhost", 7687, "neo4j", "neo", Encryption.DEFAULT, ABSENT_DB_NAME ) );
4950

5051
// Multiple databases are only available from 4.0
5152
assumeTrue( majorVersion( shell.getServerVersion() ) >= 4 );
@@ -137,7 +138,7 @@ public void switchingToNonExistingDatabaseShouldGiveErrorResponseFromServerInter
137138
{
138139
shell = new CypherShell( linePrinter, new PrettyConfig( Format.PLAIN, true, 1000 ), true, new ShellParameterMap() );
139140
useCommand = new Use( shell );
140-
shell.connect( new ConnectionConfig( "bolt://", "localhost", 7687, "neo4j", "neo", false, ABSENT_DB_NAME ) );
141+
shell.connect( new ConnectionConfig( "bolt://", "localhost", 7687, "neo4j", "neo", Encryption.DEFAULT, ABSENT_DB_NAME ) );
141142

142143
useCommand.execute( SYSTEM_DB_NAME );
143144

cypher-shell/src/integration-test/java/org/neo4j/shell/commands/CypherShellProtocolIntegrationTest.java

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,59 @@
66
import org.neo4j.shell.CypherShell;
77
import org.neo4j.shell.ShellParameterMap;
88
import org.neo4j.shell.StringLinePrinter;
9+
import org.neo4j.shell.cli.Encryption;
910
import org.neo4j.shell.cli.Format;
1011
import org.neo4j.shell.prettyprint.PrettyConfig;
11-
import static org.junit.Assert.assertTrue;
1212

13+
import static org.junit.Assert.assertTrue;
14+
import static org.junit.Assume.assumeTrue;
1315
import static org.neo4j.shell.DatabaseManager.ABSENT_DB_NAME;
16+
import static org.neo4j.shell.util.Versions.majorVersion;
17+
import static org.neo4j.shell.util.Versions.minorVersion;
1418

1519
public class CypherShellProtocolIntegrationTest{
1620

21+
@Test
22+
public void shouldConnectWithBoltProtocol() throws Exception {
23+
CypherShell shell = new CypherShell( new StringLinePrinter(), new PrettyConfig( Format.PLAIN, true, 1000), false, new ShellParameterMap());
24+
shell.connect( new ConnectionConfig( "bolt://", "localhost", 7687, "neo4j", "neo", Encryption.DEFAULT, ABSENT_DB_NAME ) );
25+
assertTrue(shell.isConnected());
26+
}
27+
1728
@Test
1829
public void shouldConnectWithNeo4jProtocol() throws Exception {
1930
CypherShell shell = new CypherShell( new StringLinePrinter(), new PrettyConfig( Format.PLAIN, true, 1000), false, new ShellParameterMap());
2031
// This should work even on older databases without the neo4j protocol, by falling back to bolt
21-
shell.connect( new ConnectionConfig( "neo4j://", "localhost", 7687, "neo4j", "neo", false, ABSENT_DB_NAME ) );
32+
shell.connect( new ConnectionConfig( "neo4j://", "localhost", 7687, "neo4j", "neo", Encryption.DEFAULT, ABSENT_DB_NAME ) );
33+
assertTrue(shell.isConnected());
34+
}
35+
36+
@Test
37+
public void shouldConnectWithBoltSSCProtocol() throws Exception {
38+
CypherShell shell = new CypherShell( new StringLinePrinter(), new PrettyConfig( Format.PLAIN, true, 1000), false, new ShellParameterMap());
39+
// Given 3.X series where X > 1, where SSC are the default. Hard to test in 4.0 sadly.
40+
onlyIn3_2to3_6( shell);
41+
shell.connect( new ConnectionConfig( "bolt+ssc://", "localhost", 7687, "neo4j", "neo", Encryption.DEFAULT, ABSENT_DB_NAME ) );
42+
assertTrue(shell.isConnected());
43+
}
44+
45+
@Test
46+
public void shouldConnectWithNeo4jSSCProtocol() throws Exception {
47+
CypherShell shell = new CypherShell( new StringLinePrinter(), new PrettyConfig( Format.PLAIN, true, 1000), false, new ShellParameterMap());
48+
// Given 3.X series where X > 1, where SSC are the default. Hard to test in 4.0 sadly.
49+
onlyIn3_2to3_6( shell);
50+
// This should work by falling back to bolt+ssc
51+
shell.connect( new ConnectionConfig( "neo4j+ssc://", "localhost", 7687, "neo4j", "neo", Encryption.DEFAULT, ABSENT_DB_NAME ) );
2252
assertTrue(shell.isConnected());
2353
}
54+
55+
// Here should be tests for "neo4j+s" and "bolt+s", but we don't have the infrastructure for those.
56+
57+
private void onlyIn3_2to3_6( CypherShell shell) throws Exception {
58+
// Default connection settings
59+
shell.connect( new ConnectionConfig( "bolt://", "localhost", 7687, "neo4j", "neo", Encryption.DEFAULT, ABSENT_DB_NAME ) );
60+
assumeTrue( majorVersion( shell.getServerVersion() ) == 3 );
61+
assumeTrue( minorVersion( shell.getServerVersion() ) > 1 );
62+
shell.disconnect();
63+
}
2464
}

cypher-shell/src/main/java/org/neo4j/shell/ConnectionConfig.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import javax.annotation.Nonnull;
44

5+
import org.neo4j.shell.cli.Encryption;
6+
57
public class ConnectionConfig {
68
public static final String USERNAME_ENV_VAR = "NEO4J_USERNAME";
79
public static final String PASSWORD_ENV_VAR = "NEO4J_PASSWORD";
@@ -10,7 +12,7 @@ public class ConnectionConfig {
1012
private final String scheme;
1113
private final String host;
1214
private final int port;
13-
private final boolean encryption;
15+
private final Encryption encryption;
1416
private String username;
1517
private String password;
1618
private String newPassword;
@@ -21,7 +23,7 @@ public ConnectionConfig(@Nonnull String scheme,
2123
int port,
2224
@Nonnull String username,
2325
@Nonnull String password,
24-
boolean encryption,
26+
Encryption encryption,
2527
@Nonnull String database) {
2628
this.host = host;
2729
this.port = port;
@@ -78,7 +80,7 @@ public String driverUrl() {
7880
}
7981

8082
@Nonnull
81-
public boolean encryption() {
83+
public Encryption encryption() {
8284
return encryption;
8385
}
8486

cypher-shell/src/main/java/org/neo4j/shell/cli/CliArgHelper.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ private static CliArgs getCliArgs( CliArgs cliArgs, ArgumentParser parser, Names
9696
if (!pass.isEmpty()) {
9797
cliArgs.setPassword(pass, cliArgs.getPassword());
9898
}
99-
cliArgs.setEncryption(ns.getBoolean("encryption"));
99+
cliArgs.setEncryption(Encryption.parse(ns.get("encryption")));
100100
cliArgs.setDatabase(ns.getString("database"));
101101
cliArgs.setInputFilename(ns.getString( "file" ) );
102102

@@ -161,10 +161,15 @@ private static ArgumentParser setupParser(ParameterMap parameterMap)
161161
.setDefault("")
162162
.help("password to connect with. Can also be specified using environment variable " + ConnectionConfig.PASSWORD_ENV_VAR);
163163
connGroup.addArgument("--encryption")
164-
.help("whether the connection to Neo4j should be encrypted; must be consistent with Neo4j's " +
165-
"configuration")
166-
.type(new BooleanArgumentType())
167-
.setDefault(false);
164+
.help("whether the connection to Neo4j should be encrypted. This must be consistent with Neo4j's " +
165+
"configuration. If choosing '" + Encryption.DEFAULT.name().toLowerCase() +
166+
"' the encryption setting is deduced from the specified address. " +
167+
"For example the 'neo4j+ssc' protocol would use encryption.")
168+
.choices(new CollectionArgumentChoice<>(
169+
Encryption.TRUE.name().toLowerCase(),
170+
Encryption.FALSE.name().toLowerCase(),
171+
Encryption.DEFAULT.name().toLowerCase()))
172+
.setDefault(Encryption.DEFAULT.name().toLowerCase());
168173
connGroup.addArgument("-d", "--database")
169174
.help("database to connect to. Can also be specified using environment variable " + ConnectionConfig.DATABASE_ENV_VAR)
170175
.setDefault("");

cypher-shell/src/main/java/org/neo4j/shell/cli/CliArgs.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public class CliArgs {
2525
private Format format = Format.AUTO;
2626
@SuppressWarnings( "OptionalUsedAsFieldOrParameterType" )
2727
private Optional<String> cypher = Optional.empty();
28-
private boolean encryption;
28+
private Encryption encryption = Encryption.DEFAULT;
2929
private boolean debugMode;
3030
private boolean nonInteractive = false;
3131
private boolean version = false;
@@ -101,7 +101,7 @@ public void setCypher(@Nullable String cypher) {
101101
/**
102102
* Set whether the connection should be encrypted
103103
*/
104-
public void setEncryption(boolean encryption) {
104+
public void setEncryption(Encryption encryption) {
105105
this.encryption = encryption;
106106
}
107107

@@ -171,7 +171,7 @@ public Format getFormat() {
171171
return format;
172172
}
173173

174-
public boolean getEncryption() {
174+
public Encryption getEncryption() {
175175
return encryption;
176176
}
177177

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package org.neo4j.shell.cli;
2+
3+
import javax.annotation.Nonnull;
4+
5+
public enum Encryption
6+
{
7+
TRUE,
8+
FALSE,
9+
DEFAULT;
10+
11+
public static Encryption parse( @Nonnull String format) {
12+
if (format.equalsIgnoreCase(TRUE.name())) {
13+
return TRUE;
14+
} else if (format.equalsIgnoreCase( FALSE.name() )) {
15+
return FALSE;
16+
} else {
17+
return DEFAULT;
18+
}
19+
}
20+
}

cypher-shell/src/main/java/org/neo4j/shell/state/BoltStateHandler.java

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,24 @@ public void connect( @Nonnull ConnectionConfig connectionConfig, ThrowingAction<
163163
driver = getDriver(connectionConfig, authToken);
164164
reconnect(command);
165165
} catch (org.neo4j.driver.exceptions.ServiceUnavailableException e) {
166-
if (!connectionConfig.scheme().equals( Scheme.NEO4J_URI_SCHEME + "://")) {
166+
String scheme = connectionConfig.scheme();
167+
String fallbackScheme;
168+
switch ( scheme )
169+
{
170+
case Scheme.NEO4J_URI_SCHEME + "://":
171+
fallbackScheme = Scheme.BOLT_URI_SCHEME;
172+
break;
173+
case Scheme.NEO4J_LOW_TRUST_URI_SCHEME + "://":
174+
fallbackScheme = Scheme.BOLT_LOW_TRUST_URI_SCHEME;
175+
break;
176+
case Scheme.NEO4J_HIGH_TRUST_URI_SCHEME + "://":
177+
fallbackScheme = Scheme.BOLT_HIGH_TRUST_URI_SCHEME;
178+
break;
179+
default:
167180
throw e;
168181
}
169182
connectionConfig = new ConnectionConfig(
170-
Scheme.BOLT_URI_SCHEME + "://",
183+
fallbackScheme + "://",
171184
connectionConfig.host(),
172185
connectionConfig.port(),
173186
connectionConfig.username(),
@@ -438,10 +451,14 @@ private void clearTransactionStatements() {
438451

439452
private Driver getDriver(@Nonnull ConnectionConfig connectionConfig, @Nullable AuthToken authToken) {
440453
Config.ConfigBuilder configBuilder = Config.builder().withLogging(NullLogging.NULL_LOGGING);
441-
if (connectionConfig.encryption()) {
454+
switch(connectionConfig.encryption())
455+
{
456+
case TRUE:
442457
configBuilder = configBuilder.withEncryption();
443-
} else {
458+
break;
459+
case FALSE:
444460
configBuilder = configBuilder.withoutEncryption();
461+
break;
445462
}
446463
return driverProvider.apply(connectionConfig.driverUrl(), authToken, configBuilder.build());
447464
}

cypher-shell/src/test/java/org/neo4j/shell/ConnectionConfigTest.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,15 @@
22

33
import org.junit.Rule;
44
import org.junit.Test;
5+
import org.junit.contrib.java.lang.system.EnvironmentVariables;
6+
7+
import org.neo4j.shell.cli.Encryption;
58
import org.neo4j.shell.log.Logger;
69

710
import static org.junit.Assert.assertEquals;
8-
import static org.junit.Assert.assertFalse;
9-
import static org.junit.Assert.assertTrue;
1011
import static org.mockito.Mockito.mock;
1112
import static org.neo4j.shell.DatabaseManager.ABSENT_DB_NAME;
1213

13-
import org.junit.contrib.java.lang.system.EnvironmentVariables;
14-
1514
public class ConnectionConfigTest {
1615

1716
@Rule
@@ -20,7 +19,7 @@ public class ConnectionConfigTest {
2019

2120
private Logger logger = mock(Logger.class);
2221
private ConnectionConfig config = new ConnectionConfig("bolt://", "localhost", 1, "bob",
23-
"pass", false, "db");
22+
"pass", Encryption.DEFAULT, "db");
2423

2524

2625
@Test
@@ -47,7 +46,7 @@ public void username() {
4746
public void usernameDefaultsToEnvironmentVar() {
4847
environmentVariables.set(ConnectionConfig.USERNAME_ENV_VAR, "alice");
4948
ConnectionConfig configWithEmptyParams = new ConnectionConfig("bolt://", "localhost", 1, "",
50-
"", false, ABSENT_DB_NAME);
49+
"", Encryption.DEFAULT, ABSENT_DB_NAME);
5150
assertEquals("alice", configWithEmptyParams.username());
5251
}
5352

@@ -60,7 +59,7 @@ public void password() {
6059
public void passwordDefaultsToEnvironmentVar() {
6160
environmentVariables.set(ConnectionConfig.PASSWORD_ENV_VAR, "ssap");
6261
ConnectionConfig configWithEmptyParams = new ConnectionConfig("bolt://", "localhost", 1, "",
63-
"", false, ABSENT_DB_NAME);
62+
"", Encryption.DEFAULT, ABSENT_DB_NAME);
6463
assertEquals("ssap", configWithEmptyParams.password());
6564
}
6665

@@ -73,7 +72,7 @@ public void database() {
7372
public void databaseDefaultsToEnvironmentVar() {
7473
environmentVariables.set(ConnectionConfig.DATABASE_ENV_VAR, "funnyDB");
7574
ConnectionConfig configWithEmptyParams = new ConnectionConfig("bolt://", "localhost", 1, "",
76-
"", false, ABSENT_DB_NAME);
75+
"", Encryption.DEFAULT, ABSENT_DB_NAME);
7776
assertEquals("funnyDB", configWithEmptyParams.database());
7877
}
7978
@Test
@@ -83,7 +82,8 @@ public void driverUrlDefaultScheme() {
8382

8483
@Test
8584
public void encryption() {
86-
assertTrue(new ConnectionConfig("bolt://", "", -1, "", "", true, ABSENT_DB_NAME).encryption());
87-
assertFalse(new ConnectionConfig("bolt://", "", -1, "", "", false, ABSENT_DB_NAME).encryption());
85+
assertEquals(Encryption.DEFAULT, new ConnectionConfig("bolt://", "", -1, "", "", Encryption.DEFAULT, ABSENT_DB_NAME).encryption());
86+
assertEquals(Encryption.TRUE, new ConnectionConfig("bolt://", "", -1, "", "", Encryption.TRUE, ABSENT_DB_NAME).encryption());
87+
assertEquals(Encryption.FALSE, new ConnectionConfig("bolt://", "", -1, "", "", Encryption.FALSE, ABSENT_DB_NAME).encryption());
8888
}
8989
}

cypher-shell/src/test/java/org/neo4j/shell/CypherShellTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.neo4j.driver.Session;
1414
import org.neo4j.driver.Value;
1515
import org.neo4j.driver.summary.ResultSummary;
16+
import org.neo4j.shell.cli.Encryption;
1617
import org.neo4j.shell.commands.CommandExecutable;
1718
import org.neo4j.shell.commands.CommandHelper;
1819
import org.neo4j.shell.exception.CommandException;
@@ -62,7 +63,7 @@ public void setup() {
6263

6364
@Test
6465
public void verifyDelegationOfConnectionMethods() throws CommandException {
65-
ConnectionConfig cc = new ConnectionConfig("bolt://", "", 1, "", "", false, ABSENT_DB_NAME);
66+
ConnectionConfig cc = new ConnectionConfig("bolt://", "", 1, "", "", Encryption.DEFAULT, ABSENT_DB_NAME);
6667
CypherShell shell = new CypherShell(logger, mockedBoltStateHandler, mockedPrettyPrinter, new ShellParameterMap());
6768

6869
shell.connect(cc);

0 commit comments

Comments
 (0)