Skip to content

Commit c5e68a4

Browse files
GH-2423 - Support Neo4j 4.4 user impersonation.
This change adds an API analogue to `DatabaseSelection` called `UserSelection`. The configuration and usage is identical to a dynamic database selection. It requires both Neo4j 4.4 server and database driver. To stay compatible with older driver version, we reflect over `withImpersonatedUser` inside `org.springframework.data.neo4j.core.transaction.Neo4jTransactionUtils`. This closes #2423 and the linked PR #2422.
1 parent d6ac49f commit c5e68a4

File tree

56 files changed

+1263
-262
lines changed

Some content is hidden

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

56 files changed

+1263
-262
lines changed

src/main/asciidoc/faq/faq.adoc

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,82 @@ public class Neo4jHealthConfig {
326326
}
327327
----
328328

329+
[[faq.impersonation]]
330+
== Neo4j 4.4 supports impersonation of different users - How can I use them?
331+
332+
User impersonation is especially interesting in big multi-tenant settings, in which one physically connected (or technical)
333+
user can impersonate many tenants. Depending on your setup this will drastically reduce the number of physical driver instances needed.
334+
335+
The feature requires Neo4j Enterprise 4.4+ on the server side and a 4.4+ driver on the client side (`org.neo4j.driver:neo4j-java-driver:4.4.0` or higher).
336+
337+
For both imperative and reactive versions you need to provide a `UserSelectionProvider` respectively a `ReactiveUserSelectionProvider`.
338+
The same instance needs to be passed along to the `Neo4Client` and `Neo4jTransactionManager` respectively their reactive variants.
339+
340+
In <<bootless-imperative-configuration,Bootless imperative>> and <<bootless-reactive-configuration, and reactive>> configurations you just need to provide a bean of the
341+
type in question:
342+
343+
[[faq.impersonation.userselectionbean]]
344+
.User selection provider bean
345+
[source,java]
346+
----
347+
import org.springframework.data.neo4j.core.UserSelection;
348+
import org.springframework.data.neo4j.core.UserSelectionProvider;
349+
350+
public class CustomConfig {
351+
352+
@Bean
353+
public UserSelectionProvider getUserSelectionProvider() {
354+
return () -> UserSelection.impersonate("someUser");
355+
}
356+
}
357+
----
358+
359+
In a typical Spring Boot scenario this feature requires a bit more work, as Boot supports also SDN versions without that feature.
360+
So given the bean in <<faq.impersonation.userselectionbean>>, you would need fully customize the client and transaction manager:
361+
362+
[[faq.impersonation.boot]]
363+
.Necessary customization for Spring Boot
364+
[source,java]
365+
----
366+
import org.neo4j.driver.Driver;
367+
368+
import org.springframework.data.neo4j.core.DatabaseSelectionProvider;
369+
import org.springframework.data.neo4j.core.Neo4jClient;
370+
import org.springframework.data.neo4j.core.UserSelectionProvider;
371+
import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager;
372+
373+
import org.springframework.transaction.PlatformTransactionManager;
374+
375+
public class CustomConfig {
376+
377+
@Bean
378+
public Neo4jClient neo4jClient(
379+
Driver driver,
380+
DatabaseSelectionProvider databaseSelectionProvider,
381+
UserSelectionProvider userSelectionProvider
382+
) {
383+
384+
return Neo4jClient.with(driver)
385+
.withDatabaseSelectionProvider(databaseSelectionProvider)
386+
.withUserSelectionProvider(userSelectionProvider)
387+
.build();
388+
}
389+
390+
@Bean
391+
public PlatformTransactionManager transactionManager(
392+
Driver driver,
393+
DatabaseSelectionProvider databaseSelectionProvider,
394+
UserSelectionProvider userSelectionProvider
395+
) {
396+
397+
return Neo4jTransactionManager
398+
.with(driver)
399+
.withDatabaseSelectionProvider(databaseSelectionProvider)
400+
.withUserSelectionProvider(userSelectionProvider)
401+
.build();
402+
}
403+
}
404+
----
329405

330406
[[faq.transactions.cluster]]
331407
== Do I need specific configuration so that transactions work seamless with a Neo4j Causal Cluster?

src/main/java/org/springframework/data/neo4j/config/AbstractNeo4jConfig.java

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,19 @@
1717

