Skip to content

Commit 1955074

Browse files
christophstroblmp911de
authored andcommitted
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: spring-projects#495.
1 parent df9c57a commit 1955074

File tree

7 files changed

+292
-31
lines changed

7 files changed

+292
-31
lines changed

src/main/asciidoc/reference/redis.adoc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,13 +174,14 @@ public RedisConnectionFactory lettuceConnectionFactory() {
174174
.Configuration Properties
175175
* `spring.redis.sentinel.master`: name of the master node.
176176
* `spring.redis.sentinel.nodes`: Comma delimited list of host:port pairs.
177+
* `spring.redis.sentinel.password`: The password to apply when authenticating with Redis Sentinel
177178
====
178179

179180
Sometimes, direct interaction with one of the Sentinels is required. Using `RedisConnectionFactory.getSentinelConnection()` or `RedisConnection.getSentinelCommands()` gives you access to the first active Sentinel configured.
180181

181182
[NOTE]
182183
====
183-
Configuration options like password, timeouts, SSL... also get applied to Redis Sentinel when using https://lettuce.io/[Lettuce].
184+
Sentinel authentication is only available using https://lettuce.io/[Lettuce].
184185
====
185186

186187
[[redis:template]]

src/main/java/org/springframework/data/redis/connection/RedisConfiguration.java

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,79 @@ default void setMaster(final String name) {
337337
* @return {@link Set} of sentinels. Never {@literal null}.
338338
*/
339339
Set<RedisNode> getSentinels();
340+
341+
/**
342+
* Get the {@link RedisPassword} used when authenticating with a Redis Server.
343+
*
344+
* @return never {@literal null}.
345+
*/
346+
default RedisPassword getDataNodePassword() {
347+
return getPassword();
348+
}
349+
350+
/**
351+
* Create and set a {@link RedisPassword} to be used when authenticating with Sentinel from the given {@link String}
352+
*
353+
* @param password can be {@literal null}.
354+
* @throws IllegalStateException if the {@link #useDataNodeAuthenticationForSentinel(boolean) Data Node Password}
355+
* should be used for authenticating with Redis Sentinel.
356+
* @since 2.2.2
357+
*/
358+
default void setSentinelPassword(@Nullable String password) {
359+
setSentinelPassword(RedisPassword.of(password));
360+
}
361+
362+
/**
363+
* Create and set a {@link RedisPassword} to be used when authenticating with Sentinel from the given
364+
* {@link Character} sequence.
365+
*
366+
* @param password can be {@literal null}.
367+
* @throws IllegalStateException if the {@link #useDataNodeAuthenticationForSentinel(boolean) Data Node Password}
368+
* should be used for authenticating with Redis Sentinel.
369+
* @since 2.2.2
370+
*/
371+
default void setSentinelPassword(@Nullable char[] password) {
372+
setSentinelPassword(RedisPassword.of(password));
373+
}
374+
375+
/**
376+
* Set a {@link RedisPassword} to be used when authenticating with Sentinel.
377+
*
378+
* @param password must not be {@literal null} use {@link RedisPassword#none()} instead.
379+
* @throws IllegalStateException if the {@link #useDataNodeAuthenticationForSentinel(boolean) Data Node Password}
380+
* should be used for authenticating with Redis Sentinel.
381+
* @since 2.2.2
382+
*/
383+
void setSentinelPassword(RedisPassword password);
384+
385+
/**
386+
* Get the {@link RedisPassword} to use when connecting to a Redis Sentinel. <br />
387+
* This can be the password explicitly set via {@link #setSentinelPassword(RedisPassword)}, or the
388+
* {@link #getDataNodePassword() Data Node password} if it should be also used for
389+
* {@link #getUseDataNodeAuthenticationForSentinel() sentinel}, or {@link RedisPassword#none()} if no password has
390+
* been set.
391+
*
392+
* @return the {@link RedisPassword} for authenticating with Sentinel.
393+
* @since 2.2.2
394+
*/
395+
RedisPassword getSentinelPassword();
396+
397+
/**
398+
* Use the {@link #getDataNodePassword() RedisPassword} also for authentication with Redis Sentinel.
399+
*
400+
* @param useDataNodeAuthenticationForSentinel
401+
* @throws IllegalStateException if a {@link #getSentinelPassword() Sentinel Password} is already set.
402+
* @since 2.2.2
403+
*/
404+
void useDataNodeAuthenticationForSentinel(boolean useDataNodeAuthenticationForSentinel);
405+
406+
/**
407+
* Use the {@link #getDataNodePassword() RedisPassword} also for authentication with Redis Sentinel.
408+
*
409+
* @return
410+
* @since 2.2.2
411+
*/
412+
boolean getUseDataNodeAuthenticationForSentinel();
340413
}
341414

342415
/**

src/main/java/org/springframework/data/redis/connection/RedisSentinelConfiguration.java

Lines changed: 64 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515
*/
1616
package org.springframework.data.redis.connection;
1717

18-
import static org.springframework.util.Assert.*;
19-
import static org.springframework.util.Assert.hasText;
2018
import static org.springframework.util.StringUtils.*;
2119

2220
import java.util.Collections;
@@ -46,11 +44,15 @@ public class RedisSentinelConfiguration implements RedisConfiguration, SentinelC
4644

4745
private static final String REDIS_SENTINEL_MASTER_CONFIG_PROPERTY = "spring.redis.sentinel.master";
4846
private static final String REDIS_SENTINEL_NODES_CONFIG_PROPERTY = "spring.redis.sentinel.nodes";
47+
private static final String REDIS_SENTINEL_PASSWORD_CONFIG_PROPERTY = "spring.redis.sentinel.password";
4948

5049
private @Nullable NamedNode master;
5150
private Set<RedisNode> sentinels;
5251
private int database;
53-
private RedisPassword password = RedisPassword.none();
52+
53+
private RedisPassword dataNodePassword = RedisPassword.none();
54+
private RedisPassword sentinelPassword = RedisPassword.none();
55+
private boolean useDataNodePasswordForSentinel = false;
5456

5557
/**
5658
* Creates new {@link RedisSentinelConfiguration}.
@@ -89,7 +91,7 @@ public RedisSentinelConfiguration(String master, Set<String> sentinelHostAndPort
8991
*/
9092
public RedisSentinelConfiguration(PropertySource<?> propertySource) {
9193

92-
notNull(propertySource, "PropertySource must not be null!");
94+
Assert.notNull(propertySource, "PropertySource must not be null!");
9395

9496
this.sentinels = new LinkedHashSet<>();
9597

@@ -101,6 +103,10 @@ public RedisSentinelConfiguration(PropertySource<?> propertySource) {
101103
appendSentinels(
102104
commaDelimitedListToSet(propertySource.getProperty(REDIS_SENTINEL_NODES_CONFIG_PROPERTY).toString()));
103105
}
106+
107+
if (propertySource.containsProperty(REDIS_SENTINEL_PASSWORD_CONFIG_PROPERTY)) {
108+
this.setSentinelPassword(propertySource.getProperty(REDIS_SENTINEL_PASSWORD_CONFIG_PROPERTY).toString());
109+
}
104110
}
105111

106112
/**
@@ -110,7 +116,7 @@ public RedisSentinelConfiguration(PropertySource<?> propertySource) {
110116
*/
111117
public void setSentinels(Iterable<RedisNode> sentinels) {
112118

113-
notNull(sentinels, "Cannot set sentinels to 'null'.");
119+
Assert.notNull(sentinels, "Cannot set sentinels to 'null'.");
114120

115121
this.sentinels.clear();
116122

@@ -134,7 +140,7 @@ public Set<RedisNode> getSentinels() {
134140
*/
135141
public void addSentinel(RedisNode sentinel) {
136142

137-
notNull(sentinel, "Sentinel must not be 'null'.");
143+
Assert.notNull(sentinel, "Sentinel must not be 'null'.");
138144
this.sentinels.add(sentinel);
139145
}
140146

@@ -144,7 +150,7 @@ public void addSentinel(RedisNode sentinel) {
144150
*/
145151
public void setMaster(NamedNode master) {
146152

147-
notNull(master, "Sentinel master node must not be 'null'.");
153+
Assert.notNull(master, "Sentinel master node must not be 'null'.");
148154
this.master = master;
149155
}
150156

@@ -230,7 +236,7 @@ public void setDatabase(int index) {
230236
*/
231237
@Override
232238
public RedisPassword getPassword() {
233-
return password;
239+
return dataNodePassword;
234240
}
235241

236242
/*
@@ -242,15 +248,60 @@ public void setPassword(RedisPassword password) {
242248

243249
Assert.notNull(password, "RedisPassword must not be null!");
244250

245-
this.password = password;
251+
this.dataNodePassword = password;
252+
}
253+
254+
/*
255+
* (non-Javadoc)
256+
* @see org.springframework.data.redis.connection.RedisConfiguration.WithPassword#setSentinelPassword(org.springframework.data.redis.connection.RedisPassword)
257+
*/
258+
public void setSentinelPassword(RedisPassword sentinelPassword) {
259+
260+
Assert.state(!useDataNodePasswordForSentinel,
261+
"Configuration uses Redis Data Node password for authenticating with Sentinel. Please set 'RedisSentinelConfiguration.useDataNodeAuthenticationForSentinel(false)' before using this option.");
262+
Assert.notNull(sentinelPassword, "SentinelPassword must not be null!");
263+
this.sentinelPassword = sentinelPassword;
264+
}
265+
266+
/*
267+
* (non-Javadoc)
268+
* @see org.springframework.data.redis.connection.RedisConfiguration.WithPassword#setSentinelPassword()
269+
*/
270+
@Override
271+
public RedisPassword getSentinelPassword() {
272+
return getUseDataNodeAuthenticationForSentinel() ? this.dataNodePassword : sentinelPassword;
273+
}
274+
275+
/*
276+
* (non-Javadoc)
277+
* @see org.springframework.data.redis.connection.RedisConfiguration.WithPassword#useDataNodeAuthenticationForSentinel(boolean)
278+
*/
279+
@Override
280+
public void useDataNodeAuthenticationForSentinel(boolean useDataNodeAuthenticationForSentinel) {
281+
282+
if (useDataNodeAuthenticationForSentinel) {
283+
Assert.state(!this.sentinelPassword.isPresent(),
284+
"Configuration already defines a password for authenticating with Sentinel. Please use 'RedisSentinelConfiguration.setSentinelPassword(RedisPassword.none())' remove the password.");
285+
}
286+
287+
this.useDataNodePasswordForSentinel = useDataNodeAuthenticationForSentinel;
288+
}
289+
290+
/*
291+
* (non-Javadoc)
292+
* @see org.springframework.data.redis.connection.RedisConfiguration.WithPassword#getUseDataNodeAuthenticationForSentinel()
293+
*/
294+
@Override
295+
public boolean getUseDataNodeAuthenticationForSentinel() {
296+
return this.useDataNodePasswordForSentinel;
246297
}
247298

248299
private RedisNode readHostAndPortFromString(String hostAndPort) {
249300

250301
String[] args = split(hostAndPort, ":");
251302

252-
notNull(args, "HostAndPort need to be seperated by ':'.");
253-
isTrue(args.length == 2, "Host and Port String needs to specified as host:port");
303+
Assert.notNull(args, "HostAndPort need to be seperated by ':'.");
304+
Assert.isTrue(args.length == 2, "Host and Port String needs to specified as host:port");
254305
return new RedisNode(args[0], Integer.valueOf(args[1]).intValue());
255306
}
256307

@@ -261,8 +312,8 @@ private RedisNode readHostAndPortFromString(String hostAndPort) {
261312
*/
262313
private static Map<String, Object> asMap(String master, Set<String> sentinelHostAndPorts) {
263314

264-
hasText(master, "Master address must not be null or empty!");
265-
notNull(sentinelHostAndPorts, "SentinelHostAndPorts must not be null!");
315+
Assert.hasText(master, "Master address must not be null or empty!");
316+
Assert.notNull(sentinelHostAndPorts, "SentinelHostAndPorts must not be null!");
266317

267318
Map<String, Object> map = new HashMap<>();
268319
map.put(REDIS_SENTINEL_MASTER_CONFIG_PROPERTY, master);

src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1080,7 +1080,6 @@ private RedisURI getSentinelRedisURI() {
10801080

10811081
applyToAll(redisUri, it -> {
10821082

1083-
getRedisPassword().toOptional().ifPresent(it::setPassword);
10841083
clientConfiguration.getClientName().ifPresent(it::setClientName);
10851084

10861085
it.setSsl(clientConfiguration.isUseSsl());

src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import org.springframework.data.redis.connection.RedisListCommands.Position;
5151
import org.springframework.data.redis.connection.RedisNode;
5252
import org.springframework.data.redis.connection.RedisNode.NodeType;
53+
import org.springframework.data.redis.connection.RedisPassword;
5354
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
5455
import org.springframework.data.redis.connection.RedisServer;
5556
import org.springframework.data.redis.connection.RedisStringCommands.SetOption;
@@ -198,7 +199,8 @@ abstract public class LettuceConverters extends Converters {
198199
};
199200

200201
SCORED_VALUE_TO_TUPLE = source -> source != null
201-
? new DefaultTuple(source.getValue(), Double.valueOf(source.getScore())) : null;
202+
? new DefaultTuple(source.getValue(), Double.valueOf(source.getScore()))
203+
: null;
202204

203205
BYTES_LIST_TO_TUPLE_LIST_CONVERTER = source -> {
204206

@@ -297,7 +299,8 @@ private Set<Flag> parseFlags(Set<NodeFlag> source) {
297299
};
298300

299301
GEO_COORDINATE_TO_POINT_CONVERTER = geoCoordinate -> geoCoordinate != null
300-
? new Point(geoCoordinate.getX().doubleValue(), geoCoordinate.getY().doubleValue()) : null;
302+
? new Point(geoCoordinate.getX().doubleValue(), geoCoordinate.getY().doubleValue())
303+
: null;
301304
GEO_COORDINATE_LIST_TO_POINT_LIST_CONVERTER = new ListConverter<>(GEO_COORDINATE_TO_POINT_CONVERTER);
302305

303306
KEY_VALUE_UNWRAPPER = source -> source.getValueOrElse(null);
@@ -637,15 +640,21 @@ public static RedisURI sentinelConfigurationToRedisURI(RedisSentinelConfiguratio
637640
Assert.notNull(sentinelConfiguration, "RedisSentinelConfiguration is required");
638641

639642
Set<RedisNode> sentinels = sentinelConfiguration.getSentinels();
640-
RedisURI.Builder builder = null;
643+
RedisURI.Builder builder = RedisURI.builder();
641644
for (RedisNode sentinel : sentinels) {
642645

643-
if (builder == null) {
644-
builder = RedisURI.Builder.sentinel(sentinel.getHost(), sentinel.getPort(),
645-
sentinelConfiguration.getMaster().getName());
646-
} else {
647-
builder.withSentinel(sentinel.getHost(), sentinel.getPort());
646+
RedisURI.Builder uri = RedisURI.Builder.sentinel(sentinel.getHost(), sentinel.getPort(),
647+
sentinelConfiguration.getMaster().getName());
648+
649+
if (sentinelConfiguration.getSentinelPassword().isPresent()) {
650+
uri.withPassword(sentinelConfiguration.getSentinelPassword().get());
648651
}
652+
builder.withSentinel(uri.build());
653+
}
654+
655+
RedisPassword password = sentinelConfiguration.getPassword();
656+
if (password.isPresent()) {
657+
builder.withPassword(password.get());
649658
}
650659

651660
return builder.build();

0 commit comments

Comments
 (0)