[java-plugin-shibd-saml] branch main updated: Generate SOAP fault on ECP errors.

Codeberg noreply at shibboleth.net
Mon Aug 10 17:51:23 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/dc449330e7b51c8ff177e943124ca710fc2f4ecb

The following commit(s) were added to refs/heads/main by this push:
     new dc44933  Generate SOAP fault on ECP errors.
dc44933 is described below

commit dc449330e7b51c8ff177e943124ca710fc2f4ecb
Author: Scott Cantor <scott at restingparrotsoftware.com>
AuthorDate: Mon Aug 10 12:16:34 2026 -0400

    Generate SOAP fault on ECP errors.
---
 .../idp/flows/sp/initiator/ecp/ecp-beans.xml       | 29 ++++++++++
 .../idp/flows/sp/initiator/ecp/ecp-flow.xml        | 19 +++++++
 .../flows/saml2/ECPSessionInitiatorFlowTest.java   | 61 +++++++++++++++++++---
 .../idp/module/conf/sp/saml-test-agents.xml        |  4 +-
 4 files changed, 104 insertions(+), 9 deletions(-)

diff --git a/sp-saml-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/initiator/ecp/ecp-beans.xml b/sp-saml-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/initiator/ecp/ecp-beans.xml
index 53f0b38..1cbd99a 100644
--- a/sp-saml-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/initiator/ecp/ecp-beans.xml
+++ b/sp-saml-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/initiator/ecp/ecp-beans.xml
@@ -104,6 +104,35 @@
         </property>
     </bean>
 
+    <!-- Fault handling. -->
+
+    <bean id="InitializeOutboundMessageContextForSOAPFault"
+        class="net.shibboleth.idp.saml.profile.impl.InitializeOutboundMessageContextForError" scope="prototype"
+        p:outboundBinding-ref="shibboleth.Binding.SAML2PAOS" />
+
+    <bean id="AddSOAPFault"
+            class="org.opensaml.soap.soap11.profile.impl.AddSOAPFault" scope="prototype"
+            p:faultString="An error occurred.">
+        <property name="detailedErrorsCondition">
+            <bean class="net.shibboleth.profile.config.logic.DetailedErrorsPredicate" />
+        </property>
+        <property name="faultCode">
+            <util:list>
+                <util:constant static-field="org.opensaml.soap.soap11.FaultCode.SERVER" />
+            </util:list>
+        </property>
+        <property name="faultCodeLookupStrategy">
+            <bean class="org.opensaml.soap.soap11.profile.impl.AddSOAPFault.FaultCodeMappingFunction"
+                    c:mappings-ref="shibboleth.SOAPFaultCodeMappings" />
+        </property>
+        <property name="faultStringLookupStrategy">
+            <bean class="net.shibboleth.idp.profile.context.navigate.SpringStatusMessageLookupFunction" />
+        </property>
+    </bean>
+
+    <bean id="EncodeSOAPFault" class="net.shibboleth.sp.profile.impl.EncodeMessage" scope="prototype"
+        p:messageEncoderFactory-ref="messageEncoderFactory" />
+
     <!-- Binding objects (not in IdP at present). -->
 
     <bean parent="shibboleth.BindingDescriptor" id="shibboleth.Binding.SAML2PAOS"
diff --git a/sp-saml-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/initiator/ecp/ecp-flow.xml b/sp-saml-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/initiator/ecp/ecp-flow.xml
index bd91e52..d26c00c 100644
--- a/sp-saml-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/initiator/ecp/ecp-flow.xml
+++ b/sp-saml-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/initiator/ecp/ecp-flow.xml
@@ -14,6 +14,7 @@
         <evaluate expression="'proceed'" />
         
         <transition on="proceed" to="CheckForRelyingParty" />
+        <transition to="OutputSOAPFault" />
     </action-state>
     
     <decision-state id="CheckForRelyingParty">
@@ -28,6 +29,7 @@
         <evaluate expression="'proceed'" />
         
         <transition on="proceed" to="ContinueECPRequest" />
+        <transition to="OutputSOAPFault" />
     </action-state>
     
     <action-state id="ContinueECPRequest">