1818
import org.apiguardian.api.API;
1919
import org.neo4j.driver.Driver;
20+
import org.springframework.beans.factory.ObjectProvider;
21+
import org.springframework.beans.factory.annotation.Autowired;
2022
import org.springframework.context.annotation.Bean;
2123
import org.springframework.context.annotation.Configuration;
2224
import org.springframework.data.neo4j.core.DatabaseSelectionProvider;
2325
import org.springframework.data.neo4j.core.Neo4jClient;
2426
import org.springframework.data.neo4j.core.Neo4jOperations;
2527
import org.springframework.data.neo4j.core.Neo4jTemplate;
28+
import org.springframework.data.neo4j.core.UserSelectionProvider;
2629
import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext;
2730
import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager;
2831
import org.springframework.data.neo4j.repository.config.Neo4jRepositoryConfigurationExtension;
32+
import org.springframework.lang.Nullable;
2933
import org.springframework.transaction.PlatformTransactionManager;
3034

3135
/**
@@ -40,6 +44,9 @@
4044
@API(status = API.Status.STABLE, since = "6.0")
4145
public abstract class AbstractNeo4jConfig extends Neo4jConfigurationSupport {
4246

47+
@Autowired
48+
private ObjectProvider<UserSelectionProvider> userSelectionProviders;
49+
4350
/**
4451
* The driver to be used for interacting with Neo4j.
4552
*
@@ -55,7 +62,16 @@ public abstract class AbstractNeo4jConfig extends Neo4jConfigurationSupport {
5562
*/
5663
@Bean(Neo4jRepositoryConfigurationExtension.DEFAULT_NEO4J_CLIENT_BEAN_NAME)
5764
public Neo4jClient neo4jClient(Driver driver, DatabaseSelectionProvider databaseSelectionProvider) {
58-
return Neo4jClient.create(driver, databaseSelectionProvider);
65+
66+
return Neo4jClient.with(driver)
67+
.withDatabaseSelectionProvider(databaseSelectionProvider)
68+
.withUserSelectionProvider(getUserSelectionProvider())
69+
.build();
70+
}
71+
72+
@Nullable
73+
private UserSelectionProvider getUserSelectionProvider() {
74+
return this.userSelectionProviders == null ? null : this.userSelectionProviders.getIfUnique();
5975
}
6076

