[java-oidc-common] branch main updated: Add a map based cache and associated metadata resolver
Phil Smart
philip.smart at jisc.ac.uk
Thu Oct 21 14:07:30 UTC 2021
This is an automated email from the git hooks/post-receive script.
philsmart pushed a commit to branch main
in repository java-oidc-common.
View the commit online:
http://git.shibboleth.net/view/?p=java-oidc-common.git;a=commit;h=94daf4a872142fada7d1c71d41d08f782e539683
The following commit(s) were added to refs/heads/main by this push:
new 94daf4a Add a map based cache and associated metadata resolver
94daf4a is described below
commit 94daf4a872142fada7d1c71d41d08f782e539683
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Thu Oct 21 15:07:27 2021 +0100
Add a map based cache and associated metadata resolver
---
.../oidc/metadata/MapBasedMetadataResolver.java | 37 +++++
....java => MapBasedMetadataCacheFactoryBean.java} | 35 ++--
.../metadata/cache/impl/MetadataCacheBuilder.java | 14 ++
.../impl/OIDCProviderMetadataCacheFactoryBean.java | 13 --
.../impl/OIDCMapBasedMetadataResolver.java | 48 ++++++
.../metadata/impl/OIDCMapMetadataResolverTest.java | 177 +++++++++++++++++++++
6 files changed, 285 insertions(+), 39 deletions(-)
diff --git a/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/MapBasedMetadataResolver.java b/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/MapBasedMetadataResolver.java
new file mode 100644
index 0000000..9ff8baa
--- /dev/null
+++ b/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/MapBasedMetadataResolver.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.oidc.metadata;
+
+import java.util.Map;
+
+import net.shibboleth.oidc.metadata.criterion.IssuerIDCriterion;
+import net.shibboleth.utilities.java.support.component.IdentifiedComponent;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import net.shibboleth.utilities.java.support.resolver.Resolver;
+
+/**
+ * A resolver that is capable of resolving Maps which meet certain supplied criteria.
+ * <p>
+ * At a minimum, a {@link MapBasedMetadataResolver} implementation MUST support the following criteria:</p>
+ * <ul>
+ * <li>{@link IssuerIDCriterion}</li>
+ * </ul>
+ */
+public interface MapBasedMetadataResolver extends Resolver<Map<String,Object>, CriteriaSet>, IdentifiedComponent {
+
+}
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/OIDCProviderMetadataCacheFactoryBean.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/MapBasedMetadataCacheFactoryBean.java
similarity index 70%
copy from oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/OIDCProviderMetadataCacheFactoryBean.java
copy to oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/MapBasedMetadataCacheFactoryBean.java
index eeea722..111d3bd 100644
--- a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/OIDCProviderMetadataCacheFactoryBean.java
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/MapBasedMetadataCacheFactoryBean.java
@@ -1,27 +1,23 @@
package net.shibboleth.oidc.metadata.cache.impl;
+import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
-import org.springframework.beans.factory.FactoryBeanNotInitializedException;
-
-import com.nimbusds.oauth2.sdk.id.Issuer;
-import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
-
import net.shibboleth.oidc.metadata.cache.MetadataCache;
import net.shibboleth.oidc.metadata.impl.DefaultBatchBackingStore;
import net.shibboleth.oidc.metadata.impl.DefaultDynamicBackingStore;
/**
- * Factory bean to create an OIDC specific metadata cache. Either a {@link DynamicMetadataCache}
+ * Factory bean to create a map based metadata cache. Either a {@link DynamicMetadataCache}
* or {@link BatchMetadataCache} is created depending on the cache properties configured.
*/
-public class OIDCProviderMetadataCacheFactoryBean extends MetadataCacheBuilder<Issuer, OIDCProviderMetadata> {
+public class MapBasedMetadataCacheFactoryBean extends MetadataCacheBuilder<String, Map<String, Object>> {
private final AtomicInteger cacheID;
/** Constructor.*/
- public OIDCProviderMetadataCacheFactoryBean() {
+ public MapBasedMetadataCacheFactoryBean() {
cacheID = new AtomicInteger(1);
}
@@ -31,24 +27,11 @@ public class OIDCProviderMetadataCacheFactoryBean extends MetadataCacheBuilder<I
return MetadataCache.class;
}
- /**
- * {@inheritDoc}
- * <p>Only supports prototype metadata instances, each resolver must have its own cache.</p>
- */
- @Override
- public void afterPropertiesSet() throws Exception {
- if (isSingleton()) {
- throw new
- FactoryBeanNotInitializedException("Only prototype metadata cache instances are supported");
- }
- super.afterPropertiesSet();
- }
-
@Override
- protected MetadataCache<OIDCProviderMetadata> createInstance() throws Exception {
+ protected MetadataCache<Map<String, Object>> createInstance() throws Exception {
if (getCacheOperationMode() == CacheOperationMode.DYNAMIC) {
- final DynamicMetadataCache<Issuer, OIDCProviderMetadata> cache = new DynamicMetadataCache<>(
+ final DynamicMetadataCache<String, Map<String, Object>> cache = new DynamicMetadataCache<>(
new DefaultDynamicBackingStore<>(getMaxCacheDuration()), getFetchStrategy());
cache.setMinCacheDuration(getMinCacheDuration());
//FIXME what did we do with this.
@@ -63,12 +46,12 @@ public class OIDCProviderMetadataCacheFactoryBean extends MetadataCacheBuilder<I
cache.setInitialCleanupTaskDelay(getInitialCleanupTaskDelay());
cache.setMetadataFilterStrategy(getMetadataFilterStrategy());
cache.setMetadataBeforeRemovalHook(getMetadataBeforeRemovalHook());
- cache.setId("OIDCProviderDynamicMetadataCache"+cacheID.getAndIncrement());
+ cache.setId("MapBasedDynamicMetadataCache"+cacheID.getAndIncrement());
cache.initialize();
return cache;
} else if (getCacheOperationMode() == CacheOperationMode.BATCH) {
//TODO why have the backing stores as interfaces if you are setting the concrete type here? inject somehow?
- final BatchMetadataCache<Issuer, OIDCProviderMetadata> cache =
+ final BatchMetadataCache<String, Map<String, Object>> cache =
new BatchMetadataCache<>(
new DefaultBatchBackingStore<>(), getLoadingStrategy(), getParsingStrategy());
cache.setMinCacheDuration(getMinCacheDuration());
@@ -81,7 +64,7 @@ public class OIDCProviderMetadataCacheFactoryBean extends MetadataCacheBuilder<I
cache.setCriteriaToIdentifierStrategy(getCriteriaToIdentifierStrategy());
cache.setMetadataFilterStrategy(getMetadataFilterStrategy());
cache.setMetadataBeforeRemovalHook(getMetadataBeforeRemovalHook());
- cache.setId("OIDCProviderBatchMetadataCache"+cacheID.getAndIncrement());
+ cache.setId("MapBasedBatchMetadataCache"+cacheID.getAndIncrement());
cache.initialize();
return cache;
} else {
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/MetadataCacheBuilder.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/MetadataCacheBuilder.java
index a609f44..f0d44d7 100644
--- a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/MetadataCacheBuilder.java
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/MetadataCacheBuilder.java
@@ -27,6 +27,7 @@ import java.util.function.Function;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import org.springframework.beans.factory.FactoryBeanNotInitializedException;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import net.shibboleth.oidc.metadata.cache.CacheLoadingContext;
@@ -148,6 +149,19 @@ public abstract class MetadataCacheBuilder<T,U> extends AbstractFactoryBean<Meta
cacheOperationMode = CacheOperationMode.BATCH;
}
+ /**
+ * {@inheritDoc}
+ * <p>Only supports prototype metadata instances, each resolver must have its own cache.</p>
+ */
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ if (isSingleton()) {
+ throw new
+ FactoryBeanNotInitializedException("Only prototype metadata cache instances are supported");
+ }
+ super.afterPropertiesSet();
+ }
+
/**
* Set the cache operation mode.
*
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/OIDCProviderMetadataCacheFactoryBean.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/OIDCProviderMetadataCacheFactoryBean.java
index eeea722..dedfdc6 100644
--- a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/OIDCProviderMetadataCacheFactoryBean.java
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/OIDCProviderMetadataCacheFactoryBean.java
@@ -31,19 +31,6 @@ public class OIDCProviderMetadataCacheFactoryBean extends MetadataCacheBuilder<I
return MetadataCache.class;
}
- /**
- * {@inheritDoc}
- * <p>Only supports prototype metadata instances, each resolver must have its own cache.</p>
- */
- @Override
- public void afterPropertiesSet() throws Exception {
- if (isSingleton()) {
- throw new
- FactoryBeanNotInitializedException("Only prototype metadata cache instances are supported");
- }
- super.afterPropertiesSet();
- }
-
@Override
protected MetadataCache<OIDCProviderMetadata> createInstance() throws Exception {
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/OIDCMapBasedMetadataResolver.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/OIDCMapBasedMetadataResolver.java
new file mode 100644
index 0000000..af9deae
--- /dev/null
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/OIDCMapBasedMetadataResolver.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.oidc.metadata.impl;
+
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.oidc.metadata.MapBasedMetadataResolver;
+import net.shibboleth.oidc.metadata.cache.MetadataCache;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+
+/** Concrete metadata resolver for OIDC Provider configuration metadata resolution.*/
+public class OIDCMapBasedMetadataResolver extends AbstractOIDCMetadataResolver<String, Map<String,Object>>
+ implements MapBasedMetadataResolver {
+
+ /**
+ * Constructor.
+ *
+ * @param metadataCache the cache to hold metadata.
+ */
+ protected OIDCMapBasedMetadataResolver(
+ @Nonnull final MetadataCache<Map<String,Object>> metadataCache) {
+ super(metadataCache);
+
+ }
+
+ @Override
+ protected void initMetadataResolver() throws ComponentInitializationException {
+ // Do nothing
+ }
+
+}
diff --git a/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/impl/OIDCMapMetadataResolverTest.java b/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/impl/OIDCMapMetadataResolverTest.java
new file mode 100644
index 0000000..96f877e
--- /dev/null
+++ b/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/impl/OIDCMapMetadataResolverTest.java
@@ -0,0 +1,177 @@
+package net.shibboleth.oidc.metadata.impl;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.time.Duration;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.nimbusds.oauth2.sdk.id.Issuer;
+
+import net.shibboleth.oidc.metadata.BatchBackingStore;
+import net.shibboleth.oidc.metadata.cache.CacheLoadingContext;
+import net.shibboleth.oidc.metadata.cache.impl.BatchMetadataCache;
+import net.shibboleth.oidc.metadata.cache.impl.ManuallyTriggeredScheduledExecutorService;
+import net.shibboleth.oidc.metadata.criterion.IssuerIDCriterion;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
+
+//TODO complete tests if we keep this.
+public class OIDCMapMetadataResolverTest {
+
+ private final String GOOD_PROVIDER_CONFIGURATION_INFO = "{\n"
+ + "\"issuer\": \"https://example.oidc.op.org\",\n"
+ + "\"authorization_endpoint\": \"https://example.oidc.op.org/o/oauth2/v2/auth\",\n"
+ + "\"device_authorization_endpoint\": \"https://oauth2.googleapis.com/device/code\",\n"
+ + "\"token_endpoint\": \"https://oauth2.googleapis.com/token\",\n"
+ + "\"userinfo_endpoint\": \"https://openidconnect.googleapis.com/v1/userinfo\",\n"
+ + "\"revocation_endpoint\": \"https://oauth2.googleapis.com/revoke\",\n"
+ + "\"jwks_uri\": \"https://www.googleapis.com/oauth2/v3/certs\",\n"
+ + "\"response_types_supported\": [\n"
+ + "\"code\",\n"
+ + "\"token\",\n"
+ + "\"id_token\",\n"
+ + "\"code token\",\n"
+ + "\"code id_token\",\n"
+ + "\"token id_token\",\n"
+ + "\"code token id_token\",\n"
+ + "\"none\"\n"
+ + "],\n"
+ + "\"subject_types_supported\": [\n"
+ + "\"public\"\n"
+ + "],\n"
+ + "\"id_token_signing_alg_values_supported\": [\n"
+ + "\"RS256\"\n"
+ + "],\n"
+ + "\"scopes_supported\": [\n"
+ + "\"openid\",\n"
+ + "\"email\",\n"
+ + "\"profile\"\n"
+ + "],\n"
+ + "\"token_endpoint_auth_methods_supported\": [\n"
+ + "\"client_secret_post\",\n"
+ + "\"client_secret_basic\"\n"
+ + "],\n"
+ + "\"claims_supported\": [\n"
+ + "\"aud\",\n"
+ + "\"email\",\n"
+ + "\"email_verified\",\n"
+ + "\"exp\",\n"
+ + "\"family_name\",\n"
+ + "\"given_name\",\n"
+ + "\"iat\",\n"
+ + "\"iss\",\n"
+ + "\"locale\",\n"
+ + "\"name\",\n"
+ + "\"picture\",\n"
+ + "\"sub\"\n"
+ + "],\n"
+ + "\"code_challenge_methods_supported\": [\n"
+ + "\"plain\",\n"
+ + "\"S256\"\n"
+ + "],\n"
+ + "\"grant_types_supported\": [\n"
+ + "\"authorization_code\",\n"
+ + "\"refresh_token\",\n"
+ + "\"urn:ietf:params:oauth:grant-type:device_code\",\n"
+ + "\"urn:ietf:params:oauth:grant-type:jwt-bearer\"\n"
+ + "]\n"
+ + "}";
+
+
+ /**
+ * Batch metadata cache. Cast to the testable metadata cache and not the interface
+ * to allow access to backing store.
+ */
+ private TestableBatchMetadataCache<String, Map<String,Object>> batchCache;
+
+ private OIDCMapBasedMetadataResolver batchResolver;
+
+ @BeforeMethod
+ public void setup() throws Exception {
+
+ final Function<CacheLoadingContext, byte[]> metadataLoadingStrat =
+ c -> GOOD_PROVIDER_CONFIGURATION_INFO.getBytes();
+
+ final Function<byte[], List<Map<String,Object>>> parsingStrat =
+ in -> {
+ try {
+ ObjectMapper mapper = new ObjectMapper();
+ return List.of(
+ mapper.readValue(new String(in, "UTF-8"), new TypeReference<Map<String,Object>>(){}));
+ } catch (UnsupportedEncodingException | JsonProcessingException e) {
+ return null;
+ }
+ };
+
+ ManuallyTriggeredScheduledExecutorService scheduler = new ManuallyTriggeredScheduledExecutorService();
+ batchCache = new TestableBatchMetadataCache<String, Map<String,Object>>(new DefaultBatchBackingStore<>(),
+ metadataLoadingStrat, parsingStrat, scheduler);
+
+ batchCache.setId("MockBatchCache");
+ batchCache.setIdentifierExtractionStrategy(m -> m.get("issuer").toString());
+ batchCache.setCriteriaToIdentifierStrategy(crit -> {
+ final IssuerIDCriterion issuerId = crit.get(IssuerIDCriterion.class);
+ if (issuerId != null) {
+ return issuerId.getIssuerID().getValue();
+ }
+ return null;});
+ batchCache.setMetadataFilterStrategy((metadata, context) -> metadata);
+ batchCache.setRefreshDelayFactor(0.75f);
+ batchCache.setMinRefreshDelay(Duration.ofMillis(1000));
+ batchCache.setMaxRefreshDelay(Duration.ofMillis(1000));
+ batchCache.setMinCacheDuration(Duration.ofMinutes(10));
+ batchCache.setRefreshDelayFactor(0.75f);
+ //This needs thinking about
+ batchCache.setMetadataExpirationTimeStrategy((m, time) -> time.plus(Duration.ofMinutes(5)));
+ batchCache.initialize();
+
+ batchResolver = new OIDCMapBasedMetadataResolver(batchCache);
+ batchResolver.setId("MockMapBatchResolver");
+ batchResolver.initialize();
+
+ }
+
+ @Test
+ void testBatchResolve_Success() throws ResolverException, IOException, ComponentInitializationException {
+ batchResolver.initialize();
+ Iterable<Map<String,Object>> found =
+ batchResolver.resolve(new CriteriaSet(new IssuerIDCriterion(new Issuer("https://example.oidc.op.org"))));
+ assertNotNull(found);
+ assertTrue(found.iterator().hasNext());
+ }
+
+ /** Extension of the {@link BatchMetadataCache} to expose certain internals.*/
+ class TestableBatchMetadataCache<IdentifierType, MetadataType>
+ extends BatchMetadataCache<IdentifierType, MetadataType> {
+
+ TestableBatchMetadataCache(@Nonnull final BatchBackingStore<IdentifierType, MetadataType> store,
+ @Nonnull final Function<CacheLoadingContext, byte[]> metadataLoadingStrategy,
+ @Nonnull final Function<byte[], List<MetadataType>> parseStrategy,
+ @Nullable final ScheduledExecutorService executor) {
+ super(store, metadataLoadingStrategy, parseStrategy, executor);
+ }
+
+ /* Expose the backing store with a public method.*/
+ public BatchBackingStore<IdentifierType, MetadataType> getBackingStore(){
+ return super.getBackingStore();
+ }
+
+ }
+
+}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list