Skip to content

Commit 2c0bfec

Browse files
committed
- add ManagedentityInfo
- add ServicePrincipalInfo - unwrap ExecutionException - auth with managedId s - remove EntraIDTokenAuthConfig
1 parent 49418e6 commit 2c0bfec

File tree

10 files changed

+362
-201
lines changed

10 files changed

+362
-201
lines changed

core/src/main/java/redis/clients/authentication/core/TokenManager.java

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.util.concurrent.ScheduledFuture;
99
import java.util.concurrent.TimeUnit;
1010
import java.util.concurrent.TimeoutException;
11+
import java.util.concurrent.atomic.AtomicBoolean;
1112

1213
import org.slf4j.Logger;
1314
import org.slf4j.LoggerFactory;
@@ -17,28 +18,38 @@ public class TokenManager {
1718
private TokenManagerConfig tokenManagerConfig;
1819
private IdentityProvider identityProvider;
1920
private TokenListener listener;
20-
private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
21+
private ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
2122
private ExecutorService executor = Executors.newSingleThreadExecutor();
2223
private boolean stopped = false;
2324
private ScheduledFuture<?> scheduledTask;
2425
private int numberOfRetries = 0;
2526
private Exception lastException;
2627
private Logger logger = LoggerFactory.getLogger(getClass());
28+
private Token currentToken = null;
29+
private AtomicBoolean started = new AtomicBoolean(false);
2730

2831
public TokenManager(IdentityProvider identityProvider, TokenManagerConfig tokenManagerConfig) {
2932
this.identityProvider = identityProvider;
3033
this.tokenManagerConfig = tokenManagerConfig;
3134
}
3235

33-
public void start(TokenListener listener, boolean blockForInitialToken)
34-
throws InterruptedException, ExecutionException, TimeoutException {
36+
public void start(TokenListener listener, boolean blockForInitialToken) {
3537

38+
if (!started.compareAndSet(false, true)) {
39+
throw new AuthXException("Token manager already started!");
40+
}
3641
this.listener = listener;
3742
ScheduledFuture<?> currentTask = scheduleNext(0);
3843
scheduledTask = currentTask;
3944
if (blockForInitialToken) {
40-
while (currentTask.get() == null) {
41-
currentTask = scheduledTask;
45+
try {
46+
while (currentTask.get() == null) {
47+
currentTask = scheduledTask;
48+
}
49+
} catch (RuntimeException e) {
50+
throw e;
51+
} catch (Exception e) {
52+
throw new TokenRequestException(unwrap(e), lastException);
4253
}
4354
}
4455
}
@@ -66,7 +77,8 @@ protected Token renewToken() {
6677
try {
6778
Future<Token> requestResult = executor.submit(() -> requestToken());
6879
newToken = requestResult.get(tokenManagerConfig.getTokenRequestExecTimeoutInMs(),
69-
TimeUnit.MILLISECONDS);
80+
TimeUnit.MILLISECONDS);
81+
currentToken = newToken;
7082
long delay = calculateRenewalDelay(newToken.getExpiresAt(), newToken.getReceivedAt());
7183
scheduledTask = scheduleNext(delay);
7284
listener.onTokenRenewed(newToken);
@@ -76,7 +88,7 @@ protected Token renewToken() {
7688
numberOfRetries++;
7789
scheduledTask = scheduleNext(tokenManagerConfig.getRetryPolicy().getdelayInMs());
7890
} else {
79-
TokenRequestException tre = new TokenRequestException(e, lastException);
91+
TokenRequestException tre = new TokenRequestException(unwrap(e), lastException);
8092
listener.onError(tre);
8193
throw tre;
8294
}
@@ -95,6 +107,14 @@ protected Token requestToken() {
95107
}
96108
}
97109

110+
private Throwable unwrap(Exception e) {
111+
return (e instanceof ExecutionException) ? e.getCause() : e;
112+
}
113+
114+
public Token getCurrentToken() {
115+
return currentToken;
116+
}
117+
98118
public long calculateRenewalDelay(long expireDate, long issueDate) {
99119
long ttlLowerRefresh = ttlForLowerRefresh(expireDate);
100120
long ttlRatioRefresh = ttlForRatioRefresh(expireDate, issueDate);

core/src/test/java/redis/clients/authentication/CoreAuthenticationUnitTests.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
import redis.clients.authentication.core.TokenListener;
2929
import redis.clients.authentication.core.TokenManager;
3030
import redis.clients.authentication.core.TokenManagerConfig;
31+
import redis.clients.authentication.core.TokenRequestException;
32+
3133
import static org.awaitility.Awaitility.await;
3234
import java.util.concurrent.TimeUnit;
3335

@@ -157,10 +159,10 @@ public void testBlockForInitialToken() {
157159
TokenManager tokenManager = new TokenManager(identityProvider,
158160
new TokenManagerConfig(0.7F, 200, 2000, new TokenManagerConfig.RetryPolicy(5, 100)));
159161

160-
ExecutionException e = assertThrows(ExecutionException.class,
162+
TokenRequestException e = assertThrows(TokenRequestException.class,
161163
() -> tokenManager.start(mock(TokenListener.class), true));
162164

163-
assertEquals("java.lang.RuntimeException: Test exception from identity provider!",
165+
assertEquals("Test exception from identity provider!",
164166
e.getCause().getCause().getMessage());
165167
}
166168

Lines changed: 53 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,87 @@
11
package redis.clients.authentication.entraid;
22

33
import java.net.MalformedURLException;
4-
import java.security.PrivateKey;
5-
import java.security.cert.X509Certificate;
64
import java.util.Set;
75
import java.util.concurrent.ExecutionException;
86
import java.util.concurrent.Future;
7+
import java.util.function.Supplier;
98

109
import com.microsoft.aad.msal4j.ClientCredentialFactory;
1110
import com.microsoft.aad.msal4j.ClientCredentialParameters;
1211
import com.microsoft.aad.msal4j.ConfidentialClientApplication;
1312
import com.microsoft.aad.msal4j.IAuthenticationResult;
1413
import com.microsoft.aad.msal4j.IClientCredential;
14+
import com.microsoft.aad.msal4j.ManagedIdentityApplication;
15+
import com.microsoft.aad.msal4j.ManagedIdentityParameters;
1516

1617
import redis.clients.authentication.core.IdentityProvider;
1718
import redis.clients.authentication.core.Token;
1819

1920
public final class EntraIDIdentityProvider implements IdentityProvider {
2021

21-
private ConfidentialClientApplication app;
22-
private ClientCredentialParameters clientParams;
22+
private Supplier<IAuthenticationResult> resultSupplier;
2323

24-
public EntraIDIdentityProvider(String clientId, String authority, String secret,
25-
Set<String> scopes) {
26-
IClientCredential credential = ClientCredentialFactory.createFromSecret(secret);
27-
init(clientId, authority, credential, scopes);
28-
}
29-
30-
public EntraIDIdentityProvider(String clientId, String authority, PrivateKey key, X509Certificate cert,
31-
Set<String> scopes) {
32-
IClientCredential credential = ClientCredentialFactory.createFromCertificate(key, cert);
33-
init(clientId, authority, credential, scopes);
34-
}
24+
public EntraIDIdentityProvider(ServicePrincipalInfo servicePrincipalInfo, Set<String> scopes) {
25+
IClientCredential credential = getClientCredential(servicePrincipalInfo);
26+
ConfidentialClientApplication app;
3527

36-
protected void init(String clientId, String authority, IClientCredential credential, Set<String> scopes) {
3728
try {
38-
app = ConfidentialClientApplication.builder(clientId, credential).authority(authority).build();
29+
String authority = servicePrincipalInfo.getAuthority();
30+
authority = authority == null ? ConfidentialClientApplication.DEFAULT_AUTHORITY
31+
: authority;
32+
app = ConfidentialClientApplication
33+
.builder(servicePrincipalInfo.getClientId(), credential).authority(authority)
34+
.build();
3935
} catch (MalformedURLException e) {
4036
throw new RedisEntraIDException("Failed to init EntraID client!", e);
4137
}
42-
clientParams = ClientCredentialParameters.builder(scopes).build();
38+
ClientCredentialParameters params = ClientCredentialParameters.builder(scopes).build();
39+
40+
resultSupplier = () -> supplierForConfidentialApp(app, params);
41+
}
42+
43+
public EntraIDIdentityProvider(ManagedIdentityInfo info, Set<String> scopes) {
44+
ManagedIdentityApplication app = ManagedIdentityApplication.builder(info.getId()).build();
45+
46+
ManagedIdentityParameters params = ManagedIdentityParameters
47+
.builder(scopes.iterator().next()).build();
48+
resultSupplier = () -> supplierForManagedIdentityApp(app, params);
49+
}
50+
51+
private IClientCredential getClientCredential(ServicePrincipalInfo servicePrincipalInfo) {
52+
switch (servicePrincipalInfo.getAccessWith()) {
53+
case WithSecret:
54+
return ClientCredentialFactory.createFromSecret(servicePrincipalInfo.getSecret());
55+
case WithCert:
56+
return ClientCredentialFactory.createFromCertificate(servicePrincipalInfo.getKey(),
57+
servicePrincipalInfo.getCert());
58+
default:
59+
throw new RedisEntraIDException("Invalid ServicePrincipalAccess type!");
60+
}
4361
}
4462

4563
@Override
4664
public Token requestToken() {
65+
return new JWToken(resultSupplier.get().accessToken());
66+
}
67+
68+
public IAuthenticationResult supplierForConfidentialApp(ConfidentialClientApplication app,
69+
ClientCredentialParameters params) {
4770
try {
48-
Future<IAuthenticationResult> tokenRequest = app.acquireToken(clientParams);
49-
return new JWToken(tokenRequest.get().accessToken());
71+
Future<IAuthenticationResult> tokenRequest = app.acquireToken(params);
72+
return tokenRequest.get();
5073
} catch (InterruptedException | ExecutionException e) {
5174
throw new RedisEntraIDException("Failed to acquire token!", e);
5275
}
5376
}
77+
78+
public IAuthenticationResult supplierForManagedIdentityApp(ManagedIdentityApplication app,
79+
ManagedIdentityParameters params) {
80+
try {
81+
Future<IAuthenticationResult> tokenRequest = app.acquireTokenForManagedIdentity(params);
82+
return tokenRequest.get();
83+
} catch (Exception e) {
84+
throw new RedisEntraIDException("Failed to acquire token!", e);
85+
}
86+
}
5487
}
Lines changed: 12 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,31 @@
11
package redis.clients.authentication.entraid;
22

3-
import java.security.PrivateKey;
4-
import java.security.cert.X509Certificate;
53
import java.util.Set;
64

75
import redis.clients.authentication.core.IdentityProvider;
86
import redis.clients.authentication.core.IdentityProviderConfig;
97

108
public final class EntraIDIdentityProviderConfig implements IdentityProviderConfig, AutoCloseable {
119

12-
public enum EntraIDAccess {
13-
WithSecret,
14-
WithCert,
15-
}
16-
17-
private String clientId;
18-
private EntraIDAccess accessWith;
19-
private String secret;
20-
private PrivateKey key;
21-
private X509Certificate cert;
22-
private String authority;
10+
private ServicePrincipalInfo servicePrincipalInfo;
2311
private Set<String> scopes;
12+
private ManagedIdentityInfo managedIdentityInfo;
2413

25-
public EntraIDIdentityProviderConfig(String clientId,
26-
EntraIDAccess accessWith,
27-
String secret,
28-
PrivateKey key,
29-
X509Certificate cert,
30-
String authority, Set<String> scopes) {
31-
this.clientId = clientId;
32-
this.accessWith = accessWith;
33-
this.secret = secret;
34-
this.key = key;
35-
this.cert = cert;
36-
this.authority = authority;
14+
public EntraIDIdentityProviderConfig(ServicePrincipalInfo servicePrincipalInfo,
15+
ManagedIdentityInfo info, Set<String> scopes) {
16+
this.servicePrincipalInfo = servicePrincipalInfo;
3717
this.scopes = scopes;
18+
this.managedIdentityInfo = info;
3819
}
3920

4021
@Override
4122
public IdentityProvider getProvider() {
4223
IdentityProvider identityProvider = null;
43-
switch (accessWith) {
44-
case WithSecret:
45-
identityProvider = new EntraIDIdentityProvider(clientId, authority,
46-
secret, scopes);
47-
break;
48-
case WithCert:
49-
identityProvider = new EntraIDIdentityProvider(clientId, authority,
50-
key, cert, scopes);
51-
break;
52-
default:
53-
throw new RedisEntraIDException("Access type and credentials must be set!");
24+
if (managedIdentityInfo != null) {
25+
identityProvider = new EntraIDIdentityProvider(managedIdentityInfo, scopes);
26+
} else {
27+
identityProvider = new EntraIDIdentityProvider(servicePrincipalInfo, scopes);
5428
}
55-
5629
clear();
5730
return identityProvider;
5831
}
@@ -63,11 +36,8 @@ public void close() throws Exception {
6336
}
6437

6538
private void clear() {
66-
clientId = null;
67-
secret = null;
68-
key = null;
69-
cert = null;
70-
authority = null;
39+
servicePrincipalInfo = null;
40+
managedIdentityInfo = null;
7141
scopes = null;
7242
}
7343
}

entraid/src/main/java/redis/clients/authentication/entraid/EntraIDTokenAuthConfig.java

Lines changed: 0 additions & 85 deletions
This file was deleted.

0 commit comments

Comments
 (0)