[java-plugin-shibd-saml] 02/02: Add subflow-specific prefix to allow subflows to detect their tokens.

Codeberg noreply at shibboleth.net
Tue May 26 18:50:58 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-saml.

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

commit acba4e30521a4d6cc3b9277373a4434e7d92f426
Author: Scott Cantor <scott at restingparrotsoftware.com>
AuthorDate: Tue May 26 14:41:37 2026 -0400

    Add subflow-specific prefix to allow subflows to detect their tokens.
---
 .../META-INF/net.shibboleth.idp/postconfig.xml     | 150 +++++++++++++--------
 .../flows/saml2/SAML2LogoutConsumerFlowTest.java   |  36 ++++-
 .../saml2/profile/impl/ProcessLogoutRequest.java   |   5 +-
 .../profile/impl/ProcessLogoutRequestTest.java     |   8 +-
 4 files changed, 137 insertions(+), 62 deletions(-)

diff --git a/sp-saml-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/sp-saml-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
index 6a77b33..0ad97fa 100644
--- a/sp-saml-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
+++ b/sp-saml-conf-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
@@ -80,86 +80,118 @@
     </bean>
 
     <!-- Logout consumer descriptors for each SAML binding. -->
+
+    <bean id="shibboletg.sp.TokenParameterPrefixCondition"
+        class="net.shibboleth.sp.profile.context.logic.InputStartsWithPredicate"
+        c:_0="token" c:_1="SAML:" />
     
     <bean p:id="sp/logout/consumer/saml2/redirect" class="net.shibboleth.sp.profile.BasicLogoutConsumerFlowDescriptor">
         <property name="activationCondition">
-            <bean class="net.shibboleth.sp.profile.context.logic.HttpServletRequestPredicate"
-                    p:httpServletRequestSupplier-ref="shibboleth.RemotedHttpServletRequestSupplier">
-                <property name="httpServletRequestValidator">
-                    <bean class="net.shibboleth.shared.servlet.impl.ChainingHttpServletRequestValidator">
-                        <property name="validators">
-                            <list>
-                                <bean class="net.shibboleth.shared.servlet.impl.BasicHttpServletRequestMethodValidator"
-                                    p:allowedMethods="GET" />
-                                <ref bean="shibboleth.HttpServletRequestValidator.BasicParams.SAML2.Redirect.RequestOrResponse" />
-                            </list>
-                        </property>
-                    </bean>
-                </property>
+            <bean parent="shibboleth.Conditions.OR">
+                <constructor-arg>
+                    <list>
+                        <ref bean="shibboletg.sp.TokenParameterPrefixCondition" />
+                        <bean class="net.shibboleth.sp.profile.context.logic.HttpServletRequestPredicate"
+                                p:httpServletRequestSupplier-ref="shibboleth.RemotedHttpServletRequestSupplier">
+                            <property name="httpServletRequestValidator">
+                                <bean class="net.shibboleth.shared.servlet.impl.ChainingHttpServletRequestValidator">
+                                    <property name="validators">
+                                        <list>
+                                            <bean class="net.shibboleth.shared.servlet.impl.BasicHttpServletRequestMethodValidator"
+                                                p:allowedMethods="GET" />
+                                            <ref bean="shibboleth.HttpServletRequestValidator.BasicParams.SAML2.Redirect.RequestOrResponse" />
+                                        </list>
+                                    </property>
+                                </bean>
+                            </property>
+                        </bean>
+                    </list>
+                </constructor-arg>
             </bean>
         </property>
     </bean>
     
     <bean p:id="sp/logout/consumer/saml2/post" class="net.shibboleth.sp.profile.BasicLogoutConsumerFlowDescriptor">
         <property name="activationCondition">
