Polishing.

Reformat code. Consider sentinel username in JedisConnectionFactory. Update years in license headers.

See #2218
Original pull request: #2224.
This commit is contained in:
Mark Paluch
2022-01-07 11:04:58 +01:00
parent f333b184a5
commit d0f17b167e
10 changed files with 95 additions and 42 deletions

View File

@@ -6,7 +6,7 @@ ifdef::backend-epub3[:front-cover-image: image:epub-cover.png[Front Cover,1050,1
:spring-data-commons-include: ../../../../spring-data-commons/src/main/asciidoc
:spring-data-commons-docs: https://raw.githubusercontent.com/spring-projects/spring-data-commons/master/src/main/asciidoc
(C) 2011-2021 The original authors.
(C) 2011-2022 The original authors.
NOTE: Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.

View File

@@ -3,6 +3,11 @@
This section briefly covers items that are new and noteworthy in the latest releases.
[[new-in-2.7.0]]
== New in Spring Data Redis 2.7
* Sentinel ACL authentication considering a sentinel-specific username. Setting a username enables username and password authentication requiring Redis 6.
[[new-in-2.6.0]]
== New in Spring Data Redis 2.6

View File

@@ -319,6 +319,7 @@ public RedisConnectionFactory lettuceConnectionFactory() {
.Configuration Properties
* `spring.redis.sentinel.master`: name of the master node.
* `spring.redis.sentinel.nodes`: Comma delimited list of host:port pairs.
* `spring.redis.sentinel.username`: The username to apply when authenticating with Redis Sentinel (requires Redis 6)
* `spring.redis.sentinel.password`: The password to apply when authenticating with Redis Sentinel
====

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2018-2021 the original author or authors.
* Copyright 2018-2022 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.
@@ -400,6 +400,23 @@ public interface RedisConfiguration {
return getPassword();
}
/**
* Create and set a username with the given {@link String}. Requires Redis 6 or newer.
*
* @param sentinelUsername the username for sentinel.
* @since 2.7
*/
void setSentinelUsername(@Nullable String sentinelUsername);
/**
* Get the username to use when connecting.
*
* @return {@literal null} if none set.
* @since 2.7
*/
@Nullable
String getSentinelUsername();
/**
* Create and set a {@link RedisPassword} to be used when authenticating with Redis Sentinel from the given
* {@link String}.
@@ -440,20 +457,6 @@ public interface RedisConfiguration {
*/
RedisPassword getSentinelPassword();
/**
* Create and set a username with the given {@link String}. Requires Redis 6 or newer.
*
* @param sentinelUsername the username for sentinel.
*/
void setSentinelUsername(@Nullable String sentinelUsername);
/**
* Get the username to use when connecting.
*
* @return {@literal null} if none set.
*/
@Nullable
String getSentinelUsername();
}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2021 the original author or authors.
* Copyright 2014-2022 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.
@@ -46,8 +46,8 @@ public class RedisSentinelConfiguration implements RedisConfiguration, SentinelC
private static final String REDIS_SENTINEL_MASTER_CONFIG_PROPERTY = "spring.redis.sentinel.master";
private static final String REDIS_SENTINEL_NODES_CONFIG_PROPERTY = "spring.redis.sentinel.nodes";
private static final String REDIS_SENTINEL_PASSWORD_CONFIG_PROPERTY = "spring.redis.sentinel.password";
private static final String REDIS_SENTINEL_USERNAME_CONFIG_PROPERTY = "spring.redis.sentinel.username";
private static final String REDIS_SENTINEL_PASSWORD_CONFIG_PROPERTY = "spring.redis.sentinel.password";
private @Nullable NamedNode master;
private Set<RedisNode> sentinels;
@@ -343,6 +343,9 @@ public class RedisSentinelConfiguration implements RedisConfiguration, SentinelC
if (!ObjectUtils.nullSafeEquals(dataNodePassword, that.dataNodePassword)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(sentinelUsername, that.sentinelUsername)) {
return false;
}
return ObjectUtils.nullSafeEquals(sentinelPassword, that.sentinelPassword);
}
@@ -357,6 +360,7 @@ public class RedisSentinelConfiguration implements RedisConfiguration, SentinelC
result = 31 * result + database;
result = 31 * result + ObjectUtils.nullSafeHashCode(dataNodeUsername);
result = 31 * result + ObjectUtils.nullSafeHashCode(dataNodePassword);
result = 31 * result + ObjectUtils.nullSafeHashCode(sentinelUsername);
result = 31 * result + ObjectUtils.nullSafeHashCode(sentinelPassword);
return result;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011-2021 the original author or authors.
* Copyright 2011-2022 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.
@@ -360,8 +360,9 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
this.initialized = true;
}
private JedisClientConfig createSentinelClientConfig(SentinelConfiguration sentinelConfiguration) {
return createClientConfig(0, null, sentinelConfiguration.getSentinelPassword());
JedisClientConfig createSentinelClientConfig(SentinelConfiguration sentinelConfiguration) {
return createClientConfig(0, sentinelConfiguration.getSentinelUsername(),
sentinelConfiguration.getSentinelPassword());
}
private JedisClientConfig createClientConfig(int database, @Nullable String username, RedisPassword password) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2021 the original author or authors.
* Copyright 2013-2022 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.
@@ -516,7 +516,7 @@ public abstract class LettuceConverters extends Converters {
String sentinelUsername = sentinelConfiguration.getSentinelUsername();
if (StringUtils.hasText(sentinelUsername) && sentinelPassword.isPresent()) {
// See https://github.com/lettuce-io/lettuce-core/issues/1404
sentinelBuilder.withAuthentication(sentinelUsername, new String(sentinelPassword.toOptional().orElse((new char[0]))));
sentinelBuilder.withAuthentication(sentinelUsername, sentinelPassword.get());
} else {
sentinelPassword.toOptional().ifPresent(sentinelBuilder::withPassword);
}
@@ -527,9 +527,9 @@ public abstract class LettuceConverters extends Converters {
String username = sentinelConfiguration.getUsername();
RedisPassword password = sentinelConfiguration.getPassword();
if (StringUtils.hasText(username)) {
if (StringUtils.hasText(username) && password.isPresent()) {
// See https://github.com/lettuce-io/lettuce-core/issues/1404
builder.withAuthentication(username, new String(password.toOptional().orElse(new char[0])));
builder.withAuthentication(username, password.get());
} else {
password.toOptional().ifPresent(builder::withPassword);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2021 the original author or authors.
* Copyright 2015-2022 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.
@@ -23,11 +23,12 @@ import java.util.HashSet;
import org.junit.jupiter.api.Test;
import org.springframework.core.env.PropertySource;
import org.springframework.mock.env.MockPropertySource;
import org.springframework.util.StringUtils;
/**
* Unit tests for {@link RedisSentinelConfiguration}.
*
* @author Christoph Strobl
* @author Vikas Garg
*/
@@ -134,13 +135,14 @@ class RedisSentinelConfigurationUnitTests {
assertThat(configuration.getSentinelPassword()).isEqualTo(RedisPassword.none());
}
@Test
@Test // GH-2218
void dataNodeUsernameDoesNotAffectSentinelUsername() {
RedisSentinelConfiguration configuration = new RedisSentinelConfiguration("myMaster",
Collections.singleton(HOST_AND_PORT_1));
configuration.setUsername("data-admin");
configuration.setSentinelUsername("sentinel-admin");
configuration.setUsername("app");
assertThat(configuration.getDataNodeUsername()).isEqualTo("data-admin");
assertThat(configuration.getSentinelUsername()).isEqualTo("sentinel-admin");
}
@@ -158,17 +160,19 @@ class RedisSentinelConfigurationUnitTests {
assertThat(config.getSentinels()).hasSize(1).contains(new RedisNode("127.0.0.1", 123));
}
@Test
@Test // GH-2218
void readSentinelUsernameFromConfigProperty() {
MockPropertySource propertySource = new MockPropertySource();
propertySource.setProperty("spring.redis.sentinel.master", "myMaster");
propertySource.setProperty("spring.redis.sentinel.nodes", HOST_AND_PORT_1);
propertySource.setProperty("spring.redis.sentinel.username", "sentinel-admin");
propertySource.setProperty("spring.redis.sentinel.password", "foo");
RedisSentinelConfiguration config = new RedisSentinelConfiguration(propertySource);
assertThat(config.getSentinelUsername()).isEqualTo(RedisPassword.of("sentinel-admin"));
assertThat(config.getSentinelUsername()).isEqualTo("sentinel-admin");
assertThat(config.getSentinelPassword()).isEqualTo(RedisPassword.of("foo"));
assertThat(config.getSentinels()).hasSize(1).contains(new RedisNode("127.0.0.1", 123));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2021 the original author or authors.
* Copyright 2014-2022 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.
@@ -282,6 +282,20 @@ class JedisConnectionFactoryUnitTests {
assertThat(connectionFactory.getClusterConfiguration()).isNull();
}
@Test // GH-2218
void shouldConsiderSentinelAuthentication() {
RedisSentinelConfiguration configuration = new RedisSentinelConfiguration();
configuration.setSentinelUsername("sentinel");
configuration.setSentinelPassword("the-password");
connectionFactory = new JedisConnectionFactory(configuration, JedisClientConfiguration.defaultConfiguration());
JedisClientConfig clientConfig = connectionFactory.createSentinelClientConfig(configuration);
assertThat(clientConfig.getUser()).isEqualTo("sentinel");
assertThat(clientConfig.getPassword()).isEqualTo("the-password");
}
@Test // DATAREDIS-574
void shouldReturnClusterConfiguration() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2021 the original author or authors.
* Copyright 2014-2022 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.
@@ -45,6 +45,8 @@ import org.springframework.data.redis.core.types.Expiration;
import org.springframework.data.redis.core.types.RedisClientInfo;
/**
* Unit tests for {@link LettuceConverters}.
*
* @author Christoph Strobl
* @author Vikas Garg
*/
@@ -256,54 +258,73 @@ class LettuceConvertersUnitTests {
.isEqualTo(new GetExArgs().pxAt(10));
}
@Test
@Test // GH-2218
void sentinelConfigurationWithAuth() {
RedisPassword password = RedisPassword.of("88888888-8x8-getting-creative-now");
RedisPassword dataPassword = RedisPassword.of("data-secret");
RedisPassword sentinelPassword = RedisPassword.of("sentinel-secret");
RedisSentinelConfiguration sentinelConfiguration = new RedisSentinelConfiguration()
.master(MASTER_NAME)
.sentinel("127.0.0.1", 26379)
.sentinel("127.0.0.1", 26380);
sentinelConfiguration.setSentinelUsername("admin");
sentinelConfiguration.setSentinelPassword(password);
sentinelConfiguration.setUsername("app");
sentinelConfiguration.setPassword(password);
sentinelConfiguration.setPassword(dataPassword);
sentinelConfiguration.setSentinelUsername("admin");
sentinelConfiguration.setSentinelPassword(sentinelPassword);
RedisURI redisURI = LettuceConverters.sentinelConfigurationToRedisURI(sentinelConfiguration);
assertThat(redisURI.getUsername()).isEqualTo("app");
assertThat(redisURI.getPassword()).isEqualTo(dataPassword.get());
redisURI.getSentinels().forEach(sentinel -> {
assertThat(sentinel.getUsername()).isEqualTo("admin");
assertThat(sentinel.getPassword()).isEqualTo(sentinelPassword.get());
});
}
@Test
@Test // GH-2218
void sentinelConfigurationSetSentinelPasswordIfUsernameNotPresent() {
RedisPassword password = RedisPassword.of("88888888-8x8-getting-creative-now");
RedisSentinelConfiguration sentinelConfiguration = new RedisSentinelConfiguration()
.master(MASTER_NAME)
.sentinel("127.0.0.1", 26379)
.sentinel("127.0.0.1", 26380);
sentinelConfiguration.setSentinelPassword(password);
sentinelConfiguration.setUsername("app");
sentinelConfiguration.setPassword(password);
sentinelConfiguration.setSentinelPassword(password);
RedisURI redisURI = LettuceConverters.sentinelConfigurationToRedisURI(sentinelConfiguration);
assertThat(redisURI.getUsername()).isEqualTo("app");
redisURI.getSentinels().forEach(sentinel -> {
assertThat(sentinel.getUsername()).isNull();
assertThat(sentinel.getPassword()).isNotNull();
});
}
@Test
@Test // GH-2218
void sentinelConfigurationShouldNotSetSentinelAuthIfUsernameIsPresentWithNoPassword() {
RedisPassword password = RedisPassword.of("88888888-8x8-getting-creative-now");
RedisSentinelConfiguration sentinelConfiguration = new RedisSentinelConfiguration()
.master(MASTER_NAME)
.sentinel("127.0.0.1", 26379)
.sentinel("127.0.0.1", 26380);
sentinelConfiguration.setSentinelUsername("admin");
sentinelConfiguration.setUsername("app");
sentinelConfiguration.setPassword(password);
sentinelConfiguration.setSentinelUsername("admin");
RedisURI redisURI = LettuceConverters.sentinelConfigurationToRedisURI(sentinelConfiguration);
assertThat(redisURI.getUsername()).isEqualTo("app");
redisURI.getSentinels().forEach(sentinel -> {
assertThat(sentinel.getUsername()).isNull();
assertThat(sentinel.getPassword()).isNull();