Merge pull request #36650 from michael-simons
* gh-36650: Polish "Add support for using an AuthTokenManager with Neo4j" Add support for using an AuthTokenManager with Neo4j Closes gh-36650
This commit is contained in:
commit
11753c5a72
|
@ -24,6 +24,7 @@ import java.util.Locale;
|
|||
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;
|
||||
|
@ -63,8 +64,9 @@ public class Neo4jAutoConfiguration {
|
|||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(Neo4jConnectionDetails.class)
|
||||
PropertiesNeo4jConnectionDetails neo4jConnectionDetails(Neo4jProperties properties) {
|
||||
return new PropertiesNeo4jConnectionDetails(properties);
|
||||
PropertiesNeo4jConnectionDetails neo4jConnectionDetails(Neo4jProperties properties,
|
||||
ObjectProvider<AuthTokenManager> authTokenManager) {
|
||||
return new PropertiesNeo4jConnectionDetails(properties, authTokenManager.getIfUnique());
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
@ -72,9 +74,14 @@ public class Neo4jAutoConfiguration {
|
|||
public Driver neo4jDriver(Neo4jProperties properties, Environment environment,
|
||||
Neo4jConnectionDetails connectionDetails,
|
||||
ObjectProvider<ConfigBuilderCustomizer> configBuilderCustomizers) {
|
||||
AuthToken authToken = connectionDetails.getAuthToken();
|
||||
|
||||
Config config = mapDriverConfig(properties, connectionDetails,
|
||||
configBuilderCustomizers.orderedStream().toList());
|
||||
AuthTokenManager authTokenManager = connectionDetails.getAuthTokenManager();
|
||||
if (authTokenManager != null) {
|
||||
return GraphDatabase.driver(connectionDetails.getUri(), authTokenManager, config);
|
||||
}
|
||||
AuthToken authToken = connectionDetails.getAuthToken();
|
||||
return GraphDatabase.driver(connectionDetails.getUri(), authToken, config);
|
||||
}
|
||||
|
||||
|
@ -181,8 +188,11 @@ public class Neo4jAutoConfiguration {
|
|||
|
||||
private final Neo4jProperties properties;
|
||||
|
||||
PropertiesNeo4jConnectionDetails(Neo4jProperties properties) {
|
||||
private final AuthTokenManager authTokenManager;
|
||||
|
||||
PropertiesNeo4jConnectionDetails(Neo4jProperties properties, AuthTokenManager authTokenManager) {
|
||||
this.properties = properties;
|
||||
this.authTokenManager = authTokenManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -211,6 +221,11 @@ public class Neo4jAutoConfiguration {
|
|||
return AuthTokens.none();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthTokenManager getAuthTokenManager() {
|
||||
return this.authTokenManager;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.neo4j;
|
|||
import java.net.URI;
|
||||
|
||||
import org.neo4j.driver.AuthToken;
|
||||
import org.neo4j.driver.AuthTokenManager;
|
||||
import org.neo4j.driver.AuthTokens;
|
||||
|
||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
|
||||
|
@ -49,4 +50,14 @@ public interface Neo4jConnectionDetails extends ConnectionDetails {
|
|||
return AuthTokens.none();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link AuthTokenManager} to use for authentication. Defaults to
|
||||
* {@code null} in which case the {@link #getAuthToken() auth token} should be used.
|
||||
* @return the auth token manager
|
||||
* @since 3.2.0
|
||||
*/
|
||||
default AuthTokenManager getAuthTokenManager() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,9 +16,15 @@
|
|||
|
||||
package org.springframework.boot.autoconfigure.neo4j;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.Duration;
|
||||
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.neo4j.driver.AuthToken;
|
||||
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 +37,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||
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 +50,6 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
* @author Michael J. Simons
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@SpringBootTest
|
||||
@Testcontainers(disabledWithoutDocker = true)
|
||||
class Neo4jAutoConfigurationIntegrationTests {
|
||||
|
||||
|
@ -52,28 +58,125 @@ 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 {
|
||||
|
||||
@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();
|
||||
@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;
|
||||
|
||||
@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 {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SpringBootTest
|
||||
@Nested
|
||||
class DriverWithCustomConnectionDetailsIgnoresAuthTokenManager {
|
||||
|
||||
@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("wrongagain", "stillwrong")
|
||||
.expiringAt(System.currentTimeMillis() + 5_000));
|
||||
}
|
||||
|
||||
@Bean
|
||||
Neo4jConnectionDetails connectionDetails() {
|
||||
return new Neo4jConnectionDetails() {
|
||||
|
||||
@Override
|
||||
public URI getUri() {
|
||||
return URI.create(neo4jServer.getBoltUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthToken getAuthToken() {
|
||||
return AuthTokens.basic("neo4j", neo4jServer.getAdminPassword());
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.junit.jupiter.api.Test;
|
|||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.neo4j.driver.AuthTokenManagers;
|
||||
import org.neo4j.driver.AuthTokens;
|
||||
import org.neo4j.driver.Config;
|
||||
import org.neo4j.driver.Config.ConfigBuilder;
|
||||
|
@ -143,7 +144,7 @@ class Neo4jAutoConfigurationTests {
|
|||
|
||||
@Test
|
||||
void uriShouldDefaultToLocalhost() {
|
||||
assertThat(new PropertiesNeo4jConnectionDetails(new Neo4jProperties()).getUri())
|
||||
assertThat(new PropertiesNeo4jConnectionDetails(new Neo4jProperties(), null).getUri())
|
||||
.isEqualTo(URI.create("bolt://localhost:7687"));
|
||||
}
|
||||
|
||||
|
@ -152,12 +153,12 @@ class Neo4jAutoConfigurationTests {
|
|||
URI customUri = URI.create("bolt://localhost:4242");
|
||||
Neo4jProperties properties = new Neo4jProperties();
|
||||
properties.setUri(customUri);
|
||||
assertThat(new PropertiesNeo4jConnectionDetails(properties).getUri()).isEqualTo(customUri);
|
||||
assertThat(new PropertiesNeo4jConnectionDetails(properties, null).getUri()).isEqualTo(customUri);
|
||||
}
|
||||
|
||||
@Test
|
||||
void authenticationShouldDefaultToNone() {
|
||||
assertThat(new PropertiesNeo4jConnectionDetails(new Neo4jProperties()).getAuthToken())
|
||||
assertThat(new PropertiesNeo4jConnectionDetails(new Neo4jProperties(), null).getAuthToken())
|
||||
.isEqualTo(AuthTokens.none());
|
||||
}
|
||||
|
||||
|
@ -166,8 +167,9 @@ class Neo4jAutoConfigurationTests {
|
|||
Neo4jProperties properties = new Neo4jProperties();
|
||||
properties.getAuthentication().setUsername("Farin");
|
||||
properties.getAuthentication().setPassword("Urlaub");
|
||||
assertThat(new PropertiesNeo4jConnectionDetails(properties).getAuthToken())
|
||||
.isEqualTo(AuthTokens.basic("Farin", "Urlaub"));
|
||||
PropertiesNeo4jConnectionDetails connectionDetails = new PropertiesNeo4jConnectionDetails(properties, null);
|
||||
assertThat(connectionDetails.getAuthToken()).isEqualTo(AuthTokens.basic("Farin", "Urlaub"));
|
||||
assertThat(connectionDetails.getAuthTokenManager()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -177,8 +179,22 @@ class Neo4jAutoConfigurationTests {
|
|||
authentication.setUsername("Farin");
|
||||
authentication.setPassword("Urlaub");
|
||||
authentication.setRealm("Test Realm");
|
||||
assertThat(new PropertiesNeo4jConnectionDetails(properties).getAuthToken())
|
||||
.isEqualTo(AuthTokens.basic("Farin", "Urlaub", "Test Realm"));
|
||||
PropertiesNeo4jConnectionDetails connectionDetails = new PropertiesNeo4jConnectionDetails(properties, null);
|
||||
assertThat(connectionDetails.getAuthToken()).isEqualTo(AuthTokens.basic("Farin", "Urlaub", "Test Realm"));
|
||||
assertThat(connectionDetails.getAuthTokenManager()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void authenticationWithAuthTokenManagerAndUsernameShouldProvideAuthTokenManger() {
|
||||
Neo4jProperties properties = new Neo4jProperties();
|
||||
Authentication authentication = properties.getAuthentication();
|
||||
authentication.setUsername("Farin");
|
||||
authentication.setPassword("Urlaub");
|
||||
authentication.setRealm("Test Realm");
|
||||
assertThat(new PropertiesNeo4jConnectionDetails(properties,
|
||||
AuthTokenManagers.expirationBased(
|
||||
() -> AuthTokens.basic("username", "password").expiringAt(System.currentTimeMillis() + 5000)))
|
||||
.getAuthTokenManager()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -186,7 +202,7 @@ class Neo4jAutoConfigurationTests {
|
|||
Neo4jProperties properties = new Neo4jProperties();
|
||||
Authentication authentication = properties.getAuthentication();
|
||||
authentication.setKerberosTicket("AABBCCDDEE");
|
||||
assertThat(new PropertiesNeo4jConnectionDetails(properties).getAuthToken())
|
||||
assertThat(new PropertiesNeo4jConnectionDetails(properties, null).getAuthToken())
|
||||
.isEqualTo(AuthTokens.kerberos("AABBCCDDEE"));
|
||||
}
|
||||
|
||||
|
@ -197,7 +213,7 @@ class Neo4jAutoConfigurationTests {
|
|||
authentication.setUsername("Farin");
|
||||
authentication.setKerberosTicket("AABBCCDDEE");
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> new PropertiesNeo4jConnectionDetails(properties).getAuthToken())
|
||||
.isThrownBy(() -> new PropertiesNeo4jConnectionDetails(properties, null).getAuthToken())
|
||||
.withMessage("Cannot specify both username ('Farin') and kerberos ticket ('AABBCCDDEE')");
|
||||
}
|
||||
|
||||
|
@ -313,7 +329,7 @@ class Neo4jAutoConfigurationTests {
|
|||
|
||||
private Config mapDriverConfig(Neo4jProperties properties, ConfigBuilderCustomizer... customizers) {
|
||||
return new Neo4jAutoConfiguration().mapDriverConfig(properties,
|
||||
new PropertiesNeo4jConnectionDetails(properties), Arrays.asList(customizers));
|
||||
new PropertiesNeo4jConnectionDetails(properties, null), Arrays.asList(customizers));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue