[java-idp-oidc] 01/02: JOIDC-222 - Support for OpenID Federation
Henri Mikkonen
henri.mikkonen at iki.fi
Thu Jan 16 16:08:51 UTC 2025
This is an automated email from the git hooks/post-receive script.
hjmikkon pushed a commit to branch dev/JOIDC-222
in repository java-idp-oidc.
View the commit online:
http://git.shibboleth.net/view/?p=java-idp-oidc.git;a=commit;h=2383cefc8fc8de0fd5dec187f4301adaf2da975e
commit 2383cefc8fc8de0fd5dec187f4301adaf2da975e
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Thu Jan 16 18:03:57 2025 +0200
JOIDC-222 - Support for OpenID Federation
https://shibboleth.atlassian.net/browse/JOIDC-222
Exploit local trust anchor configuration when fetching possible trust chains
- The trust chains resolved via authority hints are iterated over for local anchors that are used as intermediate authorities
- Such cases are added as new additional trust chains with local anchor as trust anchor
---
.../DefaultTrustChainFetchingStrategy.java | 102 ++++++++++++++++++++-
.../META-INF/net.shibboleth.idp/postconfig.xml | 3 +-
2 files changed, 102 insertions(+), 3 deletions(-)
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustChainFetchingStrategy.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustChainFetchingStrategy.java
index aa74b727..f20d3497 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustChainFetchingStrategy.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustChainFetchingStrategy.java
@@ -16,7 +16,9 @@ package net.shibboleth.idp.plugin.oidc.op.oidfed.metadata;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.function.Function;
+import java.util.stream.Stream;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -59,6 +61,9 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
/** Cache for subordinate entity statements. */
@NonnullAfterInit private MetadataCache<EntityStatement> subordinateStatementCache;
+ /** Cache containing local copies of trusted trust anchor keys. */
+ @NonnullAfterInit private MetadataCache<Map<String, LocalKeyContainer>> localTrustAnchorsCache;
+
/**
* Set the strategy for fetching entity ID from the criteria set.
*
@@ -90,6 +95,16 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
subordinateStatementCache = Constraint.isNotNull(cache, "Subordinate statement cache cannot be null");
}
+ /**
+ * Set the cache containing local copies of trusted trust anchor keys.
+ *
+ * @param cache cache containing local copies of trusted trust anchor keys.
+ */
+ public void setLocalTrustAnchorsCache(@Nonnull final MetadataCache<Map<String, LocalKeyContainer>> cache) {
+ checkSetterPreconditions();
+ localTrustAnchorsCache = Constraint.isNotNull(cache, "Local Trust Anchor cache cannot be null");
+ }
+
/** {@inheritDoc} */
@Override
protected void doInitialize() throws ComponentInitializationException {
@@ -103,6 +118,9 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
if (subordinateStatementCache == null) {
throw new ComponentInitializationException("Subordinate statement cache cannot be null");
}
+ if (localTrustAnchorsCache == null) {
+ throw new ComponentInitializationException("Local Trust Anchor cache cannot be null");
+ }
}
/** {@inheritDoc} */
@@ -119,9 +137,11 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
}
final EntityStatement entityConfiguration = entityConfigurationCache.get(criteria).get(0);
assert entityConfiguration != null;
- final List<List<EntityStatement>> chain = populateChain(
+ final List<List<EntityStatement>> rawChains = populateChain(
CollectionSupport.listOf(CollectionSupport.listOf(entityConfiguration)));
- return stripIntermediateConfigurations(entityConfiguration, chain);
+ final List<List<EntityStatement>> trustChains =
+ stripIntermediateConfigurations(entityConfiguration, rawChains);
+ return Stream.concat(trustChains.stream(), resolveLocallyTrustedTrustChains(trustChains).stream()).toList();
} catch (final MetadataCacheException e) {
log.error("Could not fetch entity configuration for the trust chain", e);
}
@@ -135,6 +155,7 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
* @param chains the list of trust chains to be filtered
* @return the filtered list of trust chains, intermediate configurations filtered out
*/
+ @Nonnull
private List<List<EntityStatement>> stripIntermediateConfigurations(@Nonnull final EntityStatement leaf,
@Nonnull @NonnullElements final List<List<EntityStatement>> chains) {
final List<List<EntityStatement>> result = new ArrayList<>();
@@ -148,6 +169,83 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
return result;
}
+ /**
+ * Iterates the given trust chains and checks whether they contain locally trusted trust anchors as intermediate
+ * authorities. Such trust chains are returned in a way that locally trusted anchors are the trust anchors (i.e.
+ * the final items in the chain).
+ *
+ * @param chains trust chains to be iterated over
+ * @return list of trust chains containing locally trusted trust anchors
+ */
+ @Nonnull private List<List<EntityStatement>> resolveLocallyTrustedTrustChains(
+ @Nonnull @NonnullElements final List<List<EntityStatement>> chains) {
+ final List<List<EntityStatement>> result = new ArrayList<>();
+ for (final List<EntityStatement> chain : chains) {
+ for (int i = 0; i < chain.size() - 1; i++) {
+ final EntityStatement statement = chain.get(i);
+ assert statement != null;
+ final String entityId = chain.get(i).getEntityID().getValue();
+ assert entityId != null;
+ if (isLocallyTrusted(statement) && !isTrustAnchor(entityId, result)) {
+ try {
+ final CriteriaSet criteria = new CriteriaSet(new SubjectEntityIDCriterion(entityId));
+ final EntityStatement localAnchorConfiguration =
+ getFirstIfFound(entityConfigurationCache.get(criteria));
+ if (localAnchorConfiguration != null) {
+ final List<EntityStatement> localTrustChain =
+ new ArrayList<>(chain.subList(0, chain.indexOf(statement)));
+ localTrustChain.add(localAnchorConfiguration);
+ result.add(CollectionSupport.copyToList(localTrustChain));
+ log.debug("Included a trust chain based on a local trust anchor {}", entityId);
+ }
+ } catch (final MetadataCacheException e) {
+ log.error("Could not resolve entity configuration for {}", entityId);
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Checks whether the given entity is locally trusted via local trust anchors cache.
+ *
+ * @param statement the entity to be verified
+ * @return true if the entity is locally trusted, false otherwise
+ */
+ private boolean isLocallyTrusted(@Nonnull final EntityStatement statement) {
+ final CriteriaSet criteria = new CriteriaSet(new SubjectEntityStatementCriterion(statement));
+ final String entityId = statement.getEntityID().getValue();
+ assert entityId != null;
+ final List<Map<String, LocalKeyContainer>> keyContainers;
+ try {
+ keyContainers = localTrustAnchorsCache.get(criteria);
+ } catch (final MetadataCacheException e) {
+ log.debug("Could not resolve local trust anchor keys from the cache for {}", entityId, e);
+ return false;
+ }
+ if (keyContainers.isEmpty() || !keyContainers.get(0).containsKey(entityId)) {
+ log.trace("No locally trusted keys found for {}", entityId);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Checks whether the given entity is already a trust anchor in any of the given trust chains.
+ *
+ * @param entityId the entity ID to be verified
+ * @param trustChains trust chains to be checked
+ * @return true if the entity is already a trust anchor, false otherwise
+ */
+ private boolean isTrustAnchor(@Nonnull final String entityId,
+ @Nonnull final List<List<EntityStatement>> trustChains) {
+ return trustChains.stream()
+ .filter(chain -> entityId.equals(chain.get(chain.size() - 1).getEntityID().getValue()))
+ .findAny()
+ .isPresent();
+ }
+
/**
* Recursively populates the given list of trust chains until the last entity statement in each chain doesn't
* contain any authority hints.
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
index fc1eec64..5bac9ce4 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
@@ -1032,7 +1032,8 @@
<bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.DefaultTrustChainFetchingStrategy"
p:criteriaToSubjectEntityIdStrategy-ref="shibboleth.oidfed.DefaultSubjectEntityIDCriteriaToIdentifierStrategy"
p:entityConfigurationCache-ref="shibboleth.oidfed.EntityConfigurationMetadataCache"
- p:subordinateStatementCache-ref="shibboleth.oidfed.SubordinateEntityStatementMetadataCache"/>
+ p:subordinateStatementCache-ref="shibboleth.oidfed.SubordinateEntityStatementMetadataCache"
+ p:localTrustAnchorsCache-ref="shibboleth.oidfed.LocalTrustAnchorsMetadataCache" />
</property>
</bean>
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list