-            <bean class="net.shibboleth.sp.profile.context.logic.HttpServletRequestPredicate"
-                    p:httpServletRequestSupplier-ref="shibboleth.RemotedHttpServletRequestSupplier">
-                <property name="httpServletRequestValidator">
-                    <bean class="net.shibboleth.shared.servlet.impl.ChainingHttpServletRequestValidator">
-                        <property name="validators">
-                            <list>
-                                <bean class="net.shibboleth.shared.servlet.impl.BasicHttpServletRequestMethodValidator"
-                                    p:allowedMethods="POST" />
-                                <bean class="net.shibboleth.shared.servlet.impl.BasicHttpServletRequestContentTypeValidator"
-                                    p:allowedContentTypes="application/x-www-form-urlencoded" />
-                                <ref bean="shibboleth.HttpServletRequestValidator.BasicParams.SAML2.POST.RequestOrResponse" />
-                            </list>
-                        </property>
-                    </bean>
-                </property>
+            <bean parent="shibboleth.Conditions.OR">
+                <constructor-arg>
+                    <list>
+                        <ref bean="shibboletg.sp.TokenParameterPrefixCondition" />
+                        <bean class="net.shibboleth.sp.profile.context.logic.HttpServletRequestPredicate"
+                                p:httpServletRequestSupplier-ref="shibboleth.RemotedHttpServletRequestSupplier">
+                            <property name="httpServletRequestValidator">
+                                <bean class="net.shibboleth.shared.servlet.impl.ChainingHttpServletRequestValidator">
+                                    <property name="validators">
+                                        <list>
+                                            <bean class="net.shibboleth.shared.servlet.impl.BasicHttpServletRequestMethodValidator"
+                                                p:allowedMethods="POST" />
+                                            <bean class="net.shibboleth.shared.servlet.impl.BasicHttpServletRequestContentTypeValidator"
+                                                p:allowedContentTypes="application/x-www-form-urlencoded" />
+                                            <ref bean="shibboleth.HttpServletRequestValidator.BasicParams.SAML2.POST.RequestOrResponse" />
+                                        </list>
+                                    </property>
+                                </bean>
+                            </property>
+                        </bean>
+                    </list>
+                </constructor-arg>
             </bean>
         </property>
     </bean>
 
     <bean p:id="sp/logout/consumer/saml2/post-simplesign" class="net.shibboleth.sp.profile.BasicLogoutConsumerFlowDescriptor">
         <property name="activationCondition">
-            <bean class="net.shibboleth.sp.profile.context.logic.HttpServletRequestPredicate"
-                    p:httpServletRequestSupplier-ref="shibboleth.RemotedHttpServletRequestSupplier">
-                <property name="httpServletRequestValidator">
-                    <bean class="net.shibboleth.shared.servlet.impl.ChainingHttpServletRequestValidator">
-                        <property name="validators">
-                            <list>
-                                <bean class="net.shibboleth.shared.servlet.impl.BasicHttpServletRequestMethodValidator"
-                                    p:allowedMethods="POST" />
-                                <bean class="net.shibboleth.shared.servlet.impl.BasicHttpServletRequestContentTypeValidator"
-                                    p:allowedContentTypes="application/x-www-form-urlencoded" />
-                                <ref bean="shibboleth.HttpServletRequestValidator.BasicParams.SAML2.POSTSimpleSign.RequestOrResponse" />
-                            </list>
-                        </property>
-                    </bean>
-                </property>
+            <bean parent="shibboleth.Conditions.OR">
+                <constructor-arg>
+                    <list>
+                        <ref bean="shibboletg.sp.TokenParameterPrefixCondition" />
+                        <bean class="net.shibboleth.sp.profile.context.logic.HttpServletRequestPredicate"
+                                p:httpServletRequestSupplier-ref="shibboleth.RemotedHttpServletRequestSupplier">
+                            <property name="httpServletRequestValidator">
+                                <bean class="net.shibboleth.shared.servlet.impl.ChainingHttpServletRequestValidator">
+                                    <property name="validators">
+                                        <list>
+                                            <bean class="net.shibboleth.shared.servlet.impl.BasicHttpServletRequestMethodValidator"
+                                                p:allowedMethods="POST" />
+                                            <bean class="net.shibboleth.shared.servlet.impl.BasicHttpServletRequestContentTypeValidator"
+                                                p:allowedContentTypes="application/x-www-form-urlencoded" />
+                                            <ref bean="shibboleth.HttpServletRequestValidator.BasicParams.SAML2.POSTSimpleSign.RequestOrResponse" />
+                                        </list>
+                                    </property>
+                                </bean>
+                            </property>
+                        </bean>
+                    </list>
+                </constructor-arg>
             </bean>
         </property>
     </bean>
 
     <bean p:id="sp/logout/consumer/saml2/artifact" class="net.shibboleth.sp.profile.BasicLogoutConsumerFlowDescriptor">
         <property name="activationCondition">
