[java-idp-plugin-oidc-op-oidfed] branch main updated: Refactored the configuration of trusted remote resolver entities.

Henri Mikkonen henri.mikkonen at iki.fi
Tue Oct 21 14:28:29 UTC 2025


This is an automated email from the git hooks/post-receive script.

hjmikkon pushed a commit to branch main
in repository java-idp-plugin-oidc-op-oidfed.

View the commit online:
http://git.shibboleth.net/view/?p=java-idp-plugin-oidc-op-oidfed.git;a=commit;h=bb151ebfb64538dcaf5d280930d54e5220891334

The following commit(s) were added to refs/heads/main by this push:
     new bb151eb  Refactored the configuration of trusted remote resolver entities.
bb151eb is described below

commit bb151ebfb64538dcaf5d280930d54e5220891334
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Tue Oct 21 17:27:36 2025 +0300

    Refactored the configuration of trusted remote resolver entities.
    
    - Renamed 'shibboleth.oidfed.DefaultTrustedEntitiesLookupStrategy' into 'shibboleth.oidfed.DefaultTrustedRemoteResolverEntitiesLookupStrategy'
      - May be overridden via 'shibboleth.oidfed.TrustedRemoteResolverEntitiesLookupStrategy'
    - The implementing function is expected to return List<TrustedRemoteResolverEntity> instead of the Map
      - Example bean in the list: <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.TrustedRemoteResolverEntity" c:entity="https://trust-anchor.federation.local" c:anchors="https://trust-anchor.federation.local" />
        - Multiple anchors may be fed by separating the values with commas
---
 .../profile/TrustedRemoteResolverEntity.java       | 65 ++++++++++++++++++++++
 .../oidfed/profile/impl/CallResolveEntityApi.java  | 36 ++++++------
 .../oidfed/metadata-lookup-ext-oidfed-beans.xml    |  2 +-
 .../idp/flows/oidfed/register/register-beans.xml   |  2 +-
 .../conf/oidfed/oidfed-trustchain-resolver.xml     | 20 +++----
 .../conf/oidfed/oidfed-trustchain-resolver.xml     | 17 +++---
 6 files changed, 105 insertions(+), 37 deletions(-)

diff --git a/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/TrustedRemoteResolverEntity.java b/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/TrustedRemoteResolverEntity.java
new file mode 100644
index 0000000..b8b6859
--- /dev/null
+++ b/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/TrustedRemoteResolverEntity.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed 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.idp.plugin.oidc.op.oidfed.profile;
+
+import java.util.Collection;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.shared.annotation.ParameterName;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.logic.Constraint;
+
+/**
+ * A trusted entity whose federation_resolve_endpoint is exploited.
+ */
+public class TrustedRemoteResolverEntity {
+
+    /** The entity ID of the remote resolver. */
+    @Nonnull @NotEmpty private final String entityId;
+
+    /** The trust anchors to be used within the API request. */
+    @Nonnull @NotEmpty private final Collection<String> trustAnchors;
+
+    /**
+     * Constructor.
+     *
+     * @param entity ntity ID of the remote resolver
+     * @param anchors trust anchors to be used within the API request
+     */
+    public TrustedRemoteResolverEntity(@Nonnull @NotEmpty @ParameterName(name="entity") final String entity,
+            @Nonnull @NotEmpty @ParameterName(name="anchors") final Collection<String> anchors) {
+        entityId = Constraint.isNotEmpty(entity, "Entity ID cannot be empty");
+        trustAnchors = Constraint.isNotEmpty(anchors, "Trust Anchors cannot be empty");
+    }
+
+    /**
+     * Get the entity ID of the remote resolver.
+     * 
+     * @return entity ID of the remote resolver
+     */
+    @Nonnull @NotEmpty public String getEntityId() {
+        return entityId;
+    }
+
+    /**
+     * Get the trust anchors to be used within the API request.
+     * 
+     * @return trust anchors to be used within the API request
+     */
+    @Nonnull @NotEmpty public Collection<String> getTrustAnchors() {
+        return trustAnchors;
+    }
+}
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/CallResolveEntityApi.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/CallResolveEntityApi.java
index 9533db0..127e727 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/CallResolveEntityApi.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/CallResolveEntityApi.java
@@ -53,6 +53,7 @@ import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.ResolveEntityResponseCo
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.ResponseContainerExpirationCriterion;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.SubjectEntityIDCriterion;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.SubjectEntityStatementCriterion;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.TrustedRemoteResolverEntity;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.DefaultPreSelectedTrustChainIDsLookupStrategy;
 import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.DefaultTrustChainIDsLookupStrategy;
 import net.shibboleth.idp.profile.AbstractProfileAction;
