Skip to content

Commit e0747d7

Browse files
committed
Add impersonatedUser o the driver surface
1 parent bfa4721 commit e0747d7

File tree

11 files changed

+190
-14
lines changed

11 files changed

+190
-14
lines changed

packages/bolt-connection/src/connection-provider/connection-provider-direct.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import {
2626
import { internal, error } from 'neo4j-driver-core'
2727

2828
const {
29-
constants: { BOLT_PROTOCOL_V4_0, BOLT_PROTOCOL_V3 }
29+
constants: { BOLT_PROTOCOL_V4_0, BOLT_PROTOCOL_V3, BOLT_PROTOCOL_V4_4 }
3030
} = internal
3131

3232
const { SERVICE_UNAVAILABLE, newError } = error
@@ -97,4 +97,10 @@ export default class DirectConnectionProvider extends PooledConnectionProvider {
9797
version => version >= BOLT_PROTOCOL_V3
9898
)
9999
}
100+
101+
async supportsUserImpersonation () {
102+
return await this._hasProtocolVersion(
103+
version => version >= BOLT_PROTOCOL_V4_4
104+
)
105+
}
100106
}

packages/bolt-connection/src/connection-provider/connection-provider-routing.js

+8-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ const {
3636
ACCESS_MODE_READ: READ,
3737
ACCESS_MODE_WRITE: WRITE,
3838
BOLT_PROTOCOL_V3,
39-
BOLT_PROTOCOL_V4_0
39+
BOLT_PROTOCOL_V4_0,
40+
BOLT_PROTOCOL_V4_4
4041
}
4142
} = internal
4243

@@ -224,6 +225,12 @@ export default class RoutingConnectionProvider extends PooledConnectionProvider
224225
)
225226
}
226227