6177
@Bean(Neo4jRepositoryConfigurationExtension.DEFAULT_NEO4J_TEMPLATE_BEAN_NAME)
@@ -68,17 +84,21 @@ public Neo4jOperations neo4jTemplate(final Neo4jClient neo4jClient, final Neo4jM
6884
* Provides a {@link PlatformTransactionManager} for Neo4j based on the driver resulting from {@link #driver()}.
6985
*
7086
* @param driver The driver to synchronize against
71-
* @param databaseNameProvider The configured database name provider
87+
* @param databaseSelectionProvider The configured database selection provider
7288
* @return A platform transaction manager
7389
*/
7490
@Bean(Neo4jRepositoryConfigurationExtension.DEFAULT_TRANSACTION_MANAGER_BEAN_NAME)
75-
public PlatformTransactionManager transactionManager(Driver driver, DatabaseSelectionProvider databaseNameProvider) {
91+
public PlatformTransactionManager transactionManager(Driver driver, DatabaseSelectionProvider databaseSelectionProvider) {
7692

77-
return new Neo4jTransactionManager(driver, databaseNameProvider);
93+
return Neo4jTransactionManager
94+
.with(driver)
95+
.withDatabaseSelectionProvider(databaseSelectionProvider)
96+
.withUserSelectionProvider(getUserSelectionProvider())
97+
.build();
7898
}
7999

80100
/**
81-
* Configures the database name provider.
101+
* Configures the database selection provider.
82102
*
83103
* @return The default database name provider, defaulting to the default database on Neo4j 4.0 and on no default on
84104
* Neo4j 3.5 and prior.

src/main/java/org/springframework/data/neo4j/config/AbstractReactiveNeo4jConfig.java

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,18 @@
1717

1818
import org.apiguardian.api.API;
1919
import org.neo4j.driver.Driver;
20+
import org.springframework.beans.factory.ObjectProvider;
21+
import org.springframework.beans.factory.annotation.Autowired;
2022
import org.springframework.context.annotation.Bean;
2123
import org.springframework.context.annotation.Configuration;
2224
import org.springframework.data.neo4j.core.ReactiveDatabaseSelectionProvider;
2325
import org.springframework.data.neo4j.core.ReactiveNeo4jClient;
2426
import org.springframework.data.neo4j.core.ReactiveNeo4jTemplate;
27+
import org.springframework.data.neo4j.core.ReactiveUserSelectionProvider;
2528
import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext;
2629
import org.springframework.data.neo4j.core.transaction.ReactiveNeo4jTransactionManager;
2730
import org.springframework.data.neo4j.repository.config.ReactiveNeo4jRepositoryConfigurationExtension;
31+
import org.springframework.lang.Nullable;
2832
import org.springframework.transaction.PlatformTransactionManager;
2933
import org.springframework.transaction.ReactiveTransactionManager;
3034

@@ -40,6 +44,9 @@
4044
@API(status = API.Status.STABLE, since = "6.0")
4145
public abstract class AbstractReactiveNeo4jConfig extends Neo4jConfigurationSupport {
4246

47+
@Autowired
48+
private ObjectProvider<ReactiveUserSelectionProvider> userSelectionProviders;
49+
4350
/**
4451
* The driver to be used for interacting with Neo4j.
4552
*
@@ -54,8 +61,17 @@ public abstract class AbstractReactiveNeo4jConfig extends Neo4jConfigurationSupp
5461
* @return A reactive Neo4j client.
5562
*/
5663
@Bean(ReactiveNeo4jRepositoryConfigurationExtension.DEFAULT_NEO4J_CLIENT_BEAN_NAME)
57-
public ReactiveNeo4jClient neo4jClient(Driver driver, ReactiveDatabaseSelectionProvider databaseNameProvider) {
58-
return ReactiveNeo4jClient.create(driver, databaseNameProvider);
64+
public ReactiveNeo4jClient neo4jClient(Driver driver, ReactiveDatabaseSelectionProvider databaseSelectionProvider) {
65+
66+
return ReactiveNeo4jClient.with(driver)
67+
.withDatabaseSelectionProvider(databaseSelectionProvider)
68+
.withUserSelectionProvider(getUserSelectionProvider())
69+
.build();
70+
}
71+
72+
@Nullable
73+
private ReactiveUserSelectionProvider getUserSelectionProvider() {
74+
return this.userSelectionProviders == null ? null : this.userSelectionProviders.getIfUnique();
5975
}
6076

6177
@Bean(ReactiveNeo4jRepositoryConfigurationExtension.DEFAULT_NEO4J_TEMPLATE_BEAN_NAME)
@@ -73,9 +89,12 @@ public ReactiveNeo4jTemplate neo4jTemplate(final ReactiveNeo4jClient neo4jClient
7389
*/
7490
@Bean(ReactiveNeo4jRepositoryConfigurationExtension.DEFAULT_TRANSACTION_MANAGER_BEAN_NAME)
7591
public ReactiveTransactionManager reactiveTransactionManager(Driver driver,
76-
ReactiveDatabaseSelectionProvider databaseNameProvider) {
92+
ReactiveDatabaseSelectionProvider databaseSelectionProvider) {
7793

78-
return new ReactiveNeo4jTransactionManager(driver, databaseNameProvider);
94+
return ReactiveNeo4jTransactionManager.with(driver)
95+
.withDatabaseSelectionProvider(databaseSelectionProvider)
96+
.withUserSelectionProvider(getUserSelectionProvider())
97+
.build();
7998
}
8099

81100
/**

src/main/java/org/springframework/data/neo4j/core/DatabaseSelection.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
@API(status = API.Status.STABLE, since = "6.0")
3232
public final class DatabaseSelection {
3333

34-
private final static DatabaseSelection DEFAULT_DATABASE_NAME = new DatabaseSelection(null);
34+
private static final DatabaseSelection DEFAULT_DATABASE_NAME = new DatabaseSelection(null);
3535

3636
@Nullable private final String value;
3737

@@ -51,7 +51,7 @@ public static DatabaseSelection byName(String databaseName) {
5151
return new DatabaseSelection(databaseName);
5252
}
5353

54-
private DatabaseSelection(String value) {
54+
private DatabaseSelection(@Nullable String value) {
5555
this.value = value;
5656
}
5757

0 commit comments

Comments
 (0)