Skip to content

Commit

Permalink
use factory method for JwksPublicKeyResolverImpl
Browse files Browse the repository at this point in the history
  • Loading branch information
paullatzelsperger committed Jun 17, 2024
1 parent 8f86c3b commit d17abb5
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public void initialize(ServiceExtensionContext context) {
var tolerance = context.getConfig().getInteger(AUTH_SETTING_VALIDATION_TOLERANCE_MS, DEFAULT_VALIDATION_TOLERANCE);

//todo: currently, only JWKS urls are supported
var resolver = new JwksPublicKeyResolver(keyParserRegistry, keyUrl, cacheValidityMs, monitor);
var resolver = JwksPublicKeyResolver.create(keyParserRegistry, keyUrl, monitor, cacheValidityMs);

tokenValidationRulesRegistry.addRule(MANAGEMENT_API_CONTEXT, new NotBeforeValidationRule(clock, tolerance, true));
tokenValidationRulesRegistry.addRule(MANAGEMENT_API_CONTEXT, new ExpirationIssuedAtValidationRule(clock, tolerance, true));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,34 @@ public class JwksPublicKeyResolver implements PublicKeyResolver {
private final KeyParserRegistry keyParserRegistry;
private final JWKSource<SecurityContext> jwkSource;

private JwksPublicKeyResolver(KeyParserRegistry keyParserRegistry, Monitor monitor, JWKSource<SecurityContext> jwkSource) {
this.keyParserRegistry = keyParserRegistry;
this.monitor = monitor;
this.jwkSource = jwkSource;
}

/**
* Instantiates the resolver
* Creates a new resolver that does use any cache. That means, that every request hits the server.
*
* @param keyParserRegistry Should contain all relevant key parsers. The minimum recommendation is adding a {@code JwkParser}.
* @param jwksUrl The URL of the public key server, where a JWK Set can be obtained.
* @param cacheValidityMs The time in milliseconds that public keys may be cached locally.
* @param monitor A monitor
* @throws EdcException if the jwksUrl is malformed
*/
public JwksPublicKeyResolver(KeyParserRegistry keyParserRegistry, String jwksUrl, long cacheValidityMs, Monitor monitor) {
this.monitor = monitor;
this.keyParserRegistry = keyParserRegistry;
public static JwksPublicKeyResolver create(KeyParserRegistry keyParserRegistry, String jwksUrl, Monitor monitor) {
return create(keyParserRegistry, jwksUrl, monitor, 0);
}

/**
* Creates a new resolver that does use any cache. That means, that every request hits the server.
*
* @param keyParserRegistry Should contain all relevant key parsers. The minimum recommendation is adding a {@code JwkParser}.
* @param jwksUrl The URL of the public key server, where a JWK Set can be obtained.
* @param monitor A monitor
* @param cacheValidityMs The time in milliseconds that public keys may be cached locally.
* @throws EdcException if the jwksUrl is malformed
*/
public static JwksPublicKeyResolver create(KeyParserRegistry keyParserRegistry, String jwksUrl, Monitor monitor, long cacheValidityMs) {

try {
var builder = JWKSourceBuilder.create(URI.create(jwksUrl).toURL()).retrying(false);
Expand All @@ -74,8 +90,15 @@ public JwksPublicKeyResolver(KeyParserRegistry keyParserRegistry, String jwksUrl
builder.refreshAheadCache(false);
}

} else {
// disable all optimizations
builder.cache(false);
builder.rateLimited(false);
builder.refreshAheadCache(false);
}
jwkSource = builder.build();
var jwkSource = builder.build();
return new JwksPublicKeyResolver(keyParserRegistry, monitor, jwkSource);

} catch (MalformedURLException e) {
monitor.warning("Malformed JWK URL: " + jwksUrl, e);
throw new EdcException(e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ static void teardown() {
void setup() {
jwksServer.reset();
keyParserRegistry.register(new JwkParser(mapper, monitor));
resolver = new JwksPublicKeyResolver(keyParserRegistry, jwksServerUrl(), DEFAULT_CACHE_TIME_TO_LIVE, monitor);
resolver = JwksPublicKeyResolver.create(keyParserRegistry, jwksServerUrl(), monitor, DEFAULT_CACHE_TIME_TO_LIVE);
}

@Test
Expand Down Expand Up @@ -151,7 +151,7 @@ void resolve_singleKey_noKeyId() {
@Test
void resolve_malformedKeyUrl() {

assertThatThrownBy(() -> new JwksPublicKeyResolver(keyParserRegistry, "foobar://invalid.url", DEFAULT_CACHE_TIME_TO_LIVE, monitor))
assertThatThrownBy(() -> JwksPublicKeyResolver.create(keyParserRegistry, "foobar://invalid.url", monitor, DEFAULT_CACHE_TIME_TO_LIVE))
.isInstanceOf(EdcException.class)
.hasRootCauseInstanceOf(MalformedURLException.class);

Expand All @@ -160,7 +160,7 @@ void resolve_malformedKeyUrl() {

@Test
void resolve_invalidKeyUrl() {
resolver = new JwksPublicKeyResolver(keyParserRegistry, "http:_invalid.url", DEFAULT_CACHE_TIME_TO_LIVE, monitor);
resolver = JwksPublicKeyResolver.create(keyParserRegistry, "http:_invalid.url", monitor, DEFAULT_CACHE_TIME_TO_LIVE);
assertThat(resolver.resolveKey("test-key")).isFailed()
.detail().contains("Error while retrieving JWKSet");
jwksServer.verify(jwksRequest(), never());
Expand All @@ -170,7 +170,7 @@ void resolve_invalidKeyUrl() {
@Test
void resolve_verifyHitsCache() {
var cacheTtl = 1000;
resolver = new JwksPublicKeyResolver(keyParserRegistry, jwksServerUrl(), cacheTtl, monitor);
resolver = JwksPublicKeyResolver.create(keyParserRegistry, jwksServerUrl(), monitor, cacheTtl);

jwksServer.when(jwksRequest())
.respond(response().withStatusCode(200).withBody(jwksObject(generateKey("foo-bar-key").toPublicJWK())));
Expand All @@ -188,6 +188,19 @@ void resolve_verifyHitsCache() {

}

@Test
void resolve_verifyNoHitsCache() {
resolver = JwksPublicKeyResolver.create(keyParserRegistry, jwksServerUrl(), monitor);

jwksServer.when(jwksRequest())
.respond(response().withStatusCode(200).withBody(jwksObject(generateKey("foo-bar-key").toPublicJWK())));

assertThat(resolver.resolveKey("foo-bar-key")).isSucceeded();
assertThat(resolver.resolveKey("foo-bar-key")).isSucceeded();
assertThat(resolver.resolveKey("foo-bar-key")).isSucceeded();
jwksServer.verify(jwksRequest(), exactly(3));
}

private @NotNull String jwksServerUrl() {
return "http://localhost:%d/.well-known/jwks.json".formatted(jwksServer.getPort());
}
Expand Down

0 comments on commit d17abb5

Please sign in to comment.