[java-oidc-common] branch main updated: JCOMOIDC-22 - Move ProviderMetadataResolver implementations from OP to commons
Phil Smart
philip.smart at jisc.ac.uk
Fri Aug 6 13:06:25 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=12a57e8761033cce5a7eda38030bc15480450161
The following commit(s) were added to refs/heads/main by this push:
new 12a57e8 JCOMOIDC-22 - Move ProviderMetadataResolver implementations from OP to commons
12a57e8 is described below
commit 12a57e8761033cce5a7eda38030bc15480450161
Author: Phil Smart <philip.smart at jisc.ac.uk>
AuthorDate: Fri Aug 6 14:06:19 2021 +0100
JCOMOIDC-22 - Move ProviderMetadataResolver implementations from OP to
commons
- Moved ProviderMetadataResolver related classes into oidc-common.
Changed the resolver public API from accepting a PRC criteria type to a
CriteriaSet.
- Added new chaining providers
- Converted the serviceable provider to a container type.
https://issues.shibboleth.net/jira/browse/JCOMOIDC-22
---
oidc-common-metadata-api/pom.xml | 7 +-
...java => ProviderMetadataProviderContainer.java} | 102 +++--------
.../oidc/metadata/ProviderMetadataResolver.java | 37 ++++
.../RefreshableProviderMetadataResolver.java | 60 ++++++
.../RelyingPartyClientInformationProvider.java | 2 +-
.../oidc/metadata/criterion/IssuerIDCriterion.java | 73 ++++++++
oidc-common-metadata-impl/pom.xml | 5 +
.../metadata/impl/AbstractOIDCEntityResolver.java | 15 ++
.../impl/ChainingProviderMetadataResolver.java | 202 +++++++++++++++++++++
.../impl/FilesystemProviderMetadataResolver.java | 146 +++++++++++++++
.../impl/ReloadingProviderMetadataProvider.java | 90 +++++++++
.../ProviderMetadataResolverServiceStrategy.java | 101 +++++++++++
.../impl/ChainingProviderMetadataResolverTest.java | 59 ++++++
...loadClientResolverServiceConfigurationTest.java | 1 -
...rMetadataResolverServiceConfigurationTest.java} | 37 ++--
.../impl/oidc-provider-metadata-providers.xml | 40 ++++
.../impl/oidc-provider-metadata-providers2.xml | 56 ++++++
.../oidc/metadata/impl/openid-configuration.json | 55 ++++++
18 files changed, 983 insertions(+), 105 deletions(-)
diff --git a/oidc-common-metadata-api/pom.xml b/oidc-common-metadata-api/pom.xml
index 10b0186..88e8daf 100644
--- a/oidc-common-metadata-api/pom.xml
+++ b/oidc-common-metadata-api/pom.xml
@@ -23,7 +23,12 @@
<groupId>net.shibboleth.idp</groupId>
<artifactId>idp-core</artifactId>
<scope>provided</scope>
- </dependency>
+ </dependency>
+ <dependency>
+ <groupId>${opensaml.groupId}</groupId>
+ <artifactId>opensaml-profile-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>oauth2-oidc-sdk</artifactId>
diff --git a/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/RelyingPartyClientInformationProvider.java b/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/ProviderMetadataProviderContainer.java
similarity index 50%
copy from oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/RelyingPartyClientInformationProvider.java
copy to oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/ProviderMetadataProviderContainer.java
index 228f898..03a6253 100644
--- a/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/RelyingPartyClientInformationProvider.java
+++ b/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/ProviderMetadataProviderContainer.java
@@ -17,51 +17,40 @@
package net.shibboleth.oidc.metadata;
-import java.time.Instant;
-
import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Objects;
-import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
import net.shibboleth.ext.spring.service.AbstractServiceableComponent;
import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.component.ComponentSupport;
import net.shibboleth.utilities.java.support.logic.Constraint;
-import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
-import net.shibboleth.utilities.java.support.resolver.ResolverException;
/**
- * This class exists primarily to allow the parsing of relying-party.xml to create a serviceable implementation of
- * {@link ClientInformationResolver}. Based on net.shibboleth.idp.saml.metadata.RelyingPartyMetadataProvider.
+ * This class is a sortable container of {@link ProviderMetadataResolver}s, wrapped into a serviceable component.
+ *
*/
-
-public class RelyingPartyClientInformationProvider extends AbstractServiceableComponent<ClientInformationResolver>
- implements RefreshableClientInformationResolver, Comparable<RelyingPartyClientInformationProvider> {
+public class ProviderMetadataProviderContainer extends AbstractServiceableComponent<ProviderMetadataResolver>
+ implements Comparable<ProviderMetadataProviderContainer> {
/** If we autogenerate a sort key it comes from this count. */
private static int sortKeyValue;
/** Class logger. */
- private final Logger log = LoggerFactory.getLogger(RelyingPartyClientInformationProvider.class);
+ private final Logger log = LoggerFactory.getLogger(ProviderMetadataProviderContainer.class);
/** The embedded resolver. */
@NonnullAfterInit
- private ClientInformationResolver resolver;
+ private ProviderMetadataResolver resolver;
/** The key by which we sort the provider. */
@NonnullAfterInit
private Integer sortKey;
- /** Constructor. */
- public RelyingPartyClientInformationProvider() {
- }
-
/**
* Set the sort key.
*
@@ -69,18 +58,18 @@ public class RelyingPartyClientInformationProvider extends AbstractServiceableCo
*/
public void setSortKey(final int key) {
ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
- sortKey = Integer.valueOf(key);
+ sortKey = key;
}
/**
- * Set the {@link ClientInformationResolver} to embed.
+ * Set the {@link ProviderMetadataResolver} to embed.
*
- * @param theResolver The {@link ClientInformationResolver} to embed.
+ * @param theResolver The {@link ProviderMetadataResolver} to embed.
*/
@Nonnull
- public void setEmbeddedResolver(@Nonnull final ClientInformationResolver theResolver) {
+ public void setEmbeddedResolver(@Nonnull final ProviderMetadataResolver theResolver) {
ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
- resolver = Constraint.isNotNull(theResolver, "ClientInformationResolver cannot be null");
+ resolver = Constraint.isNotNull(theResolver, "ProviderMetadataResolver cannot be null");
}
/**
@@ -89,83 +78,38 @@ public class RelyingPartyClientInformationProvider extends AbstractServiceableCo
* @return the parameter we got as a constructor
*/
@Nonnull
- public ClientInformationResolver getEmbeddedResolver() {
+ public ProviderMetadataResolver getEmbeddedResolver() {
return resolver;
}
- /** {@inheritDoc} */
- @Override
- @Nonnull
- public Iterable<OIDCClientInformation> resolve(@Nullable final CriteriaSet criteria) throws ResolverException {
- return resolver.resolve(criteria);
- }
-
- /** {@inheritDoc} */
- @Override
- @Nullable
- public OIDCClientInformation resolveSingle(@Nullable final CriteriaSet criteria) throws ResolverException {
-
- return resolver.resolveSingle(criteria);
- }
-
- /** {@inheritDoc} */
@Override
protected void doInitialize() throws ComponentInitializationException {
setId(resolver.getId());
super.doInitialize();
if (null == resolver) {
- throw new ComponentInitializationException("ClientInformationResolver cannot be null");
+ throw new ComponentInitializationException("ProviderMetadataResolver cannot be null");
}
if (null == sortKey) {
- synchronized (this) {
+ synchronized (ProviderMetadataProviderContainer.class) {
sortKeyValue++;
- sortKey = Integer.valueOf(sortKeyValue);
+ setSortKey(sortKeyValue);
}
- log.info("Top level ClientInformation Provider '{}' did not have a sort key; giving it value '{}'", getId(),
+ log.info("Top level ProviderMetadata Provider '{}' did not have a sort key; giving it value '{}'", getId(),
sortKey);
}
}
+
- /** {@inheritDoc} */
@Override
@Nonnull
- public ClientInformationResolver getComponent() {
- return this;
+ public ProviderMetadataResolver getComponent() {
+ return getEmbeddedResolver();
}
- /** {@inheritDoc} */
@Override
- public void refresh() throws ResolverException {
- if (resolver instanceof RefreshableClientInformationResolver) {
- ((RefreshableClientInformationResolver) resolver).refresh();
- }
- }
-
- /** {@inheritDoc} */
- @Override
- public Instant getLastRefresh() {
- if (resolver instanceof RefreshableClientInformationResolver) {
- return ((RefreshableClientInformationResolver) resolver).getLastRefresh();
- } else {
- return null;
- }
- }
-
- /** {@inheritDoc} */
- @Override
- public Instant getLastUpdate() {
- if (resolver instanceof RefreshableClientInformationResolver) {
- return ((RefreshableClientInformationResolver) resolver).getLastUpdate();
- } else {
- return null;
- }
- }
-
- /** {@inheritDoc} */
- @Override
- public int compareTo(final RelyingPartyClientInformationProvider other) {
+ public int compareTo(final ProviderMetadataProviderContainer other) {
ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
final int result = sortKey.compareTo(other.sortKey);
if (result != 0) {
@@ -186,18 +130,16 @@ public class RelyingPartyClientInformationProvider extends AbstractServiceableCo
if (null == other) {
return false;
}
- if (!(other instanceof RelyingPartyClientInformationProvider)) {
+ if (!(other instanceof ProviderMetadataProviderContainer)) {
return false;
}
- final RelyingPartyClientInformationProvider otherRp = (RelyingPartyClientInformationProvider) other;
+ final ProviderMetadataProviderContainer otherRp = (ProviderMetadataProviderContainer) other;
return Objects.equal(otherRp.sortKey, sortKey) && Objects.equal(getId(), otherRp.getId());
}
- /** {@inheritDoc} */
@Override
public int hashCode() {
return Objects.hashCode(sortKey, getId());
}
-
}
\ No newline at end of file
diff --git a/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/ProviderMetadataResolver.java b/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/ProviderMetadataResolver.java
new file mode 100644
index 0000000..0beb81a
--- /dev/null
+++ b/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/ProviderMetadataResolver.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 com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+
+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 {@link OIDCProviderMetadata} instances which meet certain supplied criteria.
+ * <p>
+ * At a minimum, a {@link OIDCProviderMetadata} implementation MUST support the following criteria:</p>
+ * <ul>
+ * <li>{@link IssuerIDCriterion}</li>
+ * </ul>
+ */
+public interface ProviderMetadataResolver extends Resolver<OIDCProviderMetadata, CriteriaSet>, IdentifiedComponent {
+
+}
diff --git a/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/RefreshableProviderMetadataResolver.java b/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/RefreshableProviderMetadataResolver.java
new file mode 100644
index 0000000..f7e4d24
--- /dev/null
+++ b/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/RefreshableProviderMetadataResolver.java
@@ -0,0 +1,60 @@
+/*
+ * 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.time.Instant;
+
+import javax.annotation.Nullable;
+
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
+
+/**
+ * Specialization of {@link ProviderMetadataResolver} that supports on-demand refresh.
+ */
+public interface RefreshableProviderMetadataResolver extends ProviderMetadataResolver {
+
+ /**
+ * Refresh the data exposed by the resolver.
+ *
+ * <p>
+ * An implementation of this method should typically be either <code>synchronized</code>
+ * or make use other locking mechanisms to protect against concurrent access.
+ * </p>
+ *
+ * @throws ResolverException if the refresh operation was unsuccessful
+ */
+ void refresh() throws ResolverException;
+
+ /**
+ * Gets the time the last refresh cycle occurred.
+ *
+ * @return time the last refresh cycle occurred
+ */
+ @Nullable Instant getLastRefresh();
+
+ /**
+ * Get the time that the currently available metadata was last updated. Note, this may be different than
+ * the time retrieved by {@link #getLastRefresh()} is the metadata was known not to have changed during
+ * the last refresh cycle.
+ *
+ * @return time when the currently metadata was last updated, null if it has never successfully been read
+ * in
+ */
+ @Nullable Instant getLastUpdate();
+
+}
\ No newline at end of file
diff --git a/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/RelyingPartyClientInformationProvider.java b/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/RelyingPartyClientInformationProvider.java
index 228f898..281170a 100644
--- a/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/RelyingPartyClientInformationProvider.java
+++ b/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/RelyingPartyClientInformationProvider.java
@@ -40,7 +40,7 @@ import net.shibboleth.utilities.java.support.resolver.ResolverException;
* This class exists primarily to allow the parsing of relying-party.xml to create a serviceable implementation of
* {@link ClientInformationResolver}. Based on net.shibboleth.idp.saml.metadata.RelyingPartyMetadataProvider.
*/
-
+//TODO should change to a Container e.g. ProviderMetadataProviderContainer.java
public class RelyingPartyClientInformationProvider extends AbstractServiceableComponent<ClientInformationResolver>
implements RefreshableClientInformationResolver, Comparable<RelyingPartyClientInformationProvider> {
diff --git a/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/criterion/IssuerIDCriterion.java b/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/criterion/IssuerIDCriterion.java
new file mode 100644
index 0000000..67ace84
--- /dev/null
+++ b/oidc-common-metadata-api/src/main/java/net/shibboleth/oidc/metadata/criterion/IssuerIDCriterion.java
@@ -0,0 +1,73 @@
+package net.shibboleth.oidc.metadata.criterion;
+
+import java.util.Objects;
+
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.Immutable;
+
+import com.nimbusds.oauth2.sdk.id.Issuer;
+
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.resolver.Criterion;
+
+/**
+ * A {@link Criterion} representing an OIDC issuer ID.
+ */
+ at Immutable
+public final class IssuerIDCriterion implements Criterion {
+
+ /** The issuer ID. */
+ @Nonnull @NotEmpty private final Issuer id;
+
+ /**
+ *
+ * Constructor.
+ *
+ * @param issuerId the id of the issuer, can not be null or empty
+ */
+ public IssuerIDCriterion(@Nonnull final Issuer issuerId) {
+ id = Constraint.isNotNull(issuerId, "Issuer ID can not be null");
+ }
+
+ /**
+ * Get the issuer ID.
+ *
+ * @return the issuer ID.
+ */
+ @Nonnull public Issuer getIssuerID() {
+ return id;
+ }
+
+
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return "IssuerIDCriterion [id=" + id + "]";
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int hashCode() {
+ return Objects.hash(id);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ IssuerIDCriterion other = (IssuerIDCriterion) obj;
+ return id.equals(other.id);
+ }
+
+
+
+
+
+}
diff --git a/oidc-common-metadata-impl/pom.xml b/oidc-common-metadata-impl/pom.xml
index 5d83809..43bb419 100644
--- a/oidc-common-metadata-impl/pom.xml
+++ b/oidc-common-metadata-impl/pom.xml
@@ -31,6 +31,11 @@
<artifactId>spring-extensions</artifactId>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>net.shibboleth.idp</groupId>
+ <artifactId>idp-profile-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-profile-api</artifactId>
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/AbstractOIDCEntityResolver.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/AbstractOIDCEntityResolver.java
index 0507a7b..a3ef4af 100644
--- a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/AbstractOIDCEntityResolver.java
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/AbstractOIDCEntityResolver.java
@@ -55,6 +55,9 @@ public abstract class AbstractOIDCEntityResolver<Key extends Identifier, Value>
/** Backing store for runtime JSON data. */
private JsonBackingStore jsonBackingStore;
+ /** Logging prefix. */
+ private String logPrefix;
+
/**
* Whether problems during initialization should cause the provider to fail or go on without metadata. The
* assumption being that in most cases a provider will recover at some point in the future. Default: true.
@@ -67,6 +70,18 @@ public abstract class AbstractOIDCEntityResolver<Key extends Identifier, Value>
public AbstractOIDCEntityResolver() {
failFastInitialization = true;
}
+
+ /**
+ * Return a prefix for logging messages for this component.
+ *
+ * @return a string for insertion at the beginning of any log messages
+ */
+ @Nonnull @NotEmpty protected String getLogPrefix() {
+ if (logPrefix == null) {
+ logPrefix = String.format("Metadata Resolver %s %s:", getClass().getSimpleName(), getId());
+ }
+ return logPrefix;
+ }
/**
* Gets whether problems during initialization should cause the provider to fail or go on without metadata. The
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/ChainingProviderMetadataResolver.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/ChainingProviderMetadataResolver.java
new file mode 100644
index 0000000..c79a56d
--- /dev/null
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/ChainingProviderMetadataResolver.java
@@ -0,0 +1,202 @@
+package net.shibboleth.oidc.metadata.impl;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+
+import net.shibboleth.oidc.metadata.ProviderMetadataResolver;
+import net.shibboleth.oidc.metadata.RefreshableProviderMetadataResolver;
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotLive;
+import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
+import net.shibboleth.utilities.java.support.component.AbstractIdentifiableInitializableComponent;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
+
+/**
+ * A provider metadata provider that uses registered resolvers, in turn, to answer queries.
+ *
+ * The Iterable of provider metadata returned is the first non-null and non-empty Iterable found while iterating over
+ * the registered resolvers in resolver list order.
+ */
+public class ChainingProviderMetadataResolver extends AbstractIdentifiableInitializableComponent implements
+ RefreshableProviderMetadataResolver {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(ChainingProviderMetadataResolver.class);
+
+ /** Registered resolvers. */
+ @Nonnull @NonnullElements private List<ProviderMetadataResolver> resolvers;
+
+ /** Constructor. */
+ public ChainingProviderMetadataResolver() {
+ resolvers = Collections.emptyList();
+ }
+
+ /**
+ * Get an immutable the list of currently registered resolvers.
+ *
+ * @return list of currently registered resolvers
+ */
+ @Nonnull @NonnullElements @Unmodifiable @NotLive public List<ProviderMetadataResolver> getResolvers() {
+ return resolvers;
+ }
+
+ /**
+ * Set the registered provider metadata resolvers.
+ *
+ * @param newResolvers the provider metadata resolvers to use
+ *
+ * @throws ResolverException thrown if there is a problem adding the provider metadata resolvers
+ */
+ public void setResolvers(@Nullable @NonnullElements final List<? extends ProviderMetadataResolver> newResolvers)
+ throws ResolverException {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+ ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+
+ if (newResolvers == null || newResolvers.isEmpty()) {
+ resolvers = Collections.emptyList();
+ } else {
+ resolvers = List.copyOf(newResolvers);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nullable public OIDCProviderMetadata resolveSingle(@Nullable final CriteriaSet criteria)
+ throws ResolverException {
+ ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
+
+ final Iterable<OIDCProviderMetadata> iterable = resolve(criteria);
+ if (iterable != null) {
+ final Iterator<OIDCProviderMetadata> iterator = iterable.iterator();
+ if (iterator != null && iterator.hasNext()) {
+ return iterator.next();
+ }
+ }
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nonnull public Iterable<OIDCProviderMetadata> resolve(@Nullable final CriteriaSet criteria)
+ throws ResolverException {
+ ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
+
+ for (final ProviderMetadataResolver resolver : resolvers) {
+ try {
+ final Iterable<OIDCProviderMetadata> providerMetadatas = resolver.resolve(criteria);
+ if (providerMetadatas != null && providerMetadatas.iterator().hasNext()) {
+ return providerMetadatas;
+ }
+ } catch (final ResolverException e) {
+ log.warn("Error retrieving provider metadata from resolver of type {}, proceeding to next resolver",
+ resolver.getClass().getName(), e);
+ continue;
+ }
+ }
+
+ return Collections.emptyList();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void refresh() throws ResolverException {
+ for (final ProviderMetadataResolver resolver : resolvers) {
+ if (resolver instanceof RefreshableProviderMetadataResolver) {
+ ((RefreshableProviderMetadataResolver) resolver).refresh();
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nullable public Instant getLastUpdate() {
+ Instant ret = null;
+ for (final ProviderMetadataResolver resolver : resolvers) {
+ if (resolver instanceof RefreshableProviderMetadataResolver) {
+ final Instant lastUpdate = ((RefreshableProviderMetadataResolver) resolver).getLastUpdate();
+ if (ret == null || ret.isBefore(lastUpdate)) {
+ ret = lastUpdate;
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nullable public Instant getLastRefresh() {
+ Instant ret = null;
+ for (final ProviderMetadataResolver resolver : resolvers) {
+ if (resolver instanceof RefreshableProviderMetadataResolver) {
+ final Instant lastRefresh = ((RefreshableProviderMetadataResolver) resolver).getLastRefresh();
+ if (ret == null || ret.isBefore(lastRefresh)) {
+ ret = lastRefresh;
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+ if (resolvers == null) {
+ log.warn("ProviderMetadataResolver was not configured with any member " +
+ "ProviderMetadataResolvers");
+ resolvers = Collections.emptyList();
+ } else {
+ final List<String> resolverDetails = new ArrayList<>();
+ for (final ProviderMetadataResolver resolver : resolvers) {
+ resolverDetails.add(resolver.getId() + ": " + countClients(resolver) + " clients");
+ }
+ log.info("ChainingProviderMetadataResolver was configured with the following resolvers: {}",
+ resolverDetails);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void doDestroy() {
+ super.doDestroy();
+ resolvers = Collections.emptyList();
+ }
+
+ /**
+ * Counts the clients found from the given resolver.
+ *
+ * @param resolver The resolver whose clients are counted.
+ * @return The amount of resolvable clients.
+ */
+ protected int countClients(final ProviderMetadataResolver resolver) {
+ int count = 0;
+ final Iterable<OIDCProviderMetadata> iterable;
+ try {
+ iterable = resolver.resolve(new CriteriaSet());
+ } catch (final ResolverException e) {
+ log.warn("ChainingProviderMetadataResolver could not count clients for {}", resolver.getId());
+ return 0;
+ }
+
+ final Iterator<OIDCProviderMetadata> iterator = iterable.iterator();
+ while (iterator.hasNext()) {
+ iterator.next();
+ count++;
+ }
+ return count;
+ }
+
+}
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/FilesystemProviderMetadataResolver.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/FilesystemProviderMetadataResolver.java
new file mode 100644
index 0000000..32321df
--- /dev/null
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/FilesystemProviderMetadataResolver.java
@@ -0,0 +1,146 @@
+/*
+ * 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.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Timer;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.io.Resource;
+
+import com.nimbusds.oauth2.sdk.ParseException;
+import com.nimbusds.oauth2.sdk.id.Issuer;
+import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
+import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.oidc.metadata.RefreshableProviderMetadataResolver;
+import net.shibboleth.oidc.metadata.criterion.IssuerIDCriterion;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
+
+/**
+ * Based on {@link org.opensaml.saml.metadata.resolver.impl.FilesystemMetadataResolver}.
+ */
+public class FilesystemProviderMetadataResolver extends AbstractFileOIDCEntityResolver<Issuer, OIDCProviderMetadata>
+ implements RefreshableProviderMetadataResolver {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(FilesystemProviderMetadataResolver.class);
+
+ /**
+ * Strategy used to locate the {@link RelyingPartyContext} associated with a given {@link ProfileRequestContext}.
+ */
+ @Nonnull private Function<ProfileRequestContext, RelyingPartyContext> relyingPartyContextLookupStrategy;
+
+ /**
+ * Constructor.
+ *
+ * @param metadata the metadata file
+ *
+ * @throws IOException If the metedata cannot be loaded.
+ */
+ public FilesystemProviderMetadataResolver(@Nonnull final Resource metadata) throws IOException {
+ super(metadata);
+ relyingPartyContextLookupStrategy = new ChildContextLookup<>(RelyingPartyContext.class);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param metadata the metadata file
+ * @param backgroundTaskTimer timer used to refresh metadata in the background
+ *
+ * @throws IOException If the metedata cannot be loaded.
+ */
+ public FilesystemProviderMetadataResolver(@Nullable final Timer backgroundTaskTimer,
+ @Nonnull final Resource metadata) throws IOException {
+ super(backgroundTaskTimer, metadata);
+ relyingPartyContextLookupStrategy = new ChildContextLookup<>(RelyingPartyContext.class);
+ }
+
+ /**
+ * Set the strategy used to locate the {@link RelyingPartyContext} associated with a given
+ * {@link ProfileRequestContext}.
+ *
+ * @param strategy strategy used to locate the {@link RelyingPartyContext} associated with a given
+ * {@link ProfileRequestContext}
+ */
+ public void setRelyingPartyContextLookupStrategy(
+ @Nonnull final Function<ProfileRequestContext, RelyingPartyContext> strategy) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ relyingPartyContextLookupStrategy =
+ Constraint.isNotNull(strategy, "RelyingPartyContext lookup strategy cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nullable public Iterable<OIDCProviderMetadata> resolve(@Nullable final CriteriaSet criteria)
+ throws ResolverException {
+ ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
+ ComponentSupport.ifDestroyedThrowDestroyedComponentException(this);
+
+ final IssuerIDCriterion issuerIdCriterion = criteria.get(IssuerIDCriterion.class);
+ if (issuerIdCriterion == null) {
+ log.trace("No issuer ID criteria found, returning all");
+ return new ArrayList<>(getBackingStore().getOrderedInformation());
+ }
+ // TODO: support other criterion
+ return new ArrayList<>((lookupIdentifier(issuerIdCriterion.getIssuerID())));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nullable public OIDCProviderMetadata resolveSingle(@Nullable final CriteriaSet criteria)
+ throws ResolverException {
+ final Iterable<OIDCProviderMetadata> iterable = resolve(criteria);
+ if (iterable != null) {
+ final Iterator<OIDCProviderMetadata> iterator = iterable.iterator();
+ if (iterator != null && iterator.hasNext()) {
+ return iterator.next();
+ }
+ }
+ log.warn("Could not find any provider metadata with the given criteria");
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected List<OIDCProviderMetadata> parse(final byte[] bytes) throws ParseException {
+ return List.of(OIDCProviderMetadata.parse(JSONObjectUtils.parse(new String(bytes))));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected Issuer getKey(@Nonnull final OIDCProviderMetadata value) {
+ return value.getIssuer();
+ }
+}
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/ReloadingProviderMetadataProvider.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/ReloadingProviderMetadataProvider.java
new file mode 100644
index 0000000..9e13bf8
--- /dev/null
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/metadata/impl/ReloadingProviderMetadataProvider.java
@@ -0,0 +1,90 @@
+package net.shibboleth.oidc.metadata.impl;
+
+import java.util.Collections;
+
+import javax.annotation.Nonnull;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+
+import net.shibboleth.oidc.metadata.ProviderMetadataResolver;
+import net.shibboleth.utilities.java.support.component.AbstractIdentifiableInitializableComponent;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
+import net.shibboleth.utilities.java.support.service.ReloadableService;
+import net.shibboleth.utilities.java.support.service.ServiceableComponent;
+
+
+/**
+ * This class uses the service interface to implement {@link ProviderMetadataResolver}.
+ */
+public class ReloadingProviderMetadataProvider extends AbstractIdentifiableInitializableComponent
+ implements ProviderMetadataResolver {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(ReloadingProviderMetadataProvider.class);
+
+ /** The service which managed the reloading. */
+ private final ReloadableService<ProviderMetadataResolver> service;
+
+ /**
+ * Constructor.
+ *
+ * @param resolverService the service which will manage the loading.
+ */
+ public ReloadingProviderMetadataProvider(
+ @Nonnull final ReloadableService<ProviderMetadataResolver> resolverService) {
+ service = Constraint.isNotNull(resolverService, "IssuerMetadataResolver Service cannot be null");
+ }
+
+ @Override
+ public Iterable<OIDCProviderMetadata> resolve(CriteriaSet criteria) throws ResolverException {
+ ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
+ ServiceableComponent<ProviderMetadataResolver> component = null;
+ try {
+ component = service.getServiceableComponent();
+ if (null == component) {
+ log.error("ReloadingIssuerMetadataProvider '{}': Error accessing underlying source: "
+ + "Invalid configuration.", getId());
+ } else {
+ final ProviderMetadataResolver resolver = component.getComponent();
+ return resolver.resolve(criteria);
+ }
+ } catch (final ResolverException e) {
+ log.error("IssuerMetadataResolver '{}': Error during resolution", getId(), e);
+ } finally {
+ if (null != component) {
+ component.unpinComponent();
+ }
+ }
+ return Collections.emptySet();
+ }
+
+ @Override
+ public OIDCProviderMetadata resolveSingle(CriteriaSet criteria) throws ResolverException {
+ ComponentSupport.ifNotInitializedThrowUninitializedComponentException(this);
+ ServiceableComponent<ProviderMetadataResolver> component = null;
+ try {
+ component = service.getServiceableComponent();
+ if (null == component) {
+ log.error("ReloadingIssuerMetadataProvider '{}': Error accessing underlying source: "
+ + "Invalid configuration.", getId());
+ } else {
+ final ProviderMetadataResolver resolver = component.getComponent();
+ return resolver.resolveSingle(criteria);
+ }
+ } catch (final ResolverException e) {
+ log.error("IssuerMetadataResolver '{}': Error during resolution", getId(), e);
+ } finally {
+ if (null != component) {
+ component.unpinComponent();
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/profile/spring/relyingparty/metadata/impl/ProviderMetadataResolverServiceStrategy.java b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/profile/spring/relyingparty/metadata/impl/ProviderMetadataResolverServiceStrategy.java
new file mode 100644
index 0000000..381c6e9
--- /dev/null
+++ b/oidc-common-metadata-impl/src/main/java/net/shibboleth/oidc/profile/spring/relyingparty/metadata/impl/ProviderMetadataResolverServiceStrategy.java
@@ -0,0 +1,101 @@
+/*
+ * 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.profile.spring.relyingparty.metadata.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.context.ApplicationContext;
+
+import net.shibboleth.oidc.metadata.ProviderMetadataProviderContainer;
+import net.shibboleth.oidc.metadata.ProviderMetadataResolver;
+import net.shibboleth.oidc.metadata.impl.ChainingProviderMetadataResolver;
+import net.shibboleth.utilities.java.support.component.AbstractIdentifiableInitializableComponent;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+import net.shibboleth.utilities.java.support.resolver.ResolverException;
+import net.shibboleth.utilities.java.support.service.ServiceException;
+import net.shibboleth.utilities.java.support.service.ServiceableComponent;
+
+/**
+ * Strategy for summoning up a {@link ProviderMetadataResolver} from a populated {@link ApplicationContext}.
+ *
+ * <p>
+ * The logic is the same as in
+ * net.shibboleth.idp.profile.spring.relyingparty.metadata.impl.MetadataResolverServiceStrategy.
+ * </p>
+ */
+public class ProviderMetadataResolverServiceStrategy extends AbstractIdentifiableInitializableComponent
+ implements Function<ApplicationContext, ServiceableComponent<ProviderMetadataResolver>> {
+
+ /** {@inheritDoc} */
+ @Nullable
+ public ServiceableComponent<ProviderMetadataResolver> apply(@Nonnull final ApplicationContext appContext) {
+ final Collection<ProviderMetadataProviderContainer> containers =
+ appContext.getBeansOfType(ProviderMetadataProviderContainer.class).values();
+
+ if (containers.isEmpty()) {
+ throw new ServiceException(
+ "Reload did not produce any bean of type " + ProviderMetadataProviderContainer.class.getName());
+ }
+ if (1 == containers.size()) {
+ // done
+ return containers.iterator().next();
+ }
+
+ // initialize so we can sort
+ for (final ProviderMetadataProviderContainer container : containers) {
+ try {
+ container.initialize();
+ } catch (final ComponentInitializationException e) {
+ throw new BeanCreationException("could not preinitialize , provider metadata provider "
+ + container.getId(), e);
+ }
+ }
+ // wrap many containers into a single top level container with a chain of all the individual chains/resolvers.
+ // The result of the strategy needs a single serviceable component.
+
+ final List<ProviderMetadataProviderContainer> containerList = new ArrayList<>(containers.size());
+ containerList.addAll(containers);
+ Collections.sort(containerList);
+ final ChainingProviderMetadataResolver chain = new ChainingProviderMetadataResolver();
+ try {
+ chain.setResolvers(containerList.stream().
+ map(ProviderMetadataProviderContainer::getEmbeddedResolver).
+ collect(Collectors.toList()));
+ chain.setId("MultiFileResolverFor:" + containers.size() + ":Resources");
+ chain.initialize();
+ final ProviderMetadataProviderContainer result = new ProviderMetadataProviderContainer();
+ result.setEmbeddedResolver(chain);
+ result.setApplicationContext(appContext);
+ result.initialize();
+ return result;
+ } catch (final ResolverException | ComponentInitializationException e) {
+ throw new ServiceException("Chaining constructor create failed", e);
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/impl/ChainingProviderMetadataResolverTest.java b/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/impl/ChainingProviderMetadataResolverTest.java
new file mode 100644
index 0000000..4473813
--- /dev/null
+++ b/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/metadata/impl/ChainingProviderMetadataResolverTest.java
@@ -0,0 +1,59 @@
+package net.shibboleth.oidc.metadata.impl;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import org.springframework.core.io.ClassPathResource;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.nimbusds.oauth2.sdk.id.Issuer;
+import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
+
+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;
+
+public class ChainingProviderMetadataResolverTest {
+
+ @Nonnull private ChainingProviderMetadataResolver resolver;
+
+ @BeforeMethod
+ public void setup() throws ResolverException, IOException, ComponentInitializationException {
+ resolver = new ChainingProviderMetadataResolver();
+ resolver.setId("mockChainResolver");
+ final FilesystemProviderMetadataResolver provider = new FilesystemProviderMetadataResolver(
+ new ClassPathResource("/net/shibboleth/oidc/metadata/impl/openid-configuration.json"));
+ provider.setId("mockFileProvider");
+ provider.initialize();
+ resolver.setResolvers(List.of(provider));
+ resolver.initialize();
+ }
+
+ @Test public void success() throws ResolverException, IOException {
+
+ final Issuer issuer = new Issuer("https://op.example.com");
+ final CriteriaSet set = new CriteriaSet(new IssuerIDCriterion(issuer));
+ final Iterator<OIDCProviderMetadata> metadatas = resolver.resolve(set).iterator();
+
+ Assert.assertEquals(metadatas.next().getIssuer().getValue(), issuer.getValue());
+ Assert.assertFalse(metadatas.hasNext());
+
+ }
+
+ @Test public void fail() throws ResolverException, IOException {
+
+ final Issuer issuer = new Issuer("no-metadata");
+ final CriteriaSet set = new CriteriaSet(new IssuerIDCriterion(issuer));
+ final Iterator<OIDCProviderMetadata> metadatas = resolver.resolve(set).iterator();
+
+ Assert.assertFalse(metadatas.hasNext());
+
+ }
+
+}
diff --git a/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/profile/spring/relyingparty/metadata/impl/ReloadClientResolverServiceConfigurationTest.java b/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/profile/spring/relyingparty/metadata/impl/ReloadClientResolverServiceConfigurationTest.java
index e760cb4..737c10a 100644
--- a/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/profile/spring/relyingparty/metadata/impl/ReloadClientResolverServiceConfigurationTest.java
+++ b/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/profile/spring/relyingparty/metadata/impl/ReloadClientResolverServiceConfigurationTest.java
@@ -44,7 +44,6 @@ import net.shibboleth.idp.profile.testing.RequestContextBuilder;
import net.shibboleth.oidc.metadata.ClientInformationResolver;
import net.shibboleth.oidc.metadata.RelyingPartyClientInformationProvider;
import net.shibboleth.oidc.metadata.impl.ChainingClientInformationResolver;
-import net.shibboleth.oidc.profile.spring.relyingparty.metadata.impl.ClientInformationResolverServiceStrategy;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.component.ComponentSupport;
import net.shibboleth.utilities.java.support.service.ReloadableService;
diff --git a/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/profile/spring/relyingparty/metadata/impl/ReloadClientResolverServiceConfigurationTest.java b/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/profile/spring/relyingparty/metadata/impl/ReloadProviderMetadataResolverServiceConfigurationTest.java
similarity index 75%
copy from oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/profile/spring/relyingparty/metadata/impl/ReloadClientResolverServiceConfigurationTest.java
copy to oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/profile/spring/relyingparty/metadata/impl/ReloadProviderMetadataResolverServiceConfigurationTest.java
index e760cb4..9adb5dc 100644
--- a/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/profile/spring/relyingparty/metadata/impl/ReloadClientResolverServiceConfigurationTest.java
+++ b/oidc-common-metadata-impl/src/test/java/net/shibboleth/oidc/profile/spring/relyingparty/metadata/impl/ReloadProviderMetadataResolverServiceConfigurationTest.java
@@ -41,24 +41,20 @@ import net.shibboleth.ext.spring.service.ReloadableSpringService;
import net.shibboleth.idp.profile.impl.ReloadServiceConfiguration;
import net.shibboleth.idp.profile.testing.ActionTestingSupport;
import net.shibboleth.idp.profile.testing.RequestContextBuilder;
-import net.shibboleth.oidc.metadata.ClientInformationResolver;
-import net.shibboleth.oidc.metadata.RelyingPartyClientInformationProvider;
-import net.shibboleth.oidc.metadata.impl.ChainingClientInformationResolver;
-import net.shibboleth.oidc.profile.spring.relyingparty.metadata.impl.ClientInformationResolverServiceStrategy;
+import net.shibboleth.oidc.metadata.ProviderMetadataResolver;
+import net.shibboleth.oidc.metadata.impl.ChainingProviderMetadataResolver;
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.component.ComponentSupport;
import net.shibboleth.utilities.java.support.service.ReloadableService;
import net.shibboleth.utilities.java.support.service.ServiceableComponent;
/**
- * Unit tests for {@link ClientInformationResolverServiceStrategy}.
- *
- * Mostly based on <pre>net.shibboleth.idp.profile.spring.relyingparty.metadata.ReloadServiceConfigurationTest</pre>.
+ * Unit tests for {@link ProviderMetadataResolverServiceStrategy}.
*/
-public class ReloadClientResolverServiceConfigurationTest {
+public class ReloadProviderMetadataResolverServiceConfigurationTest {
/** The service. */
- private ReloadableSpringService<ClientInformationResolver> service;
+ private ReloadableSpringService<ProviderMetadataResolver> service;
private RequestContext src;
@@ -68,14 +64,14 @@ public class ReloadClientResolverServiceConfigurationTest {
@BeforeClass public void setup() throws IOException, ComponentInitializationException {
oneResolver = new ArrayList<>();
- oneResolver.add(new ClassPathResource("/net/shibboleth/oidc/metadata/impl/oidc-metadata-providers.xml"));
+ oneResolver.add(new ClassPathResource("/net/shibboleth/oidc/metadata/impl/oidc-provider-metadata-providers.xml"));
twoResolvers = new ArrayList<>();
- twoResolvers.add(new ClassPathResource("/net/shibboleth/oidc/metadata/impl/oidc-metadata-providers2.xml"));
+ twoResolvers.add(new ClassPathResource("/net/shibboleth/oidc/metadata/impl/oidc-provider-metadata-providers2.xml"));
}
public void setupResolver(final List<Resource> serviceLocations) throws ComponentInitializationException {
- service = new ReloadableSpringService<>(ClientInformationResolver.class, new ClientInformationResolverServiceStrategy());
+ service = new ReloadableSpringService<>(ProviderMetadataResolver.class, new ProviderMetadataResolverServiceStrategy());
service.setFailFast(true);
service.setId("mockId");
@@ -92,18 +88,15 @@ public class ReloadClientResolverServiceConfigurationTest {
final Instant time = service.getLastReloadAttemptInstant();
service.reload();
Assert.assertNotEquals(time, service.getLastReloadAttemptInstant());
- final ServiceableComponent<ClientInformationResolver> component = service.getServiceableComponent();
- final ClientInformationResolver resolver = component.getComponent();
+ final ServiceableComponent<ProviderMetadataResolver> component = service.getServiceableComponent();
+ final ProviderMetadataResolver resolver = component.getComponent();
component.unpinComponent();
Assert.assertEquals(getChainSize(resolver), 1);
}
- protected int getChainSize(final ClientInformationResolver resolver) {
- Assert.assertTrue(resolver instanceof RelyingPartyClientInformationProvider);
- final RelyingPartyClientInformationProvider rpProvider = (RelyingPartyClientInformationProvider) resolver;
- final ClientInformationResolver embedded = rpProvider.getEmbeddedResolver();
- Assert.assertTrue(embedded instanceof ChainingClientInformationResolver);
- final ChainingClientInformationResolver chain = (ChainingClientInformationResolver) embedded;
+ protected int getChainSize(final ProviderMetadataResolver resolver) {
+ Assert.assertTrue(resolver instanceof ChainingProviderMetadataResolver);
+ final ChainingProviderMetadataResolver chain = (ChainingProviderMetadataResolver) resolver;
return chain.getResolvers().size();
}
@@ -112,8 +105,8 @@ public class ReloadClientResolverServiceConfigurationTest {
final Instant time = service.getLastReloadAttemptInstant();
service.reload();
Assert.assertNotEquals(time, service.getLastReloadAttemptInstant());
- final ServiceableComponent<ClientInformationResolver> component = service.getServiceableComponent();
- final ClientInformationResolver resolver = component.getComponent();
+ final ServiceableComponent<ProviderMetadataResolver> component = service.getServiceableComponent();
+ final ProviderMetadataResolver resolver = component.getComponent();
component.unpinComponent();
Assert.assertEquals(getChainSize(resolver), 2);
}
diff --git a/oidc-common-metadata-impl/src/test/resources/net/shibboleth/oidc/metadata/impl/oidc-provider-metadata-providers.xml b/oidc-common-metadata-impl/src/test/resources/net/shibboleth/oidc/metadata/impl/oidc-provider-metadata-providers.xml
new file mode 100644
index 0000000..88d839c
--- /dev/null
+++ b/oidc-common-metadata-impl/src/test/resources/net/shibboleth/oidc/metadata/impl/oidc-provider-metadata-providers.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:context="http://www.springframework.org/schema/context"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:p="http://www.springframework.org/schema/p"
+ xmlns:c="http://www.springframework.org/schema/c"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+ http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+
+ default-init-method="initialize"
+ default-destroy-method="destroy">
+
+ <!-- Generic beans for configuring the OIDC metadata system, in most cases don't need any modifications. -->
+
+ <bean id="shibboleth.oidc.rp.ProviderMetadataProvider" lazy-init="false"
+ class="net.shibboleth.oidc.metadata.ProviderMetadataProviderContainer"
+ p:embeddedResolver-ref="shibboleth.oidc.rp.ChainingProviderMetadataResolver">
+ </bean>
+
+ <bean id="shibboleth.oidc.rp.ChainingProviderMetadataResolver"
+ class="net.shibboleth.oidc.metadata.impl.ChainingProviderMetadataResolver"
+ p:id="InternalEmbeddedChainResolver"
+ p:resolvers="#{getObject('shibboleth.oidc.rp.ProviderMetadataResolvers')}"/>
+
+ <!-- Generic beans end, in most cases only edit after this line. -->
+
+ <!-- The following example contains one OIDC provider metadata resolvers: reading the OP metadata from a file. -->
+
+ <util:list id="shibboleth.oidc.rp.ProviderMetadataResolvers">
+ <ref bean="ExampleFileResolver" />
+ </util:list>
+
+ <bean id="ExampleFileResolver"
+ class="net.shibboleth.oidc.metadata.impl.FilesystemProviderMetadataResolver" p:id="ExampleFileResolver1"
+ c:metadata="/net/shibboleth/oidc/metadata/impl/openid-configuration.json" />
+
+</beans>
+
diff --git a/oidc-common-metadata-impl/src/test/resources/net/shibboleth/oidc/metadata/impl/oidc-provider-metadata-providers2.xml b/oidc-common-metadata-impl/src/test/resources/net/shibboleth/oidc/metadata/impl/oidc-provider-metadata-providers2.xml
new file mode 100644
index 0000000..c3887b1
--- /dev/null
+++ b/oidc-common-metadata-impl/src/test/resources/net/shibboleth/oidc/metadata/impl/oidc-provider-metadata-providers2.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:context="http://www.springframework.org/schema/context"
+ xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
+ xmlns:c="http://www.springframework.org/schema/c" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+ http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+
+ default-init-method="initialize" default-destroy-method="destroy">
+
+ <!-- Generic beans for configuring the OIDC metadata system, in most cases don't need any modifications. -->
+
+ <!-- The following example contains two OIDC provider metadata resolvers: reading the OP metadata from a file
+ in both cases. -->
+
+ <bean id="shibboleth.oidc.rp.ProviderMetadataProvider" lazy-init="false"
+ class="net.shibboleth.oidc.metadata.ProviderMetadataProviderContainer"
+ p:embeddedResolver-ref="shibboleth.oidc.rp.ChainingProviderMetadataResolver">
+ </bean>
+
+ <bean id="shibboleth.oidc.rp.ChainingProviderMetadataResolver"
+ class="net.shibboleth.oidc.metadata.impl.ChainingProviderMetadataResolver" p:id="InternalEmbeddedChainResolver"
+ p:resolvers="#{getObject('shibboleth.oidc.rp.ProviderMetadataResolvers')}" />
+
+ <util:list id="shibboleth.oidc.rp.ProviderMetadataResolvers">
+ <ref bean="ExampleFileResolver" />
+ </util:list>
+
+ <bean id="ExampleFileResolver" class="net.shibboleth.oidc.metadata.impl.FilesystemProviderMetadataResolver"
+ p:id="ExampleFileResolver1" c:metadata="/net/shibboleth/oidc/metadata/impl/openid-configuration.json" />
+
+ <!-- Second set of beans -->
+
+ <bean id="shibboleth.oidc.rp.ProviderMetadataProvider.2" lazy-init="false"
+ class="net.shibboleth.oidc.metadata.ProviderMetadataProviderContainer"
+ p:embeddedResolver-ref="shibboleth.oidc.rp.ChainingProviderMetadataResolver.2">
+ </bean>
+
+ <bean id="shibboleth.oidc.rp.ChainingProviderMetadataResolver.2"
+ class="net.shibboleth.oidc.metadata.impl.ChainingProviderMetadataResolver" p:id="InternalEmbeddedChainResolver"
+ p:resolvers="#{getObject('shibboleth.oidc.rp.ProviderMetadataResolvers.2')}" />
+
+ <util:list id="shibboleth.oidc.rp.ProviderMetadataResolvers.2">
+ <ref bean="ExampleFileResolver.2" />
+ </util:list>
+
+ <bean id="ExampleFileResolver.2" class="net.shibboleth.oidc.metadata.impl.FilesystemProviderMetadataResolver"
+ p:id="ExampleFileResolver2" c:metadata="/net/shibboleth/oidc/metadata/impl/openid-configuration.json" />
+
+
+
+
+
+</beans>
+
diff --git a/oidc-common-metadata-impl/src/test/resources/net/shibboleth/oidc/metadata/impl/openid-configuration.json b/oidc-common-metadata-impl/src/test/resources/net/shibboleth/oidc/metadata/impl/openid-configuration.json
new file mode 100644
index 0000000..912ff94
--- /dev/null
+++ b/oidc-common-metadata-impl/src/test/resources/net/shibboleth/oidc/metadata/impl/openid-configuration.json
@@ -0,0 +1,55 @@
+{
+"issuer": "https://op.example.com",
+"authorization_endpoint": "https://op.example.com/o/oauth2/v2/auth",
+"token_endpoint": "https://oauth2.op.example.com/token",
+"userinfo_endpoint": "https://openidconnect.op.example.com/v1/userinfo",
+"revocation_endpoint": "https://oauth2.op.example.com/revoke",
+"jwks_uri": "https://op.example.com/oauth2/v3/certs",
+"response_types_supported": [
+"code",
+"token",
+"id_token",
+"code token",
+"code id_token",
+"token id_token",
+"code token id_token",
+"none"
+],
+"subject_types_supported": [
+"public"
+],
+"id_token_signing_alg_values_supported": [
+"RS256"
+],
+"scopes_supported": [
+"openid",
+"email",
+"profile"
+],
+"token_endpoint_auth_methods_supported": [
+"client_secret_post",
+"client_secret_basic"
+],
+"claims_supported": [
+"aud",
+"email",
+"email_verified",
+"exp",
+"family_name",
+"given_name",
+"iat",
+"iss",
+"locale",
+"name",
+"picture",
+"sub"
+],
+"code_challenge_methods_supported": [
+"plain",
+"S256"
+],
+"grant_types_supported": [
+"authorization_code",
+"refresh_token"
+]
+}
\ No newline at end of file
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list