@@ -110,7 +111,8 @@ public class CallResolveEntityApi extends AbstractProfileAction {
     @Nonnull private Predicate<ProfileRequestContext> requireEntityConfigurationCondition;
 
     /** Strategy used to get map of trusted entities for resolve entity APIs. */
-    @NonnullAfterInit private Function<ProfileRequestContext, Map<String,List<String>>> trustedEntitiesLookupStrategy;
+    @NonnullAfterInit
+    private Function<ProfileRequestContext, List<TrustedRemoteResolverEntity>> trustedEntitiesLookupStrategy;
 
     /** Strategy used to fetch the entity types used in the resolve entity request. */
     @Nonnull private Function<ProfileRequestContext, List<String>> entityTypesLookupStrategy;
@@ -125,7 +127,7 @@ public class CallResolveEntityApi extends AbstractProfileAction {
     @NonnullBeforeExec private String clientId;
 
     /** Trusted entities to use. */
-    @NonnullBeforeExec private Map<String,List<String>> trustedEntities;
+    @NonnullBeforeExec private List<TrustedRemoteResolverEntity> trustedEntities;
 
     /**
      * Constructor.
@@ -234,7 +236,7 @@ public class CallResolveEntityApi extends AbstractProfileAction {
      * @param strategy lookup strategy
      */
     public void setTrustedEntitiesLookupStrategy(
-            @Nonnull final Function<ProfileRequestContext,Map<String,List<String>>> strategy) {
+            @Nonnull final Function<ProfileRequestContext, List<TrustedRemoteResolverEntity>> strategy) {
         checkSetterPreconditions();
         trustedEntitiesLookupStrategy = Constraint.isNotNull(strategy, "TrustedEntitiesLookupStrategy cannot be null");
     }
@@ -327,11 +329,11 @@ public class CallResolveEntityApi extends AbstractProfileAction {
     protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
         log.debug("{} Resolving trust chain via resolve entity API for {}", getLogPrefix(), clientId);
         assert clientId != null;
-        final CriteriaSet criteriaSet = new CriteriaSet(new SubjectEntityIDCriterion(clientId));
+        final CriteriaSet baseCriteriaSet = new CriteriaSet(new SubjectEntityIDCriterion(clientId));
         final EntityStatement entityConfiguration = entityConfigurationLookupStrategy.apply(profileRequestContext);
         if (entityConfiguration != null) {
             log.debug("{} Entity configuration resolved and included to the criteria set", getLogPrefix());
-            criteriaSet.add(new SubjectEntityStatementCriterion(entityConfiguration));
+            baseCriteriaSet.add(new SubjectEntityStatementCriterion(entityConfiguration));
         } else if (requireEntityConfigurationCondition.test(profileRequestContext)) {
             log.error("{} Mandatory entity configuration could not be resolved", getLogPrefix());
             ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.INVALID_CREDENTIALS);
@@ -343,16 +345,15 @@ public class CallResolveEntityApi extends AbstractProfileAction {
             ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
             return;
         }
-        criteriaSet.add(new ResponseContainerExpirationCriterion(Instant.now().plus(cachedLifetime)));
+        baseCriteriaSet.add(new ResponseContainerExpirationCriterion(Instant.now().plus(cachedLifetime)));
 
         final List<String> preSelectedChain =
                 Optional.ofNullable(preSelectedTrustChainIdsLookupStrategy.apply(profileRequestContext))
                 .orElse(CollectionSupport.emptyList());
 
-        for (final String trustedEntity : trustedEntities.keySet()) {
-            final List<String> trustAnchors = trustedEntities.get(trustedEntity);
-            if (trustAnchors == null || trustAnchors.isEmpty()) {
-                log.warn("{} No trust anchors defined for trusted entity {}", getLogPrefix(), trustedEntity);
+        for (final TrustedRemoteResolverEntity trustedEntity : trustedEntities) {
+            if (trustedEntity == null) {
+                log.warn("{} Ignoring null trusted entity entry", getLogPrefix());
                 continue;
             }
             final URI uri = fetchResolveEntityEndpoint(trustedEntity);
@@ -361,7 +362,9 @@ public class CallResolveEntityApi extends AbstractProfileAction {
                 continue;
             }
             final ResolveEntityRequest entityRequest = new ResolveEntityRequest(
-                    uri, clientId, trustAnchors, List.of("openid_relying_party"));
+                    uri, clientId, List.copyOf(trustedEntity.getTrustAnchors()), List.of("openid_relying_party"));
+            final CriteriaSet criteriaSet = new CriteriaSet();
+            baseCriteriaSet.forEach(c -> criteriaSet.add(c));
             criteriaSet.add(new ResolveEntityRequestCriterion(entityRequest));
             final List<ResolveEntityResponseContainer> cacheResult;
             try {
@@ -470,10 +473,11 @@ public class CallResolveEntityApi extends AbstractProfileAction {
     /**
      * Fetch the resolve entity endpoint via entity configuration from the metadata cache.
      * 
-     * @param subject the entity ID
+     * @param trustedEntity the trusted entity
      * @return the resolve entity endpoint, or null if could not be fetched
      */
-    @Nullable protected URI fetchResolveEntityEndpoint(@Nonnull final String subject) {
+    @Nullable protected URI fetchResolveEntityEndpoint(@Nonnull final TrustedRemoteResolverEntity trustedEntity) {
+        final String subject = trustedEntity.getEntityId();
         final CriteriaSet criteria = new CriteriaSet(new SubjectEntityIDCriterion(subject));
         try {
             final List<EntityStatement> result = entityConfigurationCache.get(criteria);
@@ -489,9 +493,9 @@ public class CallResolveEntityApi extends AbstractProfileAction {
                         .orElse(null);
             }
         } catch (final MetadataCacheException e) {
-            log.debug("Error while fetching entity configuration for {}", subject, e);
+            log.debug("{} Error while fetching entity configuration for {}", getLogPrefix(), subject, e);
         }
-        log.warn("Could not fetch entity configuration for {}", subject);
+        log.warn("{} Could not fetch entity configuration for {}", getLogPrefix(), subject);
         return null;
     }
 
@@ -506,7 +510,7 @@ public class CallResolveEntityApi extends AbstractProfileAction {
             try {
                 return SignedJWT.parse(string);
             } catch (final ParseException e) {
-                log.error("Could not parse the trust mark into a JWT", e);
+                log.error("{} Could not parse the trust mark into a JWT", getLogPrefix(), e);
             }
         }
         return null;
diff --git a/idp-oidfed-op-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/metadata-lookup-ext/oidfed/metadata-lookup-ext-oidfed-beans.xml b/idp-oidfed-op-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/metadata-lookup-ext/oidfed/metadata-lookup-ext-oidfed-beans.xml
index d8c2f11..1ebbf25 100644
--- a/idp-oidfed-op-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/metadata-lookup-ext/oidfed/metadata-lookup-ext-oidfed-beans.xml
+++ b/idp-oidfed-op-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/metadata-lookup-ext/oidfed/metadata-lookup-ext-oidfed-beans.xml
@@ -22,7 +22,7 @@
         p:entityConfigurationCache-ref="shibboleth.oidfed.EntityConfigurationMetadataCache"
         p:resolveEntityTrustChainMetadataCache-ref="shibboleth.oidfed.ResolveEntityTrustChainMetadataCache"
         p:objectMapper-ref="shibboleth.oidc.JSONObjectMapper"
-        p:trustedEntitiesLookupStrategy="#{getObject('shibboleth.oidfed.TrustedEntitiesLookupStrategy') ?: getObject('shibboleth.oidfed.DefaultTrustedEntitiesLookupStrategy')}"
+        p:trustedEntitiesLookupStrategy="#{getObject('shibboleth.oidfed.TrustedRemoteResolverEntitiesLookupStrategy') ?: getObject('shibboleth.oidfed.DefaultTrustedRemoteResolverEntitiesLookupStrategy')}"
         p:preSelectedTrustChainIdsLookupStrategy="#{getObject('shibboleth.oidfed.PreSelectedTrustChainIDsLookupStrategy') ?: getObject('shibboleth.oidfed.DefaultPreSelectedTrustChainIDsLookupStrategy')}">
         <property name="clientIDLookupStrategy">
             <bean class="net.shibboleth.idp.plugin.oidc.op.profile.logic.DefaultMetadataLookupExtensionContextClientIDLookupFunction" />
diff --git a/idp-oidfed-op-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/register/register-beans.xml b/idp-oidfed-op-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/register/register-beans.xml
index 33283ad..23e4083 100644
--- a/idp-oidfed-op-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/register/register-beans.xml
+++ b/idp-oidfed-op-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/register/register-beans.xml
@@ -84,7 +84,7 @@
         p:entityConfigurationCache-ref="shibboleth.oidfed.EntityConfigurationMetadataCache"
         p:resolveEntityTrustChainMetadataCache-ref="shibboleth.oidfed.ResolveEntityTrustChainMetadataCache"
         p:objectMapper-ref="shibboleth.oidc.JSONObjectMapper"
-        p:trustedEntitiesLookupStrategy="#{getObject('shibboleth.oidfed.TrustedEntitiesLookupStrategy') ?: getObject('shibboleth.oidfed.DefaultTrustedEntitiesLookupStrategy')}"
+        p:trustedEntitiesLookupStrategy="#{getObject('shibboleth.oidfed.TrustedRemoteResolverEntitiesLookupStrategy') ?: getObject('shibboleth.oidfed.DefaultTrustedRemoteResolverEntitiesLookupStrategy')}"
         p:preSelectedTrustChainIdsLookupStrategy="#{getObject('shibboleth.oidfed.PreSelectedTrustChainIDsLookupStrategy') ?: getObject('shibboleth.oidfed.DefaultPreSelectedTrustChainIDsLookupStrategy')}"
         p:requireEntityConfigurationCondition-ref="shibboleth.Conditions.TRUE">
         <property name="entityConfigurationLookupStrategy">
diff --git a/idp-oidfed-op-impl/src/main/resources/net/shibboleth/idp/plugin/oidc/op/oidfed/conf/oidfed/oidfed-trustchain-resolver.xml b/idp-oidfed-op-impl/src/main/resources/net/shibboleth/idp/plugin/oidc/op/oidfed/conf/oidfed/oidfed-trustchain-resolver.xml
index 8a26100..11a1387 100644
--- a/idp-oidfed-op-impl/src/main/resources/net/shibboleth/idp/plugin/oidc/op/oidfed/conf/oidfed/oidfed-trustchain-resolver.xml
+++ b/idp-oidfed-op-impl/src/main/resources/net/shibboleth/idp/plugin/oidc/op/oidfed/conf/oidfed/oidfed-trustchain-resolver.xml
@@ -15,25 +15,23 @@
         The entity configuration is used as a trusted source for the endpoint. The map value is a list of trust anchors
         that will be used for the trust_anchor paramter(s) in the API request.
         
-        The default function may be overridden via 'shibboleth.oidfed.TrustedEntitiesLookupStrategy' bean that must
-        implement Function<ProfileRequestContext, Map<String,List<String>>>.
+        The default function may be overridden via 'shibboleth.oidfed.TrustedRemoteResolverEntitiesLookupStrategy' bean
+        that must implement Function<ProfileRequestContext, List<TrustedRemoteResolverEntity>>.
     -->
-    <bean id="shibboleth.oidfed.DefaultTrustedEntitiesLookupStrategy" parent="shibboleth.Functions.Constant">
+    <bean id="shibboleth.oidfed.DefaultTrustedRemoteResolverEntitiesLookupStrategy" parent="shibboleth.Functions.Constant">
         <constructor-arg name="target">
-            <util:map>
+            <util:list value-type="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.TrustedRemoteResolverEntity">
                 <!-- 
                     Example entry for a trusted entity 'https://trust-anchor.federation.local'
                     its 'federation_resolve_endpoint' is exploited with the configured trust_anchor parameters:
-                    '...&trust_anchor=https://trust-anchor.federation.local'
+                    '...&trust_anchor=https://trust-anchor.federation.local&trust_anchor=https://another.federation.local'
                 -->
                 <!--
-                <entry key="https://trust-anchor.federation.local">
-                    <util:list value-type="java.lang.String">
-                        <value>https://trust-anchor.federation.local</value>
-                    </util:list>
-                </entry>
+                <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.TrustedRemoteResolverEntity"
+                    c:entity="https://trust-anchor.federation.local"
+                    c:anchors="https://trust-anchor.federation.local,https://another.federation.local" />
                 -->
-            </util:map>
+            </util:list>
         </constructor-arg>
     </bean>
 
diff --git a/idp-oidfed-op-impl/src/test/resources/net/shibboleth/idp/module/conf/oidfed/oidfed-trustchain-resolver.xml b/idp-oidfed-op-impl/src/test/resources/net/shibboleth/idp/module/conf/oidfed/oidfed-trustchain-resolver.xml
index e831bef..82f7d6b 100644
--- a/idp-oidfed-op-impl/src/test/resources/net/shibboleth/idp/module/conf/oidfed/oidfed-trustchain-resolver.xml
+++ b/idp-oidfed-op-impl/src/test/resources/net/shibboleth/idp/module/conf/oidfed/oidfed-trustchain-resolver.xml
@@ -9,15 +9,16 @@
 
     default-init-method="initialize" default-destroy-method="destroy">
 
-    <bean id="shibboleth.oidfed.DefaultTrustedEntitiesLookupStrategy" parent="shibboleth.Functions.Constant">
+    <bean id="shibboleth.oidfed.DefaultTrustedRemoteResolverEntitiesLookupStrategy" parent="shibboleth.Functions.Constant">
         <constructor-arg name="target">
-            <util:map>
-                <entry key="https://trust-anchor.federation.local">
-                    <util:list value-type="java.lang.String">
-                        <value>https://trust-anchor.federation.local</value>
-                    </util:list>
-                </entry>
-            </util:map>
+            <util:list value-type="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.TrustedRemoteResolverEntity">
+                <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.TrustedRemoteResolverEntity"
+                    c:entity="https://trust-anchor.federation.local"
+                    c:anchors="https://notworking.local,https://neither.another.local" />
+                <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.TrustedRemoteResolverEntity"
+                    c:entity="https://trust-anchor.federation.local"
+                    c:anchors="https://trust-anchor.federation.local" />
+            </util:list>
         </constructor-arg>
     </bean>
 

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


More information about the commits mailing list