@@ -54,8 +56,25 @@
         <evaluate expression="'proceed'" />
 
         <transition on="proceed" to="proceed" />
+        <transition to="OutputSOAPFault" />
     </action-state>
     
+    <action-state id="OutputSOAPFault">
+        <evaluate expression="InitializeOutboundMessageContextForSOAPFault" />
+        <evaluate expression="AddSOAPFault" />
+        <evaluate expression="OutboundRequestPopulateAuditContext" />
+        <evaluate expression="EncodeSOAPFault" />
+        <evaluate expression="'proceed'" />
+        
+        <transition on="proceed" to="SOAPFault" />
+    </action-state>
+    
+    <end-state id="SOAPFault" />
+    
+    <global-transitions>
+        <transition on="SOAPFault" to="SOAPFault" />
+    </global-transitions>
+    
     <!-- The file really exists in this directory, but it's referenced from extending flow-directories -->
     <bean-import resource="classpath:/META-INF/net/shibboleth/idp/flows/sp/initiator/ecp/ecp-beans.xml" />
 
diff --git a/sp-saml-conf-impl/src/test/java/net/shibboleth/sp/saml/flows/saml2/ECPSessionInitiatorFlowTest.java b/sp-saml-conf-impl/src/test/java/net/shibboleth/sp/saml/flows/saml2/ECPSessionInitiatorFlowTest.java
index b3be667..fa04d8b 100644
--- a/sp-saml-conf-impl/src/test/java/net/shibboleth/sp/saml/flows/saml2/ECPSessionInitiatorFlowTest.java
+++ b/sp-saml-conf-impl/src/test/java/net/shibboleth/sp/saml/flows/saml2/ECPSessionInitiatorFlowTest.java
@@ -25,7 +25,6 @@ import javax.annotation.Nullable;
 import org.opensaml.core.xml.XMLObject;
 import org.opensaml.core.xml.io.UnmarshallingException;
 import org.opensaml.core.xml.util.XMLObjectSupport;
-import org.opensaml.profile.action.EventIds;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.opensaml.saml.common.binding.SAMLBindingSupport;
 import org.opensaml.saml.common.xml.SAMLConstants;
@@ -37,6 +36,9 @@ import org.opensaml.saml.saml2.ecp.Request;
 import org.opensaml.soap.soap11.ActorBearing;
 import org.opensaml.soap.soap11.Body;
 import org.opensaml.soap.soap11.Envelope;
+import org.opensaml.soap.soap11.Fault;
+import org.opensaml.soap.soap11.FaultCode;
+import org.opensaml.soap.soap11.FaultString;
 import org.opensaml.soap.soap11.Header;
 import org.springframework.test.context.ContextConfiguration;
 import org.springframework.test.context.web.WebAppConfiguration;
@@ -44,7 +46,6 @@ import org.springframework.webflow.executor.FlowExecutionResult;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
-import net.shibboleth.idp.profile.IdPEventIds;
 import net.shibboleth.idp.spring.IdPPropertiesApplicationContextInitializer;
 import net.shibboleth.idp.test.PreferFileSystemApplicationContextInitializer;
 import net.shibboleth.shared.logic.ConstraintViolationException;
