[java-plugin-shibd-saml] branch main updated: Add RelayState to SAML initiator and tests.

Scott Cantor cantor.2 at osu.edu
Wed Aug 21 16:08:36 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-saml.

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

The following commit(s) were added to refs/heads/main by this push:
     new b4e2e83  Add RelayState to SAML initiator and tests.
b4e2e83 is described below

commit b4e2e83baf4b8c24e45365581b448be57b565092
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Aug 21 12:08:33 2024 -0400

    Add RelayState to SAML initiator and tests.
---
 .../flows/saml2/SAML2SessionInitiatorFlowTest.java | 99 +++++++++++++++++++++-
 .../saml/saml2/profile/impl/AddAuthnRequest.java   |  7 ++
 2 files changed, 102 insertions(+), 4 deletions(-)

diff --git a/sp-saml-conf-impl/src/test/java/net/shibboleth/sp/saml/flows/saml2/SAML2SessionInitiatorFlowTest.java b/sp-saml-conf-impl/src/test/java/net/shibboleth/sp/saml/flows/saml2/SAML2SessionInitiatorFlowTest.java
index 06ae546..fdf59ff 100644
--- a/sp-saml-conf-impl/src/test/java/net/shibboleth/sp/saml/flows/saml2/SAML2SessionInitiatorFlowTest.java
+++ b/sp-saml-conf-impl/src/test/java/net/shibboleth/sp/saml/flows/saml2/SAML2SessionInitiatorFlowTest.java
@@ -16,13 +16,17 @@ package net.shibboleth.sp.saml.flows.saml2;
 
 import java.io.IOException;
 import java.time.Instant;
+import java.util.List;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
+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.common.binding.SAMLBindingSupport;
+import org.opensaml.saml.saml2.binding.decoding.impl.HTTPRedirectDeflateDecoder;
 import org.opensaml.saml.saml2.core.AuthnContext;
 import org.opensaml.saml.saml2.core.AuthnContextClassRef;
 import org.opensaml.saml.saml2.core.AuthnContextComparisonTypeEnumeration;
@@ -31,6 +35,7 @@ import org.opensaml.saml.saml2.core.NameIDPolicy;
 import org.opensaml.saml.saml2.core.NameIDType;
 import org.opensaml.saml.saml2.core.RequestedAuthnContext;
 import org.opensaml.saml.saml2.metadata.NameIDFormat;
+import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.test.context.ContextConfiguration;
 import org.springframework.test.context.web.WebAppConfiguration;
 import org.springframework.webflow.executor.FlowExecutionResult;
@@ -38,9 +43,14 @@ import org.testng.Assert;
 import org.testng.annotations.Test;
 
 import net.shibboleth.idp.authn.AuthnEventIds;
+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.sp.context.AgentRequestContext;
 import net.shibboleth.sp.ddf.DDF;
 import net.shibboleth.sp.flows.AbstractSPFlowTest;
+import net.shibboleth.sp.messaging.RemotedHttpServletRequest;
 import net.shibboleth.sp.profile.InitiatorConstants;
 
 /**
@@ -141,17 +151,18 @@ public class SAML2SessionInitiatorFlowTest extends AbstractSPFlowTest {
     }
     
     /**
-     * Test simple success case.
+     * Test simple success case with preset relay state.
      * 
      * @throws IOException 
      * @throws MessageDecodingException 
      */
     @Test
