[java-oidfed-common] branch main updated: Improvements for the trust chain fetching
Codeberg
noreply at shibboleth.net
Fri Sep 18 11:47:47 UTC 2026
This is an automated email from the git hooks/post-receive script.
codeberg pushed a commit to branch main
in repository java-oidfed-common.
View the commit online:
https://codeberg.org/Shibboleth/java-oidfed-common/commit/1dd71e954f97fbf2f73427093b72ceb9974dead5
The following commit(s) were added to refs/heads/main by this push:
new 1dd71e9 Improvements for the trust chain fetching
1dd71e9 is described below
commit 1dd71e954f97fbf2f73427093b72ceb9974dead5
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Fri Sep 18 14:45:38 2026 +0300
Improvements for the trust chain fetching
- Comparison against the predefined trust chains now works
- Improved handling of unresolvable authority hints
---
.../DefaultTrustChainFetchingStrategy.java | 21 ++-
.../DefaultTrustChainFetchingStrategyTest.java | 148 +++++++++++++++++++--
2 files changed, 153 insertions(+), 16 deletions(-)
diff --git a/oidfed-common-impl/src/main/java/net/shibboleth/oidfed/metadata/cache/trustchain/DefaultTrustChainFetchingStrategy.java b/oidfed-common-impl/src/main/java/net/shibboleth/oidfed/metadata/cache/trustchain/DefaultTrustChainFetchingStrategy.java
index 088c627..38bb8a1 100644
--- a/oidfed-common-impl/src/main/java/net/shibboleth/oidfed/metadata/cache/trustchain/DefaultTrustChainFetchingStrategy.java
+++ b/oidfed-common-impl/src/main/java/net/shibboleth/oidfed/metadata/cache/trustchain/DefaultTrustChainFetchingStrategy.java
@@ -252,7 +252,7 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
return null;
}
final Integer maximumAuthorityHints = criteriaToMaximumAuthorityHintsStrategy.apply(criteria);
- if (maximumAuthorityHints == null || maximumLength.intValue() < 1) {
+ if (maximumAuthorityHints == null || maximumAuthorityHints.intValue() < 1) {
log.warn("Could not fetch valid maximum authority hints, it must be greater than 0: {}",
maximumAuthorityHints);
return null;
@@ -355,7 +355,7 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
.map(id -> fetchAuthority(entityStatement, id))
.filter(pair -> pair != null && pair.getFirst() != null && pair.getSecond() != null)
.toList();
- hints = !authorities.isEmpty();
+ hints = hints || !authorities.isEmpty();
authorities.forEach(authority -> {
final ArrayList<EntityStatement<?>> newChain = new ArrayList<>(chain.size() > 2 ?
chain.subList(0, chain.size() - 1) : chain);
@@ -401,7 +401,8 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
if (preSelectedChain.isEmpty()) {
return true;
}
- if (preSelectedChain.size() <= chain.size() || !preSelectedChain.get(chain.size()).equals(authority)) {
+ int index = chain.size() > 1 ? chain.size() - 1 : chain.size();
+ if (preSelectedChain.size() < chain.size() || !preSelectedChain.get(index).equals(authority)) {
log.debug("Ignoring authority {} as it doesn't match with the preselected chain {}", authority,
preSelectedChain);
return false;
@@ -444,12 +445,20 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
try {
final List<EntityConfigurationContainer> authorityConfigurations =
entityConfigurationCache.get(new CriteriaSet(new SubjectEntityIDCriterion(authorityHint)));
- log.trace("Fetched comfigurations {} for authority {}", authorityConfigurations, authorityHint);
+ log.trace("Fetched configurations {} for authority {}", authorityConfigurations, authorityHint);
+ if (authorityConfigurations.isEmpty()) {
+ log.warn("Could not fetch configuration for authority {}", authorityHint);
+ return null;
+ }
final List<SubordinateStatementContainer> subordinateStatements =
subordinateStatementCache.get(new CriteriaSet(new SubjectEntityIDCriterion(entityId),
new IssuerEntityIDCriterion(authorityHint)));
- log.trace("Fetched subordinate statements {} for authority {}", subordinateStatements, authorityHint);
- return new Pair<>(authorityConfigurations.isEmpty() ? null : authorityConfigurations.get(0).getStatement(),
+ log.trace("Fetched subordinate statements {} from authority {} for {}", subordinateStatements,
+ authorityHint, entityId);
+ if (subordinateStatements.isEmpty()) {
+ log.warn("Could not fetch subordinate statement of {} from {}", entityId, authorityHint);
+ }
+ return new Pair<>(authorityConfigurations.get(0).getStatement(),
subordinateStatements.isEmpty() ? null : subordinateStatements.get(0).getStatement());
} catch (final MetadataCacheException e) {
log.error("Could not resolve authority hint {} for {}", authorityHint, entityId);
diff --git a/oidfed-common-impl/src/test/java/net/shibboleth/oidfed/metadata/cache/trustchain/DefaultTrustChainFetchingStrategyTest.java b/oidfed-common-impl/src/test/java/net/shibboleth/oidfed/metadata/cache/trustchain/DefaultTrustChainFetchingStrategyTest.java
index b69c37d..124a893 100644
--- a/oidfed-common-impl/src/test/java/net/shibboleth/oidfed/metadata/cache/trustchain/DefaultTrustChainFetchingStrategyTest.java
+++ b/oidfed-common-impl/src/test/java/net/shibboleth/oidfed/metadata/cache/trustchain/DefaultTrustChainFetchingStrategyTest.java
@@ -256,7 +256,7 @@ public class DefaultTrustChainFetchingStrategyTest {
@SuppressWarnings("unchecked")
@Test
- public void entityConfigurationOneResolvableLocalAuthorityWithWorkingHint_maxlnght3_returnsContainerWithTwoChains()
+ public void entityConfigurationOneResolvableLocalAuthorityWithWorkingHint_maxlnght3_returnsContainerWithOneChain()
throws MetadataCacheException {
final String leaf = "https://federation.local/leaf";
final String immediate = "https://federation.local/immediate";
@@ -289,34 +289,162 @@ public class DefaultTrustChainFetchingStrategyTest {
List.of(leaf, immediate));
}
+ @SuppressWarnings("unchecked")
+ @Test
+ public void entityConfigurationTwoHints_latterOneNotWorking_returnsContainerWithTwoChains()
+ throws MetadataCacheException {
+ final String leaf = "https://federation.local/leaf";
+ final String localAnchor = "https://federation.local/localAnchor";
+ final String anchor = "https://federation.local/anchor";
+ final String workingIntermediate = "http://federation.local/intermediate1";
+ final String anotherIntermediate = "https://federation.local/intermediate2";
+ final String untrustedAnchor = "https://federation.local/untrustedAnchor";
+ when(criteriaToValidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
+ when(criteriaToInvalidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
+ when(criteriaToMaximumChainLengthStrategy.apply(any())).thenReturn(5);
+ when(criteriaToMaximumAuthorityHintsStrategy.apply(any())).thenReturn(2);
+ final EntityConfigurationContainer ecContainer =
+ ecContainer(leaf, CollectionSupport.listOf(workingIntermediate, anotherIntermediate));
+ final EntityConfigurationContainer workingIntermediateContainer =
+ ecContainer(workingIntermediate, List.of(localAnchor));
+ final EntityConfigurationContainer authorityContainer =
+ ecContainer(anchor, CollectionSupport.emptyList());
+ final EntityConfigurationContainer anotherIntermediateContainer =
+ ecContainer(anotherIntermediate, List.of(untrustedAnchor));
+ final EntityConfigurationContainer localAnchorContainer =
+ ecContainer(localAnchor, CollectionSupport.listOf(anchor));
+ final EntityConfigurationContainer untrustedContainer =
+ ecContainer(untrustedAnchor, CollectionSupport.emptyList());
+ when(entityConfigurationCache.get(any())).thenReturn(
+ CollectionSupport.listOf(ecContainer),
+ CollectionSupport.listOf(workingIntermediateContainer),
+ CollectionSupport.listOf(anotherIntermediateContainer),
+ CollectionSupport.listOf(localAnchorContainer),
+ CollectionSupport.listOf(untrustedContainer),
+ CollectionSupport.listOf(authorityContainer),
+ CollectionSupport.emptyList());
+ final SubordinateStatementContainer ssContainer1 = ssContainer(leaf, workingIntermediate);
+ final SubordinateStatementContainer ssContainer2 = ssContainer(leaf, anotherIntermediate);
+ final SubordinateStatementContainer ssContainer3 = ssContainer(workingIntermediate, localAnchor);
+ final SubordinateStatementContainer ssContainer5 = ssContainer(localAnchor, anchor);
+ when(subordinateStatementCache.get(any())).thenReturn(
+ CollectionSupport.listOf(ssContainer1),
+ CollectionSupport.listOf(ssContainer2),
+ CollectionSupport.listOf(ssContainer3),
+ CollectionSupport.emptyList(),
+ CollectionSupport.listOf(ssContainer5),
+ CollectionSupport.emptyList());
+ when(localTrustAnchorsCache.get(any())).thenReturn(
+ CollectionSupport.emptyList(),
+ CollectionSupport.emptyList(),
+ CollectionSupport.listOf(CollectionSupport.singletonMap(localAnchor, mock(LocalKeyContainer.class))),
+ CollectionSupport.emptyList());
+ final TrustChainsContainer result = function.apply(new CriteriaSet());
+ Assert.assertNotNull(result);
+ assert result != null;
+ Assert.assertEquals(result.getTrustChains().size(), 2);
+ Assert.assertEquals(EntityStatementHelper.getEntityIds(result.getTrustChains().get(0)),
+ List.of(leaf, workingIntermediate, localAnchor));
+ Assert.assertEquals(EntityStatementHelper.getEntityIds(result.getTrustChains().get(1)),
+ List.of(leaf, workingIntermediate, localAnchor, anchor));
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void entityConfigurationTwoHints_firstOneNotWorking_returnsContainerWithTwoChains()
+ throws MetadataCacheException {
+ final String leaf = "https://federation.local/leaf";
+ final String localAnchor = "https://federation.local/localAnchor";
+ final String anchor = "https://federation.local/anchor";
+ final String workingIntermediate = "http://federation.local/intermediate1";
+ final String anotherIntermediate = "https://federation.local/intermediate2";
+ final String untrustedAnchor = "https://federation.local/untrustedAnchor";
+ when(criteriaToValidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
+ when(criteriaToInvalidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
+ when(criteriaToMaximumChainLengthStrategy.apply(any())).thenReturn(5);
+ when(criteriaToMaximumAuthorityHintsStrategy.apply(any())).thenReturn(2);
+ final EntityConfigurationContainer ecContainer =
+ ecContainer(leaf, CollectionSupport.listOf(anotherIntermediate, workingIntermediate));
+ final EntityConfigurationContainer workingIntermediateContainer =
+ ecContainer(workingIntermediate, List.of(localAnchor));
+ final EntityConfigurationContainer authorityContainer =
+ ecContainer(anchor, CollectionSupport.emptyList());
+ final EntityConfigurationContainer anotherIntermediateContainer =
+ ecContainer(anotherIntermediate, List.of(untrustedAnchor));
+ final EntityConfigurationContainer localAnchorContainer =
+ ecContainer(localAnchor, CollectionSupport.listOf(anchor));
+ final EntityConfigurationContainer untrustedContainer =
+ ecContainer(untrustedAnchor, CollectionSupport.emptyList());
+ when(entityConfigurationCache.get(any())).thenReturn(
+ CollectionSupport.listOf(ecContainer),
+ CollectionSupport.listOf(anotherIntermediateContainer),
+ CollectionSupport.listOf(workingIntermediateContainer),
+ CollectionSupport.listOf(untrustedContainer),
+ CollectionSupport.listOf(localAnchorContainer),
+ CollectionSupport.listOf(authorityContainer),
+ CollectionSupport.emptyList());
+ final SubordinateStatementContainer ssContainer1 = ssContainer(leaf, anotherIntermediate);
+ final SubordinateStatementContainer ssContainer2 = ssContainer(leaf, workingIntermediate);
+ final SubordinateStatementContainer ssContainer3 = ssContainer(workingIntermediate, localAnchor);
+ final SubordinateStatementContainer ssContainer5 = ssContainer(localAnchor, anchor);
+ when(subordinateStatementCache.get(any())).thenReturn(
+ CollectionSupport.listOf(ssContainer1),
+ CollectionSupport.listOf(ssContainer2),
+ CollectionSupport.emptyList(),
+ CollectionSupport.listOf(ssContainer3),
+ CollectionSupport.listOf(ssContainer5),
+ CollectionSupport.emptyList());
+ when(localTrustAnchorsCache.get(any())).thenReturn(
+ CollectionSupport.emptyList(),
+ CollectionSupport.emptyList(),
+ CollectionSupport.listOf(CollectionSupport.singletonMap(localAnchor, mock(LocalKeyContainer.class))),
+ CollectionSupport.emptyList());
+ final TrustChainsContainer result = function.apply(new CriteriaSet());
+ Assert.assertNotNull(result);
+ assert result != null;
+ Assert.assertEquals(result.getTrustChains().size(), 2);
+ Assert.assertEquals(EntityStatementHelper.getEntityIds(result.getTrustChains().get(0)),
+ List.of(leaf, workingIntermediate, localAnchor));
+ Assert.assertEquals(EntityStatementHelper.getEntityIds(result.getTrustChains().get(1)),
+ List.of(leaf, workingIntermediate, localAnchor, anchor));
+ }
+
@SuppressWarnings("unchecked")
@Test
public void entityConfigurationOneResolvableHint_matchPreSelected_returnsContainerWithOneChain()
throws MetadataCacheException {
final String leaf = "https://federation.local/leaf";
- final String anchor = "https://federation.local/immediate";
+ final String intermediate = "https://federation.local/intermediate";
+ final String anchor = "https://federation.local/anchor";
final PreSelectedTrustChainCriterion preSelectedCriterion =
- new PreSelectedTrustChainCriterion(CollectionSupport.listOf(leaf, anchor));
+ new PreSelectedTrustChainCriterion(CollectionSupport.listOf(leaf, intermediate, anchor));
when(criteriaToValidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
when(criteriaToInvalidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
when(criteriaToMaximumChainLengthStrategy.apply(any())).thenReturn(4);
when(criteriaToMaximumAuthorityHintsStrategy.apply(any())).thenReturn(2);
final EntityConfigurationContainer ecContainer =
- ecContainer(leaf, CollectionSupport.listOf(anchor, "https://federation.local/other"));
+ ecContainer(leaf, CollectionSupport.listOf(intermediate, "https://federation.local/other"));
+ final EntityConfigurationContainer intermediateContainer =
+ ecContainer(intermediate, CollectionSupport.listOf(anchor));
final EntityConfigurationContainer authorityContainer =
ecContainer(anchor, CollectionSupport.emptyList());
- when(entityConfigurationCache.get(any()))
- .thenReturn(CollectionSupport.listOf(ecContainer), CollectionSupport.listOf(authorityContainer),
- CollectionSupport.emptyList());
- final SubordinateStatementContainer ssContainer = ssContainer(leaf, anchor);
- when(subordinateStatementCache.get(any())).thenReturn(CollectionSupport.listOf(ssContainer),
+ when(entityConfigurationCache.get(any())).thenReturn(
+ CollectionSupport.listOf(ecContainer),
+ CollectionSupport.listOf(intermediateContainer),
+ CollectionSupport.listOf(authorityContainer),
+ CollectionSupport.emptyList());
+ final SubordinateStatementContainer ssContainer = ssContainer(leaf, intermediate);
+ final SubordinateStatementContainer ssContainer2 = ssContainer(intermediate, anchor);
+ when(subordinateStatementCache.get(any())).thenReturn(
+ CollectionSupport.listOf(ssContainer),
+ CollectionSupport.listOf(ssContainer2),
CollectionSupport.emptyList());
final TrustChainsContainer result = function.apply(new CriteriaSet(preSelectedCriterion));
Assert.assertNotNull(result);
assert result != null;
Assert.assertEquals(result.getTrustChains().size(), 1);
Assert.assertEquals(EntityStatementHelper.getEntityIds(result.getTrustChains().get(0)),
- List.of(leaf, anchor));
+ List.of(leaf, intermediate, anchor));
}
@SuppressWarnings("unchecked")
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list