Skip to content

Commit 82b0e74

Browse files
committed
Introduce an override AuthToken support to ExecutableQuery
This update adds support for specifying an override `AuthToken` for `ExecutableQuery`. This feature requires Bolt 5.1 or greater.
1 parent 43f8a65 commit 82b0e74

File tree

8 files changed

+91
-19
lines changed

8 files changed

+91
-19
lines changed

driver/clirr-ignored-differences.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,4 +597,10 @@
597597
<differenceType>8001</differenceType>
598598
</difference>
599599

600+
<difference>
601+
<className>org/neo4j/driver/ExecutableQuery</className>
602+
<differenceType>7012</differenceType>
603+
<method>org.neo4j.driver.ExecutableQuery withAuthToken(org.neo4j.driver.AuthToken)</method>
604+
</difference>
605+
600606
</differences>

driver/src/main/java/org/neo4j/driver/Driver.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.concurrent.CompletionStage;
2020
import org.neo4j.driver.async.AsyncSession;
2121
import org.neo4j.driver.exceptions.ClientException;
22+
import org.neo4j.driver.exceptions.UnsupportedFeatureException;
2223
import org.neo4j.driver.reactive.ReactiveSession;
2324
import org.neo4j.driver.reactive.RxSession;
2425
import org.neo4j.driver.types.TypeSystem;
@@ -143,7 +144,7 @@ default <T extends BaseSession> T session(Class<T> sessionClass) {
143144
* Instantiate a new session of a supported type with the supplied {@link AuthToken}.
144145
* <p>
145146
* This method allows creating a session with a different {@link AuthToken} to the one used on the driver level.
146-
* The minimum Bolt protocol version is 5.1. An {@link IllegalStateException} will be emitted on session interaction
147+
* The minimum Bolt protocol version is 5.1. An {@link UnsupportedFeatureException} will be emitted on session interaction
147148
* for previous Bolt versions.
148149
* <p>
149150
* Supported types are:
@@ -214,7 +215,7 @@ default <T extends BaseSession> T session(Class<T> sessionClass, SessionConfig s
214215
* {@link AuthToken}.
215216
* <p>
216217
* This method allows creating a session with a different {@link AuthToken} to the one used on the driver level.
217-
* The minimum Bolt protocol version is 5.1. An {@link IllegalStateException} will be emitted on session interaction
218+
* The minimum Bolt protocol version is 5.1. An {@link UnsupportedFeatureException} will be emitted on session interaction
218219
* for previous Bolt versions.
219220
* <p>
220221
* Supported types are:

driver/src/main/java/org/neo4j/driver/ExecutableQuery.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.function.Consumer;
2323
import java.util.stream.Collector;
2424
import java.util.stream.Collectors;
25+
import org.neo4j.driver.exceptions.UnsupportedFeatureException;
2526
import org.neo4j.driver.internal.EagerResultValue;
2627
import org.neo4j.driver.summary.ResultSummary;
2728

@@ -97,7 +98,7 @@ public interface ExecutableQuery {
9798
/**
9899
* Sets query parameters.
99100
*
100-
* @param parameters parameters map, must not be {@code null}
101+
* @param parameters parameters map, must not be {@literal null}
101102
* @return a new executable query
102103
*/
103104
ExecutableQuery withParameters(Map<String, Object> parameters);
@@ -107,11 +108,27 @@ public interface ExecutableQuery {
107108
* <p>
108109
* By default, {@link ExecutableQuery} has {@link QueryConfig#defaultConfig()} value.
109110
*
110-
* @param config query config, must not be {@code null}
111+
* @param config query config, must not be {@literal null}
111112
* @return a new executable query
112113
*/
113114
ExecutableQuery withConfig(QueryConfig config);
114115

116+
/**
117+
* Sets an {@link AuthToken} to be used for this query.
118+
* <p>
119+
* The default value is {@literal null}.
120+
* <p>
121+
* The minimum Bolt protocol version for this feature is 5.1. An {@link UnsupportedFeatureException} will be emitted on
122+
* query execution for previous Bolt versions.
123+
*
124+
* @param authToken the {@link AuthToken} for this query or {@literal null} to use the driver default
125+
* @return a new executable query
126+
* @since 5.18
127+
*/
128+
default ExecutableQuery withAuthToken(AuthToken authToken) {
129+
throw new UnsupportedFeatureException("Session AuthToken is not supported.");
130+
}
131+
115132
/**
116133
* Executes query, collects all results eagerly and returns a result.
117134
*

driver/src/main/java/org/neo4j/driver/internal/InternalDriver.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public class InternalDriver implements Driver {
8383

8484
@Override
8585
public ExecutableQuery executableQuery(String query) {
86-
return new InternalExecutableQuery(this, new Query(query), QueryConfig.defaultConfig());
86+
return new InternalExecutableQuery(this, new Query(query), QueryConfig.defaultConfig(), null);
8787
}
8888

8989
@Override

driver/src/main/java/org/neo4j/driver/internal/InternalExecutableQuery.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@
2121
import java.util.Map;
2222
import java.util.stream.Collector;
2323
import org.neo4j.driver.AccessMode;
24+
import org.neo4j.driver.AuthToken;
2425
import org.neo4j.driver.Driver;
2526
import org.neo4j.driver.ExecutableQuery;
2627
import org.neo4j.driver.Query;
2728
import org.neo4j.driver.QueryConfig;
2829
import org.neo4j.driver.Record;
2930
import org.neo4j.driver.RoutingControl;
31+
import org.neo4j.driver.Session;
3032
import org.neo4j.driver.SessionConfig;
3133
import org.neo4j.driver.TransactionCallback;
3234
import org.neo4j.driver.TransactionConfig;
@@ -36,26 +38,33 @@ public class InternalExecutableQuery implements ExecutableQuery {
3638
private final Driver driver;
3739
private final Query query;
3840
private final QueryConfig config;
41+
private final AuthToken authToken;
3942

40-
public InternalExecutableQuery(Driver driver, Query query, QueryConfig config) {
43+
public InternalExecutableQuery(Driver driver, Query query, QueryConfig config, AuthToken authToken) {
4144
requireNonNull(driver, "driver must not be null");
4245
requireNonNull(query, "query must not be null");
4346
requireNonNull(config, "config must not be null");
4447
this.driver = driver;
4548
this.query = query;
4649
this.config = config;
50+
this.authToken = authToken;
4751
}
4852

4953
@Override
5054
public ExecutableQuery withParameters(Map<String, Object> parameters) {
5155
requireNonNull(parameters, "parameters must not be null");
52-
return new InternalExecutableQuery(driver, query.withParameters(parameters), config);
56+
return new InternalExecutableQuery(driver, query.withParameters(parameters), config, authToken);
5357
}
5458

5559
@Override
5660
public ExecutableQuery withConfig(QueryConfig config) {
5761
requireNonNull(config, "config must not be null");
58-
return new InternalExecutableQuery(driver, query, config);
62+
return new InternalExecutableQuery(driver, query, config, authToken);
63+
}
64+
65+
@Override
66+
public ExecutableQuery withAuthToken(AuthToken authToken) {
67+
return new InternalExecutableQuery(driver, query, config, authToken);
5968
}
6069

6170
@Override
@@ -68,7 +77,7 @@ public <A, R, T> T execute(Collector<Record, A, R> recordCollector, ResultFinish
6877
var supplier = recordCollector.supplier();
6978
var accumulator = recordCollector.accumulator();
7079
var finisher = recordCollector.finisher();
71-
try (var session = (InternalSession) driver.session(sessionConfigBuilder.build())) {
80+
try (var session = (InternalSession) driver.session(Session.class, sessionConfigBuilder.build(), authToken)) {
7281
TransactionCallback<T> txCallback = tx -> {
7382
var result = tx.run(query);
7483
var container = supplier.get();
@@ -107,4 +116,9 @@ public Map<String, Object> parameters() {
107116
public QueryConfig config() {
108117
return config;
109118
}
119+
120+
// For testing only
121+
public AuthToken authToken() {
122+
return authToken;
123+
}
110124
}

driver/src/test/java/org/neo4j/driver/internal/InternalExecutableQueryTest.java

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.neo4j.driver.internal;
1818

1919
import static org.junit.jupiter.api.Assertions.assertEquals;
20+
import static org.junit.jupiter.api.Assertions.assertNull;
2021
import static org.junit.jupiter.api.Assertions.assertThrows;
2122
import static org.mockito.ArgumentMatchers.any;
2223
import static org.mockito.ArgumentMatchers.eq;
@@ -35,6 +36,7 @@
3536
import org.junit.jupiter.params.provider.MethodSource;
3637
import org.mockito.ArgumentCaptor;
3738
import org.neo4j.driver.AccessMode;
39+
import org.neo4j.driver.AuthTokens;
3840
import org.neo4j.driver.BookmarkManager;
3941
import org.neo4j.driver.Driver;
4042
import org.neo4j.driver.ExecutableQuery;
@@ -43,6 +45,7 @@
4345
import org.neo4j.driver.Record;
4446
import org.neo4j.driver.Result;
4547
import org.neo4j.driver.RoutingControl;
48+
import org.neo4j.driver.Session;
4649
import org.neo4j.driver.SessionConfig;
4750
import org.neo4j.driver.TransactionCallback;
4851
import org.neo4j.driver.TransactionConfig;
@@ -55,27 +58,27 @@ class InternalExecutableQueryTest {
5558
void shouldNotAcceptNullDriverOnInstantiation() {
5659
assertThrows(
5760
NullPointerException.class,
58-
() -> new InternalExecutableQuery(null, new Query("string"), QueryConfig.defaultConfig()));
61+
() -> new InternalExecutableQuery(null, new Query("string"), QueryConfig.defaultConfig(), null));
5962
}
6063

6164
@Test
6265
void shouldNotAcceptNullQueryOnInstantiation() {
6366
assertThrows(
6467
NullPointerException.class,
65-
() -> new InternalExecutableQuery(mock(Driver.class), null, QueryConfig.defaultConfig()));
68+
() -> new InternalExecutableQuery(mock(Driver.class), null, QueryConfig.defaultConfig(), null));
6669
}
6770

6871
@Test
6972
void shouldNotAcceptNullConfigOnInstantiation() {
7073
assertThrows(
7174
NullPointerException.class,
72-
() -> new InternalExecutableQuery(mock(Driver.class), new Query("string"), null));
75+
() -> new InternalExecutableQuery(mock(Driver.class), new Query("string"), null, null));
7376
}
7477

7578
@Test
7679
void shouldNotAcceptNullParameters() {
7780
var executableQuery =
78-
new InternalExecutableQuery(mock(Driver.class), new Query("string"), QueryConfig.defaultConfig());
81+
new InternalExecutableQuery(mock(Driver.class), new Query("string"), QueryConfig.defaultConfig(), null);
7982
assertThrows(NullPointerException.class, () -> executableQuery.withParameters(null));
8083
}
8184

@@ -84,7 +87,7 @@ void shouldUpdateParameters() {
8487
// GIVEN
8588
var query = new Query("string");
8689
var params = Map.<String, Object>of("$param", "value");
87-
var executableQuery = new InternalExecutableQuery(mock(Driver.class), query, QueryConfig.defaultConfig());
90+
var executableQuery = new InternalExecutableQuery(mock(Driver.class), query, QueryConfig.defaultConfig(), null);
8891

8992
// WHEN
9093
executableQuery = (InternalExecutableQuery) executableQuery.withParameters(params);
@@ -96,15 +99,15 @@ void shouldUpdateParameters() {
9699
@Test
97100
void shouldNotAcceptNullConfig() {
98101
var executableQuery =
99-
new InternalExecutableQuery(mock(Driver.class), new Query("string"), QueryConfig.defaultConfig());
102+
new InternalExecutableQuery(mock(Driver.class), new Query("string"), QueryConfig.defaultConfig(), null);
100103
assertThrows(NullPointerException.class, () -> executableQuery.withConfig(null));
101104
}
102105

103106
@Test
104107
void shouldUpdateConfig() {
105108
// GIVEN
106109
var query = new Query("string");
107-
var executableQuery = new InternalExecutableQuery(mock(Driver.class), query, QueryConfig.defaultConfig());
110+
var executableQuery = new InternalExecutableQuery(mock(Driver.class), query, QueryConfig.defaultConfig(), null);
108111
var config = QueryConfig.builder().withDatabase("database").build();
109112

110113
// WHEN
@@ -127,7 +130,8 @@ void shouldExecuteAndReturnResult(RoutingControl routingControl) {
127130
var bookmarkManager = mock(BookmarkManager.class);
128131
given(driver.executableQueryBookmarkManager()).willReturn(bookmarkManager);
129132
var session = mock(InternalSession.class);
130-
given(driver.session(any(SessionConfig.class))).willReturn(session);
133+
given(driver.session(eq(Session.class), any(SessionConfig.class), eq(null)))
134+
.willReturn(session);
131135
var txContext = mock(TransactionContext.class);
132136
var accessMode = routingControl.equals(RoutingControl.WRITE) ? AccessMode.WRITE : AccessMode.READ;
133137
given(session.execute(
@@ -169,14 +173,14 @@ var record = mock(Record.class);
169173
var expectedExecuteResult = "1";
170174
given(finisherWithSummary.finish(any(List.class), any(String.class), any(ResultSummary.class)))
171175
.willReturn(expectedExecuteResult);
172-
var executableQuery = new InternalExecutableQuery(driver, query, config).withParameters(params);
176+
var executableQuery = new InternalExecutableQuery(driver, query, config, null).withParameters(params);
173177

174178
// WHEN
175179
var executeResult = executableQuery.execute(recordCollector, finisherWithSummary);
176180

177181
// THEN
178182
var sessionConfigCapture = ArgumentCaptor.forClass(SessionConfig.class);
179-
then(driver).should().session(sessionConfigCapture.capture());
183+
then(driver).should().session(eq(Session.class), sessionConfigCapture.capture(), eq(null));
180184
var sessionConfig = sessionConfigCapture.getValue();
181185
@SuppressWarnings("OptionalGetWithoutIsPresent")
182186
var expectedSessionConfig = SessionConfig.builder()
@@ -205,4 +209,25 @@ var record = mock(Record.class);
205209
then(finisherWithSummary).should().finish(keys, collectorResult, summary);
206210
assertEquals(expectedExecuteResult, executeResult);
207211
}
212+
213+
@Test
214+
void shouldAllowNullAuthToken() {
215+
var executableQuery =
216+
new InternalExecutableQuery(mock(Driver.class), new Query("string"), QueryConfig.defaultConfig(), null);
217+
218+
executableQuery.withAuthToken(null);
219+
220+
assertNull(executableQuery.authToken());
221+
}
222+
223+
@Test
224+
void shouldUpdateAuthToken() {
225+
var executableQuery =
226+
new InternalExecutableQuery(mock(Driver.class), new Query("string"), QueryConfig.defaultConfig(), null);
227+
var authToken = AuthTokens.basic("user", "password");
228+
229+
executableQuery = (InternalExecutableQuery) executableQuery.withAuthToken(authToken);
230+
231+
assertEquals(authToken, executableQuery.authToken());
232+
}
208233
}

testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/ExecuteQuery.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.util.concurrent.CompletionStage;
2626
import lombok.Getter;
2727
import lombok.Setter;
28+
import neo4j.org.testkit.backend.AuthTokenUtil;
2829
import neo4j.org.testkit.backend.TestkitState;
2930
import neo4j.org.testkit.backend.messages.requests.deserializer.TestkitCypherParamDeserializer;
3031
import neo4j.org.testkit.backend.messages.responses.EagerResult;
@@ -73,10 +74,15 @@ public TestkitResponse process(TestkitState testkitState) {
7374

7475
Optional.ofNullable(data.getConfig().getTxMeta()).ifPresent(configBuilder::withMetadata);
7576

77+
var authToken = data.getConfig().getAuth() != null
78+
? AuthTokenUtil.parseAuthToken(data.getConfig().getAuth())
79+
: null;
80+
7681
var params = data.getParams() != null ? data.getParams() : Collections.<String, Object>emptyMap();
7782
var eagerResult = driver.executableQuery(data.getCypher())
7883
.withParameters(params)
7984
.withConfig(configBuilder.build())
85+
.withAuthToken(authToken)
8086
.execute();
8187

8288
return EagerResult.builder()
@@ -135,5 +141,7 @@ public static class QueryConfigData {
135141

136142
@JsonDeserialize(using = TestkitCypherParamDeserializer.class)
137143
private Map<String, Serializable> txMeta;
144+
145+
private AuthorizationToken auth;
138146
}
139147
}

testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/GetFeatures.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ public class GetFeatures implements TestkitRequest {
7878
"Optimization:ResultListFetchAll",
7979
"Feature:API:Result.Single",
8080
"Feature:API:Driver.ExecuteQuery",
81+
"Feature:API:Driver.ExecuteQuery:WithAuth",
8182
"Feature:API:Driver.VerifyAuthentication",
8283
"Optimization:ExecuteQueryPipelining"));
8384

0 commit comments

Comments
 (0)