DATAREDIS-1060 - Redis password should not automatically be applied to Sentinel.

We now expose two separate properties in RedisSentinelConfiguration to configure the data password individually from the sentinel password.

Original pull request: #495.
This commit is contained in:
Christoph Strobl
2019-11-11 15:40:29 +01:00
committed by Mark Paluch
parent df9c57ad98
commit 1955074eb7
7 changed files with 292 additions and 31 deletions

View File

@@ -337,6 +337,79 @@ public interface RedisConfiguration {
* @return {@link Set} of sentinels. Never {@literal null}.
*/
Set<RedisNode> getSentinels();
/**
* Get the {@link RedisPassword} used when authenticating with a Redis Server.
*
* @return never {@literal null}.
*/
default RedisPassword getDataNodePassword() {
return getPassword();
}
/**
* Create and set a {@link RedisPassword} to be used when authenticating with Sentinel from the given {@link String}
*
* @param password can be {@literal null}.
* @throws IllegalStateException if the {@link #useDataNodeAuthenticationForSentinel(boolean) Data Node Password}
* should be used for authenticating with Redis Sentinel.
* @since 2.2.2
*/
default void setSentinelPassword(@Nullable String password) {
setSentinelPassword(RedisPassword.of(password));
}
/**
* Create and set a {@link RedisPassword} to be used when authenticating with Sentinel from the given
* {@link Character} sequence.
*
* @param password can be {@literal null}.
* @throws IllegalStateException if the {@link #useDataNodeAuthenticationForSentinel(boolean) Data Node Password}
* should be used for authenticating with Redis Sentinel.
* @since 2.2.2
*/
default void setSentinelPassword(@Nullable char[] password) {
setSentinelPassword(RedisPassword.of(password));
}
/**
* Set a {@link RedisPassword} to be used when authenticating with Sentinel.
*
* @param password must not be {@literal null} use {@link RedisPassword#none()} instead.
* @throws IllegalStateException if the {@link #useDataNodeAuthenticationForSentinel(boolean) Data Node Password}
* should be used for authenticating with Redis Sentinel.
* @since 2.2.2
*/
void setSentinelPassword(RedisPassword password);
/**
* Get the {@link RedisPassword} to use when connecting to a Redis Sentinel. <br />
* This can be the password explicitly set via {@link #setSentinelPassword(RedisPassword)}, or the
* {@link #getDataNodePassword() Data Node password} if it should be also used for
* {@link #getUseDataNodeAuthenticationForSentinel() sentinel}, or {@link RedisPassword#none()} if no password has
* been set.
*
* @return the {@link RedisPassword} for authenticating with Sentinel.
* @since 2.2.2
*/
RedisPassword getSentinelPassword();
/**
* Use the {@link #getDataNodePassword() RedisPassword} also for authentication with Redis Sentinel.
*
* @param useDataNodeAuthenticationForSentinel
* @throws IllegalStateException if a {@link #getSentinelPassword() Sentinel Password} is already set.
* @since 2.2.2
*/
void useDataNodeAuthenticationForSentinel(boolean useDataNodeAuthenticationForSentinel);
/**
* Use the {@link #getDataNodePassword() RedisPassword} also for authentication with Redis Sentinel.
*
* @return
* @since 2.2.2
*/
boolean getUseDataNodeAuthenticationForSentinel();
}
/**

View File

@@ -15,8 +15,6 @@
*/
package org.springframework.data.redis.connection;
import static org.springframework.util.Assert.*;
import static org.springframework.util.Assert.hasText;
import static org.springframework.util.StringUtils.*;
import java.util.Collections;
@@ -46,11 +44,15 @@ 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 @Nullable NamedNode master;
private Set<RedisNode> sentinels;
private int database;
private RedisPassword password = RedisPassword.none();
private RedisPassword dataNodePassword = RedisPassword.none();
private RedisPassword sentinelPassword = RedisPassword.none();
private boolean useDataNodePasswordForSentinel = false;
/**
* Creates new {@link RedisSentinelConfiguration}.
@@ -89,7 +91,7 @@ public class RedisSentinelConfiguration implements RedisConfiguration, SentinelC
*/
public RedisSentinelConfiguration(PropertySource<?> propertySource) {
notNull(propertySource, "PropertySource must not be null!");
Assert.notNull(propertySource, "PropertySource must not be null!");
this.sentinels = new LinkedHashSet<>();
@@ -101,6 +103,10 @@ public class RedisSentinelConfiguration implements RedisConfiguration, SentinelC
appendSentinels(
commaDelimitedListToSet(propertySource.getProperty(REDIS_SENTINEL_NODES_CONFIG_PROPERTY).toString()));
}
if (propertySource.containsProperty(REDIS_SENTINEL_PASSWORD_CONFIG_PROPERTY)) {
this.setSentinelPassword(propertySource.getProperty(REDIS_SENTINEL_PASSWORD_CONFIG_PROPERTY).toString());
}
}
/**
@@ -110,7 +116,7 @@ public class RedisSentinelConfiguration implements RedisConfiguration, SentinelC
*/
public void setSentinels(Iterable<RedisNode> sentinels) {
notNull(sentinels, "Cannot set sentinels to 'null'.");
Assert.notNull(sentinels, "Cannot set sentinels to 'null'.");
this.sentinels.clear();
@@ -134,7 +140,7 @@ public class RedisSentinelConfiguration implements RedisConfiguration, SentinelC
*/
public void addSentinel(RedisNode sentinel) {
notNull(sentinel, "Sentinel must not be 'null'.");
Assert.notNull(sentinel, "Sentinel must not be 'null'.");
this.sentinels.add(sentinel);
}
@@ -144,7 +150,7 @@ public class RedisSentinelConfiguration implements RedisConfiguration, SentinelC
*/
public void setMaster(NamedNode master) {
notNull(master, "Sentinel master node must not be 'null'.");
Assert.notNull(master, "Sentinel master node must not be 'null'.");
this.master = master;
}
@@ -230,7 +236,7 @@ public class RedisSentinelConfiguration implements RedisConfiguration, SentinelC
*/
@Override
public RedisPassword getPassword() {
return password;
return dataNodePassword;
}
/*
@@ -242,15 +248,60 @@ public class RedisSentinelConfiguration implements RedisConfiguration, SentinelC
Assert.notNull(password, "RedisPassword must not be null!");
this.password = password;
this.dataNodePassword = password;
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisConfiguration.WithPassword#setSentinelPassword(org.springframework.data.redis.connection.RedisPassword)
*/
public void setSentinelPassword(RedisPassword sentinelPassword) {
Assert.state(!useDataNodePasswordForSentinel,
"Configuration uses Redis Data Node password for authenticating with Sentinel. Please set 'RedisSentinelConfiguration.useDataNodeAuthenticationForSentinel(false)' before using this option.");
Assert.notNull(sentinelPassword, "SentinelPassword must not be null!");
this.sentinelPassword = sentinelPassword;
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisConfiguration.WithPassword#setSentinelPassword()
*/
@Override
public RedisPassword getSentinelPassword() {
return getUseDataNodeAuthenticationForSentinel() ? this.dataNodePassword : sentinelPassword;
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisConfiguration.WithPassword#useDataNodeAuthenticationForSentinel(boolean)
*/
@Override
public void useDataNodeAuthenticationForSentinel(boolean useDataNodeAuthenticationForSentinel) {
if (useDataNodeAuthenticationForSentinel) {
Assert.state(!this.sentinelPassword.isPresent(),
"Configuration already defines a password for authenticating with Sentinel. Please use 'RedisSentinelConfiguration.setSentinelPassword(RedisPassword.none())' remove the password.");
}
this.useDataNodePasswordForSentinel = useDataNodeAuthenticationForSentinel;
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisConfiguration.WithPassword#getUseDataNodeAuthenticationForSentinel()
*/
@Override
public boolean getUseDataNodeAuthenticationForSentinel() {
return this.useDataNodePasswordForSentinel;
}
private RedisNode readHostAndPortFromString(String hostAndPort) {
String[] args = split(hostAndPort, ":");
notNull(args, "HostAndPort need to be seperated by ':'.");
isTrue(args.length == 2, "Host and Port String needs to specified as host:port");
Assert.notNull(args, "HostAndPort need to be seperated by ':'.");
Assert.isTrue(args.length == 2, "Host and Port String needs to specified as host:port");
return new RedisNode(args[0], Integer.valueOf(args[1]).intValue());
}
@@ -261,8 +312,8 @@ public class RedisSentinelConfiguration implements RedisConfiguration, SentinelC
*/
private static Map<String, Object> asMap(String master, Set<String> sentinelHostAndPorts) {
hasText(master, "Master address must not be null or empty!");
notNull(sentinelHostAndPorts, "SentinelHostAndPorts must not be null!");
Assert.hasText(master, "Master address must not be null or empty!");
Assert.notNull(sentinelHostAndPorts, "SentinelHostAndPorts must not be null!");
Map<String, Object> map = new HashMap<>();
map.put(REDIS_SENTINEL_MASTER_CONFIG_PROPERTY, master);

View File

@@ -1080,7 +1080,6 @@ public class LettuceConnectionFactory
applyToAll(redisUri, it -> {
getRedisPassword().toOptional().ifPresent(it::setPassword);
clientConfiguration.getClientName().ifPresent(it::setClientName);
it.setSsl(clientConfiguration.isUseSsl());

View File

@@ -50,6 +50,7 @@ import org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusComma
import org.springframework.data.redis.connection.RedisListCommands.Position;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisNode.NodeType;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.RedisServer;
import org.springframework.data.redis.connection.RedisStringCommands.SetOption;
@@ -198,7 +199,8 @@ abstract public class LettuceConverters extends Converters {
};
SCORED_VALUE_TO_TUPLE = source -> source != null
? new DefaultTuple(source.getValue(), Double.valueOf(source.getScore())) : null;
? new DefaultTuple(source.getValue(), Double.valueOf(source.getScore()))
: null;
BYTES_LIST_TO_TUPLE_LIST_CONVERTER = source -> {
@@ -297,7 +299,8 @@ abstract public class LettuceConverters extends Converters {
};
GEO_COORDINATE_TO_POINT_CONVERTER = geoCoordinate -> geoCoordinate != null
? new Point(geoCoordinate.getX().doubleValue(), geoCoordinate.getY().doubleValue()) : null;
? new Point(geoCoordinate.getX().doubleValue(), geoCoordinate.getY().doubleValue())
: null;
GEO_COORDINATE_LIST_TO_POINT_LIST_CONVERTER = new ListConverter<>(GEO_COORDINATE_TO_POINT_CONVERTER);
KEY_VALUE_UNWRAPPER = source -> source.getValueOrElse(null);
@@ -637,15 +640,21 @@ abstract public class LettuceConverters extends Converters {
Assert.notNull(sentinelConfiguration, "RedisSentinelConfiguration is required");
Set<RedisNode> sentinels = sentinelConfiguration.getSentinels();
RedisURI.Builder builder = null;
RedisURI.Builder builder = RedisURI.builder();
for (RedisNode sentinel : sentinels) {
if (builder == null) {
builder = RedisURI.Builder.sentinel(sentinel.getHost(), sentinel.getPort(),
sentinelConfiguration.getMaster().getName());
} else {
builder.withSentinel(sentinel.getHost(), sentinel.getPort());
RedisURI.Builder uri = RedisURI.Builder.sentinel(sentinel.getHost(), sentinel.getPort(),
sentinelConfiguration.getMaster().getName());
if (sentinelConfiguration.getSentinelPassword().isPresent()) {
uri.withPassword(sentinelConfiguration.getSentinelPassword().get());
}
builder.withSentinel(uri.build());
}
RedisPassword password = sentinelConfiguration.getPassword();
if (password.isPresent()) {
builder.withPassword(password.get());
}
return builder.build();