[java-plugin-shibd] branch main updated: More unit tests, add support for parsing encoded message to check.

Scott Cantor cantor.2 at osu.edu
Tue Jul 30 15:08:01 UTC 2024


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

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

View the commit online:
http://git.shibboleth.net/view/?p=java-plugin-shibd.git;a=commit;h=ab48946c2e43ee8ddab65cf2ee1288878ad2100a

The following commit(s) were added to refs/heads/main by this push:
     new ab48946  More unit tests, add support for parsing encoded message to check.
ab48946 is described below

commit ab48946c2e43ee8ddab65cf2ee1288878ad2100a
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Tue Jul 30 11:07:58 2024 -0400

    More unit tests, add support for parsing encoded message to check.
---
 .../shibboleth/sp/flows/AbstractSPFlowTest.java    | 59 ++++++++++++++++++
 .../flows/saml2/SAML2SessionInitiatorFlowTest.java | 57 ++++++++++++++++-
 .../idp/module/conf/sp/metadata-providers.xml      | 71 +++++++++++++++++-----
 .../shibboleth/idp/module/conf/sp/test-agents.xml  |  4 ++
 .../saml/saml2/profile/impl/AddAuthnRequest.java   |  2 +
 .../sp/profile/AbstractAgentRequestAction.java     | 24 ++++++--
 .../shibboleth/sp/profile/impl/EncodeMessage.java  |  3 +-
 7 files changed, 198 insertions(+), 22 deletions(-)

diff --git a/sp-conf-impl/src/test/java/net/shibboleth/sp/flows/AbstractSPFlowTest.java b/sp-conf-impl/src/test/java/net/shibboleth/sp/flows/AbstractSPFlowTest.java
index d95d702..dd0e00a 100644
--- a/sp-conf-impl/src/test/java/net/shibboleth/sp/flows/AbstractSPFlowTest.java
+++ b/sp-conf-impl/src/test/java/net/shibboleth/sp/flows/AbstractSPFlowTest.java
@@ -19,13 +19,20 @@ import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.charset.Charset;
+import java.util.List;
 import java.util.Map;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
 import org.apache.commons.codec.binary.Base64;
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.decoder.MessageDecodingException;
 import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.common.SAMLObject;
+import org.opensaml.saml.saml2.binding.decoding.impl.HTTPRedirectDeflateDecoder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.mock.web.MockHttpServletResponse;
 import org.springframework.test.context.ContextConfiguration;
@@ -35,7 +42,12 @@ import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 
 import net.shibboleth.idp.test.flows.AbstractFlowTest;
+import net.shibboleth.shared.collection.Pair;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.net.URISupport;
+import net.shibboleth.shared.primitive.NonnullSupplier;
 import net.shibboleth.shared.servlet.impl.HttpServletRequestResponseContext;
+import net.shibboleth.shared.xml.ParserPool;
 import net.shibboleth.sp.context.AgentRequestContext;
 import net.shibboleth.sp.ddf.DDF;
 import net.shibboleth.sp.profile.impl.ResolveApplication;
