[java-plugin-shibd] branch main updated: Add descriptors with activation control for session initiator flows

Codeberg noreply at shibboleth.net
Tue Aug 4 18:53:28 UTC 2026


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

codeberg pushed a commit to branch main
in repository java-plugin-shibd.

View the commit online:
https://codeberg.org/Shibboleth/java-plugin-shibd/commit/cdbf83987884649471e61217a7ea21a9f5c67508

The following commit(s) were added to refs/heads/main by this push:
     new cdbf839  Add descriptors with activation control for session initiator flows
cdbf839 is described below

commit cdbf83987884649471e61217a7ea21a9f5c67508
Author: Scott Cantor <scott at restingparrotsoftware.com>
AuthorDate: Tue Aug 4 14:53:13 2026 -0400

    Add descriptors with activation control for session initiator flows
---
 .../META-INF/net.shibboleth.idp/postconfig.xml     | 23 +++++-
 .../session-initiator/session-initiator-beans.xml  |  1 -
 .../session-initiator/session-initiator-flow.xml   | 14 +++-
 .../profile/BasicLogoutConsumerFlowDescriptor.java |  2 +
 ...va => BasicSessionInitiatorFlowDescriptor.java} | 16 ++--
 .../profile/BasicTokenConsumerFlowDescriptor.java  |  2 +
 .../sp/profile/SessionInitiatorFlowDescriptor.java | 47 +++++++++++
 .../SessionInitiatorFlowDescriptorManager.java     | 93 ++++++++++++++++++++++
 8 files changed, 186 insertions(+), 12 deletions(-)

diff --git a/sp-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/sp-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
index ebcf35b..493341d 100644
--- a/sp-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
+++ b/sp-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
@@ -73,7 +73,11 @@
     <bean id="shibboleth.RemotedHttpServletRequestSupplier" class="net.shibboleth.sp.messaging.impl.RemotedHttpServletRequestSupplier" />
     <bean id="shibboleth.RemotedHttpServletResponseSupplier" class="net.shibboleth.sp.messaging.impl.RemotedlHttpServletResponseSupplier" />
 
-    <!-- Management bean to collect and expose consumer flow descriptors. -->
+    <!-- Management beans to collect and expose flow descriptors. -->
+
+    <bean id="shibboleth.sp.SessionInitiatorFlowDescriptorManager"
+        class="net.shibboleth.sp.profile.impl.SessionInitiatorFlowDescriptorManager"
+        p:components="#{getObject('shibboleth.sp.AvailableSessionInitiatorFlows')}" />
     
     <bean id="shibboleth.sp.TokenConsumerFlowDescriptorManager"
         class="net.shibboleth.sp.profile.impl.TokenConsumerFlowDescriptorManager"
@@ -83,6 +87,23 @@
         class="net.shibboleth.sp.profile.impl.LogoutConsumerFlowDescriptorManager"
         p:components="#{getObject('shibboleth.sp.AvailableLogoutConsumerFlows')}" />
 
+    <!-- Flow Descriptor for built-in discovery flow. -->
+    
+    <bean id="shibboleth.sp.DiscoveryParameterCondition"
+            class="net.shibboleth.sp.profile.context.logic.InputStartsWithPredicate">
+        <constructor-arg>
+            <util:constant static-field="net.shibboleth.sp.profile.InitiatorConstants.DISCOVERY_RETURN_URL" />
+        </constructor-arg>
+        <constructor-arg>
+            <value>http</value>
+        </constructor-arg>
+    </bean>
+    
+    <bean p:id="sp/initiator/disco" class="net.shibboleth.sp.profile.BasicSessionInitiatorFlowDescriptor"
+        p:activationCondition-ref="shibboleth.sp.DiscoveryParameterCondition" />
+
+    <!-- Misc beans. -->    
+
     <bean id="shibboleth.sp.RemotedCookieManager" class="net.shibboleth.shared.net.CookieManager" lazy-init="true"
         p:guardSetAttribute="false"
         p:httpServletRequestSupplier-ref="shibboleth.RemotedHttpServletRequestSupplier"
diff --git a/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/session-initiator/session-initiator-beans.xml b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/session-initiator/session-initiator-beans.xml
index 47ae7b8..fbad258 100644
--- a/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/session-initiator/session-initiator-beans.xml
+++ b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/session-initiator/session-initiator-beans.xml
@@ -14,5 +14,4 @@
     <bean id="RecoverStateData"
         class="net.shibboleth.sp.profile.impl.RecoverStateData" scope="prototype" />
     
-    
 </beans>
diff --git a/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/session-initiator/session-initiator-flow.xml b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/session-initiator/session-initiator-flow.xml
index 6e52b2c..f6c7716 100644
--- a/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/session-initiator/session-initiator-flow.xml
+++ b/sp-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/session-initiator/session-initiator-flow.xml
@@ -25,17 +25,25 @@
     </action-state>
 
     <decision-state id="CheckIterator">
-        <if test="SessionInitiatorIterator.hasNext()" then="CallInitiatorFlow" else="CheckForDiscovery" />
+        <if test="SessionInitiatorIterator.hasNext()" then="CheckFlowActivation" else="CheckForDiscovery" />
     </decision-state>
     
