Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Token based authentication integration with core extension #4011

Merged
merged 24 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6689d65
tba draft
atakavci Nov 6, 2024
a18e632
- stop authxmanager on pool close
atakavci Nov 7, 2024
392b3b0
drop use of authxmanager and authenticatedconnection from core
atakavci Nov 12, 2024
f037440
-update submodule ref
atakavci Nov 17, 2024
eb63520
- remove submodule
atakavci Nov 18, 2024
cbb7781
Merge branch 'master' into ali/authx2
atakavci Nov 18, 2024
523fe42
back to current version
atakavci Nov 19, 2024
4e71535
- move autxhmanager creation to user space
atakavci Dec 5, 2024
08750e5
- prevent use of pubsub with TBA+RESP2 combination
atakavci Dec 5, 2024
b1ab1db
- support tba with clusters
atakavci Dec 6, 2024
1ec5239
- remove onerror from authxmanager
atakavci Dec 6, 2024
7d3a0ae
- fix flaky test
atakavci Dec 9, 2024
2176505
fix renewalDuringOperationsTest
atakavci Dec 9, 2024
9ea510d
-reviews from @sazzad16
atakavci Dec 10, 2024
e077031
Merge branch 'master' into ali/authx2
atakavci Dec 10, 2024
2175c15
- fix config for managedIdentity
atakavci Dec 11, 2024
86cf6f6
review from @ggivo
atakavci Dec 12, 2024
9717c9a
handle and propogate from unsuccessful AUTH response
atakavci Dec 12, 2024
9185f44
adding reauth support for both pubsub and shardedpubsub
atakavci Dec 14, 2024
5f8159d
fix ping issue with pubsub
atakavci Dec 15, 2024
9da80c4
Merge branch 'master' into ali/authx2
atakavci Dec 17, 2024
edf631a
- review from @sazzad16 : make JedisSafeAuthenticator protected
atakavci Dec 20, 2024
88a20c2
update authx version
atakavci Dec 20, 2024
93f53a2
- remove workaround for standalone endpoint
atakavci Dec 20, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<packaging>jar</packaging>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>5.3.0-SNAPSHOT</version>
<version>5.3.1-SNAPSHOT</version>
sazzad16 marked this conversation as resolved.
Show resolved Hide resolved
<name>Jedis</name>
<description>Jedis is a blazingly small and sane Redis java client.</description>
<url>https://github.com/redis/jedis</url>
Expand Down Expand Up @@ -75,6 +75,12 @@
<version>2.11.0</version>
</dependency>

<dependency>
<groupId>redis.clients.authentication</groupId>
<artifactId>redis-authx-core</artifactId>
<version>0.1.0-SNAPSHOT</version>
atakavci marked this conversation as resolved.
Show resolved Hide resolved
</dependency>

<!-- Optional dependencies -->

<!-- UNIX socket connection support -->
Expand Down
32 changes: 24 additions & 8 deletions src/main/java/redis/clients/jedis/Connection.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import java.util.concurrent.atomic.AtomicReference;