228+
async supportsUserImpersonation () {
229+
return await this._hasProtocolVersion(
230+
version => version >= BOLT_PROTOCOL_V4_4
231+
)
232+
}
233+
227234
forget (address, database) {
228235
this._routingTableRegistry.apply(database, {
229236
applyWhenExists: routingTable => routingTable.forget(address)

packages/core/src/connection-provider.ts

+10
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,16 @@ class ConnectionProvider {
6868
throw Error('Not implemented')
6969
}
7070

71+
/**
72+
* This method checks whether the backend database supports transaction config functionality
73+
* by checking protocol handshake result.
74+
*
75+
* @returns {Promise<boolean>}
76+
*/
77+
supportsUserImpersonation(): Promise<boolean> {
78+
throw Error('Not implemented')
79+
}
80+
7181
/**
7282
* Closes this connection provider along with its internals (connections, pools, etc.)
7383
*

packages/core/src/driver.ts

+38-3
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,17 @@ type CreateConnectionProvider = (
7878
hostNameResolver: ConfiguredCustomResolver
7979
) => ConnectionProvider
8080

81+
type CreateSession = (args: {
82+
mode: SessionMode
83+
connectionProvider: ConnectionProvider
84+
bookmark?: Bookmark
85+
database: string
86+
config: any
87+
reactive: boolean
88+
fetchSize: number,
89+
impersonatedUser?: string
90+
}) => Session
91+
8192
interface DriverConfig {
8293
encrypted?: EncryptionLevel | boolean
8394
trust?: TrustStrategy
@@ -102,6 +113,7 @@ class Driver {
102113
private readonly _log: Logger
103114
private readonly _createConnectionProvider: CreateConnectionProvider
104115
private _connectionProvider: ConnectionProvider | null
116+
private readonly _createSession: CreateSession
105117

106118
/**
107119
* You should not be calling this directly, instead use {@link driver}.
@@ -110,11 +122,13 @@ class Driver {
110122
* @param {Object} meta Metainformation about the driver
111123
* @param {Object} config
112124
* @param {function(id: number, config:Object, log:Logger, hostNameResolver: ConfiguredCustomResolver): ConnectionProvider } createConnectonProvider Creates the connection provider
113-
*/
125+
* @param {function(args): Session } createSession Creates the a session
126+
*/
114127
constructor(
115128
meta: MetaInfo,
116129
config: DriverConfig = {},
117-
createConnectonProvider: CreateConnectionProvider
130+
createConnectonProvider: CreateConnectionProvider,
131+
createSession: CreateSession = args => new Session(args)
118132
) {
119133
sanitizeConfig(config)
120134
validateConfig(config)
@@ -124,6 +138,7 @@ class Driver {
124138
this._config = config
125139
this._log = Logger.create(config)
126140
this._createConnectionProvider = createConnectonProvider
141+
this._createSession = createSession
127142

128143
/**
129144
* Reference to the connection provider. Initialized lazily by {@link _getOrCreateConnectionProvider}.
@@ -177,6 +192,19 @@ class Driver {
177192
return connectionProvider.supportsTransactionConfig()
178193
}
179194

195+
/**
196+
* Returns whether the server supports user impersonation capabilities based on the protocol
197+
* version negotiated via handshake.
198+
*
199+
* Note that this function call _always_ causes a round-trip to the server.
200+
*
201+
* @returns {Promise<boolean>} promise resolved with a boolean or rejected with error.
202+
*/
203+
supportsUserImpersonation(): Promise<boolean> {
204+
const connectionProvider = this._getOrCreateConnectionProvider()
205+
return connectionProvider.supportsUserImpersonation()
206+
}
207+
180208
/**
181209
* @protected
182210
* @returns {boolean}
@@ -224,24 +252,28 @@ class Driver {
224252
* @param {number} param.fetchSize - The record fetch size of each batch of this session.
225253
* Use {@link FETCH_ALL} to always pull all records in one batch. This will override the config value set on driver config.
226254
* @param {string} param.database - The database this session will operate on.
255+
* @param {string} param.impersonatedUser - The username which the user wants to impersonate for the duration of the session.
227256
* @return {Session} new session.
228257
*/
229258
session({
230259
defaultAccessMode = WRITE,
231260
bookmarks: bookmarkOrBookmarks,
232261
database = '',
262+
impersonatedUser,
233263
fetchSize
234264
}: {
235265
defaultAccessMode?: SessionMode
236266
bookmarks?: string | string[]
237267
database?: string
268+
impersonatedUser?: string
238269
fetchSize?: number
239270
} = {}): Session {
240271
return this._newSession({
241272
defaultAccessMode,
242273
bookmarkOrBookmarks,
243274
database,
244275
reactive: false,
276+
impersonatedUser,
245277
fetchSize: validateFetchSizeValue(fetchSize, this._config.fetchSize!!)
246278
})
247279
}
@@ -277,26 +309,29 @@ class Driver {
277309
bookmarkOrBookmarks,
278310
database,
279311
reactive,
312+
impersonatedUser,
280313
fetchSize
281314
}: {
282315
defaultAccessMode: SessionMode
283316
bookmarkOrBookmarks?: string | string[]
284317
database: string
285318
reactive: boolean
319+
impersonatedUser?: string
286320
fetchSize: number
287321
}) {
288322
const sessionMode = Session._validateSessionMode(defaultAccessMode)
289323
const connectionProvider = this._getOrCreateConnectionProvider()
290324
const bookmark = bookmarkOrBookmarks
291325
? new Bookmark(bookmarkOrBookmarks)
292326
: Bookmark.empty()
293-
return new Session({
327+
return this._createSession({
294328
mode: sessionMode,
295329
database: database || '',
296330
connectionProvider,
297331
bookmark,
298332
config: this._config,
299333
reactive,
334+
impersonatedUser,
300335
fetchSize
301336
})
302337
}

packages/core/src/session.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class Session {
5757
private _hasTx: boolean
5858
private _lastBookmark: Bookmark
5959
private _transactionExecutor: TransactionExecutor
60+
private _impersonatedUser?: string
6061
private _onComplete: (meta: any) => void
6162

6263
/**
@@ -70,6 +71,7 @@ class Session {
7071
* @param {Object} args.config={} - This driver configuration.
7172
* @param {boolean} args.reactive - Whether this session should create reactive streams
7273
* @param {number} args.fetchSize - Defines how many records is pulled in each pulling batch
74+
* @param {string} args.impersonatedUser - The username which the user wants to impersonate for the duration of the session.
7375
*/
7476
constructor({
7577
mode,
@@ -78,15 +80,17 @@ class Session {
7880
database,
7981
config,
8082
reactive,
81-
fetchSize
83+
fetchSize,
84+
impersonatedUser
8285
}: {
8386
mode: SessionMode
8487
connectionProvider: ConnectionProvider
8588
bookmark?: Bookmark
8689
database: string
8790
config: any
8891
reactive: boolean
89-
fetchSize: number
92+
fetchSize: number,
93+
impersonatedUser?: string
9094
}) {
9195
this._mode = mode
9296
this._database = database
@@ -106,6 +110,7 @@ class Session {
106110
})
107111
this._open = true
108112
this._hasTx = false
113+
this._impersonatedUser = impersonatedUser
109114
this._lastBookmark = bookmark || Bookmark.empty()
110115
this._transactionExecutor = _createTransactionExecutor(config)
111116
this._onComplete = this._onCompleteCallback.bind(this)
@@ -142,6 +147,7 @@ class Session {
142147
txConfig: autoCommitTxConfig,
143148
mode: this._mode,
144149
database: this._database,
150+
impersonatedUser: this._impersonatedUser,
145151
afterComplete: this._onComplete,
146152
reactive: this._reactive,
147153
fetchSize: this._fetchSize

packages/core/src/transaction.ts

+9-3
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ class Transaction {
5252
private _onComplete: (metadata: any) => void
5353
private _fetchSize: number
5454
private _results: any[]
55+
private _impersonatedUser?: string
5556

5657
/**
5758
* @constructor
@@ -62,21 +63,24 @@ class Transaction {
6263
* is not yet released.
6364
* @param {boolean} reactive whether this transaction generates reactive streams
6465
* @param {number} fetchSize - the record fetch size in each pulling batch.
66+
* @param {string} args.impersonatedUser - The username which the user wants to impersonate for the duration of the session.
6567
*/
6668
constructor({
6769
connectionHolder,
6870
onClose,
6971
onBookmark,
7072
onConnection,
7173
reactive,
72-
fetchSize
74+
fetchSize,
75+
impersonatedUser
7376
}: {
7477
connectionHolder: ConnectionHolder
7578
onClose: () => void
7679
onBookmark: (bookmark: Bookmark) => void
7780
onConnection: () => void
7881
reactive: boolean
7982
fetchSize: number
83+
impersonatedUser?: string
8084
}) {
8185
this._connectionHolder = connectionHolder
8286
this._reactive = reactive
@@ -88,6 +92,7 @@ class Transaction {
8892
this._onComplete = this._onCompleteCallback.bind(this)
8993
this._fetchSize = fetchSize
9094
this._results = []
95+
this._impersonatedUser = impersonatedUser
9196
}
9297

9398
/**
@@ -107,6 +112,7 @@ class Transaction {
107112
txConfig: txConfig,
108113
mode: this._connectionHolder.mode(),
109114
database: this._connectionHolder.database(),
115+
impersonatedUser: this._impersonatedUser,
110116
beforeError: this._onError,
111117
afterComplete: this._onComplete
112118
})
@@ -289,7 +295,7 @@ const _states = {
289295
onComplete,
290296
onConnection,
291297
reactive,
292-
fetchSize
298+
fetchSize,
293299
}: StateTransitionParams
294300
): any => {
295301
// RUN in explicit transaction can't contain bookmarks and transaction configuration
@@ -305,7 +311,7 @@ const _states = {
305311
beforeError: onError,
306312
afterComplete: onComplete,
307313
reactive: reactive,
308-
fetchSize: fetchSize
314+
fetchSize: fetchSize,
309315
})
310316
} else {
311317
throw newError('No connection available')

0 commit comments

Comments
 (0)