Skip to content

Commit 4751815

Browse files
committed
JCL-279: Implement token cache for session objects
1 parent 3f4fe30 commit 4751815

File tree

7 files changed

+108
-10
lines changed

7 files changed

+108
-10
lines changed

access-grant/src/main/java/com/inrupt/client/accessgrant/AccessGrantSession.java

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,23 @@
2222

2323
import static java.nio.charset.StandardCharsets.UTF_8;
2424

25+
import com.inrupt.client.ClientCache;
2526
import com.inrupt.client.Request;
27+
import com.inrupt.client.auth.Authenticator;
2628
import com.inrupt.client.auth.Credential;
2729
import com.inrupt.client.auth.Session;
30+
import com.inrupt.client.spi.ServiceProvider;
2831

2932
import java.net.URI;
33+
import java.time.Duration;
34+
import java.time.Instant;
3035
import java.util.Arrays;
3136
import java.util.Base64;
3237
import java.util.Collection;
3338
import java.util.List;
3439
import java.util.Map;
3540
import java.util.NavigableMap;
41+
import java.util.Objects;
3642
import java.util.Optional;
3743
import java.util.Set;
3844
import java.util.UUID;
@@ -53,10 +59,13 @@ public final class AccessGrantSession implements Session {
5359
private final String id;
5460
private final Session session;
5561
private final NavigableMap<URI, AccessGrant> grants = new ConcurrentSkipListMap<>();
62+
private final ClientCache<URI, Credential> tokenCache;
5663

57-
private AccessGrantSession(final Session session, final List<AccessGrant> grants) {
64+
private AccessGrantSession(final Session session, final ClientCache<URI, Credential> cache,
65+
final List<AccessGrant> grants) {
5866
this.id = UUID.randomUUID().toString();
5967
this.session = session;
68+
this.tokenCache = Objects.requireNonNull(cache, "Cache may not be null!");
6069

6170
for (final AccessGrant grant : grants) {
6271
for (final URI uri : grant.getResources()) {
@@ -73,7 +82,21 @@ private AccessGrantSession(final Session session, final List<AccessGrant> grants
7382
* @return the Access Grant-based session
7483
*/
7584
public static AccessGrantSession ofAccessGrant(final Session session, final AccessGrant... accessGrants) {
76-
return new AccessGrantSession(session, Arrays.asList(accessGrants));
85+
return ofAccessGrant(session, ServiceProvider.getCacheBuilder().build(1000, Duration.ofMinutes(10)),
86+
accessGrants);
87+
}
88+
89+
/**
90+
* Create a session with a collection of known access grants.
91+
*
92+
* @param session the OpenID Session
93+
* @param cache a pre-configured cache
94+
* @param accessGrants the access grants
95+
* @return the Access Grant-based session
96+
*/
97+
public static AccessGrantSession ofAccessGrant(final Session session, final ClientCache<URI, Credential> cache,
98+
final AccessGrant... accessGrants) {
99+
return new AccessGrantSession(session, cache, Arrays.asList(accessGrants));
77100
}
78101

79102
@Override
@@ -117,6 +140,19 @@ public Optional<String> generateProof(final String jkt, final Request request) {
117140
return session.generateProof(jkt, request);
118141
}
119142

143+
@Override
144+
public CompletionStage<Optional<Credential>> authenticate(final Authenticator authenticator,
145+
final Request request, final Set<String> algorithms) {
146+
return authenticator.authenticate(this, request, algorithms)
147+
.thenApply(credential -> {
148+
if (credential != null) {
149+
tokenCache.put(request.uri(), credential);
150+
}
151+
return Optional.ofNullable(credential);
152+
});
153+
}
154+
155+
/* deprecated */
120156
@Override
121157
public CompletionStage<Optional<Credential>> authenticate(final Request request,
122158
final Set<String> algorithms) {
@@ -129,7 +165,10 @@ public CompletionStage<Optional<Credential>> authenticate(final Request request,
129165

130166
@Override
131167
public Optional<Credential> fromCache(final Request request) {
132-
// TODO add cache
168+
final Credential cachedToken = tokenCache.get(request.uri());
169+
if (cachedToken != null && cachedToken.getExpiration().isAfter(Instant.now())) {
170+
return Optional.of(cachedToken);
171+
}
133172
return Optional.empty();
134173
}
135174

api/src/main/java/com/inrupt/client/auth/ReactiveAuthorization.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public CompletionStage<Optional<Credential>> negotiate(final Session session, fi
9595
authenticators.sort(comparator);
9696
final Authenticator auth = authenticators.get(0);
9797
LOGGER.debug("Using {} authenticator", auth.getName());
98-
return auth.authenticate(session, request, algorithms).thenApply(Optional::ofNullable);
98+
return session.authenticate(auth, request, algorithms);
9999
}
100100
return CompletableFuture.completedFuture(Optional.empty());
101101
}

api/src/main/java/com/inrupt/client/auth/Session.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,22 @@ public interface Session {
9999
* @param request the HTTP request
100100
* @param algorithms the supported DPoP algorithms
101101
* @return the next stage of completion, containing an access token, if present
102+
* @deprecated as of Beta3, this method is no longer used
102103
*/
104+
@Deprecated
103105
CompletionStage<Optional<Credential>> authenticate(Request request, Set<String> algorithms);
104106

107+
/**
108+
* Fetch an authentication token from session values.
109+
*
110+
* @param authenticator the authenticator in use
111+
* @param request the HTTP request
112+
* @param algorithms the supported DPoP algorithms
113+
* @return the next stage of completion, containing an access token, if present
114+
*/
115+
CompletionStage<Optional<Credential>> authenticate(Authenticator authenticator, Request request,
116+
Set<String> algorithms);
117+
105118
/**
106119
* Create a new anonymous session.
107120
*
@@ -146,6 +159,13 @@ public Optional<String> selectThumbprint(final Collection<String> algorithms) {
146159
return Optional.empty();
147160
}
148161

162+
@Override
163+
public CompletionStage<Optional<Credential>> authenticate(final Authenticator authenticator,
164+
final Request request, final Set<String> algorithms) {
165+
return authenticator.authenticate(this, request, algorithms).thenApply(Optional::ofNullable);
166+
}
167+
168+
/* deprecated */
149169
@Override
150170
public CompletionStage<Optional<Credential>> authenticate(final Request request,
151171
final Set<String> algorithms) {

core/src/test/java/com/inrupt/client/core/MockHttpService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ private void setupMocks() {
133133
.withHeader("User-Agent", equalTo(USER_AGENT))
134134
.withRequestBody(matching("Test String 1"))
135135
.withHeader(CONTENT_TYPE, containing(TEXT_PLAIN))
136-
.withHeader("Authorization", containing("Bearer token-67890"))
136+
.withHeader("Authorization", containing("Bearer eyJ"))
137137
.withHeader("DPoP", absent())
138138
.willReturn(aResponse()
139139
.withStatus(201)));

openid/src/main/java/com/inrupt/client/openid/OpenIdSession.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,17 @@
2222

2323
import static java.nio.charset.StandardCharsets.UTF_8;
2424

25+
import com.inrupt.client.ClientCache;
2526
import com.inrupt.client.Request;
27+
import com.inrupt.client.auth.Authenticator;
2628
import com.inrupt.client.auth.Credential;
2729
import com.inrupt.client.auth.DPoP;
2830
import com.inrupt.client.auth.Session;
31+
import com.inrupt.client.spi.ServiceProvider;
2932

3033
import java.net.URI;
3134
import java.security.MessageDigest;
35+
import java.time.Duration;
3236
import java.time.Instant;
3337
import java.util.Collection;
3438
import java.util.Collections;
@@ -75,12 +79,14 @@ public final class OpenIdSession implements Session {
7579
private final AtomicReference<Credential> credential = new AtomicReference<>();
7680
private final ForkJoinPool executor = new ForkJoinPool(1);
7781
private final DPoP dpop;
82+
private final ClientCache<URI, Boolean> requestCache;
7883

7984
private OpenIdSession(final String id, final DPoP dpop,
8085
final Supplier<CompletionStage<Credential>> authenticator) {
8186
this.id = Objects.requireNonNull(id, "Session id may not be null!");
8287
this.authenticator = Objects.requireNonNull(authenticator, "OpenID authenticator may not be null!");
8388
this.dpop = Objects.requireNonNull(dpop);
89+
this.requestCache = ServiceProvider.getCacheBuilder().build(1000, Duration.ofMinutes(5));
8490

8591
// Support case-insensitive lookups
8692
final Set<String> schemeNames = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
@@ -216,16 +222,29 @@ public Optional<String> generateProof(final String jkt, final Request request) {
216222
@Override
217223
public Optional<Credential> fromCache(final Request request) {
218224
final Credential c = credential.get();
219-
if (!hasExpired(c)) {
225+
if (!hasExpired(c) && request != null && requestCache.get(request.uri()) != null) {
226+
LOGGER.debug("Using cached token for request: {}", request.uri());
220227
return Optional.of(c);
221228
}
222229
return Optional.empty();
223230
}
224231

232+
@Override
233+
public CompletionStage<Optional<Credential>> authenticate(final Authenticator auth,
234+
final Request request, final Set<String> algorithms) {
235+
final Optional<Credential> credential = getCredential(ID_TOKEN, null);
236+
if (credential.isPresent() && request != null) {
237+
LOGGER.debug("Setting cache entry for request: {}", request.uri());
238+
requestCache.put(request.uri(), Boolean.TRUE);
239+
}
240+
return CompletableFuture.completedFuture(credential);
241+
}
242+
243+
/* deprecated */
225244
@Override
226245
public CompletionStage<Optional<Credential>> authenticate(final Request request,
227246
final Set<String> algorithms) {
228-
return CompletableFuture.completedFuture(getCredential(ID_TOKEN, null));
247+
return authenticate(null, request, algorithms);
229248
}
230249

231250
boolean hasExpired(final Credential credential) {

openid/src/test/java/com/inrupt/client/openid/OpenIdSessionTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ void testClientCredentials() {
157157
assertFalse(session.fromCache(null).isPresent());
158158
final Optional<URI> principal = session.getPrincipal();
159159
assertEquals(Optional.of(URI.create(WEBID)), principal);
160-
assertTrue(session.fromCache(null).isPresent());
160+
assertFalse(session.fromCache(null).isPresent());
161161
final Optional<Credential> credential = session.authenticate(null, Collections.emptySet())
162162
.toCompletableFuture().join();
163163
assertEquals(Optional.of(URI.create(WEBID)), credential.flatMap(Credential::getPrincipal));
@@ -176,7 +176,7 @@ void testClientCredentialsWithConfig() {
176176
assertFalse(session.fromCache(null).isPresent());
177177
final Optional<URI> principal = session.getPrincipal();
178178
assertEquals(Optional.of(URI.create(WEBID)), principal);
179-
assertTrue(session.fromCache(null).isPresent());
179+
assertFalse(session.fromCache(null).isPresent());
180180
final Optional<Credential> credential = session.authenticate(null, Collections.emptySet())
181181
.toCompletableFuture().join();
182182
assertEquals(Optional.of(URI.create(WEBID)), credential.flatMap(Credential::getPrincipal));

uma/src/main/java/com/inrupt/client/uma/UmaSession.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,16 @@
2020
*/
2121
package com.inrupt.client.uma;
2222

23+
import com.inrupt.client.ClientCache;
2324
import com.inrupt.client.Request;
25+
import com.inrupt.client.auth.Authenticator;
2426
import com.inrupt.client.auth.Credential;
2527
import com.inrupt.client.auth.Session;
28+
import com.inrupt.client.spi.ServiceProvider;
2629

2730
import java.net.URI;
31+
import java.time.Duration;
32+
import java.time.Instant;
2833
import java.util.ArrayList;
2934
import java.util.Collection;
3035
import java.util.Collections;
@@ -47,6 +52,7 @@ public final class UmaSession implements Session {
4752
private final String id;
4853
private final Set<String> schemes;
4954
private final List<Session> internalSessions = new ArrayList<>();
55+
private final ClientCache<URI, Credential> tokenCache;
5056

5157
private UmaSession(final Session... sessions) {
5258
this.id = UUID.randomUUID().toString();
@@ -57,6 +63,7 @@ private UmaSession(final Session... sessions) {
5763
}
5864
schemeTypes.add("UMA");
5965
this.schemes = Collections.unmodifiableSet(schemeTypes);
66+
this.tokenCache = ServiceProvider.getCacheBuilder().build(1000, Duration.ofMinutes(5));
6067
}
6168

6269
/**
@@ -125,7 +132,20 @@ public Optional<String> selectThumbprint(final Collection<String> algorithms) {
125132

126133
@Override
127134
public Optional<Credential> fromCache(final Request request) {
128-
return Optional.empty();
135+
return Optional.ofNullable(tokenCache.get(request.uri()))
136+
.filter(credential -> credential.getExpiration().isAfter(Instant.now()));
137+
}
138+
139+
@Override
140+
public CompletionStage<Optional<Credential>> authenticate(final Authenticator authenticator,
141+
final Request request, final Set<String> algorithms) {
142+
return authenticator.authenticate(this, request, algorithms)
143+
.thenApply(credential -> {
144+
if (credential != null) {
145+
tokenCache.put(request.uri(), credential);
146+
}
147+
return Optional.ofNullable(credential);
148+
});
129149
}
130150

131151
@Override

0 commit comments

Comments
 (0)