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
This commit is contained in:
committed by
Christoph Strobl
parent
da5bab1fb2
commit
d97c5df130
27
Makefile
27
Makefile
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
REDIS_VERSION:=5.0.5
|
||||
REDIS_VERSION:=6.0.7
|
||||
SPRING_PROFILE?=ci
|
||||
SHELL=/bin/bash -euo pipefail
|
||||
|
||||
@@ -36,6 +36,24 @@ work/redis-%.conf:
|
||||
echo save \"\" >> $@
|
||||
echo slaveof 127.0.0.1 6379 >> $@
|
||||
|
||||
# Handled separately because it's a node with authentication. User: spring, password: data. Default password: foobared
|
||||
work/redis-6382.conf:
|
||||
@mkdir -p $(@D)
|
||||
|
||||
echo port 6382 >> $@
|
||||
echo daemonize yes >> $@
|
||||
echo protected-mode no >> $@
|
||||
echo bind 0.0.0.0 >> $@
|
||||
echo notify-keyspace-events Ex >> $@
|
||||
echo pidfile $(shell pwd)/work/redis-6382.pid >> $@
|
||||
echo logfile $(shell pwd)/work/redis-6382.log >> $@
|
||||
echo unixsocket $(shell pwd)/work/redis-6382.sock >> $@
|
||||
echo unixsocketperm 755 >> $@
|
||||
echo "requirepass foobared" >> $@
|
||||
echo "user default on #1b58ee375b42e41f0e48ef2ff27d10a5b1f6924a9acdcdba7cae868e7adce6bf ~* +@all" >> $@
|
||||
echo "user spring on #3a6eb0790f39ac87c94f3856b2dd2c5d110e6811602261a9a923d3bb23adc8b7 +@all" >> $@
|
||||
echo save \"\" >> $@
|
||||
|
||||
# Handled separately because it's the master and all others are slaves
|
||||
work/redis-6379.conf:
|
||||
@mkdir -p $(@D)
|
||||
@@ -54,9 +72,9 @@ work/redis-6379.conf:
|
||||
work/redis-%.pid: work/redis-%.conf work/redis/bin/redis-server
|
||||
work/redis/bin/redis-server $<
|
||||
|
||||
redis-start: work/redis-6379.pid work/redis-6380.pid work/redis-6381.pid
|
||||
redis-start: work/redis-6379.pid work/redis-6380.pid work/redis-6381.pid work/redis-6382.pid
|
||||
|
||||
redis-stop: stop-6379 stop-6380 stop-6381
|
||||
redis-stop: stop-6379 stop-6380 stop-6381 stop-6382
|
||||
|
||||
##########
|
||||
# Sentinel
|
||||
@@ -150,6 +168,9 @@ start: redis-start sentinel-start cluster-init
|
||||
stop-%: work/redis/bin/redis-cli
|
||||
-work/redis/bin/redis-cli -p $* shutdown
|
||||
|
||||
stop-6382: work/redis/bin/redis-cli
|
||||
-work/redis/bin/redis-cli -a foobared -p 6382 shutdown
|
||||
|
||||
stop: redis-stop sentinel-stop cluster-stop
|
||||
|
||||
test:
|
||||
|
||||
@@ -7,9 +7,9 @@ This section briefly covers items that are new and noteworthy in the latest rele
|
||||
== New in Spring Data Redis 2.4
|
||||
|
||||
* `RedisCache` now exposes `CacheStatistics`.
|
||||
* ACL authentication support for Redis Standalone, Redis Cluster and Master/Replica.
|
||||
|
||||
[[new-in-2.3.0]]
|
||||
|
||||
== New in Spring Data Redis 2.3
|
||||
|
||||
* Template API Method Refinements for `Duration` and `Instant`.
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
@@ -49,6 +50,7 @@ public class RedisClusterConfiguration implements RedisConfiguration, ClusterCon
|
||||
|
||||
private Set<RedisNode> clusterNodes;
|
||||
private @Nullable Integer maxRedirects;
|
||||
private Optional<String> username = Optional.empty();
|
||||
private RedisPassword password = RedisPassword.none();
|
||||
|
||||
/**
|
||||
@@ -182,6 +184,24 @@ public class RedisClusterConfiguration implements RedisConfiguration, ClusterCon
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisConfiguration.WithAuthentication#setUsername(String)
|
||||
*/
|
||||
@Override
|
||||
public void setUsername(String username) {
|
||||
this.username = Optional.of(username);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisConfiguration.WithAuthentication#getUsername()
|
||||
*/
|
||||
@Override
|
||||
public Optional<String> getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisConfiguration.WithPassword#getPassword()
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.springframework.data.redis.connection;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.IntSupplier;
|
||||
import java.util.function.Supplier;
|
||||
@@ -51,11 +52,11 @@ public interface RedisConfiguration {
|
||||
|
||||
/**
|
||||
* Get the configured {@link RedisPassword} if the current {@link RedisConfiguration} is
|
||||
* {@link #isPasswordAware(RedisConfiguration) password aware} or evaluate and return the value of the given
|
||||
* {@link #isAuthenticationAware(RedisConfiguration) password aware} or evaluate and return the value of the given
|
||||
* {@link Supplier}.
|
||||
*
|
||||
* @param other a {@code Supplier} whose result is returned if given {@link RedisConfiguration} is not
|
||||
* {@link #isPasswordAware(RedisConfiguration) password aware}.
|
||||
* {@link #isAuthenticationAware(RedisConfiguration) password aware}.
|
||||
* @return never {@literal null}.
|
||||
* @throws IllegalArgumentException if {@code other} is {@literal null}.
|
||||
*/
|
||||
@@ -67,8 +68,8 @@ public interface RedisConfiguration {
|
||||
* @param configuration can be {@literal null}.
|
||||
* @return {@code true} if given {@link RedisConfiguration} is instance of {@link WithPassword}.
|
||||
*/
|
||||
static boolean isPasswordAware(@Nullable RedisConfiguration configuration) {
|
||||
return configuration instanceof WithPassword;
|
||||
static boolean isAuthenticationAware(@Nullable RedisConfiguration configuration) {
|
||||
return configuration instanceof WithAuthentication;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,14 +137,28 @@ public interface RedisConfiguration {
|
||||
/**
|
||||
* @param configuration can be {@literal null}.
|
||||
* @param other a {@code Supplier} whose result is returned if given {@link RedisConfiguration} is not
|
||||
* {@link #isPasswordAware(RedisConfiguration) password aware}.
|
||||
* {@link #isAuthenticationAware(RedisConfiguration) password aware}.
|
||||
* @return never {@literal null}.
|
||||
* @throws IllegalArgumentException if {@code other} is {@literal null}.
|
||||
*/
|
||||
static Optional<String> getUsernameOrElse(@Nullable RedisConfiguration configuration,
|
||||
Supplier<Optional<String>> other) {
|
||||
|
||||
Assert.notNull(other, "Other must not be null!");
|
||||
return isAuthenticationAware(configuration) ? ((WithAuthentication) configuration).getUsername() : other.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param configuration can be {@literal null}.
|
||||
* @param other a {@code Supplier} whose result is returned if given {@link RedisConfiguration} is not
|
||||
* {@link #isAuthenticationAware(RedisConfiguration) password aware}.
|
||||
* @return never {@literal null}.
|
||||
* @throws IllegalArgumentException if {@code other} is {@literal null}.
|
||||
*/
|
||||
static RedisPassword getPasswordOrElse(@Nullable RedisConfiguration configuration, Supplier<RedisPassword> other) {
|
||||
|
||||
Assert.notNull(other, "Other must not be null!");
|
||||
return isPasswordAware(configuration) ? ((WithPassword) configuration).getPassword() : other.get();
|
||||
return isAuthenticationAware(configuration) ? ((WithAuthentication) configuration).getPassword() : other.get();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,9 +193,17 @@ public interface RedisConfiguration {
|
||||
* {@link RedisConfiguration} part suitable for configurations that may use authentication when connecting.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.1
|
||||
* @author Mark Paluch
|
||||
* @since 2.4
|
||||
*/
|
||||
interface WithPassword {
|
||||
interface WithAuthentication {
|
||||
|
||||
/**
|
||||
* Create and set a username with the given {@link String}. Requires Redis 6 or newer.
|
||||
*
|
||||
* @param username the username.
|
||||
*/
|
||||
void setUsername(String username);
|
||||
|
||||
/**
|
||||
* Create and set a {@link RedisPassword} for given {@link String}.
|
||||
@@ -207,6 +230,13 @@ public interface RedisConfiguration {
|
||||
*/
|
||||
void setPassword(RedisPassword password);
|
||||
|
||||
/**
|
||||
* Get the username to use when connecting.
|
||||
*
|
||||
* @return {@link Optional#empty()} if none set.
|
||||
*/
|
||||
Optional<String> getUsername();
|
||||
|
||||
/**
|
||||
* Get the RedisPassword to use when connecting.
|
||||
*
|
||||
@@ -215,6 +245,16 @@ public interface RedisConfiguration {
|
||||
RedisPassword getPassword();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link RedisConfiguration} part suitable for configurations that may use authentication when connecting.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.1
|
||||
*/
|
||||
interface WithPassword extends WithAuthentication {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link RedisConfiguration} part suitable for configurations that use a specific database.
|
||||
*
|
||||
@@ -339,7 +379,17 @@ public interface RedisConfiguration {
|
||||
Set<RedisNode> getSentinels();
|
||||
|
||||
/**
|
||||
* Get the {@link RedisPassword} used when authenticating with a Redis Server..
|
||||
* Get the username used when authenticating with a Redis Server.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
* @since 2.4
|
||||
*/
|
||||
default Optional<String> getDataNodeUsername() {
|
||||
return getUsername();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link RedisPassword} used when authenticating with a Redis Server.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
* @since 2.2.2
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
@@ -50,6 +51,7 @@ public class RedisSentinelConfiguration implements RedisConfiguration, SentinelC
|
||||
private Set<RedisNode> sentinels;
|
||||
private int database;
|
||||
|
||||
private Optional<String> dataNodeUsername = Optional.empty();
|
||||
private RedisPassword dataNodePassword = RedisPassword.none();
|
||||
private RedisPassword sentinelPassword = RedisPassword.none();
|
||||
|
||||
@@ -229,6 +231,24 @@ public class RedisSentinelConfiguration implements RedisConfiguration, SentinelC
|
||||
this.database = index;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisConfiguration.WithAuthentication#setUsername(String)
|
||||
*/
|
||||
@Override
|
||||
public void setUsername(String username) {
|
||||
this.dataNodeUsername = Optional.of(username);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisConfiguration.WithAuthentication#getUsername()
|
||||
*/
|
||||
@Override
|
||||
public Optional<String> getUsername() {
|
||||
return this.dataNodeUsername;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisConfiguration.WithPassword#getPassword()
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.redis.connection;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.redis.connection.RedisConfiguration.DomainSocketConfiguration;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -32,6 +34,7 @@ public class RedisSocketConfiguration implements RedisConfiguration, DomainSocke
|
||||
|
||||
private String socket = DEFAULT_SOCKET;
|
||||
private int database;
|
||||
private Optional<String> username = Optional.empty();
|
||||
private RedisPassword password = RedisPassword.none();
|
||||
|
||||
/**
|
||||
@@ -92,6 +95,24 @@ public class RedisSocketConfiguration implements RedisConfiguration, DomainSocke
|
||||
this.database = index;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisConfiguration.WithAuthentication#setUsername(String)
|
||||
*/
|
||||
@Override
|
||||
public void setUsername(String username) {
|
||||
this.username = Optional.of(username);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisConfiguration.WithAuthentication#getUsername()
|
||||
*/
|
||||
@Override
|
||||
public Optional<String> getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisConfiguration.WithPassword#getPassword()
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.redis.connection;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.redis.connection.RedisConfiguration.WithDatabaseIndex;
|
||||
import org.springframework.data.redis.connection.RedisConfiguration.WithHostAndPort;
|
||||
import org.springframework.data.redis.connection.RedisConfiguration.WithPassword;
|
||||
@@ -37,6 +39,7 @@ public class RedisStandaloneConfiguration
|
||||
private String hostName = DEFAULT_HOST;
|
||||
private int port = DEFAULT_PORT;
|
||||
private int database;
|
||||
private Optional<String> username = Optional.empty();
|
||||
private RedisPassword password = RedisPassword.none();
|
||||
|
||||
/**
|
||||
@@ -125,6 +128,24 @@ public class RedisStandaloneConfiguration
|
||||
this.database = index;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisConfiguration.WithAuthentication#setUsername(String)
|
||||
*/
|
||||
@Override
|
||||
public void setUsername(String username) {
|
||||
this.username = Optional.of(username);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisConfiguration.WithAuthentication#getUsername()
|
||||
*/
|
||||
@Override
|
||||
public Optional<String> getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisConfiguration.WithPassword#getPassword()
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.redis.connection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.redis.connection.RedisConfiguration.StaticMasterReplicaConfiguration;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -40,6 +41,7 @@ public class RedisStaticMasterReplicaConfiguration implements RedisConfiguration
|
||||
|
||||
private List<RedisStandaloneConfiguration> nodes = new ArrayList<>();
|
||||
private int database;
|
||||
private Optional<String> username = Optional.empty();
|
||||
private RedisPassword password = RedisPassword.none();
|
||||
|
||||
/**
|
||||
@@ -130,6 +132,24 @@ public class RedisStaticMasterReplicaConfiguration implements RedisConfiguration
|
||||
this.nodes.forEach(it -> it.setDatabase(database));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisConfiguration.WithAuthentication#setUsername(String)
|
||||
*/
|
||||
@Override
|
||||
public void setUsername(String username) {
|
||||
this.username = Optional.of(username);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisConfiguration.WithAuthentication#getUsername()
|
||||
*/
|
||||
@Override
|
||||
public Optional<String> getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.redis.connection.RedisConfiguration.WithPassword#getPassword()
|
||||
|
||||
@@ -328,6 +328,7 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
|
||||
clientConfiguration.getHostnameVerifier().orElse(null));
|
||||
|
||||
getRedisPassword().map(String::new).ifPresent(shardInfo::setPassword);
|
||||
getRedisUsername().ifPresent(shardInfo::setUser);
|
||||
|
||||
int readTimeout = getReadTimeout();
|
||||
|
||||
@@ -369,9 +370,11 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
|
||||
*/
|
||||
protected Pool<Jedis> createRedisSentinelPool(RedisSentinelConfiguration config) {
|
||||
|
||||
GenericObjectPoolConfig poolConfig = getPoolConfig() != null ? getPoolConfig() : new JedisPoolConfig();
|
||||
GenericObjectPoolConfig<?> poolConfig = getPoolConfig() != null ? getPoolConfig() : new JedisPoolConfig();
|
||||
|
||||
return new JedisSentinelPool(config.getMaster().getName(), convertToJedisSentinelSet(config.getSentinels()),
|
||||
poolConfig, getConnectTimeout(), getReadTimeout(), getPassword(), getDatabase(), getClientName());
|
||||
poolConfig, getConnectTimeout(), getReadTimeout(), getUsername(), getPassword(), getDatabase(),
|
||||
getClientName());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -383,7 +386,7 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
|
||||
protected Pool<Jedis> createRedisPool() {
|
||||
|
||||
return new JedisPool(getPoolConfig(), getHostName(), getPort(), getConnectTimeout(), getReadTimeout(),
|
||||
getPassword(), getDatabase(), getClientName(), isUseSsl(),
|
||||
getUsername(), getPassword(), getDatabase(), getClientName(), isUseSsl(),
|
||||
clientConfiguration.getSslSocketFactory().orElse(null), //
|
||||
clientConfiguration.getSslParameters().orElse(null), //
|
||||
clientConfiguration.getHostnameVerifier().orElse(null));
|
||||
@@ -414,7 +417,7 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
|
||||
* @return the actual {@link JedisCluster}.
|
||||
* @since 1.7
|
||||
*/
|
||||
protected JedisCluster createCluster(RedisClusterConfiguration clusterConfig, GenericObjectPoolConfig poolConfig) {
|
||||
protected JedisCluster createCluster(RedisClusterConfiguration clusterConfig, GenericObjectPoolConfig<?> poolConfig) {
|
||||
|
||||
Assert.notNull(clusterConfig, "Cluster configuration must not be null!");
|
||||
|
||||
@@ -425,7 +428,7 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
|
||||
|
||||
int redirects = clusterConfig.getMaxRedirects() != null ? clusterConfig.getMaxRedirects() : 5;
|
||||
|
||||
return new JedisCluster(hostAndPort, getConnectTimeout(), getReadTimeout(), redirects, getPassword(),
|
||||
return new JedisCluster(hostAndPort, getConnectTimeout(), getReadTimeout(), redirects, getUsername(), getPassword(),
|
||||
getClientName(), poolConfig, isUseSsl(), clientConfiguration.getSslSocketFactory().orElse(null),
|
||||
clientConfiguration.getSslParameters().orElse(null), clientConfiguration.getHostnameVerifier().orElse(null),
|
||||
null);
|
||||
@@ -544,6 +547,16 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
|
||||
getMutableConfiguration().setUseSsl(useSsl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the username used for authenticating with the Redis server.
|
||||
*
|
||||
* @return username for authentication.
|
||||
*/
|
||||
@Nullable
|
||||
private String getUsername() {
|
||||
return getRedisUsername().orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the password used for authenticating with the Redis server.
|
||||
*
|
||||
@@ -554,6 +567,10 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
|
||||
return getRedisPassword().map(String::new).orElse(null);
|
||||
}
|
||||
|
||||
private Optional<String> getRedisUsername() {
|
||||
return RedisConfiguration.getUsernameOrElse(this.configuration, standaloneConfig::getUsername);
|
||||
}
|
||||
|
||||
private RedisPassword getRedisPassword() {
|
||||
return RedisConfiguration.getPasswordOrElse(this.configuration, standaloneConfig::getPassword);
|
||||
}
|
||||
@@ -568,7 +585,7 @@ public class JedisConnectionFactory implements InitializingBean, DisposableBean,
|
||||
@Deprecated
|
||||
public void setPassword(String password) {
|
||||
|
||||
if (RedisConfiguration.isPasswordAware(configuration)) {
|
||||
if (RedisConfiguration.isAuthenticationAware(configuration)) {
|
||||
|
||||
((WithPassword) configuration).setPassword(password);
|
||||
return;
|
||||
|
||||
@@ -788,6 +788,9 @@ public class LettuceConnectionFactory
|
||||
this.getMutableConfiguration().setClientName(clientName);
|
||||
}
|
||||
|
||||
private Optional<String> getRedisUsername() {
|
||||
return RedisConfiguration.getUsernameOrElse(configuration, standaloneConfig::getUsername);
|
||||
}
|
||||
/**
|
||||
* Returns the password used for authenticating with the Redis server.
|
||||
*
|
||||
@@ -812,7 +815,7 @@ public class LettuceConnectionFactory
|
||||
@Deprecated
|
||||
public void setPassword(String password) {
|
||||
|
||||
if (RedisConfiguration.isPasswordAware(configuration)) {
|
||||
if (RedisConfiguration.isAuthenticationAware(configuration)) {
|
||||
|
||||
((WithPassword) configuration).setPassword(password);
|
||||
return;
|
||||
@@ -1135,7 +1138,8 @@ public class LettuceConnectionFactory
|
||||
|
||||
RedisURI.Builder builder = RedisURI.Builder.redis(host, port);
|
||||
|
||||
getRedisPassword().toOptional().ifPresent(builder::withPassword);
|
||||
applyAuthentication(builder);
|
||||
|
||||
clientConfiguration.getClientName().ifPresent(builder::withClientName);
|
||||
|
||||
builder.withDatabase(getDatabase());
|
||||
@@ -1151,13 +1155,25 @@ public class LettuceConnectionFactory
|
||||
|
||||
RedisURI.Builder builder = RedisURI.Builder.socket(socketPath);
|
||||
|
||||
getRedisPassword().toOptional().ifPresent(builder::withPassword);
|
||||
applyAuthentication(builder);
|
||||
builder.withDatabase(getDatabase());
|
||||
builder.withTimeout(clientConfiguration.getCommandTimeout());
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private void applyAuthentication(RedisURI.Builder builder) {
|
||||
|
||||
Optional<String> username = getRedisUsername();
|
||||
if (username.isPresent()) {
|
||||
// See https://github.com/lettuce-io/lettuce-core/issues/1404
|
||||
username.ifPresent(
|
||||
it -> builder.withAuthentication(it, new String(getRedisPassword().toOptional().orElse(new char[0]))));
|
||||
} else {
|
||||
getRedisPassword().toOptional().ifPresent(builder::withPassword);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedisSentinelConnection getSentinelConnection() {
|
||||
return new LettuceSentinelConnection(connectionProvider);
|
||||
|
||||
@@ -18,16 +18,11 @@ package org.springframework.data.redis.connection.lettuce;
|
||||
import io.lettuce.core.*;
|
||||
import io.lettuce.core.cluster.models.partitions.Partitions;
|
||||
import io.lettuce.core.cluster.models.partitions.RedisClusterNode.NodeFlag;
|
||||
import io.lettuce.core.models.stream.PendingMessage;
|
||||
import io.lettuce.core.models.stream.PendingMessages;
|
||||
import io.lettuce.core.models.stream.PendingParser;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
@@ -69,9 +64,6 @@ import org.springframework.data.redis.connection.convert.Converters;
|
||||
import org.springframework.data.redis.connection.convert.ListConverter;
|
||||
import org.springframework.data.redis.connection.convert.LongToBooleanConverter;
|
||||
import org.springframework.data.redis.connection.convert.StringToRedisClientInfoConverter;
|
||||
import org.springframework.data.redis.connection.stream.Consumer;
|
||||
import org.springframework.data.redis.connection.stream.PendingMessagesSummary;
|
||||
import org.springframework.data.redis.connection.stream.RecordId;
|
||||
import org.springframework.data.redis.core.ScanOptions;
|
||||
import org.springframework.data.redis.core.types.Expiration;
|
||||
import org.springframework.data.redis.core.types.RedisClientInfo;
|
||||
@@ -79,7 +71,6 @@ import org.springframework.data.redis.util.ByteUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.NumberUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -657,15 +648,19 @@ abstract public class LettuceConverters extends Converters {
|
||||
|
||||
RedisURI.Builder sentinelBuilder = RedisURI.Builder.redis(sentinel.getHost(), sentinel.getPort());
|
||||
|
||||
if (sentinelPassword.isPresent()) {
|
||||
sentinelBuilder.withPassword(sentinelPassword.get());
|
||||
}
|
||||
sentinelPassword.toOptional().ifPresent(sentinelBuilder::withPassword);
|
||||
|
||||
builder.withSentinel(sentinelBuilder.build());
|
||||
}
|
||||
|
||||
Optional<String> username = sentinelConfiguration.getUsername();
|
||||
RedisPassword password = sentinelConfiguration.getPassword();
|
||||
if (password.isPresent()) {
|
||||
builder.withPassword(password.get());
|
||||
|
||||
if (username.isPresent()) {
|
||||
// See https://github.com/lettuce-io/lettuce-core/issues/1404
|
||||
builder.withAuthentication(username.get(), new String(password.toOptional().orElse(new char[0])));
|
||||
} else {
|
||||
password.toOptional().ifPresent(builder::withPassword);
|
||||
}
|
||||
|
||||
builder.withSentinelMasterId(sentinelConfiguration.getMaster().getName());
|
||||
|
||||
@@ -41,6 +41,7 @@ public class RedisTestProfileValueSource implements ProfileValueSource {
|
||||
private static final String REDIS_30 = "3.0";
|
||||
private static final String REDIS_32 = "3.2";
|
||||
private static final String REDIS_50 = "5.0";
|
||||
private static final String REDIS_60 = "6.0";
|
||||
private static final String REDIS_VERSION_KEY = "redisVersion";
|
||||
|
||||
private static RedisTestProfileValueSource INSTANCE;
|
||||
@@ -97,6 +98,10 @@ public class RedisTestProfileValueSource implements ProfileValueSource {
|
||||
return System.getProperty(key);
|
||||
}
|
||||
|
||||
if (redisVersion.compareTo(RedisVersionUtils.parseVersion(REDIS_60)) >= 0) {
|
||||
return REDIS_60;
|
||||
}
|
||||
|
||||
if (redisVersion.compareTo(RedisVersionUtils.parseVersion(REDIS_50)) >= 0) {
|
||||
return REDIS_50;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 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.data.redis.connection.jedis;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.junit.Assume.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.data.redis.RedisTestProfileValueSource;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
|
||||
|
||||
/**
|
||||
* Integration tests for Redis 6 ACL.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class JedisAclIntegrationTests {
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
assumeTrue(RedisTestProfileValueSource.atLeast("redisVersion", "6.0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldConnectWithDefaultAuthentication() {
|
||||
|
||||
RedisStandaloneConfiguration standaloneConfiguration = new RedisStandaloneConfiguration("localhost", 6382);
|
||||
standaloneConfiguration.setPassword("foobared");
|
||||
|
||||
JedisConnectionFactory connectionFactory = new JedisConnectionFactory(standaloneConfiguration);
|
||||
connectionFactory.afterPropertiesSet();
|
||||
|
||||
RedisConnection connection = connectionFactory.getConnection();
|
||||
|
||||
assertThat(connection.ping()).isEqualTo("PONG");
|
||||
connection.close();
|
||||
|
||||
connectionFactory.destroy();
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-1046
|
||||
public void shouldConnectStandaloneWithAclAuthentication() {
|
||||
|
||||
RedisStandaloneConfiguration standaloneConfiguration = new RedisStandaloneConfiguration("localhost", 6382);
|
||||
standaloneConfiguration.setUsername("spring");
|
||||
standaloneConfiguration.setPassword("data");
|
||||
|
||||
JedisConnectionFactory connectionFactory = new JedisConnectionFactory(standaloneConfiguration);
|
||||
connectionFactory.afterPropertiesSet();
|
||||
|
||||
RedisConnection connection = connectionFactory.getConnection();
|
||||
|
||||
assertThat(connection.ping()).isEqualTo("PONG");
|
||||
connection.close();
|
||||
|
||||
connectionFactory.destroy();
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-1046
|
||||
public void shouldConnectStandaloneWithAclAuthenticationAndPooling() {
|
||||
|
||||
RedisStandaloneConfiguration standaloneConfiguration = new RedisStandaloneConfiguration("localhost", 6382);
|
||||
standaloneConfiguration.setUsername("spring");
|
||||
standaloneConfiguration.setPassword("data");
|
||||
|
||||
JedisConnectionFactory connectionFactory = new JedisConnectionFactory(standaloneConfiguration);
|
||||
connectionFactory.setUsePool(true);
|
||||
connectionFactory.afterPropertiesSet();
|
||||
|
||||
RedisConnection connection = connectionFactory.getConnection();
|
||||
|
||||
assertThat(connection.ping()).isEqualTo("PONG");
|
||||
connection.close();
|
||||
|
||||
connectionFactory.destroy();
|
||||
}
|
||||
}
|
||||
@@ -22,24 +22,22 @@ import io.lettuce.core.pubsub.StatefulRedisPubSubConnection;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Integration test of {@link AuthenticatingRedisClient}. Enable requirepass and comment out the @Ignore to run.
|
||||
* Integration test of {@link AuthenticatingRedisClient}.
|
||||
*
|
||||
* @author Jennifer Hickey
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@Ignore("Redis must have requirepass set to run this test")
|
||||
public class AuthenticatingRedisClientTests {
|
||||
|
||||
private RedisClient client;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
client = new AuthenticatingRedisClient("localhost", "foo");
|
||||
client = new AuthenticatingRedisClient("localhost", 6382, "foobared");
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -63,7 +61,7 @@ public class AuthenticatingRedisClientTests {
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
RedisClient badClient = new AuthenticatingRedisClient("localhost", "notthepassword");
|
||||
RedisClient badClient = new AuthenticatingRedisClient("localhost", 6382, "notthepassword");
|
||||
badClient.connect();
|
||||
}
|
||||
|
||||
|
||||
@@ -171,16 +171,6 @@ public class DefaultLettucePoolTests {
|
||||
pool.getResource();
|
||||
}
|
||||
|
||||
@Test(expected = PoolException.class)
|
||||
public void testCreateWithPasswordNoPassword() {
|
||||
|
||||
pool = new DefaultLettucePool(SettingsUtils.getHost(), SettingsUtils.getPort());
|
||||
pool.setClientResources(LettuceTestClientResources.getSharedClientResources());
|
||||
pool.setPassword("notthepassword");
|
||||
pool.afterPropertiesSet();
|
||||
pool.getResource();
|
||||
}
|
||||
|
||||
@Ignore("Redis must have requirepass set to run this test")
|
||||
@Test
|
||||
public void testCreatePassword() {
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 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.data.redis.connection.lettuce;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.junit.Assume.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.data.redis.RedisTestProfileValueSource;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
|
||||
import org.springframework.data.redis.connection.RedisStaticMasterReplicaConfiguration;
|
||||
|
||||
/**
|
||||
* Integration tests for Redis 6 ACL.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class LettuceAclIntegrationTests {
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
assumeTrue(RedisTestProfileValueSource.atLeast("redisVersion", "6.0"));
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-1046
|
||||
public void shouldConnectWithDefaultAuthentication() {
|
||||
|
||||
RedisStandaloneConfiguration standaloneConfiguration = new RedisStandaloneConfiguration("localhost", 6382);
|
||||
standaloneConfiguration.setPassword("foobared");
|
||||
|
||||
LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(standaloneConfiguration);
|
||||
connectionFactory.setClientResources(LettuceTestClientResources.getSharedClientResources());
|
||||
connectionFactory.afterPropertiesSet();
|
||||
|
||||
RedisConnection connection = connectionFactory.getConnection();
|
||||
|
||||
assertThat(connection.ping()).isEqualTo("PONG");
|
||||
connection.close();
|
||||
|
||||
connectionFactory.destroy();
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-1046
|
||||
public void shouldConnectStandaloneWithAclAuthentication() {
|
||||
|
||||
RedisStandaloneConfiguration standaloneConfiguration = new RedisStandaloneConfiguration("localhost", 6382);
|
||||
standaloneConfiguration.setUsername("spring");
|
||||
standaloneConfiguration.setPassword("data");
|
||||
|
||||
LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(standaloneConfiguration);
|
||||
connectionFactory.setClientResources(LettuceTestClientResources.getSharedClientResources());
|
||||
connectionFactory.afterPropertiesSet();
|
||||
|
||||
RedisConnection connection = connectionFactory.getConnection();
|
||||
|
||||
assertThat(connection.ping()).isEqualTo("PONG");
|
||||
connection.close();
|
||||
|
||||
connectionFactory.destroy();
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-1046
|
||||
@Ignore("https://github.com/lettuce-io/lettuce-core/issues/1406")
|
||||
public void shouldConnectMasterReplicaWithAclAuthentication() {
|
||||
|
||||
RedisStaticMasterReplicaConfiguration masterReplicaConfiguration = new RedisStaticMasterReplicaConfiguration(
|
||||
"localhost", 6382);
|
||||
masterReplicaConfiguration.setUsername("spring");
|
||||
masterReplicaConfiguration.setPassword("data");
|
||||
|
||||
LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(masterReplicaConfiguration);
|
||||
connectionFactory.setClientResources(LettuceTestClientResources.getSharedClientResources());
|
||||
connectionFactory.afterPropertiesSet();
|
||||
|
||||
RedisConnection connection = connectionFactory.getConnection();
|
||||
|
||||
assertThat(connection.ping()).isEqualTo("PONG");
|
||||
connection.close();
|
||||
|
||||
connectionFactory.destroy();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user