Skip to content

Commit 0d4b5c5

Browse files
committed
Introduce Bolt Protocol 5.1
This version of the protocol introduces two new messages for manage credentials. * LOGIN with a given auth token. * LOGOFF of the current auth token. The HELLO message is not more responsabile for negotiating credentials in this version of the protocol.
1 parent 1364002 commit 0d4b5c5

36 files changed

+2130
-27
lines changed

packages/bolt-connection/src/bolt/bolt-protocol-v1.js

Lines changed: 84 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,13 @@ import { structure, v1 } from '../packstream'
2727
import RequestMessage from './request-message'
2828
import {
2929
LoginObserver,
30+
LogoffObserver,
3031
ResetObserver,
3132
ResultStreamObserver,
3233
// eslint-disable-next-line no-unused-vars
3334
StreamObserver
3435
} from './stream-observers'
35-
import { internal } from 'neo4j-driver-core'
36+
import { internal, newError } from 'neo4j-driver-core'
3637
import transformersFactories from './bolt-protocol-v1.transformer'
3738
import Transformer from './transformer'
3839

@@ -99,6 +100,27 @@ export default class BoltProtocol {
99100
return BOLT_PROTOCOL_V1
100101
}
101102

103+
/**
104+
* @property {boolean} supportsReAuth Either if the protocol version supports re-auth or not.
105+
*/
106+
get supportsReAuth () {
107+
return false
108+
}
109+
110+
/**
111+
* @property {boolean} initialized Either if the protocol was initialized or not
112+
*/
113+
get initialized () {
114+
return !!this._initialized
115+
}
116+
117+
/**
118+
* @property {object} authToken The token used in the last login
119+
*/
120+
get authToken () {
121+
return this._authToken
122+
}
123+
102124
/**
103125
* Get the packer.
104126
* @return {Packer} the protocol's packer.
@@ -162,6 +184,61 @@ export default class BoltProtocol {
162184
return observer
163185
}
164186

187+
/**
188+
* Performs logoff of the underlying connection
189+
*
190+
* @param {Object} param
191+
* @param {function(err: Error)} param.onError the callback to invoke on error.
192+
* @param {function()} param.onComplete the callback to invoke on completion.
193+
* @param {boolean} param.flush whether to flush the buffered messages.
194+
*
195+
* @returns {StreamObserver} the stream observer that monitors the corresponding server response.
196+
*/
197+
logoff ({ onComplete, onError, flush } = {}) {
198+
const observer = new LogoffObserver({
199+
onCompleted: onComplete,
200+
onError: onError
201+
})
202+
203+
const error = newError(
204+
'Driver is connected to a database that does not support logoff. ' +
205+
'Please upgrade to Neo4j 5.5.0 or later in order to use this functionality.'
206+
)
207+
208+
// unsupported API was used, consider this a fatal error for the current connection
209+
this._onProtocolError(error.message)
210+
observer.onError(error)
211+
throw error
212+
}
213+
214+
/**
215+
* Performs login of the underlying connection
216+
*
217+
* @param {Object} args
218+
* @param {Object} args.authToken the authentication token.
219+
* @param {function(err: Error)} args.onError the callback to invoke on error.
220+
* @param {function()} args.onComplete the callback to invoke on completion.
221+
* @param {boolean} args.flush whether to flush the buffered messages.
222+
*
223+
* @returns {StreamObserver} the stream observer that monitors the corresponding server response.
224+
*/
225+
logon ({ authToken, onComplete, onError, flush } = {}) {
226+
const observer = new LoginObserver({
227+
onCompleted: () => this._onLoginCompleted({}, authToken, onComplete),
228+
onError: (error) => this._onLoginError(error, onError)
229+
})
230+
231+
const error = newError(
232+
'Driver is connected to a database that does not support logon. ' +
233+
'Please upgrade to Neo4j 5.5.0 or later in order to use this functionality.'
234+
)
235+
236+
// unsupported API was used, consider this a fatal error for the current connection
237+
this._onProtocolError(error.message)
238+
observer.onError(error)
239+
throw error
240+
}
241+
165242
/**
166243
* Perform protocol related operations for closing this connection
167244
*/
@@ -391,15 +468,15 @@ export default class BoltProtocol {
391468
this.packable(messageStruct)()
392469

393470
this._chunker.messageBoundary()
394-
395471
if (flush) {
396472
this._chunker.flush()
397473
}
398474
}
399475
}
400476

