Skip to content

Commit a699004

Browse files
committed
Introduce change of AuthToken support
This update brings support for a change of `AuthToken` during driver's lifetime via a new type called `AuthTokenManager` that has the following 2 responsibilities: - supplying a valid token, which may be one of the following - a current token - a new token if driver should update the token - handling a token expired failure that originates from the server if it determines the current token to be expired Driver does not make judgements on whether `AuthToken` is expired. It calls `AuthTokenManager` to check if the provided token is the same as the previously used and takes action if not. A change of `AuthToken` is most efficient with the Bolt 5.1 `LOGOFF` and `LOGON` messages. Therefore, this update also includes Bolt 5.1 support.
1 parent c7b1987 commit a699004

File tree

139 files changed

+5753
-413
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

139 files changed

+5753
-413
lines changed

driver/clirr-ignored-differences.xml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,4 +433,28 @@
433433
<method>org.neo4j.driver.BookmarkManager queryTaskBookmarkManager()</method>
434434
</difference>
435435

436+
<difference>
437+
<className>org/neo4j/driver/Driver</className>
438+
<differenceType>7012</differenceType>
439+
<method>org.neo4j.driver.BaseSession session(java.lang.Class, org.neo4j.driver.AuthToken)</method>
440+
</difference>
441+
442+
<difference>
443+
<className>org/neo4j/driver/Driver</className>
444+
<differenceType>7012</differenceType>
445+
<method>org.neo4j.driver.BaseSession session(java.lang.Class, org.neo4j.driver.SessionConfig, org.neo4j.driver.AuthToken)</method>
446+
</difference>
447+
448+
<difference>
449+
<className>org/neo4j/driver/Driver</className>
450+
<differenceType>7012</differenceType>
451+
<method>boolean verifyAuthentication(org.neo4j.driver.AuthToken)</method>
452+
</difference>
453+
454+
<difference>
455+
<className>org/neo4j/driver/Driver</className>
456+
<differenceType>7012</differenceType>
457+
<method>boolean supportsSessionAuth()</method>
458+
</difference>
459+
436460
</differences>
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright (c) "Neo4j"
3+
* Neo4j Sweden AB [http://neo4j.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
package org.neo4j.driver;
20+
21+
import java.util.concurrent.CompletionStage;
22+
23+
/**
24+
* A manager of {@link AuthToken} used by the driver.
25+
* <p>
26+
* Implementations should supply the same token unless it needs to be updated since a change of token might result in
27+
* extra processing by the driver.
28+
* <p>
29+
* Driver initializes new connections with a token supplied by the manager. If token changes, driver action depends on
30+
* connection's Bolt protocol version:
31+
* <ul>
32+
* <li>Bolt 5.1 or above - {@code LOGOFF} and {@code LOGON} messages are dispatched to update the token on next interaction</li>
33+
* <li>Bolt 5.0 or below - connection is closed an a new one is initialized with the new token</li>
34+
* </ul>
35+
* <p>
36+
* All implementations of this interface must be non-blocking for caller threads. For instance, IO operations must not
37+
* be done on the calling thread.
38+
* @since 5.7
39+
*/
40+
public interface AuthTokenManager {
41+
/**
42+
* Returns a {@link CompletionStage} for a valid {@link AuthToken}.
43+
* <p>
44+
* Driver invokes this method often to check if token has changed.
45+
* <p>
46+
* Failures will surface via the driver API, like {@link Session#beginTransaction()} method and others.
47+
* @return a stage for a valid token, must not be {@code null} or complete with {@code null}
48+
* @see org.neo4j.driver.exceptions.AuthTokenManagerExecutionException
49+
*/
50+
CompletionStage<AuthToken> getToken();
51+
52+
/**
53+
* Handles an error notification emitted by the server if the token is expired.
54+
* <p>
55+
* This will be called when driver emits the {@link org.neo4j.driver.exceptions.TokenExpiredRetryableException}.
56+
*
57+
* @param authToken the expired token
58+
*/
59+
void onExpired(AuthToken authToken);
60+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
* Copyright (c) "Neo4j"
3+
* Neo4j Sweden AB [http://neo4j.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
package org.neo4j.driver;
20+
21+
import java.time.Clock;
22+
import java.util.concurrent.CompletableFuture;
23+
import java.util.concurrent.CompletionStage;
24+
import java.util.concurrent.ForkJoinPool;
25+
import java.util.function.Supplier;
26+
import org.neo4j.driver.internal.security.TemporalAuthTokenManager;
27+
28+
/**
29+
* Implementations of {@link AuthTokenManager}.
30+
* @since 5.7
31+
*/
32+
public final class AuthTokenManagers {
33+
private AuthTokenManagers() {}
34+
35+
/**
36+
* Returns an {@link AuthTokenManager} that manages {@link AuthToken} instances with UTC expiration timestamp.
37+
* <p>
38+
* The implementation will only use the token supplier when it needs a new token instance. This includes the
39+
* following conditions:
40+
* <ol>
41+
* <li>token's UTC timestamp is expired</li>
42+
* <li>server rejects the current token (see {@link AuthTokenManager#onExpired(AuthToken)})</li>
43+
* </ol>
44+
* <p>
45+
* The supplier will be called by a task running in the {@link ForkJoinPool#commonPool()} as documented in the
46+
* {@link CompletableFuture#supplyAsync(Supplier)}.
47+
*
48+
* @param newTokenSupplier a new token supplier
49+
* @return a new token manager
50+
*/
51+
public static AuthTokenManager temporal(Supplier<TemporalAuthData> newTokenSupplier) {
52+
return new TemporalAuthTokenManager(() -> CompletableFuture.supplyAsync(newTokenSupplier), Clock.systemUTC());
53+
}
54+
55+
/**
56+
* Returns an {@link AuthTokenManager} that manages {@link AuthToken} instances with UTC expiration timestamp.
57+
* <p>
58+
* The implementation will only use the token supplier when it needs a new token instance. This includes the
59+
* following conditions:
60+
* <ol>
61+
* <li>token's UTC timestamp is expired</li>
62+
* <li>server rejects the current token (see {@link AuthTokenManager#onExpired(AuthToken)})</li>
63+
* </ol>
64+
* <p>
65+
* The provided supplier and its completion stages must be non-blocking as documented in the {@link AuthTokenManager}.
66+
*
67+
* @param newTokenStageSupplier a new token stage supplier
68+
* @return a new token manager
69+
*/
70+
public static AuthTokenManager temporalAsync(Supplier<CompletionStage<TemporalAuthData>> newTokenStageSupplier) {
71+
return new TemporalAuthTokenManager(newTokenStageSupplier, Clock.systemUTC());
72+
}
73+
74+
/**
75+
* A container used by the temporal {@link AuthTokenManager} implementation provided by the driver, it contains an
76+
* {@link AuthToken} and its UTC expiration timestamp.
77+
* @since 5.7
78+
*/
79+
public interface TemporalAuthData {
80+
/**
81+
* Returns a new instance with the provided token and {@link Long#MAX_VALUE} expiration timestamp.
82+
* @param authToken the token
83+
* @return a new instance
84+
*/
85+
static TemporalAuthData of(AuthToken authToken) {
86+
return of(authToken, Long.MAX_VALUE);
87+
}
88+
89+
/**
90+
* Returns a new instance with the provided token and its expiration timestamp.
91+
* @param authToken the token
92+
* @param expirationTimestamp the expiration timestamp
93+
* @return a new instance
94+
*/
95+
static TemporalAuthData of(AuthToken authToken, long expirationTimestamp) {
96+
return new TemporalAuthTokenManager.InternalTemporalAuthData(authToken, expirationTimestamp);
97+
}
98+
99+
/**
100+
* Returns the {@link AuthToken}.
101+
*
102+
* @return the token
103+
*/
104+
AuthToken authToken();
105+
106+
/**
107+
* Returns the token's UTC expiration timestamp.
108+
*
109+
* @return the token's UTC expiration timestamp
110+
*/
111+
long expirationTimestamp();
112+
}
113+
}

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

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,42 @@ default <T extends BaseSession> T session(Class<T> sessionClass) {
144144
return session(sessionClass, SessionConfig.defaultConfig());
145145
}
146146

147+
/**
148+
* Instantiate a new session of a supported type with the supplied {@link AuthToken}.
149+
* <p>
150+
* This method allows creating a session with a different {@link AuthToken} to the one used on the driver level.
151+
* The minimum Bolt protocol version is 5.1. An {@link IllegalStateException} will be emitted on session interaction
152+
* for previous Bolt versions.
153+
* <p>
154+
* Supported types are:
155+
* <ul>
156+
* <li>{@link org.neo4j.driver.Session} - synchronous session</li>
157+
* <li>{@link org.neo4j.driver.async.AsyncSession} - asynchronous session</li>
158+
* <li>{@link org.neo4j.driver.reactive.ReactiveSession} - reactive session using Flow API</li>
159+
* <li>{@link org.neo4j.driver.reactivestreams.ReactiveSession} - reactive session using Reactive Streams
160+
* API</li>
161+
* <li>{@link org.neo4j.driver.reactive.RxSession} - deprecated reactive session using Reactive Streams
162+
* API, superseded by {@link org.neo4j.driver.reactivestreams.ReactiveSession}</li>
163+
* </ul>
164+
* <p>
165+
* Sample usage:
166+
* <pre>
167+
* {@code
168+
* var session = driver.session(AsyncSession.class);
169+
* }
170+
* </pre>
171+
*
172+
* @param sessionClass session type class, must not be null
173+
* @param sessionAuthToken a token, null will result in driver-level configuration being used
174+
* @return session instance
175+
* @param <T> session type
176+
* @throws IllegalArgumentException for unsupported session types
177+
* @since 5.7
178+
*/
179+
default <T extends BaseSession> T session(Class<T> sessionClass, AuthToken sessionAuthToken) {
180+
return session(sessionClass, SessionConfig.defaultConfig(), sessionAuthToken);
181+
}
182+
147183
/**
148184
* Create a new session of supported type with a specified {@link SessionConfig session configuration}.
149185
* <p>
@@ -172,7 +208,45 @@ default <T extends BaseSession> T session(Class<T> sessionClass) {
172208
* @throws IllegalArgumentException for unsupported session types
173209
* @since 5.2
174210
*/
175-
<T extends BaseSession> T session(Class<T> sessionClass, SessionConfig sessionConfig);
211+
default <T extends BaseSession> T session(Class<T> sessionClass, SessionConfig sessionConfig) {
212+
return session(sessionClass, sessionConfig, null);
213+
}
214+
215+
/**
216+
* Instantiate a new session of a supported type with the supplied {@link SessionConfig session configuration} and
217+
* {@link AuthToken}.
218+
* <p>
219+
* This method allows creating a session with a different {@link AuthToken} to the one used on the driver level.
220+
* The minimum Bolt protocol version is 5.1. An {@link IllegalStateException} will be emitted on session interaction
221+
* for previous Bolt versions.
222+
* <p>
223+
* Supported types are:
224+
* <ul>
225+
* <li>{@link org.neo4j.driver.Session} - synchronous session</li>
226+
* <li>{@link org.neo4j.driver.async.AsyncSession} - asynchronous session</li>
227+
* <li>{@link org.neo4j.driver.reactive.ReactiveSession} - reactive session using Flow API</li>
228+
* <li>{@link org.neo4j.driver.reactivestreams.ReactiveSession} - reactive session using Reactive Streams
229+
* API</li>
230+
* <li>{@link org.neo4j.driver.reactive.RxSession} - deprecated reactive session using Reactive Streams
231+
* API, superseded by {@link org.neo4j.driver.reactivestreams.ReactiveSession}</li>
232+
* </ul>
233+
* <p>
234+
* Sample usage:
235+
* <pre>
236+
* {@code
237+
* var session = driver.session(AsyncSession.class);
238+
* }
239+
* </pre>
240+
*
241+
* @param sessionClass session type class, must not be null
242+
* @param sessionConfig session config, must not be null
243+
* @param sessionAuthToken a token, null will result in driver-level configuration being used
244+
* @return session instance
245+
* @param <T> session type
246+
* @throws IllegalArgumentException for unsupported session types
247+
* @since 5.7
248+
*/
249+
<T extends BaseSession> T session(Class<T> sessionClass, SessionConfig sessionConfig, AuthToken sessionAuthToken);
176250

177251
/**
178252
* Create a new general purpose {@link RxSession} with default {@link SessionConfig session configuration}. The {@link RxSession} provides a reactive way to
@@ -325,6 +399,24 @@ default AsyncSession asyncSession(SessionConfig sessionConfig) {
325399
*/
326400
CompletionStage<Void> verifyConnectivityAsync();
327401

402+
/**
403+
* Verifies if the given {@link AuthToken} is valid.
404+
* <p>
405+
* This check works on Bolt 5.1 version or above only.
406+
* @param authToken the token
407+
* @return the verification outcome
408+
* @since 5.7
409+
*/
410+
boolean verifyAuthentication(AuthToken authToken);
411+
412+
/**
413+
* Checks if session auth is supported.
414+
* @return the check outcome
415+
* @since 5.7
416+
* @see Driver#session(Class, SessionConfig, AuthToken)
417+
*/
418+
boolean supportsSessionAuth();
419+
328420
/**
329421
* Returns true if the server or cluster the driver connects to supports multi-databases, otherwise false.
330422
*

0 commit comments

Comments
 (0)