diff --git a/src/main/java/io/lettuce/core/RenewableRedisCredentialsProvider.java b/src/main/java/io/lettuce/core/RenewableRedisCredentialsProvider.java index 30c31164d..61340ea9c 100644 --- a/src/main/java/io/lettuce/core/RenewableRedisCredentialsProvider.java +++ b/src/main/java/io/lettuce/core/RenewableRedisCredentialsProvider.java @@ -2,7 +2,8 @@ import reactor.core.publisher.Flux; -public interface RenewableRedisCredentialsProvider extends RedisCredentialsProvider{ +public interface RenewableRedisCredentialsProvider extends RedisCredentialsProvider { + + Flux credentialsStream(); - Flux credentialsStream(); } diff --git a/src/main/java/io/lettuce/core/StatefulRedisConnectionImpl.java b/src/main/java/io/lettuce/core/StatefulRedisConnectionImpl.java index a87a31edf..a83673eb8 100644 --- a/src/main/java/io/lettuce/core/StatefulRedisConnectionImpl.java +++ b/src/main/java/io/lettuce/core/StatefulRedisConnectionImpl.java @@ -122,7 +122,6 @@ protected RedisAsyncCommandsImpl newRedisAsyncCommandsImpl() { return new RedisAsyncCommandsImpl<>(this, codec, parser); } - @Override public RedisReactiveCommands reactive() { return reactive; @@ -311,16 +310,17 @@ public ConnectionState getConnectionState() { public void activated() { super.activated(); RedisCredentialsProvider credentialsProvider = state.getCredentialsProvider(); - if (credentialsProvider != null && authHandler != null ) { + if (credentialsProvider != null && authHandler != null) { authHandler.subscribe(credentialsProvider); } } @Override public void deactivated() { - if (authHandler != null ) { + if (authHandler != null) { authHandler.unsubscribe(); } super.deactivated(); } + } diff --git a/src/main/java/io/lettuce/core/StaticRedisCredentials.java b/src/main/java/io/lettuce/core/StaticRedisCredentials.java index 20841f171..42d5cc4c2 100644 --- a/src/main/java/io/lettuce/core/StaticRedisCredentials.java +++ b/src/main/java/io/lettuce/core/StaticRedisCredentials.java @@ -8,13 +8,13 @@ * @author Jon Iantosca * @author Mark Paluch */ -public class StaticRedisCredentials implements RedisCredentials { +class StaticRedisCredentials implements RedisCredentials { private final String username; private final char[] password; - public StaticRedisCredentials(String username, char[] password) { + StaticRedisCredentials(String username, char[] password) { this.username = username; this.password = password != null ? Arrays.copyOf(password, password.length) : null; } diff --git a/src/main/java/io/lettuce/core/TokenBasedRedisCredentialsProvider.java b/src/main/java/io/lettuce/core/TokenBasedRedisCredentialsProvider.java index 26b9c27ee..875200e74 100644 --- a/src/main/java/io/lettuce/core/TokenBasedRedisCredentialsProvider.java +++ b/src/main/java/io/lettuce/core/TokenBasedRedisCredentialsProvider.java @@ -1,6 +1,5 @@ package io.lettuce.core; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import reactor.core.publisher.Flux; @@ -12,17 +11,14 @@ import redis.clients.authentication.core.TokenListener; import redis.clients.authentication.core.TokenManager; - -public class TokenBasedRedisCredentialsProvider implements RenewableRedisCredentialsProvider -{ - +public class TokenBasedRedisCredentialsProvider implements RenewableRedisCredentialsProvider { private final TokenManager tokenManager; + private final Sinks.Many credentialsSink = Sinks.many().replay().latest(); public TokenBasedRedisCredentialsProvider(TokenAuthConfig tokenAuthConfig) { - this(new TokenManager( - tokenAuthConfig.getIdentityProviderConfig().getProvider(), + this(new TokenManager(tokenAuthConfig.getIdentityProviderConfig().getProvider(), tokenAuthConfig.getTokenManagerConfig())); } @@ -35,6 +31,7 @@ public TokenBasedRedisCredentialsProvider(TokenManager tokenManager) { */ public void start() { TokenListener listener = new TokenListener() { + @Override public void onTokenRenewed(Token token) { String username = token.tryGet("oid"); @@ -47,6 +44,7 @@ public void onTokenRenewed(Token token) { public void onError(Exception exception) { credentialsSink.tryEmitError(exception); } + }; try { @@ -80,4 +78,5 @@ public void stop() { credentialsSink.tryEmitComplete(); tokenManager.stop(); } -} \ No newline at end of file + +} diff --git a/src/test/java/io/lettuce/core/RedisURIBuilderUnitTests.java b/src/test/java/io/lettuce/core/RedisURIBuilderUnitTests.java index 456be0233..3fd340683 100644 --- a/src/test/java/io/lettuce/core/RedisURIBuilderUnitTests.java +++ b/src/test/java/io/lettuce/core/RedisURIBuilderUnitTests.java @@ -346,7 +346,6 @@ void shouldApplyAuthentication() { assertThat(sourceCp.getCredentialsProvider()).isEqualTo(targetCp.getCredentialsProvider()); } - @Test void shouldInitializeBuilder() { diff --git a/src/test/java/io/lettuce/core/TestTokenManager.java b/src/test/java/io/lettuce/core/TestTokenManager.java index 4e4059ed6..0f8ef2754 100644 --- a/src/test/java/io/lettuce/core/TestTokenManager.java +++ b/src/test/java/io/lettuce/core/TestTokenManager.java @@ -42,4 +42,5 @@ public void emitTokenWithDelay(SimpleToken token, long delayMillis) { } }).start(); } + } diff --git a/src/test/java/io/lettuce/core/TokenBasedRedisCredentialsProviderTest.java b/src/test/java/io/lettuce/core/TokenBasedRedisCredentialsProviderTest.java index 562917b6d..804e2ddb1 100644 --- a/src/test/java/io/lettuce/core/TokenBasedRedisCredentialsProviderTest.java +++ b/src/test/java/io/lettuce/core/TokenBasedRedisCredentialsProviderTest.java @@ -13,15 +13,15 @@ import static org.assertj.core.api.Assertions.assertThat; - public class TokenBasedRedisCredentialsProviderTest { private TestTokenManager tokenManager; + private TokenBasedRedisCredentialsProvider credentialsProvider; @BeforeEach public void setUp() { - //Use TestToken manager to emit tokens/errors on request + // Use TestToken manager to emit tokens/errors on request tokenManager = new TestTokenManager(null, null); credentialsProvider = new TokenBasedRedisCredentialsProvider(tokenManager); } @@ -32,12 +32,10 @@ public void shouldReturnPreviouslyEmittedTokenWhenResolved() { Mono credentials = credentialsProvider.resolveCredentials(); - StepVerifier.create(credentials) - .assertNext(actual -> { - assertThat(actual.getUsername()).isEqualTo("test-username"); - assertThat(new String(actual.getPassword())).isEqualTo("token-1"); - }) - .verifyComplete(); + StepVerifier.create(credentials).assertNext(actual -> { + assertThat(actual.getUsername()).isEqualTo("test-username"); + assertThat(new String(actual.getPassword())).isEqualTo("token-1"); + }).verifyComplete(); } @Test @@ -47,13 +45,12 @@ public void shouldReturnLatestEmittedTokenWhenResolved() { Mono credentials = credentialsProvider.resolveCredentials(); - StepVerifier.create(credentials) - .assertNext(actual -> { - assertThat(actual.getUsername()).isEqualTo("test-username"); - assertThat(new String(actual.getPassword())).isEqualTo("token-3"); - }) - .verifyComplete(); + StepVerifier.create(credentials).assertNext(actual -> { + assertThat(actual.getUsername()).isEqualTo("test-username"); + assertThat(new String(actual.getPassword())).isEqualTo("token-3"); + }).verifyComplete(); } + @Test public void shouldReturnTokenEmittedBeforeSubscription() { @@ -63,9 +60,8 @@ public void shouldReturnTokenEmittedBeforeSubscription() { Mono credentials1 = credentialsProvider.resolveCredentials(); StepVerifier.create(credentials1) - .expectNextMatches(credentials -> - "token-1".equals(new String(credentials.getPassword())) - && "test-username".equals(credentials.getUsername())) + .expectNextMatches(credentials -> "token-1".equals(new String(credentials.getPassword())) + && "test-username".equals(credentials.getUsername())) .verifyComplete(); // Emit second token and subscribe another @@ -73,9 +69,8 @@ public void shouldReturnTokenEmittedBeforeSubscription() { tokenManager.emitToken(testToken("token-3")); Mono credentials2 = credentialsProvider.resolveCredentials(); StepVerifier.create(credentials2) - .expectNextMatches(credentials -> - "token-3".equals(new String(credentials.getPassword())) - && "test-username".equals(credentials.getUsername())) + .expectNextMatches(credentials -> "token-3".equals(new String(credentials.getPassword())) + && "test-username".equals(credentials.getUsername())) .verifyComplete(); } @@ -85,8 +80,7 @@ public void shouldWaitForAndReturnTokenWhenEmittedLater() { tokenManager.emitTokenWithDelay(testToken("delayed-token"), 100); // Emit token after 100ms StepVerifier.create(result) - .assertNext(credentials -> - assertThat(String.valueOf(credentials.getPassword())).isEqualTo("delayed-token")) + .assertNext(credentials -> assertThat(String.valueOf(credentials.getPassword())).isEqualTo("delayed-token")) .verifyComplete(); } @@ -109,24 +103,20 @@ public void shouldCompleteAllSubscribersOnStop() { credentialsProvider.stop(); }).start(); - StepVerifier.create(credentialsFlux1) - .verifyComplete(); + StepVerifier.create(credentialsFlux1).verifyComplete(); - StepVerifier.create(credentialsFlux2) - .verifyComplete(); + StepVerifier.create(credentialsFlux2).verifyComplete(); } @Test public void shouldPropagateMultipleTokensOnStream() { Flux result = credentialsProvider.credentialsStream(); - StepVerifier.create(result) - .then(() -> tokenManager.emitToken(testToken("token1"))) + StepVerifier.create(result).then(() -> tokenManager.emitToken(testToken("token1"))) .then(() -> tokenManager.emitToken(testToken("token2"))) - .assertNext(credentials -> assertThat(String.valueOf(credentials.getPassword())).isEqualTo("token1")) - .assertNext(credentials -> assertThat(String.valueOf(credentials.getPassword())).isEqualTo("token2")) - .thenCancel() - .verify(Duration.ofMillis(100)); + .assertNext(credentials -> assertThat(String.valueOf(credentials.getPassword())).isEqualTo("token1")) + .assertNext(credentials -> assertThat(String.valueOf(credentials.getPassword())).isEqualTo("token2")) + .thenCancel().verify(Duration.ofMillis(100)); } @Test @@ -136,19 +126,15 @@ public void shouldHandleTokenRequestErrorGracefully() { Flux result = credentialsProvider.credentialsStream(); - StepVerifier.create(result) - .expectErrorMatches(throwable -> throwable instanceof RuntimeException - && "Token request failed".equals(throwable.getMessage())) + StepVerifier.create(result).expectErrorMatches( + throwable -> throwable instanceof RuntimeException && "Token request failed".equals(throwable.getMessage())) .verify(); } - private SimpleToken testToken(String value){ - return new SimpleToken( - value, - System.currentTimeMillis() + 5000, // expires in 5 seconds - System.currentTimeMillis(), - Collections.emptyMap() - ); + private SimpleToken testToken(String value) { + return new SimpleToken(value, System.currentTimeMillis() + 5000, // expires in 5 seconds + System.currentTimeMillis(), Collections.emptyMap()); } -} \ No newline at end of file + +} diff --git a/src/test/java/io/lettuce/examples/TokenBasedAuthExample.java b/src/test/java/io/lettuce/examples/TokenBasedAuthExample.java index aeab34608..cc065af4a 100644 --- a/src/test/java/io/lettuce/examples/TokenBasedAuthExample.java +++ b/src/test/java/io/lettuce/examples/TokenBasedAuthExample.java @@ -5,39 +5,27 @@ import io.lettuce.core.api.async.RedisAsyncCommands; import io.lettuce.core.api.sync.RedisCommands; import io.lettuce.core.codec.StringCodec; -import org.junit.Test; import redis.clients.authentication.core.*; -import redis.clients.authentication.entraid.EntraIDIdentityProvider; import redis.clients.authentication.entraid.EntraIDTokenAuthConfig; -import java.net.MalformedURLException; import java.util.Collections; import java.util.Set; import static org.junit.Assert.assertNotNull; - public class TokenBasedAuthExample { - public static void main(String[] args) { + public static void main(String[] args) { // Configure TokenManager String authority = "https://login.microsoftonline.com/562f7bf2-f594-47bf-8ac3-a06514b5d434"; - String clientId = ""; //application id - String secret = ""; // client secret value + String clientId = ""; // application id + String secret = ""; // client secret value Set scopes = Collections.singleton("https://redis.azure.com/.default"); - IdentityProviderConfig config = EntraIDTokenAuthConfig.builder() - .authority(authority) - .clientId(clientId) - .secret(secret) - .scopes(scopes) - .tokenRequestExecTimeoutInMs(10000) - .build().getIdentityProviderConfig(); + IdentityProviderConfig config = EntraIDTokenAuthConfig.builder().authority(authority).clientId(clientId).secret(secret) + .scopes(scopes).tokenRequestExecTimeoutInMs(10000).build().getIdentityProviderConfig(); - TokenAuthConfig tokenAuthConfig = TokenAuthConfig.builder() - .tokenRequestExecTimeoutInMs(10000) - .expirationRefreshRatio(0.1f) - .identityProviderConfig(config) - .build(); + TokenAuthConfig tokenAuthConfig = TokenAuthConfig.builder().tokenRequestExecTimeoutInMs(10000) + .expirationRefreshRatio(0.1f).identityProviderConfig(config).build(); // Create RedisURI and set custom credentials provider TokenBasedRedisCredentialsProvider credentialsProvider = new TokenBasedRedisCredentialsProvider(tokenAuthConfig); @@ -46,7 +34,6 @@ public static void main(String[] args) { RedisURI redisURI = RedisURI.create("redis://40.115.58.0:19431"); redisURI.setCredentialsProvider(credentialsProvider); - // Create RedisClient RedisClient redisClient = RedisClient.create(); @@ -61,16 +48,14 @@ public static void main(String[] args) { } // another connection using same credentials provider - try (StatefulRedisConnection connection = redisClient.connect(StringCodec.UTF8, redisURI);){ + try (StatefulRedisConnection connection = redisClient.connect(StringCodec.UTF8, redisURI);) { RedisAsyncCommands commands = connection.async(); - commands.set("hello", "Hello, Redis!") - .thenCompose(s -> commands.get("hello")) - .thenAccept(result -> System.out.println("async hello: " + result)) - .toCompletableFuture() - .join(); + commands.set("hello", "Hello, Redis!").thenCompose(s -> commands.get("hello")) + .thenAccept(result -> System.out.println("async hello: " + result)).toCompletableFuture().join(); } credentialsProvider.stop(); redisClient.shutdown(); } -} \ No newline at end of file + +}