401-
isLastMessageLogin () {
402-
return this._lastMessageSignature === 0x01
477+
isLastMessageLogon () {
478+
return this._lastMessageSignature === 0x01 ||
479+
this._lastMessageSignature === 0x6A
403480
}
404481

405482
isLastMessageReset () {
@@ -472,7 +549,9 @@ export default class BoltProtocol {
472549
this._responseHandler._resetFailure()
473550
}
474551

475-
_onLoginCompleted (metadata, onCompleted) {
552+
_onLoginCompleted (metadata, authToken, onCompleted) {
553+
this._initialized = true
554+
this._authToken = authToken
476555
if (metadata) {
477556
const serverVersion = metadata.server
478557
if (!this._server.version) {

packages/bolt-connection/src/bolt/bolt-protocol-v3.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export default class BoltProtocol extends BoltProtocolV2 {
7272
initialize ({ userAgent, authToken, onError, onComplete } = {}) {
7373
const observer = new LoginObserver({
7474
onError: error => this._onLoginError(error, onError),
75-
onCompleted: metadata => this._onLoginCompleted(metadata, onComplete)
75+
onCompleted: metadata => this._onLoginCompleted(metadata, authToken, onComplete)
7676
})
7777

7878
this.write(RequestMessage.hello(userAgent, authToken), observer, true)

packages/bolt-connection/src/bolt/bolt-protocol-v4x1.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export default class BoltProtocol extends BoltProtocolV4 {
7575
initialize ({ userAgent, authToken, onError, onComplete } = {}) {
7676
const observer = new LoginObserver({
7777
onError: error => this._onLoginError(error, onError),
78-
onCompleted: metadata => this._onLoginCompleted(metadata, onComplete)
78+
onCompleted: metadata => this._onLoginCompleted(metadata, authToken, onComplete)
7979
})
8080

8181
this.write(

packages/bolt-connection/src/bolt/bolt-protocol-v4x3.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ export default class BoltProtocol extends BoltProtocolV42 {
9494
if (metadata.patch_bolt !== undefined) {
9595
this._applyPatches(metadata.patch_bolt)
9696
}
97-
return this._onLoginCompleted(metadata, onComplete)
97+
return this._onLoginCompleted(metadata, authToken, onComplete)
9898
}
9999
})
100100

packages/bolt-connection/src/bolt/bolt-protocol-v5x0.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export default class BoltProtocol extends BoltProtocolV44 {
5454
initialize ({ userAgent, authToken, onError, onComplete } = {}) {
5555
const observer = new LoginObserver({
5656
onError: error => this._onLoginError(error, onError),
57-
onCompleted: metadata => this._onLoginCompleted(metadata, onComplete)
57+
onCompleted: metadata => this._onLoginCompleted(metadata, authToken, onComplete)
5858
})
5959

6060
this.write(
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
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+
import BoltProtocolV5x0 from './bolt-protocol-v5x0'
20+
21+
import transformersFactories from './bolt-protocol-v5x1.transformer'
22+
import Transformer from './transformer'
23+
import RequestMessage from './request-message'
24+
import { LoginObserver, LogoffObserver } from './stream-observers'
25+
26+
import { internal } from 'neo4j-driver-core'
27+
28+
const {
29+
constants: { BOLT_PROTOCOL_V5_1 }
30+
} = internal
31+
32+
export default class BoltProtocol extends BoltProtocolV5x0 {
33+
get version () {
34+
return BOLT_PROTOCOL_V5_1
35+
}
36+
37+
get transformer () {
38+
if (this._transformer === undefined) {
39+
this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log)))
40+
}
41+
return this._transformer
42+
}
43+
44+
get supportsReAuth () {
45+
return true
46+
}
47+
48+
/**
49+
* Initialize a connection with the server
50+
*
51+
* @param {Object} param0 The params
52+
* @param {string} param0.userAgent The user agent
53+
* @param {any} param0.authToken The auth token
54+
* @param {function(error)} param0.onError On error callback
55+
* @param {function(onComplte)} param0.onComplete On complete callback
56+
* @returns {LoginObserver} The Login observer
57+
*/
58+
initialize ({ userAgent, authToken, onError, onComplete } = {}) {
59+
const state = {}
60+
const observer = new LoginObserver({
61+
onError: error => this._onLoginError(error, onError),
62+
onCompleted: metadata => {
63+
state.metadata = metadata
64+
return this._onLoginCompleted(metadata)
65+
}
66+
})
67+
68+
this.write(
69+
RequestMessage.hello5x1(userAgent, this._serversideRouting),
70+
observer,
71+
false
72+
)
73+
74+
return this.logon({
75+
authToken,
76+
onComplete: metadata => onComplete({ ...metadata, ...state.metadata }),
77+
onError,
78+
flush: true
79+
})
80+
}
81+
82+
/**
83+
* Performs login of the underlying connection
84+
*
85+
* @param {Object} args
86+
* @param {Object} args.authToken the authentication token.
87+
* @param {function(err: Error)} args.onError the callback to invoke on error.
88+
* @param {function()} args.onComplete the callback to invoke on completion.
89+
* @param {boolean} args.flush whether to flush the buffered messages.
90+
*
91+
* @returns {StreamObserver} the stream observer that monitors the corresponding server response.
92+
*/
93+
logon ({ authToken, onComplete, onError, flush } = {}) {
94+
const observer = new LoginObserver({
95+
onCompleted: () => this._onLoginCompleted(null, authToken, onComplete),
96+
onError: (error) => this._onLoginError(error, onError)
97+
})
98+
99+
this.write(
100+
RequestMessage.logon(authToken),
101+
observer,
102+
flush
103+
)
104+
105+
return observer
106+
}
107+
108+
/**
109+
* Performs logoff of the underlying connection
110+
*
111+
* @param {Object} param
112+
* @param {function(err: Error)} param.onError the callback to invoke on error.
113+
* @param {function()} param.onComplete the callback to invoke on completion.
114+
* @param {boolean} param.flush whether to flush the buffered messages.
115+
*
116+
* @returns {StreamObserver} the stream observer that monitors the corresponding server response.
117+
*/
118+
logoff ({ onComplete, onError, flush } = {}) {
119+
const observer = new LogoffObserver({
120+
onCompleted: onComplete,
121+
onError: onError
122+
})
123+
124+
this.write(
125+
RequestMessage.logoff(),
126+
observer,
127+
flush
128+
)
129+
130+
return observer
131+
}
132+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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+
20+
import v5x0 from './bolt-protocol-v5x0.transformer'
21+
22+
export default {
23+
...v5x0
24+
}

packages/bolt-connection/src/bolt/create.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import BoltProtocolV4x2 from './bolt-protocol-v4x2'
2727
import BoltProtocolV4x3 from './bolt-protocol-v4x3'
2828
import BoltProtocolV4x4 from './bolt-protocol-v4x4'
2929
import BoltProtocolV5x0 from './bolt-protocol-v5x0'
30+
import BoltProtocolV5x1 from './bolt-protocol-v5x1'
3031
// eslint-disable-next-line no-unused-vars
3132
import { Chunker, Dechunker } from '../channel'
3233
import ResponseHandler from './response-handler'
@@ -191,6 +192,16 @@ function createProtocol (
191192
onProtocolError,
192193
serversideRouting
193194
)
195+
case 5.1:
196+
return new BoltProtocolV5x1(
197+
server,
198+
chunker,
199+
packingConfig,
200+
createResponseHandler,
201+
log,
202+
onProtocolError,
203+
serversideRouting
204+
)
194205
default:
195206
throw newError('Unknown Bolt protocol version: ' + version)
196207
}

packages/bolt-connection/src/bolt/handshake.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ function parseNegotiatedResponse (buffer) {
7676
*/
7777
function newHandshakeBuffer () {
7878
return createHandshakeMessage([
79-
version(5, 0),
79+
[version(5, 1), version(5, 0)],
8080
[version(4, 4), version(4, 2)],
8181
version(4, 1),
8282
version(3, 0)

0 commit comments

Comments
 (0)