@@ -68,6 +80,10 @@ public abstract class AbstractSPFlowTest extends AbstractFlowTest {
     protected String flowId;
     
     protected String endStateId;
+
+    @Autowired
+    @Qualifier("shibboleth.ParserPool")
+    protected ParserPool parserPool;
     
     protected AbstractSPFlowTest(final String id) {
         this(id, END_STATE_ID);
@@ -192,4 +208,47 @@ public abstract class AbstractSPFlowTest extends AbstractFlowTest {
         }
     }
     
+    /**
+     * Decodes the SAML message encoded via HTTP-Redirect binding.
+     * 
+     * @param url the encoded redirect
+     * 
+     * @return decoded message
+     * @throws MessageDecodingException 
+     */
+    @Nonnull protected SAMLObject decodeRedirect(@Nullable final String url) throws MessageDecodingException {
+        final MockHttpServletRequest mock = new MockHttpServletRequest("GET", "url");
+        final int index = url != null ? url.indexOf('?') : -1;
+        if (url == null || index < 0) {
+            throw new MessageDecodingException("No query string");
+        }
+        final List<Pair<String,String>> params = URISupport.parseQueryString(url.substring(index + 1));
+        for (final var param : params) {
+            final String name = param.getFirst();
+            if (name != null) {
+                mock.addParameter(name, param.getSecond());
+            }
+        }
+        
+        final HTTPRedirectDeflateDecoder decoder = new HTTPRedirectDeflateDecoder();
+        decoder.setHttpServletRequestSupplier(NonnullSupplier.of(mock));
+        decoder.setParserPool(parserPool);
+        
+        try {
+            decoder.initialize();
+        } catch (final ComponentInitializationException e) {
+            throw new MessageDecodingException(e);
+        }
+        
+        decoder.decode();
+        
+        final MessageContext mc = decoder.getMessageContext();
+        decoder.destroy();
+        
+        if (mc != null && mc.getMessage() instanceof SAMLObject saml) {
+            return saml;
+        }
+        throw new MessageDecodingException("No message, or incorrect type.");
+    }
+
 }
\ No newline at end of file
diff --git a/sp-conf-impl/src/test/java/net/shibboleth/sp/flows/saml2/SAML2SessionInitiatorFlowTest.java b/sp-conf-impl/src/test/java/net/shibboleth/sp/flows/saml2/SAML2SessionInitiatorFlowTest.java
index 2779f3f..465fff0 100644
--- a/sp-conf-impl/src/test/java/net/shibboleth/sp/flows/saml2/SAML2SessionInitiatorFlowTest.java
+++ b/sp-conf-impl/src/test/java/net/shibboleth/sp/flows/saml2/SAML2SessionInitiatorFlowTest.java
@@ -15,9 +15,15 @@
 package net.shibboleth.sp.flows.saml2;
 
 import java.io.IOException;
+import java.nio.charset.Charset;
+import java.time.Instant;
 
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 
+import org.opensaml.messaging.decoder.MessageDecodingException;
+import org.opensaml.saml.common.SAMLObject;
+import org.opensaml.saml.saml2.core.AuthnRequest;
 import org.springframework.webflow.executor.FlowExecutionResult;
 import org.testng.Assert;
 import org.testng.annotations.Test;
@@ -25,6 +31,7 @@ import org.testng.annotations.Test;
 import net.shibboleth.idp.authn.AuthnEventIds;
 import net.shibboleth.sp.ddf.DDF;
 import net.shibboleth.sp.flows.AbstractSPFlowTest;
+import net.shibboleth.sp.messaging.RemotedHttpServletResponse;
 
 /**
  * Unit test for the SP session-initiator flow.
@@ -70,13 +77,30 @@ public class SAML2SessionInitiatorFlowTest extends AbstractSPFlowTest {
         assertOutputMessageEvent(result, AuthnEventIds.NO_POTENTIAL_FLOW);
     }
 
+    /**
+     * Test flow with no valid endpoint
+     * 
+     * @throws IOException 
+     */
+    @Test
+    public void testNoEndpoint() throws IOException {
+        setDefaultAuth();
+        setApplicationRequest("no-endpoint");
+
+        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+        assertFlowExecutionResult(result, FLOW_ID);
+        assertFlowExecutionOutcome(result.getOutcome());
+        assertOutputMessageEvent(result, AuthnEventIds.NO_POTENTIAL_FLOW);
+    }
+
     /**
      * Test basic use of flow.
      * 
      * @throws IOException 
+     * @throws MessageDecodingException 
      */
     @Test
