[java-oidfed-common] branch main updated: Add property to control maximum amount of authority hints to be locally resolved

Codeberg noreply at shibboleth.net
Fri Sep 18 07:40:15 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/fef3693f9b6cf8ed5897e87145e4a80fac24ae8d

The following commit(s) were added to refs/heads/main by this push:
     new fef3693  Add property to control maximum amount of authority hints to be locally resolved
fef3693 is described below

commit fef3693f9b6cf8ed5897e87145e4a80fac24ae8d
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Fri Sep 18 10:39:57 2026 +0300

    Add property to control maximum amount of authority hints to be locally resolved
    
    - idp.oidfed.cache.trustChain.maximumAuthorityHints, defaults to 5
    - If the limit is exceeded in entity configuration, the respective trust chain is ignored
---
 .../META-INF/net.shibboleth.idp/postconfig.xml     |  7 ++++
 .../DefaultTrustChainFetchingStrategy.java         | 38 +++++++++++++++++--
 .../oidfed/conf/oidfed/oidfed.properties           |  4 ++
 .../DefaultTrustChainFetchingStrategyTest.java     | 44 ++++++++++++++++++++++
 4 files changed, 90 insertions(+), 3 deletions(-)

diff --git a/oidfed-common-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/oidfed-common-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
index f22ca63..d0e5188 100644
--- a/oidfed-common-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
+++ b/oidfed-common-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
@@ -412,6 +412,13 @@
                         </constructor-arg>
                     </bean>
                 </property>
+                <property name="criteriaToMaximumAuthorityHintsStrategy">
+                    <bean parent="shibboleth.Functions.Constant">
+                        <constructor-arg>
+                            <bean class="java.lang.Integer" factory-method="parseInt" c:_0="%{idp.oidfed.cache.trustChain.maximumAuthorityHints:5}" />
+                        </constructor-arg>
+                    </bean>
+                </property>
             </bean>
         </property>
     </bean>
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 bf2b825..5d19f09 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
@@ -84,6 +84,9 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
     /** Strategy to fetch maximum length for trust chains. */
     @NonnullAfterInit private Function<CriteriaSet, Integer> criteriaToMaximumChainLengthStrategy;
 
+    /** Strategy to fetch maximum amount of authority hints to be processed. */
+    @NonnullAfterInit private Function<CriteriaSet, Integer> criteriaToMaximumAuthorityHintsStrategy;
+
     /**
      * Set the strategy for fetching entity ID from the criteria set.
      * 
@@ -160,6 +163,17 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
                 Constraint.isNotNull(strategy, "Criteria to maximum trust chain length strategy cannot be null");
     }
 
+    /**
+     * Set the strategy to fetch maximum amount of authority hints to be processed.
+     * 
+     * @param strategy maximum authority hints strategy
+     */
+    public void setCriteriaToMaximumAuthorityHintsStrategy(@Nonnull final Function<CriteriaSet, Integer> strategy) {
+        checkSetterPreconditions();
+        criteriaToMaximumAuthorityHintsStrategy =
+                Constraint.isNotNull(strategy, "Criteria to maximum authority hints strategy cannot be null");
+    }
+
     /** {@inheritDoc} */
     @Override
     protected void doInitialize() throws ComponentInitializationException {
@@ -187,6 +201,10 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
             throw new ComponentInitializationException(
                     "Criteria to maximum trust chain length strategy cannot be null");
         }
+        if (criteriaToMaximumAuthorityHintsStrategy == null) {
+            throw new ComponentInitializationException(
+                    "Criteria to maximum authority hints strategy cannot be null");
+        }
     }
 
     /** {@inheritDoc} */
@@ -233,6 +251,12 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
             log.warn("Could not fetch valid maximum trust chain length, it must be greater than 2: {}", maximumLength);
             return null;
         }
