Add SSL service connection support for Cassandra
See gh-41137
This commit is contained in:
parent
e26ccbe028
commit
9c520d6af7
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -17,15 +17,12 @@
|
||||
package org.springframework.boot.autoconfigure.cassandra;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
import com.datastax.oss.driver.api.core.CqlSession;
|
||||
import com.datastax.oss.driver.api.core.CqlSessionBuilder;
|
||||
import com.datastax.oss.driver.api.core.config.DefaultDriverOption;
|
||||
@ -99,8 +96,8 @@ public class CassandraAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(CassandraConnectionDetails.class)
|
||||
PropertiesCassandraConnectionDetails cassandraConnectionDetails() {
|
||||
return new PropertiesCassandraConnectionDetails(this.properties);
|
||||
PropertiesCassandraConnectionDetails cassandraConnectionDetails(ObjectProvider<SslBundles> sslBundles) {
|
||||
return new PropertiesCassandraConnectionDetails(this.properties, sslBundles.getIfAvailable());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ -115,10 +112,10 @@ public class CassandraAutoConfiguration {
|
||||
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
|
||||
public CqlSessionBuilder cassandraSessionBuilder(DriverConfigLoader driverConfigLoader,
|
||||
CassandraConnectionDetails connectionDetails,
|
||||
ObjectProvider<CqlSessionBuilderCustomizer> builderCustomizers, ObjectProvider<SslBundles> sslBundles) {
|
||||
ObjectProvider<CqlSessionBuilderCustomizer> builderCustomizers) {
|
||||
CqlSessionBuilder builder = CqlSession.builder().withConfigLoader(driverConfigLoader);
|
||||
configureAuthentication(builder, connectionDetails);
|
||||
configureSsl(builder, sslBundles.getIfAvailable());
|
||||
configureSsl(builder, connectionDetails);
|
||||
builder.withKeyspace(this.properties.getKeyspaceName());
|
||||
builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
|
||||
return builder;
|
||||
@ -131,30 +128,11 @@ public class CassandraAutoConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
private void configureSsl(CqlSessionBuilder builder, SslBundles sslBundles) {
|
||||
Ssl properties = this.properties.getSsl();
|
||||
if (properties == null || !properties.isEnabled()) {
|
||||
private void configureSsl(CqlSessionBuilder builder, CassandraConnectionDetails connectionDetails) {
|
||||
SslBundle sslBundle = connectionDetails.getSslBundle();
|
||||
if (sslBundle == null) {
|
||||
return;
|
||||
}
|
||||
String bundleName = properties.getBundle();
|
||||
if (!StringUtils.hasLength(bundleName)) {
|
||||
configureDefaultSslContext(builder);
|
||||
}
|
||||
else {
|
||||
configureSsl(builder, sslBundles.getBundle(bundleName));
|
||||
}
|
||||
}
|
||||
|
||||
private void configureDefaultSslContext(CqlSessionBuilder builder) {
|
||||
try {
|
||||
builder.withSslContext(SSLContext.getDefault());
|
||||
}
|
||||
catch (NoSuchAlgorithmException ex) {
|
||||
throw new IllegalStateException("Could not setup SSL default context for Cassandra", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void configureSsl(CqlSessionBuilder builder, SslBundle sslBundle) {
|
||||
SslOptions options = sslBundle.getOptions();
|
||||
Assert.state(options.getEnabledProtocols() == null, "SSL protocol options cannot be specified with Cassandra");
|
||||
builder
|
||||
@ -320,8 +298,11 @@ public class CassandraAutoConfiguration {
|
||||
|
||||
private final CassandraProperties properties;
|
||||
|
||||
private PropertiesCassandraConnectionDetails(CassandraProperties properties) {
|
||||
private final SslBundles sslBundles;
|
||||
|
||||
private PropertiesCassandraConnectionDetails(CassandraProperties properties, SslBundles sslBundles) {
|
||||
this.properties = properties;
|
||||
this.sslBundles = sslBundles;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -346,6 +327,19 @@ public class CassandraAutoConfiguration {
|
||||
return this.properties.getLocalDatacenter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SslBundle getSslBundle() {
|
||||
Ssl ssl = this.properties.getSsl();
|
||||
if (ssl == null || !ssl.isEnabled()) {
|
||||
return null;
|
||||
}
|
||||
if (StringUtils.hasLength(ssl.getBundle())) {
|
||||
Assert.notNull(this.sslBundles, "SSL bundle name has been set but no SSL bundles found in context");
|
||||
return this.sslBundles.getBundle(ssl.getBundle());
|
||||
}
|
||||
return SslBundle.systemDefault();
|
||||
}
|
||||
|
||||
private Node asNode(String contactPoint) {
|
||||
int i = contactPoint.lastIndexOf(':');
|
||||
if (i >= 0) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.cassandra;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.autoconfigure.service.connection.ConnectionDetails;
|
||||
import org.springframework.boot.ssl.SslBundle;
|
||||
|
||||
/**
|
||||
* Details required to establish a connection to a Cassandra service.
|
||||
@ -59,6 +60,15 @@ public interface CassandraConnectionDetails extends ConnectionDetails {
|
||||
*/
|
||||
String getLocalDatacenter();
|
||||
|
||||
/**
|
||||
* SSL bundle to use.
|
||||
* @return the SSL bundle to use
|
||||
* @since 3.5.0
|
||||
*/
|
||||
default SslBundle getSslBundle() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A Cassandra node.
|
||||
*
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -22,6 +22,7 @@ import java.util.List;
|
||||
import org.testcontainers.cassandra.CassandraContainer;
|
||||
|
||||
import org.springframework.boot.autoconfigure.cassandra.CassandraConnectionDetails;
|
||||
import org.springframework.boot.ssl.SslBundle;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
@ -75,6 +76,11 @@ class CassandraContainerConnectionDetailsFactory
|
||||
return getContainer().getLocalDatacenter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SslBundle getSslBundle() {
|
||||
return super.getSslBundle();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -22,6 +22,7 @@ import java.util.List;
|
||||
import org.testcontainers.containers.CassandraContainer;
|
||||
|
||||
import org.springframework.boot.autoconfigure.cassandra.CassandraConnectionDetails;
|
||||
import org.springframework.boot.ssl.SslBundle;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
@ -78,6 +79,11 @@ class DeprecatedCassandraContainerConnectionDetailsFactory
|
||||
return getContainer().getLocalDatacenter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SslBundle getSslBundle() {
|
||||
return super.getSslBundle();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -28,6 +28,8 @@ import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.TestConfiguration;
|
||||
import org.springframework.boot.testcontainers.service.connection.JksKeyStore;
|
||||
import org.springframework.boot.testcontainers.service.connection.JksTrustStore;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.container.TestImage;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@ -43,15 +45,13 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@Testcontainers(disabledWithoutDocker = true)
|
||||
@SpringBootTest(properties = { "spring.cassandra.schema-action=create-if-not-exists",
|
||||
"spring.cassandra.connection.connect-timeout=60s", "spring.cassandra.connection.init-query-timeout=60s",
|
||||
"spring.cassandra.request.timeout=60s", "spring.cassandra.ssl.bundle=client",
|
||||
"spring.ssl.bundle.jks.client.keystore.location=classpath:ssl/test-client.p12",
|
||||
"spring.ssl.bundle.jks.client.keystore.password=password",
|
||||
"spring.ssl.bundle.jks.client.truststore.location=classpath:ssl/test-ca.p12",
|
||||
"spring.ssl.bundle.jks.client.truststore.password=password" })
|
||||
"spring.cassandra.request.timeout=60s" })
|
||||
class SampleCassandraApplicationReactiveSslTests {
|
||||
|
||||
@Container
|
||||
@ServiceConnection
|
||||
@JksTrustStore(location = "classpath:ssl/test-ca.p12", password = "password")
|
||||
@JksKeyStore(location = "classpath:ssl/test-client.p12", password = "password")
|
||||
static final SecureCassandraContainer cassandra = TestImage.container(SecureCassandraContainer.class);
|
||||
|
||||
@Autowired
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -27,6 +27,8 @@ import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.data.cassandra.DataCassandraTest;
|
||||
import org.springframework.boot.test.context.TestConfiguration;
|
||||
import org.springframework.boot.testcontainers.service.connection.JksKeyStore;
|
||||
import org.springframework.boot.testcontainers.service.connection.JksTrustStore;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.boot.testsupport.container.TestImage;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@ -43,15 +45,13 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@Testcontainers(disabledWithoutDocker = true)
|
||||
@DataCassandraTest(properties = { "spring.cassandra.schema-action=create-if-not-exists",
|
||||
"spring.cassandra.connection.connect-timeout=60s", "spring.cassandra.connection.init-query-timeout=60s",
|
||||
"spring.cassandra.request.timeout=60s", "spring.cassandra.ssl.bundle=client",
|
||||
"spring.ssl.bundle.jks.client.keystore.location=classpath:ssl/test-client.p12",
|
||||
"spring.ssl.bundle.jks.client.keystore.password=password",
|
||||
"spring.ssl.bundle.jks.client.truststore.location=classpath:ssl/test-ca.p12",
|
||||
"spring.ssl.bundle.jks.client.truststore.password=password" })
|
||||
"spring.cassandra.request.timeout=60s" })
|
||||
class SampleCassandraApplicationSslTests {
|
||||
|
||||
@Container
|
||||
@ServiceConnection
|
||||
@JksTrustStore(location = "classpath:ssl/test-ca.p12", password = "password")
|
||||
@JksKeyStore(location = "classpath:ssl/test-client.p12", password = "password")
|
||||
static final SecureCassandraContainer cassandra = TestImage.container(SecureCassandraContainer.class);
|
||||
|
||||
@Autowired
|
||||
|
Loading…
x
Reference in New Issue
Block a user