-            <bean class="net.shibboleth.sp.profile.context.logic.HttpServletRequestPredicate"
-                    p:httpServletRequestSupplier-ref="shibboleth.RemotedHttpServletRequestSupplier">
-                <property name="httpServletRequestValidator">
-                    <bean class="net.shibboleth.shared.servlet.impl.ChainingHttpServletRequestValidator">
-                        <property name="validators">
-                            <list>
-                                <bean class="net.shibboleth.shared.servlet.impl.BasicHttpServletRequestMethodValidator"
-                                    p:allowedMethods="#{{ 'GET', 'POST' }}" />
-                                <bean class="net.shibboleth.shared.servlet.impl.BasicHttpServletRequestContentTypeValidator"
-                                    p:allowedContentTypes="application/x-www-form-urlencoded"
-                                    p:allowNullContentType="true" />
-                                <ref bean="shibboleth.HttpServletRequestValidator.BasicParams.SAML2.Artifact" />
-                            </list>
-                        </property>
-                    </bean>
-                </property>
+            <bean parent="shibboleth.Conditions.OR">
+                <constructor-arg>
+                    <list>
+                        <ref bean="shibboletg.sp.TokenParameterPrefixCondition" />
+                        <bean class="net.shibboleth.sp.profile.context.logic.HttpServletRequestPredicate"
+                                p:httpServletRequestSupplier-ref="shibboleth.RemotedHttpServletRequestSupplier">
+                            <property name="httpServletRequestValidator">
+                                <bean class="net.shibboleth.shared.servlet.impl.ChainingHttpServletRequestValidator">
+                                    <property name="validators">
+                                        <list>
+                                            <bean class="net.shibboleth.shared.servlet.impl.BasicHttpServletRequestMethodValidator"
+                                                p:allowedMethods="#{{ 'GET', 'POST' }}" />
+                                            <bean class="net.shibboleth.shared.servlet.impl.BasicHttpServletRequestContentTypeValidator"
+                                                p:allowedContentTypes="application/x-www-form-urlencoded"
+                                                p:allowNullContentType="true" />
+                                            <ref bean="shibboleth.HttpServletRequestValidator.BasicParams.SAML2.Artifact" />
+                                        </list>
+                                    </property>
+                                </bean>
+                            </property>
+                        </bean>
+                    </list>
+                </constructor-arg>
             </bean>
         </property>
     </bean>
diff --git a/sp-saml-conf-impl/src/test/java/net/shibboleth/sp/saml/flows/saml2/SAML2LogoutConsumerFlowTest.java b/sp-saml-conf-impl/src/test/java/net/shibboleth/sp/saml/flows/saml2/SAML2LogoutConsumerFlowTest.java
index 31f106e..21a125c 100644
--- a/sp-saml-conf-impl/src/test/java/net/shibboleth/sp/saml/flows/saml2/SAML2LogoutConsumerFlowTest.java
+++ b/sp-saml-conf-impl/src/test/java/net/shibboleth/sp/saml/flows/saml2/SAML2LogoutConsumerFlowTest.java
@@ -26,6 +26,7 @@ import javax.annotation.Nullable;
 import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
 import org.opensaml.core.xml.io.MarshallingException;
 import org.opensaml.core.xml.util.XMLObjectSupport;
+import org.opensaml.profile.action.EventIds;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.opensaml.saml.common.SAMLObject;
 import org.opensaml.saml.common.SAMLObjectBuilder;
@@ -291,6 +292,23 @@ public class SAML2LogoutConsumerFlowTest extends AbstractSPFlowTest {
         validateLogoutRequestResult(false);
     }
     
+    /**
+     * Test LogoutRequest flow when unsigned.
+     * 
+     * @throws Exception 
+     */
+    @Test
+    public void testRequestUnsigned() throws Exception {
+        final LogoutRequest request = buildLogoutRequest(ISSUER);
+        final DDF input = buildRemotedPOSTMessage(request, RELAY_STATE, null);
+        input.addmember(ConsumerConstants.SESSION_OPAQUE).addmember(PrepareAgentResponse.NAMEID_PARAM).string(
+                "<NameID xmlns='urn:oasis:names:tc:SAML:2.0:assertion' Format='"
+                        + NameIDType.EMAIL + "' SPProvidedID='" + ISSUER + "'>jdoe at example.org</NameID>");
+        setApplicationRequest(APPLICATION_ID, input);
+        
+        validateError(EventIds.INVALID_MESSAGE);
+    }    
+    
     /**
      * Test LogoutRequest flow with a match.
      * 
@@ -322,6 +340,20 @@ public class SAML2LogoutConsumerFlowTest extends AbstractSPFlowTest {
         validateError(IdPEventIds.INVALID_PROFILE_CONFIG);
     }
     
+    /**
+     * Test flow with unsigned response.
+     * 
+     * @throws IOException 
+     */
+    @Test
+    public void testResponseUnsigned() throws IOException {
+        final DDF input = buildRemotedPOSTMessage(buildLogoutResponse(ISSUER, StatusCode.SUCCESS, null), null, null);
+        setApplicationRequest(APPLICATION_ID, input);
+
+        validateError(EventIds.INVALID_MESSAGE);
+    }
+    
+    
     /**
      * Test LogoutResponse flow with a successful status.
      * 
@@ -385,6 +417,7 @@ public class SAML2LogoutConsumerFlowTest extends AbstractSPFlowTest {
         
         final String token = output.getmember(ProcessLogoutRequest.TOKEN_PARAM).string();
         assert token != null;
+        Assert.assertTrue(token.startsWith(ProcessLogoutRequest.TOKEN_PREFIX));
 
         final SAMLStateData stateData;
         try (final ServiceableComponent<AgentResolver> resolver = agentResolver.getServiceableComponent()) {
@@ -393,7 +426,8 @@ public class SAML2LogoutConsumerFlowTest extends AbstractSPFlowTest {
             assert agent != null;
             final Application app = agent.getApplication(APPLICATION_ID);
             assert app != null;
-            stateData = dummyStateManager.recoverFromStateToken(agent, app, token, SAMLStateData.class);
+            stateData = dummyStateManager.recoverFromStateToken(agent, app,
+                    token.substring(ProcessLogoutRequest.TOKEN_PREFIX.length()), SAMLStateData.class);
         }
         
         assert stateData != null;
diff --git a/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/impl/ProcessLogoutRequest.java b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/impl/ProcessLogoutRequest.java
index 8f40c6e..7b5d30f 100644
--- a/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/impl/ProcessLogoutRequest.java
+++ b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/impl/ProcessLogoutRequest.java
@@ -80,6 +80,9 @@ public class ProcessLogoutRequest extends AbstractApplicationAction {
 
     /** "token" parameter name. */
     @Nonnull @NotEmpty public static final String TOKEN_PARAM = "token";