@@ -98,10 +99,10 @@ public class ECPSessionInitiatorFlowTest extends AbstractSPFlowTest {
     /**
      * Test flow with profile disallowed.
      * 
-     * @throws IOException 
+     * @throws Exception 
      */
     @Test
-    public void testProfileDisabled() throws IOException {
+    public void testProfileDisabled() throws Exception {
         setDefaultAuth();
         
         final DDF input = new DDF(null).structure();
@@ -114,16 +115,20 @@ public class ECPSessionInitiatorFlowTest extends AbstractSPFlowTest {
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         assertFlowExecutionResult(result, FLOW_ID);
         assertFlowExecutionOutcome(result.getOutcome());
-        assertOutputMessageEvent(result, IdPEventIds.INVALID_PROFILE_CONFIG);
+        assertOutputMessageEvent(result, "SOAPFault");
+        // This rather odd message comes from the IdP if the detailedErrors flag is on, and
+        // it maps the event ID to a code in messages.properties to key off.
+        // InvalidProfileConfiguration maps to this code.
+        validateSOAPFault(result, "relying-party");
     }
 
     /**
      * Test flow without response URL.
      * 
-     * @throws IOException 
+     * @throws Exception 
      */
     @Test
-    public void testNoResponseURL() throws IOException {
+    public void testNoResponseURL() throws Exception {
         setDefaultAuth();
 
         final DDF input = new DDF(null).structure();
@@ -135,7 +140,11 @@ public class ECPSessionInitiatorFlowTest extends AbstractSPFlowTest {
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
         assertFlowExecutionResult(result, FLOW_ID);
         assertFlowExecutionOutcome(result.getOutcome());
-        assertOutputMessageEvent(result, EventIds.INVALID_MESSAGE);
+        assertOutputMessageEvent(result, "SOAPFault");
+        // This rather odd message comes from the IdP if the detailedErrors flag is on, and
+        // it maps the event ID to a code in messages.properties to key off.
+        // InvalidProfileConfiguration maps to this code.
+        validateSOAPFault(result, "unexpected");
     }
     
     /**
@@ -310,6 +319,42 @@ public class ECPSessionInitiatorFlowTest extends AbstractSPFlowTest {
         return authnRequest;
     }
 
+    /**
+     * Decode a SOAP fault and run sanity checks against it.
+     * 
+     * @param result flow execution result
+     * 
+     * @throws Exception
+     */
+    private void validateSOAPFault(@Nonnull final FlowExecutionResult result, @Nullable final String message) throws Exception {
+        final ProfileRequestContext prc = retrieveProfileRequestContext(result);
+        assert prc != null;
+        final AgentRequestContext arc = prc.ensureSubcontext(AgentRequestContext.class);
+        final DDF input = arc.getInput();
+        final DDF output = arc.getOutput();
+        assert input != null;
+        assert output != null;
+        Assert.assertTrue(output.isstruct());
+        final DDF http = output.getmember(RemotedHttpServletResponse.STRUCTURE_NAME);
+        Assert.assertTrue(http.isstruct());
+
+        final byte[] body = http.getmember(RemotedHttpServletResponse.RESPONSE).getmember(RemotedHttpServletResponse.DATA).unsafe_string();
+        assert body != null;
+        final Envelope env = decodeSOAPEnvelope(body);
+
+        final Body soapBody = env.getBody();
+        assert soapBody != null;
+        if (soapBody.getUnknownXMLObjects().get(0) instanceof Fault fault) {
+            final FaultCode code = fault.getCode();
+            assert code != null;
+            Assert.assertEquals(code.getValue(), FaultCode.SERVER);
+            final FaultString msg = fault.getMessage();
+            assert msg != null;
+            Assert.assertEquals(msg.getValue(),  message);
+        } else {
+            Assert.fail("SOAP fault not found");
+        }
+    }
     
     /**
      * Decodes a SAML message encoded via HTTP-PAOS binding.
diff --git a/sp-saml-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/saml-test-agents.xml b/sp-saml-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/saml-test-agents.xml
index 8ed9eed..de3ea87 100644
--- a/sp-saml-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/saml-test-agents.xml
+++ b/sp-saml-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/saml-test-agents.xml
@@ -17,6 +17,7 @@
     -->
 
     <bean p:id="testsp.example.org" parent="shibboleth.sp.Agent" p:credentials="#{ {null : 'foo'} }"
+            p:detailedErrors="true"
             p:issuer="https://testsp.example.org">
         <property name="applications">
             <set>
@@ -34,7 +35,8 @@
                     p:profileConfigurations-ref="test.ProfileConfigurations" />
 
                 <bean p:id="no-profile" parent="shibboleth.sp.Application.NoInheritance"
-                    p:profileConfigurations-ref="test.NoProfileConfigurations" />
+                    p:profileConfigurations-ref="test.NoProfileConfigurations"
+                    p:detailedErrors="true" />
 
                 <bean p:id="no-endpoint" parent="shibboleth.sp.Application"
                     p:authenticatingAuthority="https://noendpoint.example.org"

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


More information about the commits mailing list