+        final Integer maximumAuthorityHints = criteriaToMaximumAuthorityHintsStrategy.apply(criteria);
+        if (maximumAuthorityHints == null || maximumLength.intValue() < 1) {
+            log.warn("Could not fetch valid maximum authority hints, it must be greater than 0: {}", maximumLength);
+            return null;
+        }
+
         log.trace("Entity configuration found to build the trust chains on: {}", entityConfiguration != null);
         if (entityConfiguration == null) {
             return null;
@@ -246,7 +270,8 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
             return null;
         }
         final List<TrustChainWrapper> populatedWrappers = populateChain(
-                CollectionSupport.listOf(initialWrapper), preSelectedChain, maximumLength.intValue());
+                CollectionSupport.listOf(initialWrapper), preSelectedChain, maximumLength.intValue(),
+                maximumAuthorityHints.intValue());
         final List<List<EntityStatement<?>>> result = populatedWrappers.stream()
                 .filter(wrapper -> wrapper.isComplete())
                 .map(wrapper -> wrapper.getTrustChain())
@@ -288,11 +313,12 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
      * @param entities the list of trust chains being populated.
      * @param preSelectedChain the pre-selected trust chain (containing entity IDs as String).
      * @param maxChainLength the maximum length of trust chain, longer than this limit will be ignored
+     * @param maxAuthorityHints the maximum amount of authority hints
      * @return the list of trust chains being populated
      */
     @Nonnull @NonnullElements private List<TrustChainWrapper> populateChain(
             @Nonnull @NonnullElements final List<TrustChainWrapper> entities,
-            @Nonnull final List<String> preSelectedChain, final int maxChainLength) {
+            @Nonnull final List<String> preSelectedChain, final int maxChainLength, final int maxAuthorityHints) {
         final List<TrustChainWrapper> result = new ArrayList<>();
         boolean hints = false;
         for (final TrustChainWrapper chainWrapper : entities) {
@@ -315,6 +341,12 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
                             + ", leaf {}", authorityHints, entityStatement.getSubject(), chain.get(0).getSubject());
                     continue;
                 }
+                if (authorityHints.size() > maxAuthorityHints) {
+                    log.info("Maximum authority hints limit ({}) has been met, ignoring authority hints {} of {}",
+                            maxAuthorityHints, authorityHints, entityStatement.getSubject());
+                    continue;
+                }
+
                 final List<Pair<EntityConfiguration, SubordinateStatement>> authorities = authorityHints.stream()
                         .filter(id -> verifyNoLoop(chain, id))
                         .filter(id -> verifyPreSelected(chain, id, preSelectedChain))
@@ -333,7 +365,7 @@ public class DefaultTrustChainFetchingStrategy extends AbstractIdentifiableIniti
             }
         }
         if (hints) {
-            return populateChain(result, preSelectedChain, maxChainLength);
+            return populateChain(result, preSelectedChain, maxChainLength, maxAuthorityHints);
         }
         return result;
     }
diff --git a/oidfed-common-impl/src/main/resources/net/shibboleth/oidfed/conf/oidfed/oidfed.properties b/oidfed-common-impl/src/main/resources/net/shibboleth/oidfed/conf/oidfed/oidfed.properties
index 53beafa..bf994d4 100644
--- a/oidfed-common-impl/src/main/resources/net/shibboleth/oidfed/conf/oidfed/oidfed.properties
+++ b/oidfed-common-impl/src/main/resources/net/shibboleth/oidfed/conf/oidfed/oidfed.properties
@@ -60,6 +60,10 @@ idp.oidfed.entityConfiguration.authorityHints = https://your.authority.example.o
 # - trust anchor and intermediate: 4 (leaf configuration, subordinate statements about leaf and intermediate, anchor configuration)
 #idp.oidfed.cache.trustChain.maximumChainLength = 5
 
