Skip to content

Commit d97c5df

Browse files
mp911dechristophstrobl
authored andcommitted
DATAREDIS-1046 - Use ACL authentication when username is configured.
Upgrade to Redis 6 for tests. Introduce support for username in configurations that support authentication. Original Pull Request: #558
1 parent da5bab1 commit d97c5df

16 files changed

+437
-51
lines changed

Makefile

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
REDIS_VERSION:=5.0.5
15+
REDIS_VERSION:=6.0.7
1616
SPRING_PROFILE?=ci
1717
SHELL=/bin/bash -euo pipefail
1818

@@ -36,6 +36,24 @@ work/redis-%.conf:
3636
echo save \"\" >> $@
3737
echo slaveof 127.0.0.1 6379 >> $@
3838

39+
# Handled separately because it's a node with authentication. User: spring, password: data. Default password: foobared
40+
work/redis-6382.conf:
41+
@mkdir -p $(@D)
42+
43+
echo port 6382 >> $@
44+
echo daemonize yes >> $@
45+
echo protected-mode no >> $@
46+
echo bind 0.0.0.0 >> $@
47+
echo notify-keyspace-events Ex >> $@
48+
echo pidfile $(shell pwd)/work/redis-6382.pid >> $@
49+
echo logfile $(shell pwd)/work/redis-6382.log >> $@
50+
echo unixsocket $(shell pwd)/work/redis-6382.sock >> $@
51+
echo unixsocketperm 755 >> $@
52+
echo "requirepass foobared" >> $@
53+
echo "user default on #1b58ee375b42e41f0e48ef2ff27d10a5b1f6924a9acdcdba7cae868e7adce6bf ~* +@all" >> $@
54+
echo "user spring on #3a6eb0790f39ac87c94f3856b2dd2c5d110e6811602261a9a923d3bb23adc8b7 +@all" >> $@
55+
echo save \"\" >> $@
56+
3957
# Handled separately because it's the master and all others are slaves
4058
work/redis-6379.conf:
4159
@mkdir -p $(@D)
@@ -54,9 +72,9 @@ work/redis-6379.conf:
5472
work/redis-%.pid: work/redis-%.conf work/redis/bin/redis-server
5573
work/redis/bin/redis-server $<
5674

57-
redis-start: work/redis-6379.pid work/redis-6380.pid work/redis-6381.pid
75+
redis-start: work/redis-6379.pid work/redis-6380.pid work/redis-6381.pid work/redis-6382.pid
5876

59-
redis-stop: stop-6379 stop-6380 stop-6381
77+
redis-stop: stop-6379 stop-6380 stop-6381 stop-6382
6078

6179
##########
6280
# Sentinel
@@ -150,6 +168,9 @@ start: redis-start sentinel-start cluster-init
150168
stop-%: work/redis/bin/redis-cli
151169
-work/redis/bin/redis-cli -p $* shutdown
152170

171+
stop-6382: work/redis/bin/redis-cli
172+
-work/redis/bin/redis-cli -a foobared -p 6382 shutdown
173+
153174
stop: redis-stop sentinel-stop cluster-stop
154175

155176
test:

src/main/asciidoc/new-features.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ This section briefly covers items that are new and noteworthy in the latest rele
77
== New in Spring Data Redis 2.4
88

99
* `RedisCache` now exposes `CacheStatistics`.
10+
* ACL authentication support for Redis Standalone, Redis Cluster and Master/Replica.
1011

1112
[[new-in-2.3.0]]
12-
1313
== New in Spring Data Redis 2.3
1414

1515
* Template API Method Refinements for `Duration` and `Instant`.

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.HashMap;
2424
import java.util.LinkedHashSet;
2525
import java.util.Map;
26+
import java.util.Optional;
2627
import java.util.Set;
2728

2829
import org.springframework.core.env.MapPropertySource;
@@ -49,6 +50,7 @@ public class RedisClusterConfiguration implements RedisConfiguration, ClusterCon
4950

5051
private Set<RedisNode> clusterNodes;
5152
private @Nullable Integer maxRedirects;
53+
private Optional<String> username = Optional.empty();
5254
private RedisPassword password = RedisPassword.none();
5355

