[java-oidc-common] branch main updated: Add source metadata into cache and expiration time strategy.
Phil Smart
philip.smart at jisc.ac.uk
Fri Nov 5 13:07:27 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=333d3adce73b8c10c7527a518b8bf6fa14cb9845
The following commit(s) were added to refs/heads/main by this push:
new 333d3ad Add source metadata into cache and expiration time strategy.
333d3ad is described below
commit 333d3adce73b8c10c7527a518b8bf6fa14cb9845
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Fri Nov 5 13:07:24 2021 +0000
Add source metadata into cache and expiration time strategy.
- Although is computed from a byte array, so would need re-parsing to
be useful - when it has already been parsed in by the calling class - so
that needs thinking about.
---
.../oidc/metadata/BatchBackingStore.java | 18 +++
.../metadata/cache/impl/AbstractMetadataCache.java | 109 ++--------------
.../cache/impl/BaseMetadataCacheBuilderSpec.java | 85 +-----------
.../metadata/cache/impl/BatchMetadataCache.java | 115 ++++++++++++----
.../cache/impl/BatchMetadataCacheBuilder.java | 8 +-
.../cache/impl/BatchMetadataCacheBuilderSpec.java | 24 ++++
.../cache/impl/DefaultJSONMapParsingStrategy.java | 6 +-
...oviderMetadataCriteriaToIdentifierStrategy.java | 19 +--
...OIDCProviderMetadataExpirationTimeStrategy.java | 3 +-
...oviderMetadataIdentifierExtractionStrategy.java | 7 +-
...DefaultOIDCProviderMetadataParsingStrategy.java | 27 +++-
...viderSourceMetadataExpirationTimeStrategy.java} | 23 ++--
.../metadata/cache/impl/DynamicMetadataCache.java | 117 +++++++++++++++--
.../cache/impl/DynamicMetadataCacheBuilder.java | 5 +-
.../impl/DynamicMetadataCacheBuilderSpec.java | 83 ++++++++++++
.../metadata/impl/DefaultBatchBackingStore.java | 13 ++
.../cache/impl/BatchMetadataCacheBuilderTest.java | 23 +++-
...tCacheTest.java => BatchMetadataCacheTest.java} | 145 +++++++++++++++++----
.../cache/impl/DynamicMetadataCacheTest.java | 7 +-
.../metadata/impl/OIDCMapMetadataResolverTest.java | 40 ++++--
.../impl/OIDCProviderMetadataResolverTest.java | 32 ++++-
21 files changed, 606 insertions(+), 303 deletions(-)
diff --git a/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/BatchBackingStore.java b/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/BatchBackingStore.java
index ca4984e..6d00549 100644
--- a/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/BatchBackingStore.java
+++ b/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/BatchBackingStore.java
@@ -22,6 +22,8 @@ import java.time.Instant;
import javax.annotation.Nullable;
+import net.shibboleth.oidc.metadata.cache.LoadingStrategy;
+
/**
* A specialisation of a {@link BackingStore} that deals with batch metadata. Operations on batch metadata
* happen together e.g. a reload will evict all previous entries and all new entries are reloaded in one go.
@@ -63,6 +65,22 @@ public interface BatchBackingStore<I,T> extends BackingStore<I,T> {
* @param refreshedAt the refreshed time.
*/
void setLastRefresh(@Nullable final Instant refreshedAt);
+
+
+ /**
+ * Gets the source bytes of the source before it was parsed.
+ *
+ * @return the source bytes.
+ */
+ @Nullable byte[] getOriginalValue();
+
+ /**
+ * Sets the source bytes of the source before it was parsed.
+ *
+ * @return the source bytes.
+ */
+ void setOriginalValue(@Nullable final byte[] originalValue);
+
}
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/AbstractMetadataCache.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/AbstractMetadataCache.java
index bef03bf..bb8f12d 100644
--- a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/AbstractMetadataCache.java
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/AbstractMetadataCache.java
@@ -73,15 +73,9 @@ public abstract class AbstractMetadataCache<IdentifierType, MetadataType>
/** Class logger. */
private final Logger log = LoggerFactory.getLogger(AbstractMetadataCache.class);
- /** Maximum cache duration. */
- //@NonnullAfterInit private Duration maxCacheDuration;
-
/** Cached log prefix. */
@Nullable private String logPrefix;
- /** Minimum cache duration. */
- @NonnullAfterInit private Duration minCacheDuration;
-
/** Factor used to compute when the next refresh interval will occur. Default value: 0.75 */
@NonnullAfterInit @Positive private Float refreshDelayFactor;
@@ -96,9 +90,6 @@ public abstract class AbstractMetadataCache<IdentifierType, MetadataType>
/** Strategy used to extract an identifier from the given metadata.*/
@NonnullAfterInit private Function<MetadataType, IdentifierType> identifierExtractionStrategy;
- /** Strategy used to compute an expiration time. */
- @NonnullAfterInit private BiFunction<MetadataType, Instant, Instant> metadataExpirationTimeStrategy;
-
/** Map criteria to identifiers to use as keys to the backing store.*/
@NonnullAfterInit private Function<CriteriaSet, IdentifierType> criteriaToIdentifierStrategy;
@@ -168,18 +159,14 @@ public abstract class AbstractMetadataCache<IdentifierType, MetadataType>
if (identifierExtractionStrategy == null) {
throw new ComponentInitializationException("Identifier extraction strategy can not be null");
}
- if (metadataExpirationTimeStrategy == null) {
- throw new ComponentInitializationException("Metadata expiration strategy can not be null");
- }
+
if (criteriaToIdentifierStrategy == null) {
throw new ComponentInitializationException("Criteria to identifier strategy can not be null");
}
if (metadataFilterStrategy == null) {
throw new ComponentInitializationException("Metadata filter strategy can not be null");
}
- if (//maxCacheDuration == null ||
- minCacheDuration == null || refreshDelayFactor == null ||
- backingStore == null) {
+ if (refreshDelayFactor == null || backingStore == null) {
throw new ComponentInitializationException("Metadata cache not property initialized");
}
@@ -253,29 +240,6 @@ public abstract class AbstractMetadataCache<IdentifierType, MetadataType>
return identifierExtractionStrategy;
}
-
- /**
- * Set the metadata expiration time strategy.
- *
- * @param strategy the strategy.
- */
- public void setMetadataExpirationTimeStrategy(
- @Nonnull final BiFunction<MetadataType, Instant, Instant> strategy) {
- ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
- ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
-
- metadataExpirationTimeStrategy = Constraint.isNotNull(strategy, "Metadata expiration strategy can not be null");
- }
-
- /**
- * Get the metadata expiration time strategy.
- *
- * @return the expiration time strategy.
- */
- @NonnullAfterInit protected BiFunction<MetadataType, Instant, Instant> getMetadataExpirationTimeStrategy() {
- return metadataExpirationTimeStrategy;
- }
-
/**
* Set the metadata filtering strategy.
*
@@ -299,41 +263,6 @@ public abstract class AbstractMetadataCache<IdentifierType, MetadataType>
return metadataFilterStrategy;
}
- /**
- * Set the maximum cache duration for metadata.
- *
- * <p>Defaults to: 8 hours.</p>
- *
- * @param duration the maximum cache duration
- */
-// public void setMaxCacheDuration(@Nonnull final Duration duration) {
-// ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
-// ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
-//
-// Constraint.isNotNull(duration, "Duration cannot be null");
-// Constraint.isFalse(duration.isNegative(), "Duration cannot be negative");
-//
-// maxCacheDuration = duration;
-// }
-
-
- /**
- * Set the minimum cache duration for metadata.
- *
- * <p>Defaults to: 10 minutes.</p>
- *
- * @param duration the minimum cache duration
- */
- public void setMinCacheDuration(@Nonnull final Duration duration) {
- ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
- ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
-
- Constraint.isNotNull(duration, "Duration cannot be null");
- Constraint.isFalse(duration.isNegative(), "Duration cannot be negative");
-
- minCacheDuration = duration;
- }
-
/**
* Set a hook to run before a metadata cache entry is removed from the cache.
* <p>The hook is required to gracefully handle null metadata lists.</p>
@@ -425,7 +354,12 @@ public abstract class AbstractMetadataCache<IdentifierType, MetadataType>
*/
protected void writeToBackingStore(@Nonnull final MetadataType metadata) {
- final IdentifierType extractedIdentifier = identifierExtractionStrategy.apply(metadata);
+ final IdentifierType extractedIdentifier = getIdentifierExtractionStrategy().apply(metadata);
+
+ if (extractedIdentifier == null) {
+ log.warn("{} Identifier could not be extracted from metadata, metadata not stored",getLogPrefix());
+ return;
+ }
// will only effect the dynamic cache case, static loading should have cleared the backing store
// by this point
@@ -508,33 +442,6 @@ public abstract class AbstractMetadataCache<IdentifierType, MetadataType>
backingStore.getOrderedValues().clear();
}
- /**
- * Compute the refresh trigger time.
- *
- * @param expirationTime the time at which the metadata effectively expires
- * @param nowDateTime the current date time instant
- *
- * @return the time after which refresh attempt(s) should be made
- */
- @Nonnull protected Instant computeRefreshTriggerTime(@Nullable final Instant expirationTime,
- @Nonnull final Instant nowDateTime) {
-
- final long now = nowDateTime.toEpochMilli();
-
- long expireInstant = 0;
- if (expirationTime != null) {
- expireInstant = expirationTime.toEpochMilli();
- }
- long refreshDelay = (long) ((expireInstant - now) * refreshDelayFactor);
-
- // if the expiration time was null or the calculated refresh delay was less than the floor
- // use the floor
- if (refreshDelay < minCacheDuration.toMillis()) {
- refreshDelay = minCacheDuration.toMillis();
- }
-
- return nowDateTime.plusMillis(refreshDelay);
- }
/**
* Create a wrapper for runnables that catches any throwable and logs it. Useful
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BaseMetadataCacheBuilderSpec.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BaseMetadataCacheBuilderSpec.java
index 55e4bec..86f813d 100644
--- a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BaseMetadataCacheBuilderSpec.java
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BaseMetadataCacheBuilderSpec.java
@@ -19,7 +19,6 @@
package net.shibboleth.oidc.metadata.cache.impl;
import java.time.Duration;
-import java.time.Instant;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
@@ -31,8 +30,6 @@ import javax.annotation.Nullable;
import net.shibboleth.oidc.metadata.filter.MetadataFilterContext;
import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
import net.shibboleth.utilities.java.support.annotation.constraint.Positive;
-import net.shibboleth.utilities.java.support.component.AbstractIdentifiableInitializableComponent;
-import net.shibboleth.utilities.java.support.component.AbstractInitializableComponent;
import net.shibboleth.utilities.java.support.logic.Constraint;
import net.shibboleth.utilities.java.support.logic.ConstraintViolationException;
import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
@@ -44,12 +41,6 @@ import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
* @param <MetadataType> the metadata type.
*/
public abstract class BaseMetadataCacheBuilderSpec<IdentifierType, MetadataType> {
-
- /** Maximum cache duration. */
- @Nonnull private Duration maxCacheDuration;
-
- /** Minimum cache duration. */
- @Nonnull private Duration minCacheDuration;
/** Factor used to compute when the next refresh interval will occur. Default value: 0.75 */
@Positive private Float refreshDelayFactor;
@@ -57,8 +48,6 @@ public abstract class BaseMetadataCacheBuilderSpec<IdentifierType, MetadataType>
/** Strategy used to extract an identifier from the given metadata.*/
@Nullable private Function<MetadataType, IdentifierType> identifierExtractionStrategy;
- /** Strategy used to compute an expiration time. */
- @Nullable private BiFunction<MetadataType, Instant, Instant> metadataExpirationTimeStrategy;
/** Map criteria to identifiers to use as keys to the backing store.*/
@Nullable private Function<CriteriaSet, IdentifierType> criteriaToIdentifierStrategy;
@@ -78,9 +67,7 @@ public abstract class BaseMetadataCacheBuilderSpec<IdentifierType, MetadataType>
/** Constructor.*/
protected BaseMetadataCacheBuilderSpec() {
- // defaults
- maxCacheDuration = Duration.ofHours(8);
- minCacheDuration = Duration.ofMinutes(10);
+ // defaults
refreshDelayFactor = 0.75f;
// create a default direct in/out filter
metadataFilterStrategy = (metadata, context) -> metadata;
@@ -169,25 +156,6 @@ public abstract class BaseMetadataCacheBuilderSpec<IdentifierType, MetadataType>
return identifierExtractionStrategy;
}
- /**
- * Set the metadata expiration time strategy.
- *
- * @param strategy the strategy.
- */
- public void setMetadataExpirationTimeStrategy(
- @Nonnull final BiFunction<MetadataType, Instant, Instant> strategy) {
- metadataExpirationTimeStrategy = Constraint.isNotNull(strategy, "Strategy can not be null");
- }
-
- /**
- * Get the metadata expiration time strategy.
- *
- * @return the strategy.
- */
- @Nullable protected BiFunction<MetadataType, Instant, Instant> getMetadataExpirationTimeStrategy() {
- return metadataExpirationTimeStrategy;
- }
-
/**
* Set the criteria set to identifier lookup strategy.
*
@@ -208,57 +176,6 @@ public abstract class BaseMetadataCacheBuilderSpec<IdentifierType, MetadataType>
}
- /**
- * Get the maximum cache duration for metadata.
- *
- * <p>Defaults to: 8 hours.</p>
- *
- * @return the maximum cache duration
- */
- @Nonnull protected Duration getMaxCacheDuration() {
- return maxCacheDuration;
- }
-
- /**
- * Set the maximum cache duration for metadata.
- *
- * <p>Defaults to: 8 hours.</p>
- *
- * @param duration the maximum cache duration
- */
- public void setMaxCacheDuration(@Nonnull final Duration duration) {
- Constraint.isNotNull(duration, "Duration cannot be null");
- Constraint.isFalse(duration.isNegative(), "Duration cannot be negative");
-
- maxCacheDuration = duration;
- }
-
- /**
- * Get the minimum cache duration for metadata.
- *
- * <p>Defaults to: 10 minutes.</p>
- *
- * @return the minimum cache duration
- */
- @Nonnull protected Duration getMinCacheDuration() {
- return minCacheDuration;
- }
-
-
- /**
- * Set the minimum cache duration for metadata.
- *
- * <p>Defaults to: 10 minutes.</p>
- *
- * @param duration the minimum cache duration
- */
- public void setMinCacheDuration(@Nonnull final Duration duration) {
- Constraint.isNotNull(duration, "Duration cannot be null");
- Constraint.isFalse(duration.isNegative(), "Duration cannot be negative");
-
- minCacheDuration = duration;
- }
-
/**
* Gets the delay factor used to compute the next refresh time.
*
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCache.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCache.java
index ae168d3..688ce8e 100644
--- a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCache.java
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCache.java
@@ -48,12 +48,21 @@ import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
* A metadata cache implementation that supports 'refresh-ahead' semantics for batch cache updates.
* Does not support 'read-through' semantics if an entry does not exist in the cache.
*
+ * <p> The metadata source could either be an aggregate with more than one metadata entry, or a single source
+ * which contains a single metadata entry.</p>
*
* <p>Supports the following:</p>
* <ul>
- * <li>Batch reloading of metadata using the supplied loading strategy. The cache
- * is completely reloaded during each refresh cycle.</li>
+ * <li>Batch reloading of metadata from the source using the supplied loading strategy. The cache
+ * is wiped and reloaded during each succesful refresh cycle.</li>
* </ul>
+ *
+ * <p>The schedule for batch metadata reloads are based on:</p>
+ * <ol>
+ * <li>If no expiry is set on the metadata source, or one can not be computed, the max refresh delay is used.</li>
+ * <li>If metadata expiry exists but is less than the min refresh delay, use the min refresh delay.</li>
+ * <li>If metadata expiry exists and is greater than the min, use the metadata expiry.</li>
+ * </ol>
*
* @param <IdentifierType> the metadata identifier type.
* @param <MetadataType> the metadata type.
@@ -73,10 +82,13 @@ public class BatchMetadataCache<IdentifierType, MetadataType>
@NonnullAfterInit @Positive private Duration minRefreshDelay;
/** The function to use to load metadata.*/
- @Nonnull private final LoadingStrategy loadingStrategy;
+ @NonnullAfterInit private LoadingStrategy loadingStrategy;
/** How to parse the loaded metadata from the loadingStrategy into a usable metadatatype.*/
- @Nonnull private final Function<byte[], List<MetadataType>> parsingStrategy;
+ @NonnullAfterInit private Function<byte[], List<MetadataType>> parsingStrategy;
+
+ /** Determine the expiration time of the source batch loaded metadata.*/
+ @NonnullAfterInit private Function<byte[], Instant> sourceMetadataExpiryStrategy;
/**
* Is a match based on an identifier required? If not,
@@ -90,13 +102,9 @@ public class BatchMetadataCache<IdentifierType, MetadataType>
* Constructor.
*
* @param store the backing store.
- * @param metadataLoadingStrategy strategy used to load metadata.
- * @param parseStrategy the strategy used to convert raw metadata in bytes to the given metadata type.
*/
- public BatchMetadataCache(@Nonnull final BatchBackingStore<IdentifierType, MetadataType> store,
- @Nonnull final LoadingStrategy metadataLoadingStrategy,
- @Nonnull final Function<byte[], List<MetadataType>> parseStrategy) {
- this(store, metadataLoadingStrategy, parseStrategy, null);
+ protected BatchMetadataCache(@Nonnull final BatchBackingStore<IdentifierType, MetadataType> store) {
+ this(store, null);
}
/**
@@ -104,18 +112,11 @@ public class BatchMetadataCache<IdentifierType, MetadataType>
* Protected constructor.
*
* @param store the backing store.
- * @param metadataLoadingStrategy strategy used to load metadata.
- * @param parseStrategy the strategy used to convert raw metadata in bytes to the given metadata type.
* @param executor the scheduled executor
*/
protected BatchMetadataCache(@Nonnull final BatchBackingStore<IdentifierType, MetadataType> store,
- @Nonnull final LoadingStrategy metadataLoadingStrategy,
- @Nonnull final Function<byte[], List<MetadataType>> parseStrategy,
@Nullable final ScheduledExecutorService executor) {
super(store, executor);
- loadingStrategy =
- Constraint.isNotNull(metadataLoadingStrategy, "Metadata loading strategy can not be null");
- parsingStrategy = Constraint.isNotNull(parseStrategy, "Metadata Parsing strategy can not be null");
matchOnIdentifierRequired = true;
}
@@ -126,7 +127,16 @@ public class BatchMetadataCache<IdentifierType, MetadataType>
if (maxRefreshDelay == null || minRefreshDelay == null) {
throw new ComponentInitializationException("Refreshable metadata cache not property initialized");
}
-
+ if (sourceMetadataExpiryStrategy == null) {
+ throw new ComponentInitializationException("Source metadata expiration time strategy can not be null");
+ }
+ if (loadingStrategy == null) {
+ throw new ComponentInitializationException("Loading strategy can not be null");
+ }
+ if (parsingStrategy == null) {
+ throw new ComponentInitializationException("Parsing strategy can not be null");
+ }
+
try {
loadCache();
} catch (final MetadataCacheException e) {
@@ -134,6 +144,43 @@ public class BatchMetadataCache<IdentifierType, MetadataType>
}
}
+ /**
+ * Set the strategy used to batch load metadata from a source.
+ *
+ * @param strategy the loading strategy.
+ */
+ public void setLoadingStrategy(@Nonnull final LoadingStrategy strategy) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+ ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+
+ loadingStrategy = Constraint.isNotNull(strategy, "Loading strategy can not be null");
+ }
+
+ /**
+ * Set the strategy used to convert raw metadata in bytes to the given metadata type.
+ *
+ * @param strategy the parsing strategy.
+ */
+ public void setParsingStrategy(@Nonnull final Function<byte[], List<MetadataType>> strategy) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+ ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+
+ parsingStrategy = Constraint.isNotNull(strategy, "Parsing strategy can not be null");
+ }
+
+ /**
+ * Set a strategy to find the metadata expiry date from a source metadata document as bytes.
+ *
+ * @param strategy the strategy.
+ */
+ public void setSourceMetadataExpiryStrategy(@Nonnull final Function<byte[], Instant> strategy) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+ ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+
+ sourceMetadataExpiryStrategy =
+ Constraint.isNotNull(strategy, "Source metadata expiry strategy can not be null");
+ }
+
/**
* Set if a match on identifier is required in order to return results. If false and there are no
@@ -142,6 +189,9 @@ public class BatchMetadataCache<IdentifierType, MetadataType>
* @param required does the metadata lookup need to match the given criteria.
*/
public void setMatchOnIdentifierRequired(final boolean required) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+ ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+
matchOnIdentifierRequired = required;
}
@@ -196,23 +246,23 @@ public class BatchMetadataCache<IdentifierType, MetadataType>
final List<MetadataType> allMetadata = lookupIndexedIdentifier(identifier);
if (allMetadata.isEmpty()) {
- log.debug("{} Metadata candidates for '{}' do not exist, returning empty result",
+ log.debug("{} No metadata candidates for '{}' found, returning empty result",
getLogPrefix(), identifier);
return Collections.emptyList();
} else {
- log.debug("{} There are {} Metadata candidates for '{}' found in cache",
+ log.debug("{} There are {} metadata candidates for '{}' found in cache",
getLogPrefix(), allMetadata.size(), identifier);
return allMetadata;
}
} else if (!matchOnIdentifierRequired) {
- log.debug("{} No identifier found to lookup, matchOnIdentifierRequired is false, returning all known "
+ log.debug("{} No identifier found to lookup, identifier match is not required, returning all known "
+ "metadata",getLogPrefix());
return Collections.unmodifiableList(getBackingStore().getOrderedValues());
- } else {
- // TODO: see SAML version, could resolve from criteria even if no identifier.
+ } else {
log.debug("{} No identifier found to lookup, returning empty result", getLogPrefix());
return Collections.emptyList();
}
+ // TODO: see SAML version, could resolve from criteriafrom secondary index.
}
private CacheLoadingContext createLoadingContext() {
@@ -232,7 +282,7 @@ public class BatchMetadataCache<IdentifierType, MetadataType>
log.debug("{} Populating metadata cache for '{}'",getLogPrefix(), loadingStrategy.getSourceIdentifier());
final Instant now = Instant.now();
- Duration refreshDelay = null;
+ Instant metadataExpiration = null;
try {
if (isDestroyed()) {
return;
@@ -246,23 +296,32 @@ public class BatchMetadataCache<IdentifierType, MetadataType>
log.info("{} Parsed {} metadata candidates, loading into cache",
getLogPrefix(), parsedMetadata.size());
freshLoad(parsedMetadata);
+ // Store away the original, raw, metadata bytes.
+ getBackingStore().setOriginalValue(rawFetchedMetadata);
}
} else {
log.info("{} Metadata has not changed since last refresh", getLogPrefix());
}
+ // Compute metadata expiration from whatever is in the cache (updated or not) will
+ // remain null if no cached original value.
+ metadataExpiration = sourceMetadataExpiryStrategy.apply(getBackingStore().getOriginalValue());
} catch (final Throwable t) {
log.error("{} Error loading or parsing metadata",getLogPrefix(), t);
- refreshDelay = minRefreshDelay;
if (t instanceof Exception) {
throw new MetadataCacheException((Exception) t);
} else {
throw new MetadataCacheException(String.format("Saw an error of type '%s' with message '%s'",
t.getClass().getName(), t.getMessage()));
}
- } finally {
- // TODO compute refresh time from metadata using a function
+ } finally {
+ if (metadataExpiration == null || metadataExpiration.isBefore(now)) {
+ // Null, so forced to use max refresh delay.
+ scheduleNextRefresh(null);
+ } else {
+ final Duration nextRefreshDelay = computeNextRefreshDelay(metadataExpiration);
+ scheduleNextRefresh(nextRefreshDelay);
+ }
- scheduleNextRefresh(refreshDelay);
getBackingStore().setLastRefresh(now);
}
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCacheBuilder.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCacheBuilder.java
index f2089ef..300b17d 100644
--- a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCacheBuilder.java
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCacheBuilder.java
@@ -62,13 +62,13 @@ public final class BatchMetadataCacheBuilder {
final BatchMetadataCache<IdentifierType, MetadataType> cache =
new BatchMetadataCache<>(
- new DefaultBatchBackingStore<>(), spec.getLoadingStrategy(), spec.getParsingStrategy());
- cache.setMinCacheDuration(spec.getMinCacheDuration());
- // cache.setMaxCacheDuration(getMaxCacheDuration());
+ new DefaultBatchBackingStore<>());
+ cache.setSourceMetadataExpiryStrategy(spec.getSourceMetadataExpiryStrategy());
+ cache.setLoadingStrategy(spec.getLoadingStrategy());
+ cache.setParsingStrategy(spec.getParsingStrategy());
cache.setMinRefreshDelay(spec.getMinRefreshDelay());
cache.setMaxRefreshDelay(spec.getMaxRefreshDelay());
cache.setRefreshDelayFactor(spec.getRefreshDelayFactor());
- cache.setMetadataExpirationTimeStrategy(spec.getMetadataExpirationTimeStrategy());
cache.setIdentifierExtractionStrategy(spec.getIdentifierExtractionStrategy());
cache.setCriteriaToIdentifierStrategy(spec.getCriteriaToIdentifierStrategy());
cache.setMetadataFilterStrategy(spec.getMetadataFilterStrategy());
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCacheBuilderSpec.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCacheBuilderSpec.java
index fd0828c..7e3f19b 100644
--- a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCacheBuilderSpec.java
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCacheBuilderSpec.java
@@ -19,6 +19,7 @@
package net.shibboleth.oidc.metadata.cache.impl;
import java.time.Duration;
+import java.time.Instant;
import java.util.List;
import java.util.function.Function;
@@ -26,6 +27,7 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import net.shibboleth.oidc.metadata.cache.LoadingStrategy;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
import net.shibboleth.utilities.java.support.annotation.constraint.Positive;
import net.shibboleth.utilities.java.support.logic.Constraint;
@@ -62,6 +64,9 @@ public class BatchMetadataCacheBuilderSpec<IdentifierType, MetadataType>
*/
@Nonnull private boolean matchOnIdentifierRequired;
+ /** Determine the expiration time of the source batch loaded metadata.*/
+ @NonnullAfterInit private Function<byte[], Instant> sourceMetadataExpiryStrategy;
+
/** Constructor.*/
public BatchMetadataCacheBuilderSpec() {
maxRefreshDelay = Duration.ofHours(4);
@@ -69,6 +74,25 @@ public class BatchMetadataCacheBuilderSpec<IdentifierType, MetadataType>
matchOnIdentifierRequired = false;
}
+ /**
+ * Set a strategy to find the metadata expiry date from a source metadata document as bytes.
+ *
+ * @param strategy the strategy.
+ */
+ public void setSourceMetadataExpiryStrategy(@Nonnull final Function<byte[], Instant> strategy) {
+ sourceMetadataExpiryStrategy =
+ Constraint.isNotNull(strategy, "Source metadata expiry strategy can not be null");
+ }
+
+ /**
+ * Get the source metadata expiry date strategy.
+ *
+ * @return the strategy.
+ */
+ protected Function<byte[], Instant> getSourceMetadataExpiryStrategy() {
+ return sourceMetadataExpiryStrategy;
+ }
+
/**
* Set if a match on identifier is required in order to return results. If false and there are no
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DefaultJSONMapParsingStrategy.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DefaultJSONMapParsingStrategy.java
index 97f0f41..406cdf5 100644
--- a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DefaultJSONMapParsingStrategy.java
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DefaultJSONMapParsingStrategy.java
@@ -24,6 +24,7 @@ import java.util.Map;
import java.util.function.Function;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -64,8 +65,11 @@ public class DefaultJSONMapParsingStrategy<V extends Object>
}
@Override
- public List<Map<String, V>> apply(@Nonnull final byte[] rawMetadata) {
+ public List<Map<String, V>> apply(@Nullable final byte[] rawMetadata) {
try {
+ if (rawMetadata == null) {
+ return Collections.emptyList();
+ }
final Map<String, V> parsed =
objectMapper.readValue(new String(rawMetadata, StandardCharsets.UTF_8),
new TypeReference<Map<String,V>>(){});
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DefaultOIDCProviderMetadataCriteriaToIdentifierStrategy.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DefaultOIDCProviderMetadataCriteriaToIdentifierStrategy.java
index 40291b0..bd23885 100644
--- a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DefaultOIDCProviderMetadataCriteriaToIdentifierStrategy.java
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DefaultOIDCProviderMetadataCriteriaToIdentifierStrategy.java
@@ -19,7 +19,6 @@ package net.shibboleth.oidc.metadata.cache.impl;
import java.util.function.Function;
-import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.nimbusds.oauth2.sdk.id.Issuer;
@@ -27,16 +26,20 @@ import com.nimbusds.oauth2.sdk.id.Issuer;
import net.shibboleth.oidc.metadata.criterion.IssuerIDCriterion;
import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
-/** Strategy for extracting the {@link IssuerIDCriterion} from a {@link CriteriaSet}.*/
+/** Strategy for extracting the {@link IssuerIDCriterion} from a {@link CriteriaSet}. */
public class DefaultOIDCProviderMetadataCriteriaToIdentifierStrategy implements Function<CriteriaSet, Issuer> {
@Override
- @Nullable public Issuer apply(@Nonnull final CriteriaSet criteria) {
- final IssuerIDCriterion issuerId = criteria.get(IssuerIDCriterion.class);
- if (issuerId != null) {
- return issuerId.getIssuerID();
- }
- return null;
+ @Nullable
+ public Issuer apply(@Nullable final CriteriaSet criteria) {
+ if (criteria == null) {
+ return null;
+ }
+ final IssuerIDCriterion issuerId = criteria.get(IssuerIDCriterion.class);
+ if (issuerId != null) {
+ return issuerId.getIssuerID();
+ }
+ return null;
}
}
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DefaultOIDCProviderMetadataExpirationTimeStrategy.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DefaultOIDCProviderMetadataExpirationTimeStrategy.java
index d59e561..f797943 100644
--- a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DefaultOIDCProviderMetadataExpirationTimeStrategy.java
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DefaultOIDCProviderMetadataExpirationTimeStrategy.java
@@ -22,6 +22,7 @@ import java.time.Instant;
import java.util.function.BiFunction;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
@@ -46,7 +47,7 @@ public class DefaultOIDCProviderMetadataExpirationTimeStrategy
}
@Override
- public Instant apply(@Nonnull final OIDCProviderMetadata metadata, @Nonnull final Instant now) {
+ public Instant apply(@Nullable final OIDCProviderMetadata metadata, @Nullable final Instant now) {
return now.plus(expiryDuration);
}
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DefaultOIDCProviderMetadataIdentifierExtractionStrategy.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DefaultOIDCProviderMetadataIdentifierExtractionStrategy.java
index 6e86e76..5b3ee5f 100644
--- a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DefaultOIDCProviderMetadataIdentifierExtractionStrategy.java
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DefaultOIDCProviderMetadataIdentifierExtractionStrategy.java
@@ -19,7 +19,7 @@ package net.shibboleth.oidc.metadata.cache.impl;
import java.util.function.Function;
-import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import com.nimbusds.oauth2.sdk.id.Issuer;
import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
@@ -28,7 +28,10 @@ import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
public class DefaultOIDCProviderMetadataIdentifierExtractionStrategy implements Function<OIDCProviderMetadata, Issuer>{
@Override
- public Issuer apply(@Nonnull final OIDCProviderMetadata metadata) {
+ public Issuer apply(@Nullable final OIDCProviderMetadata metadata) {
+ if (metadata == null) {
+ return null;
+ }
return metadata.getIssuer();
}
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DefaultOIDCProviderMetadataParsingStrategy.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DefaultOIDCProviderMetadataParsingStrategy.java
index 6148cf0..42a5534 100644
--- a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DefaultOIDCProviderMetadataParsingStrategy.java
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DefaultOIDCProviderMetadataParsingStrategy.java
@@ -1,3 +1,20 @@
+/*
+ * 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.cache.impl;
import java.nio.charset.StandardCharsets;
@@ -6,6 +23,7 @@ import java.util.List;
import java.util.function.Function;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -20,8 +38,11 @@ public class DefaultOIDCProviderMetadataParsingStrategy implements Function<byte
@Nonnull private final Logger log = LoggerFactory.getLogger(DefaultOIDCProviderMetadataParsingStrategy.class);
@Override
- @Nonnull public List<OIDCProviderMetadata> apply(@Nonnull final byte[] rawMetdata) {
-
+ @Nonnull public List<OIDCProviderMetadata> apply(@Nullable final byte[] rawMetdata) {
+ if (rawMetdata == null) {
+ log.warn("Raw metadata is null, unable to parse OIDC Provider Metadata");
+ return Collections.emptyList();
+ }
try {
final OIDCProviderMetadata metadata =
OIDCProviderMetadata.parse(new String(rawMetdata,StandardCharsets.UTF_8));
@@ -29,7 +50,7 @@ public class DefaultOIDCProviderMetadataParsingStrategy implements Function<byte
return List.of(metadata);
}
return Collections.emptyList();
- } catch (ParseException e) {
+ } catch (final ParseException e) {
log.error("Error parsing bytes to metadata", e);
return Collections.emptyList();
}
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DefaultOIDCProviderMetadataExpirationTimeStrategy.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DefaultOIDCProviderSourceMetadataExpirationTimeStrategy.java
similarity index 68%
copy from oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DefaultOIDCProviderMetadataExpirationTimeStrategy.java
copy to oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DefaultOIDCProviderSourceMetadataExpirationTimeStrategy.java
index d59e561..7062261 100644
--- a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DefaultOIDCProviderMetadataExpirationTimeStrategy.java
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DefaultOIDCProviderSourceMetadataExpirationTimeStrategy.java
@@ -19,18 +19,21 @@ package net.shibboleth.oidc.metadata.cache.impl;
import java.time.Duration;
import java.time.Instant;
-import java.util.function.BiFunction;
+import java.util.function.Function;
import javax.annotation.Nonnull;
-import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
-
import net.shibboleth.utilities.java.support.logic.Constraint;
-/** Strategy for computing an expiry time for {@link OIDCProviderMetadata}.
- * Defaults to now plus the provided expiry time.*/
-public class DefaultOIDCProviderMetadataExpirationTimeStrategy
- implements BiFunction<OIDCProviderMetadata, Instant, Instant> {
+/**
+ * Strategy for computing an expiry time for source byte metadata.
+ * Given OIDC provider configuration information does not have an expiry, just add the duration
+ * to Now.
+ *
+ * TODO: check the specs here.
+ */
+public class DefaultOIDCProviderSourceMetadataExpirationTimeStrategy
+ implements Function<byte[], Instant> {
/** How long after now should the metadata expire.*/
@Nonnull private final Duration expiryDuration;
@@ -41,13 +44,13 @@ public class DefaultOIDCProviderMetadataExpirationTimeStrategy
*
* @param duration the expiry duration.
*/
- public DefaultOIDCProviderMetadataExpirationTimeStrategy(@Nonnull final Duration duration) {
+ public DefaultOIDCProviderSourceMetadataExpirationTimeStrategy(@Nonnull final Duration duration) {
expiryDuration = Constraint.isNotNull(duration, "Expiry duration can not be null");
}
@Override
- public Instant apply(@Nonnull final OIDCProviderMetadata metadata, @Nonnull final Instant now) {
- return now.plus(expiryDuration);
+ public Instant apply(final byte[] sourceMetadata) {
+ return Instant.now().plus(expiryDuration);
}
}
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCache.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCache.java
index 5f0acf1..e0deb36 100644
--- a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCache.java
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCache.java
@@ -28,6 +28,7 @@ import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.StampedLock;
+import java.util.function.BiFunction;
import java.util.function.Function;
import javax.annotation.Nonnull;
@@ -81,20 +82,22 @@ public class DynamicMetadataCache<IdentifierType, MetadataType>
/** Flag indicating whether idle entity data should be removed. */
private boolean removeIdleEntityData;
+ /** Minimum cache duration. */
+ @NonnullAfterInit private Duration minCacheDuration;
+
/** The function to use to fetch/load metadata if either none exists, or the existing is stale.*/
- @Nonnull private final Function<CriteriaSet, MetadataType> fetchStrategy;
+ @NonnullAfterInit private Function<CriteriaSet, MetadataType> fetchStrategy;
+
+ /** Strategy used to compute an expiration time from a metadata instance. */
+ @NonnullAfterInit private BiFunction<MetadataType, Instant, Instant> metadataExpirationTimeStrategy;
/**
* Constructor.
*
* @param store the backing store to use as the cache store.
- * @param metadataFetchStrategy the strategy used to fetch metadata using the 'read-through' semantics.
*/
- public DynamicMetadataCache(@Nonnull final DynamicBackingStore<IdentifierType, MetadataType> store,
- @Nonnull final Function<CriteriaSet, MetadataType> metadataFetchStrategy) {
+ protected DynamicMetadataCache(@Nonnull final DynamicBackingStore<IdentifierType, MetadataType> store) {
super(store);
- fetchStrategy =
- Constraint.isNotNull(metadataFetchStrategy, "Dynamic Metadata fetch strategy can not be null");
}
/**
@@ -113,6 +116,35 @@ public class DynamicMetadataCache<IdentifierType, MetadataType>
Constraint.isNotNull(metadataFetchStrategy, "Dynamic Metadata fetch strategy can not be null");
}
+ /**
+ * Set the minimum cache duration for metadata.
+ *
+ * <p>Defaults to: 10 minutes.</p>
+ *
+ * @param duration the minimum cache duration
+ */
+ public void setMinCacheDuration(@Nonnull final Duration duration) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+ ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+
+ Constraint.isNotNull(duration, "Duration cannot be null");
+ Constraint.isFalse(duration.isNegative(), "Duration cannot be negative");
+
+ minCacheDuration = duration;
+ }
+
+ /**
+ * Set the metadata fetching strategy.
+ *
+ * @param strategy the strategy used to fetch metadata using a 'read-through' semantic.
+ */
+ public void setFetchStrategy(@Nonnull final Function<CriteriaSet, MetadataType> strategy) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+ ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+
+ this.fetchStrategy = Constraint.isNotNull(strategy, "Dynamic Metadata fetch strategy can not be null");;
+ }
+
/**
* Set the initial cleanup task delay.
*
@@ -127,6 +159,7 @@ public class DynamicMetadataCache<IdentifierType, MetadataType>
initialCleanupTaskDelay = delay;
}
+
/**
* Set the interval at which the cleanup task should run.
@@ -174,13 +207,43 @@ public class DynamicMetadataCache<IdentifierType, MetadataType>
maxIdleEntityData = max;
}
+ /**
+ * Set the metadata expiration time strategy.
+ *
+ * @param strategy the strategy.
+ */
+ public void setMetadataExpirationTimeStrategy(
+ @Nonnull final BiFunction<MetadataType, Instant, Instant> strategy) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+ ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+
+ metadataExpirationTimeStrategy = Constraint.isNotNull(strategy, "Metadata expiration strategy can not be null");
+ }
+
+ /**
+ * Get the metadata expiration time strategy.
+ *
+ * @return the expiration time strategy.
+ */
+ @NonnullAfterInit protected BiFunction<MetadataType, Instant, Instant> getMetadataExpirationTimeStrategy() {
+ return metadataExpirationTimeStrategy;
+ }
+
+
@Override
protected void doInitialize() throws ComponentInitializationException {
super.doInitialize();
- if ( maxIdleEntityData == null || cleanupTaskInterval == null || initialCleanupTaskDelay == null) {
+ if (minCacheDuration == null || maxIdleEntityData == null || cleanupTaskInterval == null ||
+ initialCleanupTaskDelay == null) {
throw new ComponentInitializationException("Dynamic metadata cache not property initialized");
}
+ if (metadataExpirationTimeStrategy == null) {
+ throw new ComponentInitializationException("Metadata expiration strategy can not be null");
+ }
+ if (fetchStrategy == null) {
+ throw new ComponentInitializationException("Metadata fetching strategy can not be null");
+ }
getExecutorService().scheduleAtFixedRate(
errorHandlingWrapper(new ExpiredAndIdleMetadataCleanupTask()), initialCleanupTaskDelay.toMillis(),
@@ -294,7 +357,17 @@ public class DynamicMetadataCache<IdentifierType, MetadataType>
final MetadataType filteredMetadata = getMetadataFilterStrategy().apply(metadata, newFilterContext());
+ if (filteredMetadata == null) {
+ log.warn("{} Filtered metadata is null, no further processing performed", getLogPrefix());
+ return;
+ }
+
final IdentifierType extractedIdentifier = getIdentifierExtractionStrategy().apply(filteredMetadata);
+
+ if (extractedIdentifier == null) {
+ log.warn("{} Metadata identifier could not be extracted, no further processing performed", getLogPrefix());
+ }
+
// equality method of the identifier is required to be implemented correctly.
if (!Objects.equals(expectedIdentifier, extractedIdentifier)) {
log.warn("{} New metadata's identifer '{}' does not match expected identifier '{}', will not process",
@@ -323,6 +396,34 @@ public class DynamicMetadataCache<IdentifierType, MetadataType>
log.info("{} Successfully loaded new Metadata with identifer '{}'", getLogPrefix(), extractedIdentifier);
}
+ /**
+ * Compute the refresh trigger time.
+ *
+ * @param expirationTime the time at which the metadata effectively expires
+ * @param nowDateTime the current date time instant
+ *
+ * @return the time after which refresh attempt(s) should be made
+ */
+ @Nonnull private Instant computeRefreshTriggerTime(@Nullable final Instant expirationTime,
+ @Nonnull final Instant nowDateTime) {
+
+ final long now = nowDateTime.toEpochMilli();
+
+ long expireInstant = 0;
+ if (expirationTime != null) {
+ expireInstant = expirationTime.toEpochMilli();
+ }
+ long refreshDelay = (long) ((expireInstant - now) * getRefreshDelayFactor());
+
+ // if the expiration time was null or the calculated refresh delay was less than the floor
+ // use the floor
+ if (refreshDelay < minCacheDuration.toMillis()) {
+ refreshDelay = minCacheDuration.toMillis();
+ }
+
+ return nowDateTime.plusMillis(refreshDelay);
+ }
+
/**
* Read metadata from the cache under the lock relating to the Identifier of the metadata to find.
*
@@ -331,7 +432,7 @@ public class DynamicMetadataCache<IdentifierType, MetadataType>
* a write lock is obtained and a further read is attempted - to ensure a consistent state. This
* should improve efficiency given that metadata reads will vastly out number metadata fetch/writes.</p>
*
- * @param mgmtData the metadata managment data.
+ * @param mgmtData the metadata management data.
* @param identifier the metadata identifier to use as a key to fetch.
*
* @return a list of metadata that matches the given key, an empty list of none are found.
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCacheBuilder.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCacheBuilder.java
index 85ae283..4c5e4de 100644
--- a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCacheBuilder.java
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCacheBuilder.java
@@ -59,10 +59,11 @@ public final class DynamicMetadataCacheBuilder {
throws ComponentInitializationException {
final DynamicMetadataCache<IdentifierType, MetadataType> cache = new DynamicMetadataCache<>(
- new DefaultDynamicBackingStore<>(spec.getMaxCacheDuration()), spec.getFetchStrategy());
+ new DefaultDynamicBackingStore<>(spec.getMaxCacheDuration()));
+ cache.setFetchStrategy(spec.getFetchStrategy());
cache.setMinCacheDuration(spec.getMinCacheDuration());
// FIXME what did we do with this.
- // cache.setMaxCacheDuration(getMaxCacheDuration());
+ //cache.setMaxCacheDuration(spec.getMaxCacheDuration());
cache.setRefreshDelayFactor(spec.getRefreshDelayFactor());
cache.setMaxIdleEntityData(spec.getMaxIdleEntityData());
cache.setMetadataExpirationTimeStrategy(spec.getMetadataExpirationTimeStrategy());
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCacheBuilderSpec.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCacheBuilderSpec.java
index df4fdb6..13b20d4 100644
--- a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCacheBuilderSpec.java
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCacheBuilderSpec.java
@@ -19,6 +19,8 @@
package net.shibboleth.oidc.metadata.cache.impl;
import java.time.Duration;
+import java.time.Instant;
+import java.util.function.BiFunction;
import java.util.function.Function;
import javax.annotation.Nonnull;
@@ -40,6 +42,12 @@ public class DynamicMetadataCacheBuilderSpec <IdentifierType, MetadataType>
/** The function to use to fetch metadata if either none exists, or the existing is stale.*/
@Nullable private Function<CriteriaSet, MetadataType> fetchStrategy;
+ /** Maximum cache duration. */
+ @Nonnull private Duration maxCacheDuration;
+
+ /** Minimum cache duration. */
+ @Nonnull private Duration minCacheDuration;
+
/** The maximum idle time for which the cache will keep data for before it is removed. */
@Nullable private Duration maxIdleEntityData;
@@ -52,14 +60,70 @@ public class DynamicMetadataCacheBuilderSpec <IdentifierType, MetadataType>
/** The initial cleanup task delay.*/
@Nonnull private Duration initialCleanupTaskDelay;
+ /** Strategy used to compute an expiration time. */
+ @Nullable private BiFunction<MetadataType, Instant, Instant> metadataExpirationTimeStrategy;
+
/** Constructor. */
protected DynamicMetadataCacheBuilderSpec() {
+ maxCacheDuration = Duration.ofHours(8);
+ minCacheDuration = Duration.ofMinutes(10);
maxIdleEntityData = Duration.ofHours(8);
cleanupTaskInterval = Duration.ofMinutes(30);
initialCleanupTaskDelay = Duration.ofMinutes(1);
removeIdleEntityData = true;
}
+ /**
+ * Get the maximum cache duration for metadata.
+ *
+ * <p>Defaults to: 8 hours.</p>
+ *
+ * @return the maximum cache duration
+ */
+ @Nonnull protected Duration getMaxCacheDuration() {
+ return maxCacheDuration;
+ }
+
+ /**
+ * Set the maximum cache duration for metadata.
+ *
+ * <p>Defaults to: 8 hours.</p>
+ *
+ * @param duration the maximum cache duration
+ */
+ public void setMaxCacheDuration(@Nonnull final Duration duration) {
+ Constraint.isNotNull(duration, "Duration cannot be null");
+ Constraint.isFalse(duration.isNegative(), "Duration cannot be negative");
+
+ maxCacheDuration = duration;
+ }
+
+ /**
+ * Get the minimum cache duration for metadata.
+ *
+ * <p>Defaults to: 10 minutes.</p>
+ *
+ * @return the minimum cache duration
+ */
+ @Nonnull protected Duration getMinCacheDuration() {
+ return minCacheDuration;
+ }
+
+
+ /**
+ * Set the minimum cache duration for metadata.
+ *
+ * <p>Defaults to: 10 minutes.</p>
+ *
+ * @param duration the minimum cache duration
+ */
+ public void setMinCacheDuration(@Nonnull final Duration duration) {
+ Constraint.isNotNull(duration, "Duration cannot be null");
+ Constraint.isFalse(duration.isNegative(), "Duration cannot be negative");
+
+ minCacheDuration = duration;
+ }
+
/**
* Set the flag indicating whether idle entity data should be removed.
*
@@ -78,6 +142,25 @@ public class DynamicMetadataCacheBuilderSpec <IdentifierType, MetadataType>
return removeIdleEntityData;
}
+ /**
+ * Set the metadata expiration time strategy.
+ *
+ * @param strategy the strategy.
+ */
+ public void setMetadataExpirationTimeStrategy(
+ @Nonnull final BiFunction<MetadataType, Instant, Instant> strategy) {
+ metadataExpirationTimeStrategy = Constraint.isNotNull(strategy, "Strategy can not be null");
+ }
+
+ /**
+ * Get the metadata expiration time strategy.
+ *
+ * @return the strategy.
+ */
+ @Nullable protected BiFunction<MetadataType, Instant, Instant> getMetadataExpirationTimeStrategy() {
+ return metadataExpirationTimeStrategy;
+ }
+
/**
* Get the interval at which the cleanup task should run.
*
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/DefaultBatchBackingStore.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/DefaultBatchBackingStore.java
index ba93b88..f07ed35 100644
--- a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/DefaultBatchBackingStore.java
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/DefaultBatchBackingStore.java
@@ -39,6 +39,9 @@ public class DefaultBatchBackingStore<I,T> extends AbstractBackingStore<I, T> im
/** Last time a refresh cycle occurred. */
@Nullable @GuardedBy("this") private Instant lastRefresh;
+
+ /** The bytes of the original loaded metadata.*/
+ @Nullable @GuardedBy("this") private byte[] originalValue;
@Override
@Nullable public synchronized Instant getLastUpdate() {
@@ -60,5 +63,15 @@ public class DefaultBatchBackingStore<I,T> extends AbstractBackingStore<I, T> im
public synchronized void setLastRefresh(@Nullable final Instant refreshedAt) {
lastRefresh = refreshedAt;
}
+
+ @Override
+ @Nullable public synchronized byte[] getOriginalValue() {
+ return originalValue;
+ }
+
+ @Override
+ public synchronized void setOriginalValue(@Nullable final byte[] original) {
+ originalValue = original;
+ }
}
diff --git a/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCacheBuilderTest.java b/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCacheBuilderTest.java
index 1f39ac1..349c849 100644
--- a/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCacheBuilderTest.java
+++ b/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCacheBuilderTest.java
@@ -1,4 +1,3 @@
-package net.shibboleth.oidc.metadata.cache.impl;
/*
* Licensed to the University Corporation for Advanced Internet Development,
* Inc. (UCAID) under one or more contributor license agreements. See the
@@ -16,11 +15,29 @@ package net.shibboleth.oidc.metadata.cache.impl;
* limitations under the License.
*/
+package net.shibboleth.oidc.metadata.cache.impl;/*
+
+ * 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.
+ */
+
import static org.testng.Assert.assertNotNull;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.Duration;
+import java.time.Instant;
import java.util.Collections;
import java.util.List;
@@ -46,7 +63,7 @@ public class BatchMetadataCacheBuilderTest {
spec.setIdentifierExtractionStrategy(m -> m.getIssuer());
spec.setMinRefreshDelay(Duration.ofMinutes(5));
spec.setMaxRefreshDelay(Duration.ofMinutes(10));
- spec.setMetadataExpirationTimeStrategy((m, time) -> time.plus(Duration.ofMinutes(5)));
+ spec.setSourceMetadataExpiryStrategy(b -> Instant.now().plus(Duration.ofMinutes(5)));
spec.setCriteriaToIdentifierStrategy(crit -> {
final IssuerIDCriterion issuerId = crit.get(IssuerIDCriterion.class);
if (issuerId != null) {
@@ -56,8 +73,6 @@ public class BatchMetadataCacheBuilderTest {
});
spec.setRefreshDelayFactor(0.75f);
- spec.setMinCacheDuration(Duration.ofMinutes(10));
- // cache.setMaxCacheDuration(Duration.ofMinutes(20));
spec.setMetadataFilterStrategy((metadata, context) -> metadata);
spec.setLoadingStrategy(new LoadingStrategy() {
diff --git a/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadatCacheTest.java b/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCacheTest.java
similarity index 62%
rename from oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadatCacheTest.java
rename to oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCacheTest.java
index ff35478..10d5a2d 100644
--- a/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadatCacheTest.java
+++ b/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/cache/impl/BatchMetadataCacheTest.java
@@ -1,11 +1,10 @@
/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF 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
+ * 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
*
@@ -19,17 +18,21 @@
package net.shibboleth.oidc.metadata.cache.impl;
import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.Duration;
+import java.time.Instant;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import javax.annotation.Nonnull;
@@ -52,7 +55,7 @@ import net.shibboleth.oidc.metadata.impl.DefaultBatchBackingStore;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
-public class BatchMetadatCacheTest {
+public class BatchMetadataCacheTest {
// OIDC provider metadata cache
private BatchMetadataCache<Issuer, OIDCProviderMetadata> cache;
@@ -72,7 +75,7 @@ public class BatchMetadatCacheTest {
defaultLoadingStrategy = new LoadingStrategy() {
@Override
- public byte[] apply(CacheLoadingContext t) {
+ public byte[] apply(final CacheLoadingContext t) {
try {
return new OIDCProviderMetadata(new Issuer("http://www.example.org"), List.of(SubjectType.PUBLIC),
new URI("http://example.oidc.op.org")).toJSONObject().toJSONString().getBytes();
@@ -98,12 +101,13 @@ public class BatchMetadatCacheTest {
// Give our own executor, so we can manually handle the cleanup task
scheduler = new ManuallyTriggeredScheduledExecutorService();
cache = new BatchMetadataCache<Issuer, OIDCProviderMetadata>(
- new DefaultBatchBackingStore<Issuer, OIDCProviderMetadata>(), defaultLoadingStrategy,
- defaultParsingStrategy, scheduler);
+ new DefaultBatchBackingStore<Issuer, OIDCProviderMetadata>(), scheduler);
+ cache.setSourceMetadataExpiryStrategy(b -> Instant.now().plus(Duration.ofMinutes(5)));
+ cache.setParsingStrategy(defaultParsingStrategy);
+ cache.setLoadingStrategy(defaultLoadingStrategy);
cache.setIdentifierExtractionStrategy(m -> m.getIssuer());
cache.setMinRefreshDelay(Duration.ofMinutes(5));
cache.setMaxRefreshDelay(Duration.ofMinutes(10));
- cache.setMetadataExpirationTimeStrategy((m, time) -> time.plus(Duration.ofMinutes(5)));
cache.setCriteriaToIdentifierStrategy(crit -> {
final IssuerIDCriterion issuerId = crit.get(IssuerIDCriterion.class);
if (issuerId != null) {
@@ -113,8 +117,6 @@ public class BatchMetadatCacheTest {
});
cache.setRefreshDelayFactor(0.75f);
- cache.setMinCacheDuration(Duration.ofMinutes(10));
- // cache.setMaxCacheDuration(Duration.ofMinutes(20));
cache.setMetadataFilterStrategy((metadata, context) -> metadata);
cache.setId("MockRefreshableCache");
// Initialise when you need to use it, if creating a local version, do not init this one.
@@ -135,13 +137,14 @@ public class BatchMetadatCacheTest {
final BatchMetadataCache<Issuer, OIDCProviderMetadata> localCache =
new BatchMetadataCache<Issuer, OIDCProviderMetadata>(
- new DefaultBatchBackingStore<Issuer, OIDCProviderMetadata>(), defaultLoadingStrategy,
- defaultParsingStrategy);
+ new DefaultBatchBackingStore<Issuer, OIDCProviderMetadata>());
+ localCache.setParsingStrategy(defaultParsingStrategy);
+ localCache.setLoadingStrategy(defaultLoadingStrategy);
+ localCache.setSourceMetadataExpiryStrategy(b -> Instant.now().plus(Duration.ofMinutes(5)));
localCache.setIdentifierExtractionStrategy(m -> m.getIssuer());
localCache.setMinRefreshDelay(Duration.ofMillis(100));
localCache.setMaxRefreshDelay(Duration.ofMillis(200));
- localCache.setMetadataExpirationTimeStrategy((m, time) -> time.plus(Duration.ofMinutes(5)));
localCache.setCriteriaToIdentifierStrategy(crit -> {
final IssuerIDCriterion issuerId = crit.get(IssuerIDCriterion.class);
if (issuerId != null) {
@@ -150,8 +153,7 @@ public class BatchMetadatCacheTest {
return null;
});
- localCache.setRefreshDelayFactor(0.75f);
- localCache.setMinCacheDuration(Duration.ofMinutes(10));
+ localCache.setRefreshDelayFactor(0.75f);
localCache.setMetadataFilterStrategy((metadata, context) -> metadata);
localCache.setId("MockLocalRefreshableCache");
localCache.initialize();
@@ -194,13 +196,14 @@ public class BatchMetadatCacheTest {
final BatchMetadataCache<Issuer, OIDCProviderMetadata> localCache =
new BatchMetadataCache<Issuer, OIDCProviderMetadata>(
- new DefaultBatchBackingStore<Issuer, OIDCProviderMetadata>(), simpleLoadingStrategy,
- simpleParsingStrategy, scheduler);
+ new DefaultBatchBackingStore<Issuer, OIDCProviderMetadata>(), scheduler);
+ localCache.setParsingStrategy(simpleParsingStrategy);
+ localCache.setLoadingStrategy(simpleLoadingStrategy);
+ localCache.setSourceMetadataExpiryStrategy(b -> Instant.now().plus(Duration.ofMinutes(5)));
localCache.setIdentifierExtractionStrategy(m -> m.getIssuer());
localCache.setMinRefreshDelay(Duration.ofMillis(100));
- localCache.setMaxRefreshDelay(Duration.ofMillis(200));
- localCache.setMetadataExpirationTimeStrategy((m, time) -> time.plus(Duration.ofMinutes(5)));
+ localCache.setMaxRefreshDelay(Duration.ofMillis(200));
localCache.setCriteriaToIdentifierStrategy(crit -> {
final IssuerIDCriterion issuerId = crit.get(IssuerIDCriterion.class);
if (issuerId != null) {
@@ -209,8 +212,7 @@ public class BatchMetadatCacheTest {
return null;
});
- localCache.setRefreshDelayFactor(0.75f);
- localCache.setMinCacheDuration(Duration.ofMinutes(10));
+ localCache.setRefreshDelayFactor(0.75f);
localCache.setMetadataFilterStrategy((metadata, context) -> metadata);
localCache.setId("MockLocalRefreshableCache");
//If no match, return all
@@ -225,6 +227,99 @@ public class BatchMetadatCacheTest {
}
+
+ @Test(expectedExceptions = ComponentInitializationException.class)
+ public void testInitialLoadFails() throws ComponentInitializationException, MetadataCacheException {
+
+ // throw an exception from the loading strategy
+ cache.setLoadingStrategy(new LoadingStrategy() {
+
+ @Override
+ public byte[] apply(final CacheLoadingContext t) {
+ throw new RuntimeException("Could not load metadata");
+ }
+
+ @Override
+ public String getSourceIdentifier() {
+ return "Mock loading source";
+ }
+ });
+
+ // set a source metadata expiry policy that assumes nonnull source metadata
+ // fake a NPE. cache.get should throw a ComponentInitializationException.
+ cache.setSourceMetadataExpiryStrategy(b -> {throw new NullPointerException("Input bytes are null!");});
+ cache.initialize();
+
+ }
+
+ @Test
+ public void testMetadataExpiryBelowMinDelay_UseMinDelay() throws ComponentInitializationException, MetadataCacheException {
+
+ cache.setMinRefreshDelay(Duration.ofMinutes(10));
+ // Set MD expiry in 1 minute which is below the min refresh delay.
+ cache.setSourceMetadataExpiryStrategy(b -> Instant.now().plus(Duration.ofMinutes(1)));
+ // Set to .99 to make computation easier
+ cache.setRefreshDelayFactor(.99f);
+ cache.initialize();
+ assertTrue(cache.getExecutorService() instanceof ManuallyTriggeredScheduledExecutorService);
+ var manSchedular = (ManuallyTriggeredScheduledExecutorService)cache.getExecutorService();
+ assertNotNull(manSchedular.getAllScheduledTasks());
+ assertEquals(manSchedular.getAllScheduledTasks().size(),1);
+ long delay = manSchedular.getAllScheduledTasks().get(0).getDelay(TimeUnit.SECONDS);
+ // Delay should be close to the min of 10 minutes, not of the metadata 1 minute
+ if (delay > 590 && delay < 610) {
+ // Is close to 10 minutes
+ } else {
+ fail();
+ }
+ }
+
+ @Test
+ public void testMetadataExpiryGreaterThanMin_UseMetadataDelay()
+ throws ComponentInitializationException, MetadataCacheException {
+
+ cache.setMinRefreshDelay(Duration.ofMinutes(10));
+ // Set close to 1 to make computation easier
+ cache.setRefreshDelayFactor(0.99f);
+ // Set MD expiry in 20 minutes which is above the min refresh delay.
+ cache.setSourceMetadataExpiryStrategy(b -> Instant.now().plus(Duration.ofMinutes(20)));
+ cache.initialize();
+ assertTrue(cache.getExecutorService() instanceof ManuallyTriggeredScheduledExecutorService);
+ var manSchedular = (ManuallyTriggeredScheduledExecutorService)cache.getExecutorService();
+ assertNotNull(manSchedular.getAllScheduledTasks());
+ assertEquals(manSchedular.getAllScheduledTasks().size(),1);
+ long delay = manSchedular.getAllScheduledTasks().get(0).getDelay(TimeUnit.SECONDS);
+ // Delay should be close to the metadata value of 20 minutes, not of min delay
+ if (delay > 1100 && delay < 1300) {
+ // Is close to 20 minutes
+ } else {
+ fail();
+ }
+ }
+
+ @Test
+ public void testMetadataExpiryIsBeforeNow_UseMaxDelay()
+ throws ComponentInitializationException, MetadataCacheException {
+
+ cache.setMaxRefreshDelay(Duration.ofMinutes(10));
+ // Set close to 1 to make computation easier
+ cache.setRefreshDelayFactor(0.99f);
+ // Set MD expiry in 20 minutes which is above the min refresh delay.
+ cache.setSourceMetadataExpiryStrategy(b -> Instant.now().minus(Duration.ofMinutes(20)));
+ cache.initialize();
+ assertTrue(cache.getExecutorService() instanceof ManuallyTriggeredScheduledExecutorService);
+ var manSchedular = (ManuallyTriggeredScheduledExecutorService)cache.getExecutorService();
+ assertNotNull(manSchedular.getAllScheduledTasks());
+ assertEquals(manSchedular.getAllScheduledTasks().size(),1);
+ long delay = manSchedular.getAllScheduledTasks().get(0).getDelay(TimeUnit.SECONDS);
+ // Delay should be close to the metadata value of 20 minutes, not of min delay
+ if (delay > 590 && delay < 610) {
+ // Is close to 10 minutes
+ } else {
+ fail();
+ }
+ }
+
@Test
public void testNoUsableIdentifierInCriteria_EmptyList() throws ComponentInitializationException,
InterruptedException, MetadataCacheException {
diff --git a/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCacheTest.java b/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCacheTest.java
index 989b8a6..644954c 100644
--- a/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCacheTest.java
+++ b/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/cache/impl/DynamicMetadataCacheTest.java
@@ -72,13 +72,13 @@ public class DynamicMetadataCacheTest {
final Issuer iss = crit.get(IssuerIDCriterion.class).getIssuerID();
return new OIDCProviderMetadata(iss, List.of(SubjectType.PUBLIC),
new URI("http://example.oidc.op.org"));
- } catch (URISyntaxException e) {
+ } catch (final URISyntaxException e) {
return null;
}
};
// Give our own executor, so we can manually handle the cleanup task
- ManuallyTriggeredScheduledExecutorService scheduler = new ManuallyTriggeredScheduledExecutorService();
+ final ManuallyTriggeredScheduledExecutorService scheduler = new ManuallyTriggeredScheduledExecutorService();
cache = new DynamicMetadataCache<Issuer, OIDCProviderMetadata>
(new DefaultDynamicBackingStore<>(Duration.ofMinutes(5)),defaultFetchStrategy, scheduler);
cache.setIdentifierExtractionStrategy(m -> m.getIssuer());
@@ -174,8 +174,7 @@ public class DynamicMetadataCacheTest {
// Create but do not initialise
DynamicMetadataCache<Issuer, OIDCProviderMetadata> cacheLocal = new DynamicMetadataCache<>(
- new DefaultDynamicBackingStore<>(Duration.ofMinutes(5)),
- defaultFetchStrategy);
+ new DefaultDynamicBackingStore<>(Duration.ofMinutes(5)));
final Issuer iss = new Issuer("https://example.oidc.op.org");
cacheLocal.get(new CriteriaSet(new IssuerIDCriterion(iss)));
}
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
index 28dcf31..0959c97 100644
--- 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
@@ -1,3 +1,21 @@
+/*
+ * 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 static org.testng.Assert.assertNotNull;
@@ -6,6 +24,7 @@ import static org.testng.Assert.assertTrue;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.time.Duration;
+import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
@@ -123,18 +142,20 @@ public class OIDCMapMetadataResolverTest {
final Function<byte[], List<Map<String,Object>>> parsingStrat =
in -> {
try {
- ObjectMapper mapper = new ObjectMapper();
+ final ObjectMapper mapper = new ObjectMapper();
return List.of(
mapper.readValue(new String(in, "UTF-8"), new TypeReference<Map<String,Object>>(){}));
- } catch (UnsupportedEncodingException | JsonProcessingException e) {
+ } catch (final UnsupportedEncodingException | JsonProcessingException e) {
return null;
}
};
- ManuallyTriggeredScheduledExecutorService scheduler = new ManuallyTriggeredScheduledExecutorService();
+ final ManuallyTriggeredScheduledExecutorService scheduler = new ManuallyTriggeredScheduledExecutorService();
batchCache = new TestableBatchMetadataCache<String, Map<String,Object>>(new DefaultBatchBackingStore<>(),
- metadataLoadingStrat, parsingStrat, scheduler);
+ scheduler);
+ batchCache.setParsingStrategy(parsingStrat);
+ batchCache.setLoadingStrategy(metadataLoadingStrat);
batchCache.setId("MockBatchCache");
batchCache.setIdentifierExtractionStrategy(m -> m.get("issuer").toString());
batchCache.setCriteriaToIdentifierStrategy(crit -> {
@@ -147,10 +168,9 @@ public class OIDCMapMetadataResolverTest {
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.setSourceMetadataExpiryStrategy(b -> Instant.now().plus(Duration.ofMinutes(5)));
batchCache.initialize();
batchResolver = new OIDCMapBasedMetadataResolver(batchCache);
@@ -162,7 +182,7 @@ public class OIDCMapMetadataResolverTest {
@Test
void testBatchResolve_Success() throws ResolverException, IOException, ComponentInitializationException {
batchResolver.initialize();
- Iterable<Map<String,Object>> found =
+ final Iterable<Map<String,Object>> found =
batchResolver.resolve(new CriteriaSet(new IssuerIDCriterion(new Issuer("https://example.oidc.op.org"))));
assertNotNull(found);
assertTrue(found.iterator().hasNext());
@@ -172,11 +192,9 @@ public class OIDCMapMetadataResolverTest {
class TestableBatchMetadataCache<IdentifierType, MetadataType>
extends BatchMetadataCache<IdentifierType, MetadataType> {
- TestableBatchMetadataCache(@Nonnull final BatchBackingStore<IdentifierType, MetadataType> store,
- @Nonnull final LoadingStrategy metadataLoadingStrategy,
- @Nonnull final Function<byte[], List<MetadataType>> parseStrategy,
+ TestableBatchMetadataCache(@Nonnull final BatchBackingStore<IdentifierType, MetadataType> store,
@Nullable final ScheduledExecutorService executor) {
- super(store, metadataLoadingStrategy, parseStrategy, executor);
+ super(store, executor);
}
/* Expose the backing store with a public method.*/
diff --git a/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/impl/OIDCProviderMetadataResolverTest.java b/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/impl/OIDCProviderMetadataResolverTest.java
index 0a7985a..d0b9fc5 100644
--- a/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/impl/OIDCProviderMetadataResolverTest.java
+++ b/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/impl/OIDCProviderMetadataResolverTest.java
@@ -1,3 +1,21 @@
+/*
+ * 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 static org.mockito.ArgumentMatchers.any;
@@ -56,7 +74,7 @@ import net.shibboleth.utilities.java.support.resolver.ResolverException;
/** Tests for the {@link OIDCProviderMetadataResolver} .*/
public class OIDCProviderMetadataResolverTest {
- private final String GOOD_PROVIDER_CONFIGURATION_INFO = "{\n"
+ private final static 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"
@@ -121,6 +139,7 @@ public class OIDCProviderMetadataResolverTest {
/** A metadata resolver which has a batch based cache.*/
private OIDCProviderMetadataResolver batchResolver;
+ /** A test HTTP client.*/
private HttpClient httpClient;
/**
@@ -178,8 +197,10 @@ public class OIDCProviderMetadataResolverTest {
ManuallyTriggeredScheduledExecutorService scheduler = new ManuallyTriggeredScheduledExecutorService();
batchCache = new TestableBatchMetadataCache<Issuer, OIDCProviderMetadata>(new DefaultBatchBackingStore<>(),
- metadataLoadingStrat, parsingStrat, scheduler);
+ scheduler);
+ batchCache.setParsingStrategy(parsingStrat);
+ batchCache.setLoadingStrategy(metadataLoadingStrat);
batchCache.setId("MockBatchCache");
batchCache.setIdentifierExtractionStrategy(m -> m.getIssuer());
batchCache.setCriteriaToIdentifierStrategy(crit -> {
@@ -192,10 +213,9 @@ public class OIDCProviderMetadataResolverTest {
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.setSourceMetadataExpiryStrategy(n -> Instant.now().plus(Duration.ofMinutes(5)));
batchCache.initialize();
}
@@ -434,10 +454,8 @@ public class OIDCProviderMetadataResolverTest {
extends BatchMetadataCache<IdentifierType, MetadataType> {
TestableBatchMetadataCache(@Nonnull final BatchBackingStore<IdentifierType, MetadataType> store,
- @Nonnull final LoadingStrategy metadataLoadingStrategy,
- @Nonnull final Function<byte[], List<MetadataType>> parseStrategy,
@Nullable final ScheduledExecutorService executor) {
- super(store, metadataLoadingStrategy, parseStrategy, executor);
+ super(store, executor);
}
/* Expose the backing store with a public method.*/
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list