+
+    /** "token" value prefix. */
+    @Nonnull @NotEmpty public static final String TOKEN_PREFIX = "SAML:";
     
     /** Class logger. */
     @Nonnull private final Logger log = LoggerFactory.getLogger(ProcessLogoutRequest.class);
@@ -387,7 +390,7 @@ public class ProcessLogoutRequest extends AbstractApplicationAction {
         
         try {
             final String token = stateManager.preserveToStateToken(ensureAgent(), ensureApplication(), state);
-            output.addmember(TOKEN_PARAM).string(token);
+            output.addmember(TOKEN_PARAM).string(TOKEN_PREFIX + token);
         } catch (final IOException e) {
             log.error("{} Exception producing state token", getLogPrefix(), e);
         }
diff --git a/sp-saml-impl/src/test/java/net/shibboleth/sp/saml/saml2/profile/impl/ProcessLogoutRequestTest.java b/sp-saml-impl/src/test/java/net/shibboleth/sp/saml/saml2/profile/impl/ProcessLogoutRequestTest.java
index e70a2bb..bf3a40e 100644
--- a/sp-saml-impl/src/test/java/net/shibboleth/sp/saml/saml2/profile/impl/ProcessLogoutRequestTest.java
+++ b/sp-saml-impl/src/test/java/net/shibboleth/sp/saml/saml2/profile/impl/ProcessLogoutRequestTest.java
@@ -30,6 +30,7 @@ import org.opensaml.saml.saml2.core.Extensions;
 import org.opensaml.saml.saml2.core.Issuer;
 import org.opensaml.saml.saml2.core.LogoutRequest;
 import org.opensaml.saml.saml2.core.LogoutResponse;
+import org.opensaml.saml.saml2.core.NameIDType;
 import org.opensaml.saml.saml2.core.RequestAbstractType;
 import org.opensaml.saml.saml2.core.SessionIndex;
 import org.opensaml.saml.saml2.testing.SAML2ActionTestingSupport;
@@ -366,6 +367,9 @@ public class ProcessLogoutRequestTest extends BaseApplicationActionTest {
     private void buildLogoutRequest() {
         final LogoutRequest request = SAML2ActionTestingSupport.buildLogoutRequest(
                 SAML2ActionTestingSupport.buildNameID("jdoe"));
+        final Issuer issuer = request.getIssuer();
+        assert issuer != null;
+        issuer.setFormat(NameIDType.ENTITY);
         prc.ensureInboundMessageContext().setMessage(request);
         SAMLBindingSupport.setRelayState(prc.ensureInboundMessageContext(), RELAY_STATE);
     }
@@ -386,7 +390,9 @@ public class ProcessLogoutRequestTest extends BaseApplicationActionTest {
      */
     private void validateToken(@Nullable final String token) throws IOException {
         assert token != null;
-        final SAMLStateData data = stateManager.recoverFromStateToken(agent, application, token, SAMLStateData.class);
+        Assert.assertTrue(token.startsWith(ProcessLogoutRequest.TOKEN_PREFIX));
+        final SAMLStateData data = stateManager.recoverFromStateToken(agent, application,
+                token.substring(ProcessLogoutRequest.TOKEN_PREFIX.length()), SAMLStateData.class);
         assert data != null;
         
         final LogoutRequest request = (LogoutRequest) prc.ensureInboundMessageContext().getMessage();

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


More information about the commits mailing list