From 6534a9abaffc5d8700158b00ffe28808929abe07 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Wed, 10 Jun 2020 09:40:45 +0200 Subject: [PATCH] Configure password-based authentication with Cassandra This commit updates the Cassandra auto-configuration to configure password-based authentication on the CqlSession directly. Closes gh-21487 --- .../cassandra/CassandraAutoConfiguration.java | 7 ++ ...asswordAuthenticationIntegrationTests.java | 91 +++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfigurationWithPasswordAuthenticationIntegrationTests.java diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfiguration.java index 2e370765b8..19cb762a3c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfiguration.java @@ -80,12 +80,19 @@ public class CassandraAutoConfiguration { public CqlSessionBuilder cassandraSessionBuilder(CassandraProperties properties, DriverConfigLoader driverConfigLoader, ObjectProvider builderCustomizers) { CqlSessionBuilder builder = CqlSession.builder().withConfigLoader(driverConfigLoader); + configureAuthentication(properties, builder); configureSsl(properties, builder); builder.withKeyspace(properties.getKeyspaceName()); builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); return builder; } + private void configureAuthentication(CassandraProperties properties, CqlSessionBuilder builder) { + if (properties.getUsername() != null) { + builder.withAuthCredentials(properties.getUsername(), properties.getPassword()); + } + } + private void configureSsl(CassandraProperties properties, CqlSessionBuilder builder) { if (properties.isSsl()) { try { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfigurationWithPasswordAuthenticationIntegrationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfigurationWithPasswordAuthenticationIntegrationTests.java new file mode 100644 index 0000000000..4cf9f8cea2 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cassandra/CassandraAutoConfigurationWithPasswordAuthenticationIntegrationTests.java @@ -0,0 +1,91 @@ +/* + * Copyright 2012-2020 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.cassandra; + +import java.nio.charset.StandardCharsets; +import java.time.Duration; + +import com.datastax.oss.driver.api.core.ConsistencyLevel; +import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.cql.SimpleStatement; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.CassandraContainer; +import org.testcontainers.images.builder.Transferable; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.util.StreamUtils; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +/** + * Tests for {@link CassandraAutoConfiguration} that only uses password authentication. + * + * @author Stephane Nicoll + */ +@Testcontainers(disabledWithoutDocker = true) +class CassandraAutoConfigurationWithPasswordAuthenticationIntegrationTests { + + @Container + static final CassandraContainer cassandra = new PasswordAuthenticatorCassandraContainer().withStartupAttempts(5) + .withStartupTimeout(Duration.ofMinutes(10)); + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(CassandraAutoConfiguration.class)).withPropertyValues( + "spring.data.cassandra.contact-points:" + cassandra.getHost() + ":" + + cassandra.getFirstMappedPort(), + "spring.data.cassandra.local-datacenter=datacenter1", "spring.data.cassandra.read-timeout=20s", + "spring.data.cassandra.connect-timeout=10s"); + + @Test + void authenticationWithValidUsernameAndPassword() { + this.contextRunner.withPropertyValues("spring.data.cassandra.username=cassandra", + "spring.data.cassandra.password=cassandra").run((context) -> { + SimpleStatement select = SimpleStatement.newInstance("SELECT release_version FROM system.local") + .setConsistencyLevel(ConsistencyLevel.LOCAL_ONE); + assertThat(context.getBean(CqlSession.class).execute(select).one()).isNotNull(); + }); + } + + @Test + void authenticationWithInvalidCredentials() { + this.contextRunner + .withPropertyValues("spring.data.cassandra.username=not-a-user", + "spring.data.cassandra.password=invalid-password") + .run((context) -> assertThatThrownBy(() -> context.getBean(CqlSession.class)) + .hasMessageContaining("Authentication error")); + } + + static final class PasswordAuthenticatorCassandraContainer + extends CassandraContainer { + + @Override + protected void containerIsCreated(String containerId) { + String config = this.copyFileFromContainer("/etc/cassandra/cassandra.yaml", + (stream) -> StreamUtils.copyToString(stream, StandardCharsets.UTF_8)); + String updatedConfig = config.replace("authenticator: AllowAllAuthenticator", + "authenticator: PasswordAuthenticator"); + this.copyFileToContainer(Transferable.of(updatedConfig.getBytes(StandardCharsets.UTF_8)), + "/etc/cassandra/cassandra.yaml"); + } + + } + +}