From d590d6064df8b468a8b19e48d945087612d61fe5 Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Mon, 31 Jul 2023 13:48:08 +0200 Subject: [PATCH] Add support for using an AuthTokenManager with Neo4j Neo4j Java driver introduced support for an `AuthTokenManager` that can be used to define expiring tokens for authentication with a database. This commit adds an `ObjectProvider authTokenManagers` parameter to the corresponding auto configuration class. If the provider resolves to a unique object, that `AuthTokenManager` will have precedence over any static token. See gh-36650 --- .../neo4j/Neo4jAutoConfiguration.java | 12 ++- ...eo4jAutoConfigurationIntegrationTests.java | 83 +++++++++++++++---- 2 files changed, 74 insertions(+), 21 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/neo4j/Neo4jAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/neo4j/Neo4jAutoConfiguration.java index a7580754f8ff..8eaf6569ee92 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/neo4j/Neo4jAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/neo4j/Neo4jAutoConfiguration.java @@ -24,6 +24,7 @@ import java.util.concurrent.TimeUnit; import org.neo4j.driver.AuthToken; +import org.neo4j.driver.AuthTokenManager; import org.neo4j.driver.AuthTokens; import org.neo4j.driver.Config; import org.neo4j.driver.Config.TrustStrategy; @@ -70,11 +71,16 @@ PropertiesNeo4jConnectionDetails neo4jConnectionDetails(Neo4jProperties properti @Bean @ConditionalOnMissingBean public Driver neo4jDriver(Neo4jProperties properties, Environment environment, - Neo4jConnectionDetails connectionDetails, - ObjectProvider configBuilderCustomizers) { - AuthToken authToken = connectionDetails.getAuthToken(); + Neo4jConnectionDetails connectionDetails, ObjectProvider configBuilderCustomizers, + ObjectProvider authTokenManagers) { + Config config = mapDriverConfig(properties, connectionDetails, configBuilderCustomizers.orderedStream().toList()); + AuthTokenManager authTokenManager = authTokenManagers.getIfUnique(); + if (authTokenManager != null) { + return GraphDatabase.driver(connectionDetails.getUri(), authTokenManager, config); + } + AuthToken authToken = connectionDetails.getAuthToken(); return GraphDatabase.driver(connectionDetails.getUri(), authToken, config); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/neo4j/Neo4jAutoConfigurationIntegrationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/neo4j/Neo4jAutoConfigurationIntegrationTests.java index c8febbbe6056..fb97387470a4 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/neo4j/Neo4jAutoConfigurationIntegrationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/neo4j/Neo4jAutoConfigurationIntegrationTests.java @@ -18,7 +18,11 @@ import java.time.Duration; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.neo4j.driver.AuthTokenManager; +import org.neo4j.driver.AuthTokenManagers; +import org.neo4j.driver.AuthTokens; import org.neo4j.driver.Driver; import org.neo4j.driver.Result; import org.neo4j.driver.Session; @@ -31,6 +35,7 @@ import org.springframework.boot.autoconfigure.ImportAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.testsupport.testcontainers.DockerImageNames; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; @@ -43,7 +48,6 @@ * @author Michael J. Simons * @author Stephane Nicoll */ -@SpringBootTest @Testcontainers(disabledWithoutDocker = true) class Neo4jAutoConfigurationIntegrationTests { @@ -52,28 +56,71 @@ class Neo4jAutoConfigurationIntegrationTests { .withStartupAttempts(5) .withStartupTimeout(Duration.ofMinutes(10)); - @DynamicPropertySource - static void neo4jProperties(DynamicPropertyRegistry registry) { - registry.add("spring.neo4j.uri", neo4jServer::getBoltUrl); - registry.add("spring.neo4j.authentication.username", () -> "neo4j"); - registry.add("spring.neo4j.authentication.password", neo4jServer::getAdminPassword); - } + @SpringBootTest + @Nested + class DriverWithDefaultAuthToken { + + @DynamicPropertySource + static void neo4jProperties(DynamicPropertyRegistry registry) { + registry.add("spring.neo4j.uri", neo4jServer::getBoltUrl); + registry.add("spring.neo4j.authentication.username", () -> "neo4j"); + registry.add("spring.neo4j.authentication.password", neo4jServer::getAdminPassword); + } + + @Autowired + private Driver driver; - @Autowired - private Driver driver; + @Test + void driverCanHandleRequest() { + try (Session session = this.driver.session(); Transaction tx = session.beginTransaction()) { + Result statementResult = tx.run("MATCH (n:Thing) RETURN n LIMIT 1"); + assertThat(statementResult.hasNext()).isFalse(); + tx.commit(); + } + } + + @Configuration(proxyBeanMethods = false) + @ImportAutoConfiguration(Neo4jAutoConfiguration.class) + static class TestConfiguration { - @Test - void driverCanHandleRequest() { - try (Session session = this.driver.session(); Transaction tx = session.beginTransaction()) { - Result statementResult = tx.run("MATCH (n:Thing) RETURN n LIMIT 1"); - assertThat(statementResult.hasNext()).isFalse(); - tx.commit(); } + } - @Configuration(proxyBeanMethods = false) - @ImportAutoConfiguration(Neo4jAutoConfiguration.class) - static class TestConfiguration { + @SpringBootTest + @Nested + class DriverWithDynamicAuthToken { + + @DynamicPropertySource + static void neo4jProperties(DynamicPropertyRegistry registry) { + registry.add("spring.neo4j.uri", neo4jServer::getBoltUrl); + registry.add("spring.neo4j.authentication.username", () -> "wrong"); + registry.add("spring.neo4j.authentication.password", () -> "alsowrong"); + } + + @Autowired + private Driver driver; + + @Test + void driverCanHandleRequest() { + try (Session session = this.driver.session(); Transaction tx = session.beginTransaction()) { + Result statementResult = tx.run("MATCH (n:Thing) RETURN n LIMIT 1"); + assertThat(statementResult.hasNext()).isFalse(); + tx.commit(); + } + } + + @Configuration(proxyBeanMethods = false) + @ImportAutoConfiguration(Neo4jAutoConfiguration.class) + static class TestConfiguration { + + @Bean + AuthTokenManager authTokenManager() { + return AuthTokenManagers.expirationBased(() -> AuthTokens.basic("neo4j", neo4jServer.getAdminPassword()) + .expiringAt(System.currentTimeMillis() + 5_000)); + } + + } }