5456
/**
@@ -182,6 +184,24 @@ private void appendClusterNodes(Set<String> hostAndPorts) {
182184
}
183185
}
184186

187+
/*
188+
* (non-Javadoc)
189+
* @see org.springframework.data.redis.connection.RedisConfiguration.WithAuthentication#setUsername(String)
190+
*/
191+
@Override
192+
public void setUsername(String username) {
193+
this.username = Optional.of(username);
194+
}
195+
196+
/*
197+
* (non-Javadoc)
198+
* @see org.springframework.data.redis.connection.RedisConfiguration.WithAuthentication#getUsername()
199+
*/
200+
@Override
201+
public Optional<String> getUsername() {
202+
return this.username;
203+
}
204+
185205
/*
186206
* (non-Javadoc)
187207
* @see org.springframework.data.redis.connection.RedisConfiguration.WithPassword#getPassword()

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

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import java.util.Collections;
1919
import java.util.List;
20+
import java.util.Optional;
2021
import java.util.Set;
2122
import java.util.function.IntSupplier;
2223
import java.util.function.Supplier;
@@ -51,11 +52,11 @@ default Integer getDatabaseOrElse(Supplier<Integer> other) {
5152

5253
/**
5354
* Get the configured {@link RedisPassword} if the current {@link RedisConfiguration} is
54-
* {@link #isPasswordAware(RedisConfiguration) password aware} or evaluate and return the value of the given
55+
* {@link #isAuthenticationAware(RedisConfiguration) password aware} or evaluate and return the value of the given
5556
* {@link Supplier}.
5657
*
5758
* @param other a {@code Supplier} whose result is returned if given {@link RedisConfiguration} is not
58-
* {@link #isPasswordAware(RedisConfiguration) password aware}.
59+
* {@link #isAuthenticationAware(RedisConfiguration) password aware}.
5960
* @return never {@literal null}.
6061
* @throws IllegalArgumentException if {@code other} is {@literal null}.
6162
*/
@@ -67,8 +68,8 @@ default RedisPassword getPasswordOrElse(Supplier<RedisPassword> other) {
6768
* @param configuration can be {@literal null}.
6869
* @return {@code true} if given {@link RedisConfiguration} is instance of {@link WithPassword}.
6970
*/
70-
static boolean isPasswordAware(@Nullable RedisConfiguration configuration) {
71-
return configuration instanceof WithPassword;
71+
static boolean isAuthenticationAware(@Nullable RedisConfiguration configuration) {
72+
return configuration instanceof WithAuthentication;
7273
}
7374

7475
/**
@@ -136,14 +137,28 @@ static Integer getDatabaseOrElse(@Nullable RedisConfiguration configuration, Sup
136137
/**
137138
* @param configuration can be {@literal null}.
138139
* @param other a {@code Supplier} whose result is returned if given {@link RedisConfiguration} is not
139-
* {@link #isPasswordAware(RedisConfiguration) password aware}.
140+
* {@link #isAuthenticationAware(RedisConfiguration) password aware}.
141+
* @return never {@literal null}.
142+
* @throws IllegalArgumentException if {@code other} is {@literal null}.
143+
*/
144+
static Optional<String> getUsernameOrElse(@Nullable RedisConfiguration configuration,
145+
Supplier<Optional<String>> other) {
146+
147+
Assert.notNull(other, "Other must not be null!");
148+
return isAuthenticationAware(configuration) ? ((WithAuthentication) configuration).getUsername() : other.get();
149+
}
150+
151+
/**
152+
* @param configuration can be {@literal null}.
153+
* @param other a {@code Supplier} whose result is returned if given {@link RedisConfiguration} is not
154+
* {@link #isAuthenticationAware(RedisConfiguration) password aware}.
140155
* @return never {@literal null}.
141156
* @throws IllegalArgumentException if {@code other} is {@literal null}.
142157
*/
143158
static RedisPassword getPasswordOrElse(@Nullable RedisConfiguration configuration, Supplier<RedisPassword> other) {
144159

145160
Assert.notNull(other, "Other must not be null!");
146-
return isPasswordAware(configuration) ? ((WithPassword) configuration).getPassword() : other.get();
161+
return isAuthenticationAware(configuration) ? ((WithAuthentication) configuration).getPassword() : other.get();
147162
}
148163

149164
/**
@@ -178,9 +193,17 @@ static String getHostOrElse(@Nullable RedisConfiguration configuration, Supplier
178193
* {@link RedisConfiguration} part suitable for configurations that may use authentication when connecting.
179194
*
180195
* @author Christoph Strobl
181-
* @since 2.1
196+
* @author Mark Paluch
197+
* @since 2.4
182198
*/
183-
interface WithPassword {
199+
interface WithAuthentication {
200+
201+
/**
202+
* Create and set a username with the given {@link String}. Requires Redis 6 or newer.
203+
*
204+
* @param username the username.
205+
*/
206+
void setUsername(String username);
184207

185208
/**
186209
* Create and set a {@link RedisPassword} for given {@link String}.
@@ -207,6 +230,13 @@ default void setPassword(@Nullable char[] password) {
207230
*/
208231
void setPassword(RedisPassword password);
209232

233+
/**
234+
* Get the username to use when connecting.
235+
*
236+
* @return {@link Optional#empty()} if none set.
237+
*/
238+
Optional<String> getUsername();
239+
210240
/**
211241
* Get the RedisPassword to use when connecting.
212242
*
@@ -215,6 +245,16 @@ default void setPassword(@Nullable char[] password) {
215245
RedisPassword getPassword();
216246
}
217247

248+
/**
249+
* {@link RedisConfiguration} part suitable for configurations that may use authentication when connecting.
250+
*
251+
* @author Christoph Strobl
252+
* @since 2.1
253+
*/
254+
interface WithPassword extends WithAuthentication {
255+
256+
}
257+
218258
/**
219259
* {@link RedisConfiguration} part suitable for configurations that use a specific database.
220260
*
@@ -339,7 +379,17 @@ default void setMaster(final String name) {
339379
Set<RedisNode> getSentinels();
340380

341381
/**
342-
* Get the {@link RedisPassword} used when authenticating with a Redis Server..
382+
* Get the username used when authenticating with a Redis Server.
383+
*
384+
* @return never {@literal null}.
385+
* @since 2.4
386+
*/
387+
default Optional<String> getDataNodeUsername() {
388+
return getUsername();
389+
}
390+
391+
/**
392+
* Get the {@link RedisPassword} used when authenticating with a Redis Server.
343393
*
344394
* @return never {@literal null}.
345395
* @since 2.2.2

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.HashMap;
2222
import java.util.LinkedHashSet;
2323
import java.util.Map;
24+
import java.util.Optional;
2425
import java.util.Set;
2526

2627
import org.springframework.core.env.MapPropertySource;
@@ -50,6 +51,7 @@ public class RedisSentinelConfiguration implements RedisConfiguration, SentinelC
5051
private Set<RedisNode> sentinels;
5152
private int database;
5253

54+
private Optional<String> dataNodeUsername = Optional.empty();
5355
private RedisPassword dataNodePassword = RedisPassword.none();
5456
private RedisPassword sentinelPassword = RedisPassword.none();
5557

@@ -229,6 +231,24 @@ public void setDatabase(int index) {
229231
this.database = index;
230232
}
231233

234+
/*
235+
* (non-Javadoc)
236+
* @see org.springframework.data.redis.connection.RedisConfiguration.WithAuthentication#setUsername(String)
237+
*/
238+
@Override
239+
public void setUsername(String username) {
240+
this.dataNodeUsername = Optional.of(username);
241+
}
242+
243+
/*
244+
* (non-Javadoc)
245+
* @see org.springframework.data.redis.connection.RedisConfiguration.WithAuthentication#getUsername()
246+
*/
247+
@Override
248+
public Optional<String> getUsername() {
249+
return this.dataNodeUsername;
250+
}
251+
232252
/*
233253
* (non-Javadoc)
234254
* @see org.springframework.data.redis.connection.RedisConfiguration.WithPassword#getPassword()

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

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

18+
import java.util.Optional;
19+
1820
import org.springframework.data.redis.connection.RedisConfiguration.DomainSocketConfiguration;
1921
import org.springframework.util.Assert;
2022

@@ -32,6 +34,7 @@ public class RedisSocketConfiguration implements RedisConfiguration, DomainSocke
3234

3335
private String socket = DEFAULT_SOCKET;
3436
private int database;
37+
private Optional<String> username = Optional.empty();
3538
private RedisPassword password = RedisPassword.none();
3639

3740
/**
@@ -92,6 +95,24 @@ public void setDatabase(int index) {
9295
this.database = index;
9396
}
9497

98+
/*
99+
* (non-Javadoc)
100+
* @see org.springframework.data.redis.connection.RedisConfiguration.WithAuthentication#setUsername(String)
101+
*/
102+
@Override
103+
public void setUsername(String username) {
104+
this.username = Optional.of(username);
105+
}
106+
107+
/*
108+
* (non-Javadoc)
109+
* @see org.springframework.data.redis.connection.RedisConfiguration.WithAuthentication#getUsername()
110+
*/
111+
@Override
112+
public Optional<String> getUsername() {
113+
return this.username;
114+
}
115+
95116
/*
96117
* (non-Javadoc)
97118
* @see org.springframework.data.redis.connection.RedisConfiguration.WithPassword#getPassword()

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

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

18+
import java.util.Optional;
19+
1820
import org.springframework.data.redis.connection.RedisConfiguration.WithDatabaseIndex;
1921
import org.springframework.data.redis.connection.RedisConfiguration.WithHostAndPort;
2022
import org.springframework.data.redis.connection.RedisConfiguration.WithPassword;
@@ -37,6 +39,7 @@ public class RedisStandaloneConfiguration
3739
private String hostName = DEFAULT_HOST;
3840
private int port = DEFAULT_PORT;
3941
private int database;
42+
private Optional<String> username = Optional.empty();
4043
private RedisPassword password = RedisPassword.none();
4144

4245
/**
@@ -125,6 +128,24 @@ public void setDatabase(int index) {
125128
this.database = index;
126129
}
127130

131+
/*
132+
* (non-Javadoc)
133+
* @see org.springframework.data.redis.connection.RedisConfiguration.WithAuthentication#setUsername(String)
134+
*/
135+
@Override
136+
public void setUsername(String username) {
137+
this.username = Optional.of(username);
138+
}
139+
140+
/*
141+
* (non-Javadoc)
142+
* @see org.springframework.data.redis.connection.RedisConfiguration.WithAuthentication#getUsername()
143+
*/
144+
@Override
145+
public Optional<String> getUsername() {
146+
return this.username;
147+
}
148+
128149
/*
129150
* (non-Javadoc)
130151
* @see org.springframework.data.redis.connection.RedisConfiguration.WithPassword#getPassword()

0 commit comments

Comments
 (0)