import redis.clients.jedis.Protocol.Command;
import redis.clients.jedis.Protocol.Keyword;
Expand Down Expand Up @@ -44,6 +45,8 @@ public class Connection implements Closeable {
private String strVal;
protected String server;
protected String version;
protected AtomicReference<RedisCredentials> currentCredentials = new AtomicReference<RedisCredentials>(
null);

public Connection() {
this(Protocol.DEFAULT_HOST, Protocol.DEFAULT_PORT);
Expand Down Expand Up @@ -93,8 +96,8 @@ public String toIdentityString() {
SocketAddress remoteAddr = socket.getRemoteSocketAddress();
SocketAddress localAddr = socket.getLocalSocketAddress();
if (remoteAddr != null) {
strVal = String.format("%s{id: 0x%X, L:%s %c R:%s}", className, id,
localAddr, (broken ? '!' : '-'), remoteAddr);
strVal = String.format("%s{id: 0x%X, L:%s %c R:%s}", className, id, localAddr,
(broken ? '!' : '-'), remoteAddr);
} else if (localAddr != null) {
strVal = String.format("%s{id: 0x%X, L:%s}", className, id, localAddr);
} else {
Expand Down Expand Up @@ -438,8 +441,8 @@ private static boolean validateClientInfo(String info) {
for (int i = 0; i < info.length(); i++) {
char c = info.charAt(i);
if (c < '!' || c > '~') {
throw new JedisValidationException("client info cannot contain spaces, "
+ "newlines or special characters.");
throw new JedisValidationException(
"client info cannot contain spaces, " + "newlines or special characters.");
}
}
return true;
Expand Down Expand Up @@ -469,7 +472,8 @@ protected void initializeFromClientConfig(final JedisClientConfig config) {

String clientName = config.getClientName();
if (clientName != null && validateClientInfo(clientName)) {
fireAndForgetMsg.add(new CommandArguments(Command.CLIENT).add(Keyword.SETNAME).add(clientName));
fireAndForgetMsg
.add(new CommandArguments(Command.CLIENT).add(Keyword.SETNAME).add(clientName));
}

ClientSetInfoConfig setInfoConfig = config.getClientSetInfoConfig();
Expand Down Expand Up @@ -525,12 +529,13 @@ private void helloAndAuth(final RedisProtocol protocol, final RedisCredentials c
if (protocol != null && credentials != null && credentials.getUser() != null) {
byte[] rawPass = encodeToBytes(credentials.getPassword());
try {
helloResult = hello(encode(protocol.version()), Keyword.AUTH.getRaw(), encode(credentials.getUser()), rawPass);
helloResult = hello(encode(protocol.version()), Keyword.AUTH.getRaw(),
encode(credentials.getUser()), rawPass);
} finally {
Arrays.fill(rawPass, (byte) 0); // clear sensitive data
}
} else {
auth(credentials);
authenticate(credentials);
helloResult = protocol == null ? null : hello(encode(protocol.version()));
}
if (helloResult != null) {
Expand All @@ -542,7 +547,11 @@ private void helloAndAuth(final RedisProtocol protocol, final RedisCredentials c
// handled in RedisCredentialsProvider.cleanUp()
}

private void auth(RedisCredentials credentials) {
public void setCredentials(RedisCredentials credentials) {
currentCredentials.set(credentials);
}

public void authenticate(RedisCredentials credentials) {
atakavci marked this conversation as resolved.
Show resolved Hide resolved
if (credentials == null || credentials.getPassword() == null) {
return;
}
Expand All @@ -559,6 +568,13 @@ private void auth(RedisCredentials credentials) {
getStatusCodeReply();
}

public void reAuth() {
atakavci marked this conversation as resolved.
Show resolved Hide resolved
RedisCredentials temp = currentCredentials.getAndSet(null);
if (temp != null) {
atakavci marked this conversation as resolved.
Show resolved Hide resolved
authenticate(temp);
}
}

protected Map<String, Object> hello(byte[]... args) {
sendCommand(Command.HELLO, args);
return BuilderFactory.ENCODED_OBJECT_MAP.build(getOne());
Expand Down
66 changes: 53 additions & 13 deletions src/main/java/redis/clients/jedis/ConnectionFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.function.Supplier;

import redis.clients.jedis.annots.Experimental;
import redis.clients.jedis.authentication.JedisAuthXManager;
import redis.clients.jedis.csc.Cache;
import redis.clients.jedis.csc.CacheConnection;
import redis.clients.jedis.exceptions.JedisException;
Expand All @@ -20,28 +25,61 @@ public class ConnectionFactory implements PooledObjectFactory<Connection> {

private final JedisSocketFactory jedisSocketFactory;
private final JedisClientConfig clientConfig;
private Cache clientSideCache = null;
private final Cache clientSideCache;
private final Supplier<Connection> objectMaker;

public ConnectionFactory(final HostAndPort hostAndPort) {
this.clientConfig = DefaultJedisClientConfig.builder().build();
this.jedisSocketFactory = new DefaultJedisSocketFactory(hostAndPort);
this(hostAndPort, DefaultJedisClientConfig.builder().build(), null, null);
}

public ConnectionFactory(final HostAndPort hostAndPort, final JedisClientConfig clientConfig) {
this.clientConfig = clientConfig;
this.jedisSocketFactory = new DefaultJedisSocketFactory(hostAndPort, this.clientConfig);
this(hostAndPort, clientConfig, null, null);
}

@Experimental
public ConnectionFactory(final HostAndPort hostAndPort, final JedisClientConfig clientConfig, Cache csCache) {
this.clientConfig = clientConfig;
this.jedisSocketFactory = new DefaultJedisSocketFactory(hostAndPort, this.clientConfig);
this.clientSideCache = csCache;
public ConnectionFactory(final HostAndPort hostAndPort, final JedisClientConfig clientConfig,
Cache csCache, JedisAuthXManager authXManager) {
this(new DefaultJedisSocketFactory(hostAndPort, clientConfig), clientConfig, csCache,
authXManager);
}

public ConnectionFactory(final JedisSocketFactory jedisSocketFactory,
final JedisClientConfig clientConfig) {
this(jedisSocketFactory, clientConfig, null, null);
}

public ConnectionFactory(final JedisSocketFactory jedisSocketFactory, final JedisClientConfig clientConfig) {
this.clientConfig = clientConfig;
private ConnectionFactory(final JedisSocketFactory jedisSocketFactory,
final JedisClientConfig clientConfig, Cache csCache, JedisAuthXManager authXManager) {

this.jedisSocketFactory = jedisSocketFactory;
this.clientSideCache = csCache;

if (authXManager == null) {
this.clientConfig = clientConfig;
this.objectMaker = connectionSupplier();
} else {
this.clientConfig = replaceCredentialsProvider(clientConfig,
authXManager);
Supplier<Connection> supplier = connectionSupplier();
this.objectMaker = () -> (Connection) authXManager.addConnection(supplier.get());

try {
authXManager.start(true);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
throw new JedisException("AuthXManager failed to start!", e);
}
}
}

private JedisClientConfig replaceCredentialsProvider(JedisClientConfig origin,
Supplier<RedisCredentials> newCredentialsProvider) {
return DefaultJedisClientConfig.builder().from(origin)
.credentialsProvider(newCredentialsProvider).build();
}

private Supplier<Connection> connectionSupplier() {
return clientSideCache == null ? () -> new Connection(jedisSocketFactory, clientConfig)
: () -> new CacheConnection(jedisSocketFactory, clientConfig, clientSideCache);
}

@Override
Expand All @@ -64,8 +102,7 @@ public void destroyObject(PooledObject<Connection> pooledConnection) throws Exce
@Override
public PooledObject<Connection> makeObject() throws Exception {
try {
Connection jedis = clientSideCache == null ? new Connection(jedisSocketFactory, clientConfig)
: new CacheConnection(jedisSocketFactory, clientConfig, clientSideCache);
Connection jedis = objectMaker.get();
return new DefaultPooledObject<>(jedis);
} catch (JedisException je) {
logger.debug("Error while makeObject", je);
Expand All @@ -76,13 +113,16 @@ public PooledObject<Connection> makeObject() throws Exception {
@Override
public void passivateObject(PooledObject<Connection> pooledConnection) throws Exception {
// TODO maybe should select db 0? Not sure right now.
Connection jedis = pooledConnection.getObject();
jedis.reAuth();
}

@Override
public boolean validateObject(PooledObject<Connection> pooledConnection) {
final Connection jedis = pooledConnection.getObject();
try {
// check HostAndPort ??
jedis.reAuth();
return jedis.isConnected() && jedis.ping();
atakavci marked this conversation as resolved.
Show resolved Hide resolved
} catch (final Exception e) {
logger.warn("Error while validating pooled Connection object.", e);
Expand Down
69 changes: 63 additions & 6 deletions src/main/java/redis/clients/jedis/ConnectionPool.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,38 @@

import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

import redis.clients.jedis.annots.Experimental;
import redis.clients.jedis.authentication.JedisAuthXManager;
import redis.clients.jedis.csc.Cache;
import redis.clients.jedis.exceptions.JedisException;
import redis.clients.jedis.util.Pool;

public class ConnectionPool extends Pool<Connection> {

private JedisAuthXManager authXManager;

public ConnectionPool(HostAndPort hostAndPort, JedisClientConfig clientConfig) {
this(new ConnectionFactory(hostAndPort, clientConfig));
this(hostAndPort, clientConfig, createAuthXManager(clientConfig));
}

public ConnectionPool(HostAndPort hostAndPort, JedisClientConfig clientConfig,
JedisAuthXManager authXManager) {
this(new ConnectionFactory(hostAndPort, clientConfig, null, authXManager));
attachAuthenticationListener(authXManager);
}

@Experimental
public ConnectionPool(HostAndPort hostAndPort, JedisClientConfig clientConfig, Cache clientSideCache) {
this(new ConnectionFactory(hostAndPort, clientConfig, clientSideCache));
public ConnectionPool(HostAndPort hostAndPort, JedisClientConfig clientConfig,
Cache clientSideCache) {
this(hostAndPort, clientConfig, clientSideCache, createAuthXManager(clientConfig));
}

@Experimental
public ConnectionPool(HostAndPort hostAndPort, JedisClientConfig clientConfig,
Cache clientSideCache, JedisAuthXManager authXManager) {
this(new ConnectionFactory(hostAndPort, clientConfig, clientSideCache, authXManager));
attachAuthenticationListener(authXManager);
}

public ConnectionPool(PooledObjectFactory<Connection> factory) {
Expand All @@ -23,13 +42,22 @@ public ConnectionPool(PooledObjectFactory<Connection> factory) {

public ConnectionPool(HostAndPort hostAndPort, JedisClientConfig clientConfig,
GenericObjectPoolConfig<Connection> poolConfig) {
this(new ConnectionFactory(hostAndPort, clientConfig), poolConfig);
this(hostAndPort, clientConfig, null, createAuthXManager(clientConfig), poolConfig);
}

@Experimental
public ConnectionPool(HostAndPort hostAndPort, JedisClientConfig clientConfig,
Cache clientSideCache, GenericObjectPoolConfig<Connection> poolConfig) {
this(hostAndPort, clientConfig, clientSideCache, createAuthXManager(clientConfig), poolConfig);
}

@Experimental
public ConnectionPool(HostAndPort hostAndPort, JedisClientConfig clientConfig, Cache clientSideCache,
public ConnectionPool(HostAndPort hostAndPort, JedisClientConfig clientConfig,
Cache clientSideCache, JedisAuthXManager authXManager,
GenericObjectPoolConfig<Connection> poolConfig) {
this(new ConnectionFactory(hostAndPort, clientConfig, clientSideCache), poolConfig);
this(new ConnectionFactory(hostAndPort, clientConfig, clientSideCache, authXManager),
poolConfig);
attachAuthenticationListener(authXManager);
}

public ConnectionPool(PooledObjectFactory<Connection> factory,
Expand All @@ -43,4 +71,33 @@ public Connection getResource() {
conn.setHandlingPool(this);
return conn;
}

@Override
public void close() {
if (authXManager != null) {
authXManager.stop();
}
super.close();
atakavci marked this conversation as resolved.
Show resolved Hide resolved
}

private static JedisAuthXManager createAuthXManager(JedisClientConfig config) {
if (config.getTokenAuthConfig() != null) {
return new JedisAuthXManager(config.getTokenAuthConfig());
}
return null;
}

private void attachAuthenticationListener(JedisAuthXManager authXManager) {
this.authXManager = authXManager;
if (authXManager != null) {
authXManager.setListener(token -> {
try {
// this is to trigger validations on each connection via ConnectionFactory
evict();
} catch (Exception e) {
throw new JedisException("Failed to evict connections from pool", e);
}
});
}
}
}
Loading
Loading