+# Maximum amount of authority_hints
+# if any entity configuration exceeds the limit, the respective trust chain is ignored
+#idp.oidfed.cache.trustChain.maximumAuthorityHints = 5
+
 #idp.oidfed.cache.trustChain.cleanupTaskInterval = PT5M
 #idp.oidfed.cache.trustChain.customFilterStrategies = 
 #idp.oidfed.cache.trustChain.validContainerLifetime = PT5M
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 cf57464..c4fe387 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
@@ -58,6 +58,7 @@ public class DefaultTrustChainFetchingStrategyTest {
     Function<CriteriaSet, Duration> criteriaToValidContainerLifetimeStrategy;
     Function<CriteriaSet, Duration> criteriaToInvalidContainerLifetimeStrategy;
     Function<CriteriaSet, Integer> criteriaToMaximumChainLengthStrategy;
+    Function<CriteriaSet, Integer> criteriaToMaximumAuthorityHintsStrategy;
 
     @SuppressWarnings("unchecked")
     public void initMocks() {
@@ -68,6 +69,7 @@ public class DefaultTrustChainFetchingStrategyTest {
         criteriaToValidContainerLifetimeStrategy = mock(Function.class);
         criteriaToInvalidContainerLifetimeStrategy = mock(Function.class);
         criteriaToMaximumChainLengthStrategy = mock(Function.class);
+        criteriaToMaximumAuthorityHintsStrategy = mock(Function.class);
     }
 
     @BeforeMethod
@@ -81,6 +83,7 @@ public class DefaultTrustChainFetchingStrategyTest {
         function.setCriteriaToValidContainerLifetimeStrategy(criteriaToValidContainerLifetimeStrategy);
         function.setCriteriaToInvalidContainerLifetimeStrategy(criteriaToInvalidContainerLifetimeStrategy);
         function.setCriteriaToMaximumChainLengthStrategy(criteriaToMaximumChainLengthStrategy);
+        function.setCriteriaToMaximumAuthorityHintsStrategy(criteriaToMaximumAuthorityHintsStrategy);
         function.setId("mockFunction");
         try {
             function.initialize();
@@ -99,6 +102,7 @@ public class DefaultTrustChainFetchingStrategyTest {
         when(criteriaToValidContainerLifetimeStrategy.apply(any())).thenReturn(null);
         when(criteriaToInvalidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
         when(criteriaToMaximumChainLengthStrategy.apply(any())).thenReturn(4);
+        when(criteriaToMaximumAuthorityHintsStrategy.apply(any())).thenReturn(1);
         Assert.assertEquals(function.apply(new CriteriaSet()), null);
     }
 
@@ -107,6 +111,7 @@ public class DefaultTrustChainFetchingStrategyTest {
         when(criteriaToValidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
         when(criteriaToInvalidContainerLifetimeStrategy.apply(any())).thenReturn(null);
         when(criteriaToMaximumChainLengthStrategy.apply(any())).thenReturn(4);
+        when(criteriaToMaximumAuthorityHintsStrategy.apply(any())).thenReturn(1);
         Assert.assertEquals(function.apply(new CriteriaSet()), null);
     }
 
@@ -116,6 +121,7 @@ public class DefaultTrustChainFetchingStrategyTest {
         when(criteriaToInvalidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
         when(criteriaToMaximumChainLengthStrategy.apply(any())).thenReturn(4);
         when(entityConfigurationCache.get(any())).thenReturn(CollectionSupport.emptyList());
+        when(criteriaToMaximumAuthorityHintsStrategy.apply(any())).thenReturn(1);
         Assert.assertEquals(function.apply(new CriteriaSet()), null);
     }
 
@@ -125,6 +131,7 @@ public class DefaultTrustChainFetchingStrategyTest {
         when(criteriaToInvalidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
         when(criteriaToMaximumChainLengthStrategy.apply(any())).thenReturn(4);
         when(entityConfigurationCache.get(any())).thenThrow(MetadataCacheException.class);
+        when(criteriaToMaximumAuthorityHintsStrategy.apply(any())).thenReturn(1);
         Assert.assertEquals(function.apply(new CriteriaSet()), null);
     }
 
@@ -133,6 +140,7 @@ public class DefaultTrustChainFetchingStrategyTest {
         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(1);
         final EntityConfigurationContainer ecContainer =
                 ecContainer("mockEntityId", CollectionSupport.emptyList());
         when(entityConfigurationCache.get(any())).thenReturn(CollectionSupport.listOf(ecContainer));
@@ -144,6 +152,7 @@ public class DefaultTrustChainFetchingStrategyTest {
         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(1);
         final EntityConfigurationContainer ecContainer =
                 ecContainer("mockEntityId", CollectionSupport.listOf("https://federation.local/immediate"));
         when(entityConfigurationCache.get(any())).thenReturn(CollectionSupport.listOf(ecContainer));
@@ -158,6 +167,7 @@ public class DefaultTrustChainFetchingStrategyTest {
         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(1);
         final EntityConfigurationContainer ecContainer =
                 ecContainer(leaf, CollectionSupport.listOf(anchor, "https://federation.local/other"));
         final EntityConfigurationContainer authorityContainer =
@@ -186,6 +196,7 @@ public class DefaultTrustChainFetchingStrategyTest {
         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(1);
         final EntityConfigurationContainer ecContainer =
                 ecContainer(leaf, CollectionSupport.listOf(immediate, "https://federation.local/other"));
         final EntityConfigurationContainer immediateContainer =
@@ -216,6 +227,7 @@ public class DefaultTrustChainFetchingStrategyTest {
         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(1);
         final EntityConfigurationContainer ecContainer =
                 ecContainer(leaf, CollectionSupport.listOf(immediate));
         final EntityConfigurationContainer immediateContainer =
@@ -252,6 +264,7 @@ public class DefaultTrustChainFetchingStrategyTest {
         when(criteriaToValidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
         when(criteriaToInvalidContainerLifetimeStrategy.apply(any())).thenReturn(Duration.ofMinutes(5));
         when(criteriaToMaximumChainLengthStrategy.apply(any())).thenReturn(3);
+        when(criteriaToMaximumAuthorityHintsStrategy.apply(any())).thenReturn(1);
         final EntityConfigurationContainer ecContainer =
                 ecContainer(leaf, CollectionSupport.listOf(immediate));
         final EntityConfigurationContainer immediateContainer =
@@ -287,6 +300,7 @@ public class DefaultTrustChainFetchingStrategyTest {
         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(1);
         final EntityConfigurationContainer ecContainer =
                 ecContainer(leaf, CollectionSupport.listOf(anchor, "https://federation.local/other"));
         final EntityConfigurationContainer authorityContainer =
@@ -316,6 +330,7 @@ public class DefaultTrustChainFetchingStrategyTest {
         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(1);
         final EntityConfigurationContainer ecContainer =
                 ecContainer(leaf, CollectionSupport.listOf(anchor, "https://federation.local/other"));
         final EntityConfigurationContainer authorityContainer =
@@ -338,6 +353,7 @@ public class DefaultTrustChainFetchingStrategyTest {
         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(anchor1, anchor2));
         final EntityConfigurationContainer authorityContainer1 =
@@ -361,6 +377,33 @@ public class DefaultTrustChainFetchingStrategyTest {
                 List.of(leaf, anchor2));
     }
 
+    @SuppressWarnings("unchecked")
+    @Test
+    public void entityConfigurationTwoResolvableHints_limitToOne_returnsEmotyContainer()
+            throws MetadataCacheException {
+        final String leaf = "https://federation.local/leaf";
+        final String anchor1 = "https://federation.local/immediate1";
+        final String anchor2 = "https://federation.local/immediate2";
+        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(1);
+        final EntityConfigurationContainer ecContainer =
+                ecContainer(leaf, CollectionSupport.listOf(anchor1, anchor2));
+        final EntityConfigurationContainer authorityContainer1 =
+                ecContainer(anchor1, CollectionSupport.emptyList());
+        final EntityConfigurationContainer authorityContainer2 =
+                ecContainer(anchor2, CollectionSupport.emptyList());
+        when(entityConfigurationCache.get(any()))
+            .thenReturn(CollectionSupport.listOf(ecContainer), CollectionSupport.listOf(authorityContainer1),
+                    CollectionSupport.listOf(authorityContainer2));
+        final SubordinateStatementContainer ssContainer1 = ssContainer(leaf, anchor1);
+        final SubordinateStatementContainer ssContainer2 = ssContainer(leaf, anchor2);
+        when(subordinateStatementCache.get(any())).thenReturn(CollectionSupport.listOf(ssContainer1),
+                CollectionSupport.listOf(ssContainer2));
+        assertEmptyResult(function.apply(new CriteriaSet()));
+    }
+
     @SuppressWarnings("unchecked")
     @Test
     public void entityConfigurationAuthorityLoop_returnsEmptyContantainer() throws MetadataCacheException {
@@ -370,6 +413,7 @@ public class DefaultTrustChainFetchingStrategyTest {
         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(1);
         final EntityConfigurationContainer ecContainer =
                 ecContainer(leaf, CollectionSupport.listOf(anchor1));
         final EntityConfigurationContainer authorityContainer1 =

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list