-    public void testSimple() throws IOException, MessageDecodingException {
+    public void testSimpleWithState() throws IOException, MessageDecodingException {
         setDefaultAuth();
         
         final DDF input = new DDF(null).structure();
         input.addmember(InitiatorConstants.RESPONSE_URL).string(RESPONSE_URL);
+        input.addmember(InitiatorConstants.STATE).string("foostate");
         setApplicationRequest(APPLICATION_ID, input);
 
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
@@ -166,7 +177,37 @@ public class SAML2SessionInitiatorFlowTest extends AbstractSPFlowTest {
         assertFalse(req.isForceAuthn());
         assertFalse(req.isPassive());
     }
-    
+
+    /**
+     * Test simple success case with computed relay state.
+     * 
+     * @throws IOException 
+     * @throws MessageDecodingException 
+     */
+    @Test
+    public void testSimpleWithoutState() throws IOException, MessageDecodingException {
+        setDefaultAuth();
+        
+        final DDF input = new DDF(null).structure();
+        input.addmember(InitiatorConstants.RESPONSE_URL).string(RESPONSE_URL);
+        final DDF httpreq = input.addmember(RemotedHttpServletRequest.STRUCTURE_NAME);
+        httpreq.addmember(RemotedHttpServletRequest.REQUEST_URL).unsafe_string("https://sp.example.org/cgi-bin/test.cgi".getBytes());
+        httpreq.addmember(RemotedHttpServletRequest.QUERY_STRING).unsafe_string("foo=bar%20baz&frobnitz=zorkmid".getBytes());
+        setApplicationRequest(APPLICATION_ID, input);
+
+        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+        assertFlowExecutionResult(result, FLOW_ID);
+        assertFlowExecutionOutcome(result.getOutcome());
+        
+        assertOutputMessageEvent(result, null);
+        final AuthnRequest req = validateOutputMessage(result, null);
+        Assert.assertNull(req.getSubject());
+        Assert.assertNull(req.getRequestedAuthnContext());
+        Assert.assertNull(req.getScoping());
+        assertFalse(req.isForceAuthn());
+        assertFalse(req.isPassive());
+    }
+
     /**
      * Test success supplying ForceAuthn from agent.
      * 
@@ -389,6 +430,7 @@ public class SAML2SessionInitiatorFlowTest extends AbstractSPFlowTest {
         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 output != null;
@@ -399,7 +441,7 @@ public class SAML2SessionInitiatorFlowTest extends AbstractSPFlowTest {
         final AuthnRequest authnRequest;
         final String redirect = http.getmember("redirect").string();
         if (redirect != null) {
-            final SAMLObject saml = decodeRedirect(redirect);
+            final SAMLObject saml = decodeRedirect(redirect, input != null ? input.getmember(InitiatorConstants.STATE).string() : null);
             assert saml instanceof AuthnRequest;
             authnRequest = (AuthnRequest) saml;
             Assert.assertTrue(redirect.startsWith(authnRequest.getDestination()));
@@ -410,6 +452,8 @@ public class SAML2SessionInitiatorFlowTest extends AbstractSPFlowTest {
             final Object saml = prc.ensureOutboundMessageContext().ensureMessage();
             assert saml instanceof AuthnRequest;
             authnRequest = (AuthnRequest) saml;
+            Assert.assertEquals(SAMLBindingSupport.getRelayState(prc.ensureOutboundMessageContext()),
+                    input != null ? input.getmember(InitiatorConstants.STATE).string() : null);
         }
         
         assert authnRequest != null;
@@ -425,5 +469,52 @@ public class SAML2SessionInitiatorFlowTest extends AbstractSPFlowTest {
         
         return authnRequest;
     }
+
     
+    /**
+     * Decodes a SAML message encoded via HTTP-Redirect binding.
+     * 
+     * @param url the encoded redirect
+     * @param relayState RelayState to check for
+     * 
+     * @return decoded message
+     * @throws MessageDecodingException 
+     */
+    @Nonnull protected SAMLObject decodeRedirect(@Nullable final String url, @Nullable final String relayState)
+            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) {
+            Assert.assertEquals(SAMLBindingSupport.getRelayState(mc), relayState);
+            return saml;
+        }
+        throw new MessageDecodingException("No message, or incorrect type.");
+    }
+
 }
\ No newline at end of file
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 ef5ad74..2951bb6 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
@@ -47,6 +47,7 @@ import org.opensaml.profile.action.EventIds;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.opensaml.saml.common.SAMLObjectBuilder;
 import org.opensaml.saml.common.SAMLVersion;
+import org.opensaml.saml.common.binding.SAMLBindingSupport;
 import org.opensaml.saml.ext.reqattr.RequestedAttributes;
 import org.opensaml.saml.saml2.core.AuthnContextClassRef;
 import org.opensaml.saml.saml2.core.AuthnContextComparisonTypeEnumeration;
@@ -309,6 +310,12 @@ public class AddAuthnRequest extends AbstractApplicationAction {
         
         profileRequestContext.ensureOutboundMessageContext().setMessage(object);
         
+        // Check for RelayState.
+        final String relayState = input.getmember(InitiatorConstants.STATE).string();
+        if (relayState != null) {
+            SAMLBindingSupport.setRelayState(profileRequestContext.ensureOutboundMessageContext(), relayState);
+        }
+        
         log.info("{} Generated AuthnRequest with ID {} from {}", getLogPrefix(), object.getID(), issuerId);
     }
     

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


More information about the commits mailing list