-    public void testSimple() throws IOException {
+    public void testRedirect() throws IOException, MessageDecodingException {
         setDefaultAuth();
         setApplicationRequest(APPLICATION_ID);
 
@@ -85,8 +109,37 @@ public class SAML2SessionInitiatorFlowTest extends AbstractSPFlowTest {
         assertFlowExecutionOutcome(result.getOutcome());
         
         final DDF output = assertOutputMessageEvent(result, null);
+        assertOutputMessage(output);
+    }
+
+    /**
+     * Decode an encoded response and run sanity checks against it.
+     * 
+     * @param output the wrapped output message from the flow
+     * 
+     * @throws MessageDecodingException
+     */
+    private void assertOutputMessage(@Nullable final DDF output) throws MessageDecodingException {
         assert output != null;
         Assert.assertTrue(output.isstruct());
+        final DDF http = output.getmember("http");
+        Assert.assertTrue(http.isstruct());
+        
+        final AuthnRequest authnRequest;
+        final String redirect = http.getmember("redirect").string();
+        if (redirect != null) {
+            final SAMLObject saml = decodeRedirect(redirect);
+            assert saml instanceof AuthnRequest;
+            authnRequest = (AuthnRequest) saml;
+            Assert.assertTrue(redirect.startsWith(authnRequest.getDestination()));
+        } else {
+            // TODO POST
+            authnRequest = null;
+        }
+        
+        assert authnRequest != null;
+        Assert.assertNotNull(authnRequest.getID());
+        Assert.assertTrue(Instant.now().isAfter(authnRequest.getIssueInstant()));
     }
-
+    
 }
\ No newline at end of file
diff --git a/sp-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/metadata-providers.xml b/sp-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/metadata-providers.xml
index c0b438e..ba3bf0f 100644
--- a/sp-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/metadata-providers.xml
+++ b/sp-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/metadata-providers.xml
@@ -27,12 +27,13 @@
 	<!-- Example metadata provider. -->
 
     <MetadataProvider id="InlineExample" xsi:type="InlineMetadataProvider" indexesRef="testbed.MetadataIndexes">
-        <md:EntityDescriptor ID="entity" entityID="https://idp.example.org">
-            <md:IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
-                <md:KeyDescriptor>
-                    <ds:KeyInfo>
-                        <ds:X509Data>
-                            <ds:X509Certificate>
+        <md:EntitiesDescriptor Name="tests">
+            <md:EntityDescriptor entityID="https://idp.example.org">
+                <md:IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
+                    <md:KeyDescriptor>
+                        <ds:KeyInfo>
+                            <ds:X509Data>
+                                <ds:X509Certificate>
 MIIDtTCCAp2gAwIBAgIJAPmsD+VGldyPMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
 BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
 aWRnaXRzIFB0eSBMdGQwHhcNMTQwNDExMTMzOTE4WhcNMjQwNDA4MTMzOTE4WjBF
@@ -53,15 +54,55 @@ BkgVPOuVuEe803BRlKd4BVIsuxAUAy3oqdJYqf9ptPEx8Ef+ALbcDhRbWINhMgO7
 8oFt0blzXtQ2vcOVyNyG326uZBZv2Cf6FXFsYQX1L/tLeTBJegefgGkg2dqCTKIU
 1Qy/Kd2P3/S01kQxjDeG7UfXc9qtelJ68kvzK2d3WOJ2qmsdMxjMNfTItP7FO54M
 i8V7gp9HK+EimdSbgu7xktKlrqA2Rsn+dBoPSgOUs/LOGtCS9/biF0w=
-                            </ds:X509Certificate>
-                        </ds:X509Data>
-                    </ds:KeyInfo>
-                </md:KeyDescriptor>
-                <md:SingleSignOnService
-                    Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
-                    Location="https://idp.example.org/idp/profile/SAML2/SSO/Redirect" />
-            </md:IDPSSODescriptor>
-        </md:EntityDescriptor>    
+                                </ds:X509Certificate>
+                            </ds:X509Data>
+                        </ds:KeyInfo>
+                    </md:KeyDescriptor>
+                    <md:SingleSignOnService
+                        Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
+                        Location="https://idp.example.org/idp/profile/SAML2/Redirect/SSO" />
+                    <md:SingleSignOnService
+                        Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
+                        Location="https://idp.example.org/idp/profile/SAML2/POST/SSO" />
+                </md:IDPSSODescriptor>
+            </md:EntityDescriptor>
+            
+            <md:EntityDescriptor entityID="https://noendpoint.example.org">
+                <md:IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
+                    <md:KeyDescriptor>
+                        <ds:KeyInfo>
+                            <ds:X509Data>
+                                <ds:X509Certificate>
+MIIDtTCCAp2gAwIBAgIJAPmsD+VGldyPMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
+aWRnaXRzIFB0eSBMdGQwHhcNMTQwNDExMTMzOTE4WhcNMjQwNDA4MTMzOTE4WjBF
+MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50
+ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAxg0TyQAP/tIvOH89EtaXuRRn8SYzTj7W1TbNY4VvBmobjkRmSkki4hH9
+x4sQpi635wn6WtXTN/FNNmkTK3N/LspmBWxfZS+n+cc7I82E5yvCAPX67QsZgqgg
+lp2W5dvK/FsMMCS6X6SVqzBLMP88NenXKxY+HMxMs0sT0UKYh1cAEqadrHRBO65a
+DBcm5a0sBVYt9K6pgaOHrp/zSIbhnR5tFFLjBbtFktDpHL3AdGBH3OYidNGKBO3t
+J3Ms7LeKXsM0+0Y4P+9fHZINL2X3E2N6GVnKs5PZTg9sP0FtIpAbYm/+zCx7Yj1E
+T/Er8mDd6tNVGSQsn9s5xUBwGqn14wIDAQABo4GnMIGkMB0GA1UdDgQWBBSiQhSu
+p9BYjD2ZuMkEiQK7w/Zq0TB1BgNVHSMEbjBsgBSiQhSup9BYjD2ZuMkEiQK7w/Zq
+0aFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNV
+BAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAPmsD+VGldyPMAwGA1UdEwQF
+MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAHZmIo9GBTSsD5DJfKkCVUvBafwR089H
+BkgVPOuVuEe803BRlKd4BVIsuxAUAy3oqdJYqf9ptPEx8Ef+ALbcDhRbWINhMgO7
+0/S4x3pS9gOn7/Y9yZplOe4Jd2q3R8QBef+hKLcD/Uv0Sqy2nilM8BnMga5tqsL+
+8oFt0blzXtQ2vcOVyNyG326uZBZv2Cf6FXFsYQX1L/tLeTBJegefgGkg2dqCTKIU
+1Qy/Kd2P3/S01kQxjDeG7UfXc9qtelJ68kvzK2d3WOJ2qmsdMxjMNfTItP7FO54M
+i8V7gp9HK+EimdSbgu7xktKlrqA2Rsn+dBoPSgOUs/LOGtCS9/biF0w=
+                                </ds:X509Certificate>
+                            </ds:X509Data>
+                        </ds:KeyInfo>
+                    </md:KeyDescriptor>
+                    <md:SingleSignOnService
+                        Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
+                        Location="https://idp.example.org/idp/profile/SAML2/Artifact/SSO" />
+                </md:IDPSSODescriptor>
+            </md:EntityDescriptor>
+        </md:EntitiesDescriptor>    
     </MetadataProvider>
 
 </MetadataProvider>
diff --git a/sp-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/test-agents.xml b/sp-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/test-agents.xml
index 15a3693..c12021f 100644
--- a/sp-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/test-agents.xml
+++ b/sp-conf-impl/src/test/resources/net/shibboleth/idp/module/conf/sp/test-agents.xml
@@ -27,6 +27,10 @@
                     p:issuer="https://testsp.example.org"
                     p:authenticatingAuthority="https://idp.example.org"
                     p:defaultConfiguration-ref="shibboleth.UnverifiedRelyingParty" />
+
+                <bean p:id="no-endpoint" parent="shibboleth.Application"
+                    p:issuer="https://testsp.example.org"
+                    p:authenticatingAuthority="https://noendpoint.example.org" />
                     
                 <bean p:id="no-initiators" parent="shibboleth.Application"
                     p:issuer="https://testsp.example.org"
diff --git a/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/impl/AddAuthnRequest.java b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/impl/AddAuthnRequest.java
index 5c5555f..d053eec 100644
--- a/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/impl/AddAuthnRequest.java
+++ b/sp-saml-impl/src/main/java/net/shibboleth/sp/saml/saml2/profile/impl/AddAuthnRequest.java
@@ -285,6 +285,8 @@ public class AddAuthnRequest extends AbstractApplicationAction {
         final MessageContext omc = profileRequestContext.getOutboundMessageContext();
         assert omc != null;
         omc.setMessage(object);
+        
+        log.info("{} Generated AuthnRequest with ID {} from {}", getLogPrefix(), object.getID(), issuerId);
     }
     
     /**
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/profile/AbstractAgentRequestAction.java b/sp-server-api/src/main/java/net/shibboleth/sp/profile/AbstractAgentRequestAction.java
index 93037c1..710b3ad 100644
--- a/sp-server-api/src/main/java/net/shibboleth/sp/profile/AbstractAgentRequestAction.java
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/profile/AbstractAgentRequestAction.java
@@ -30,6 +30,7 @@ import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.primitive.LoggerFactory;
 import net.shibboleth.sp.Agent;
+import net.shibboleth.sp.Application;
 import net.shibboleth.sp.context.AgentRequestContext;
 
 /**
@@ -46,6 +47,9 @@ public abstract class AbstractAgentRequestAction extends AbstractProfileAction {
     /** Class logger. */
     @Nonnull private final Logger log = LoggerFactory.getLogger(AbstractAgentRequestAction.class);
     
+    /** Local copy of extended log prefix. */
+    @Nullable private String logPrefix;
+    
     /** Lookup strategy for {@link AgentRequestContext}. */
     @Nonnull private Function<ProfileRequestContext,AgentRequestContext> agentRequestContextLookupStrategy;
 
@@ -107,19 +111,31 @@ public abstract class AbstractAgentRequestAction extends AbstractProfileAction {
     }
 
     /** {@inheritDoc} */
-    @SuppressWarnings("null")
     @Override
     @Nonnull protected String getLogPrefix() {
-        final StringBuilder s = new StringBuilder(super.getLogPrefix());
+        
+        if (logPrefix != null) {
+            return logPrefix;
+        }
+
+        final StringBuilder s = new StringBuilder();
         final AgentRequestContext ctx = getAgentRequestContext();
         if (ctx != null) {
             final Agent agent = ctx.getAgent();
             if (agent != null) {
-                s.append(" Agent ").append(agent.getId()).append(":");
+                s.append("Agent ").append(agent.getId()).append(":");
+            }
+            final Application app = ctx.getApplication();
+            if (app != null) {
+                s.append(" Application ").append(app.getId()).append(": ");
             }
         }
         
-        return s.toString();
+        s.append(super.getLogPrefix());
+        logPrefix = s.toString();
+        
+        assert logPrefix != null;
+        return logPrefix;
     }
     
 }
\ No newline at end of file
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/EncodeMessage.java b/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/EncodeMessage.java
index ad60f7a..7b84503 100644
--- a/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/EncodeMessage.java
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/profile/impl/EncodeMessage.java
@@ -57,7 +57,8 @@ public class EncodeMessage extends org.opensaml.profile.action.impl.EncodeMessag
         if (createOutputObjects) {
             final DDF output = new DDF(null);
             agentRequestContext.setOutput(output);
-            agentRequestContext.setRemotedHttpServletResponse(new RemotedHttpServletResponse(output));
+            agentRequestContext.setRemotedHttpServletResponse(
+                    new RemotedHttpServletResponse(output.structure().addmember("http")));
         }
         
         try {

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


More information about the commits mailing list