-    <subflow-state id="CallInitiatorFlow" subflow="sp/initiator/#{SessionInitiatorIterator.next()}">
+    <decision-state id="CheckFlowActivation">
+        <on-entry>
+            <evaluate expression="SessionInitiatorIterator.next()" result="flowScope.SessionInitiatorFlow" />
+        </on-entry>
+        <if test="flowRequestContext.getActiveFlow().getApplicationContext().getBean('shibboleth.sp.SessionInitiatorFlowDescriptorManager').isActive('sp/initiator/' + SessionInitiatorFlow, opensamlProfileRequestContext)"
+            then="CallInitiatorFlow" else="CheckIterator" />
+    </decision-state>
+    
+    <subflow-state id="CallInitiatorFlow" subflow="sp/initiator/#{SessionInitiatorFlow}">
         <input name="calledAsSubflow" value="true" />
         <transition on="proceed" to="EncodeAgentResponse" />
         <transition on="ReselectFlow" to="CheckIterator" />
     </subflow-state>
     
     <decision-state id="CheckForDiscovery">
-        <if test="opensamlProfileRequestContext.ensureSubcontext(T(net.shibboleth.sp.context.AgentRequestContext)).getApplication().getDiscoveryService(opensamlProfileRequestContext) != null"
+        <if test="opensamlProfileRequestContext.ensureSubcontext(T(net.shibboleth.sp.context.AgentRequestContext)).getApplication().getDiscoveryService(opensamlProfileRequestContext) != null and flowRequestContext.getActiveFlow().getApplicationContext().getBean('shibboleth.sp.SessionInitiatorFlowDescriptorManager').isActive('sp/initiator/disco', opensamlProfileRequestContext)"
             then="CallDiscoveryFlow"
             else="NoPotentialFlow" />
     </decision-state>
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/profile/BasicLogoutConsumerFlowDescriptor.java b/sp-server-api/src/main/java/net/shibboleth/sp/profile/BasicLogoutConsumerFlowDescriptor.java
index 6344582..0a6b39d 100644
--- a/sp-server-api/src/main/java/net/shibboleth/sp/profile/BasicLogoutConsumerFlowDescriptor.java
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/profile/BasicLogoutConsumerFlowDescriptor.java
@@ -48,6 +48,8 @@ public class BasicLogoutConsumerFlowDescriptor extends AbstractIdentifiableIniti
     /**
      * Set activation condition to determine whether this flow supports a request.
      * 
+     * <p>Defaults to false.</p>
+     * 
      * @param condition condition to use
      */
     public void setActivationCondition(@Nonnull final Predicate<ProfileRequestContext> condition) {
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/profile/BasicTokenConsumerFlowDescriptor.java b/sp-server-api/src/main/java/net/shibboleth/sp/profile/BasicSessionInitiatorFlowDescriptor.java
similarity index 80%
copy from sp-server-api/src/main/java/net/shibboleth/sp/profile/BasicTokenConsumerFlowDescriptor.java
copy to sp-server-api/src/main/java/net/shibboleth/sp/profile/BasicSessionInitiatorFlowDescriptor.java
index a84fc1d..3cabd69 100644
--- a/sp-server-api/src/main/java/net/shibboleth/sp/profile/BasicTokenConsumerFlowDescriptor.java
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/profile/BasicSessionInitiatorFlowDescriptor.java
@@ -27,17 +27,17 @@ import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.logic.PredicateSupport;
 
 /**
- * Basic implementation of a descriptor for a token consumer flow.
+ * Basic implementation of a descriptor for a session initiator flow.
  */
-public class BasicTokenConsumerFlowDescriptor extends AbstractIdentifiableInitializableComponent
-        implements TokenConsumerFlowDescriptor {
+public class BasicSessionInitiatorFlowDescriptor extends AbstractIdentifiableInitializableComponent
+        implements SessionInitiatorFlowDescriptor {
     
     /** Whether this flow supports a particular request. */
     @Nonnull private Predicate<ProfileRequestContext> activationCondition;
     
     /** Constructor. */
-    public BasicTokenConsumerFlowDescriptor() {
-        activationCondition = PredicateSupport.alwaysFalse();
+    public BasicSessionInitiatorFlowDescriptor() {
+        activationCondition = PredicateSupport.alwaysTrue();
     }
 
     /** {@inheritDoc} */
@@ -48,6 +48,8 @@ public class BasicTokenConsumerFlowDescriptor extends AbstractIdentifiableInitia
     /**
      * Set activation condition to determine whether this flow supports a request.
      * 
+     * <p>Defaults to true.</p>
+     * 
      * @param condition condition to use
      */
     public void setActivationCondition(@Nonnull final Predicate<ProfileRequestContext> condition) {
@@ -69,8 +71,8 @@ public class BasicTokenConsumerFlowDescriptor extends AbstractIdentifiableInitia
             return true;
         }
 
-        if (obj instanceof BasicTokenConsumerFlowDescriptor) {
-            return ensureId().equals(((BasicTokenConsumerFlowDescriptor) obj).getId());
+        if (obj instanceof BasicSessionInitiatorFlowDescriptor) {
+            return ensureId().equals(((BasicSessionInitiatorFlowDescriptor) obj).getId());
         }
 
         return false;
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/profile/BasicTokenConsumerFlowDescriptor.java b/sp-server-api/src/main/java/net/shibboleth/sp/profile/BasicTokenConsumerFlowDescriptor.java
index a84fc1d..f858c14 100644
--- a/sp-server-api/src/main/java/net/shibboleth/sp/profile/BasicTokenConsumerFlowDescriptor.java
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/profile/BasicTokenConsumerFlowDescriptor.java
@@ -48,6 +48,8 @@ public class BasicTokenConsumerFlowDescriptor extends AbstractIdentifiableInitia
     /**
      * Set activation condition to determine whether this flow supports a request.
      * 
+     * <p>Defaults to false.</p>
+     * 
      * @param condition condition to use
      */
     public void setActivationCondition(@Nonnull final Predicate<ProfileRequestContext> condition) {
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/profile/SessionInitiatorFlowDescriptor.java b/sp-server-api/src/main/java/net/shibboleth/sp/profile/SessionInitiatorFlowDescriptor.java
new file mode 100644
index 0000000..4eb563e
--- /dev/null
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/profile/SessionInitiatorFlowDescriptor.java
@@ -0,0 +1,47 @@
+/*
+ * 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.sp.profile;
+
+import java.util.function.Predicate;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.profile.FlowDescriptor;
+
+/**
+ * A descriptor for a session initiator subflow.
+ * 
+ * <p>Session initiators are the portion of the SP responsible for initiating SSO protocol requests.
+ * They exist in every SSO protocol in some sense but are usually not formalized, though most protocols
+ * have extensions nobody bothers to implement to represent the idea of an endpoint where a request can
+ * get kicked off.</p>
+ * 
+ * <p>The primary need for this is to associate each subflow with a condition that can
+ * determine whether a given request is handled by a given subflow. Sometimes this may be
+ * a no-op without more information that is only known once the flow runs, but some flows
+ * can optimize not running in some cases.</p>
+ */
+public interface SessionInitiatorFlowDescriptor extends FlowDescriptor {
+
+    /**
+     * Gets the activation condition to run to determine whether this flow can handle the request.
+     * 
+     * @return a condition governing use of this flow
+     */
+    @Nonnull Predicate<ProfileRequestContext> getActivationCondition();
+    
+}
\ No newline at end of file
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/SessionInitiatorFlowDescriptorManager.java b/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/SessionInitiatorFlowDescriptorManager.java
new file mode 100644
index 0000000..89ba78d
--- /dev/null
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/SessionInitiatorFlowDescriptorManager.java
@@ -0,0 +1,93 @@
+/*
+ * 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.sp.profile.impl;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.shared.spring.config.IdentifiedComponentManager;
+import net.shibboleth.sp.profile.SessionInitiatorFlowDescriptor;
+
+/**
+ * Manager of {@link SessionInitiatorFlowDescriptor} objects.
+ */
+public class SessionInitiatorFlowDescriptorManager extends IdentifiedComponentManager<SessionInitiatorFlowDescriptor> {
+
+    /** Class logger. */
+    @Nonnull private Logger log = LoggerFactory.getLogger(SessionInitiatorFlowDescriptorManager.class);
+    
+    /** Map of flow descriptors. */
+    @Nonnull private Map<String,SessionInitiatorFlowDescriptor> sessionInitiatorMap;
+    
+    /**
+     * Constructor.
+     *
+     * @param freeObjects  free-standing objects
+     */
+    @Autowired
+    public SessionInitiatorFlowDescriptorManager(@Nullable final Collection<SessionInitiatorFlowDescriptor> freeObjects) {
+        super(freeObjects);
+        
+        sessionInitiatorMap = CollectionSupport.emptyMap();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+
+        sessionInitiatorMap = getComponents().stream()
+                .collect(CollectionSupport.nonnullCollector(
+                        Collectors.toUnmodifiableMap(SessionInitiatorFlowDescriptor::getId, fd -> fd))).get();
+    }
+
+    /**
+     * Checks if a flow ID corresponds to a valid flow that in turn ius active for the request.
+     * 
+     * @param id flow ID
+     * @param profileRequestContext current profile request context
+     * 
+     * @return true iff the flow exists and is active (should run)
+     */
+    public boolean isActive(@Nonnull @NotEmpty final String id,
+            @Nullable final ProfileRequestContext profileRequestContext) {
+        final SessionInitiatorFlowDescriptor flow = sessionInitiatorMap.get(id);
+        if (flow != null) {
+            final boolean active = flow.getActivationCondition().test(profileRequestContext);
+            if (active) {
+                log.debug("Session initiator flow ({}) active, attempting", id);
+            } else {
+                log.info("Session initiator flow ({}) not active, skipping", id);
+            }
+            return active;
+        } else {
+            log.warn("Session initiator flow ({}) not registered", id);
+            return false;
+        }
+    }
+    
+}
\ 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