[java-opensaml] 02/02: Message handler for SAML client-side inResponseTo validation.

Brent Putman putmanb at georgetown.edu
Wed Sep 26 23:33:03 EDT 2018


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

putmanb pushed a commit to branch master
in repository java-opensaml.

View the commit online:
http://git.shibboleth.net/view/?p=java-opensaml.git;a=commit;h=72d86de8d61b4e474cf1720f6387d2bbdf7c4497

commit 72d86de8d61b4e474cf1720f6387d2bbdf7c4497
Author: Brent Putman <putmanb at georgetown.edu>
AuthorDate: Wed Sep 26 23:21:28 2018 -0400

    Message handler for SAML client-side inResponseTo validation.
---
 .../security/impl/InResponseToSecurityHandler.java | 102 +++++++++++++++++++++
 .../impl/InResponseToSecurityHandlerTest.java      | 101 ++++++++++++++++++++
 2 files changed, 203 insertions(+)

diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/common/binding/security/impl/InResponseToSecurityHandler.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/common/binding/security/impl/InResponseToSecurityHandler.java
new file mode 100644
index 0000000..e96e62f
--- /dev/null
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/common/binding/security/impl/InResponseToSecurityHandler.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You 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 org.opensaml.saml.common.binding.security.impl;
+
+import java.util.Objects;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.messaging.context.InOutOperationContext;
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.handler.AbstractMessageHandler;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.opensaml.saml.common.SAMLObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.utilities.java.support.primitive.StringSupport;
+
+/**
+ * Client-side message handler for validating that the inbound SAML response inResponseTo ID matches the corresponding
+ * outbound request ID.
+ */
+public class InResponseToSecurityHandler extends AbstractMessageHandler {
+    
+    /** Logger. */
+    private Logger log = LoggerFactory.getLogger(InResponseToSecurityHandler.class);
+
+    /** {@inheritDoc} */
+    protected void doInvoke(@Nonnull final MessageContext messageContext) throws MessageHandlerException {
+        final String outboundRequestID = StringSupport.trimOrNull(resolveOutboundRequestID(messageContext));
+        log.debug("Resolved outbound request ID: {}", outboundRequestID);
+        
+        final String inboundInResponseTo = StringSupport.trimOrNull(resolveInboundInResponseTo(messageContext));
+        log.debug("Resolved inbound inResponseTo: {}", inboundInResponseTo);
+        
+        if (!Objects.equals(outboundRequestID, inboundInResponseTo)) {
+            log.warn("Inbound inResponseTo '{}' did not match outbound request ID '{}'", 
+                    inboundInResponseTo, outboundRequestID);
+            throw new MessageHandlerException("Inbound inResponseTo did not match outbound request ID");
+        }
+    }
+
+    /**
+     * Resolve the outbound request ID.
+     * 
+     * @param messageContext the message context
+     * @return the outbound request ID, or null
+     */
+    private String resolveOutboundRequestID(@Nonnull final MessageContext messageContext) {
+        if (messageContext.getParent() instanceof InOutOperationContext) {
+            final MessageContext outboundContext = 
+                    ((InOutOperationContext)messageContext.getParent()).getOutboundMessageContext();
+            if (outboundContext != null && outboundContext.getMessage() instanceof SAMLObject) {
+                final SAMLObject outboundMessage = (SAMLObject) outboundContext.getMessage();
+                if (outboundMessage instanceof org.opensaml.saml.saml2.core.RequestAbstractType) {
+                    return ((org.opensaml.saml.saml2.core.RequestAbstractType)outboundMessage).getID();
+                } else if (outboundMessage instanceof org.opensaml.saml.saml1.core.RequestAbstractType) {
+                    return ((org.opensaml.saml.saml1.core.RequestAbstractType)outboundMessage).getID();
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Resolve the inbound inResponseTo ID.
+     * 
+     * @param messageContext the message context
+     * @return the inbound inResponseTo, or null
+     */
+    private String resolveInboundInResponseTo(@Nonnull final MessageContext messageContext) {
+        if (messageContext.getParent() instanceof InOutOperationContext) {
+            final MessageContext inboundContext = 
+                    ((InOutOperationContext)messageContext.getParent()).getInboundMessageContext();
+            if (inboundContext != null && inboundContext.getMessage() instanceof SAMLObject) {
+                final SAMLObject inboundMessage = (SAMLObject) inboundContext.getMessage();
+                if (inboundMessage instanceof org.opensaml.saml.saml2.core.StatusResponseType) {
+                    return ((org.opensaml.saml.saml2.core.StatusResponseType)inboundMessage).getInResponseTo();
+                } else if (inboundMessage instanceof org.opensaml.saml.saml1.core.ResponseAbstractType) {
+                    return ((org.opensaml.saml.saml1.core.ResponseAbstractType)inboundMessage).getInResponseTo();
+                }
+            }
+        }
+        return null;
+    }
+
+}
diff --git a/opensaml-saml-impl/src/test/java/org/opensaml/saml/common/binding/security/impl/InResponseToSecurityHandlerTest.java b/opensaml-saml-impl/src/test/java/org/opensaml/saml/common/binding/security/impl/InResponseToSecurityHandlerTest.java
new file mode 100644
index 0000000..e98f91a
--- /dev/null
+++ b/opensaml-saml-impl/src/test/java/org/opensaml/saml/common/binding/security/impl/InResponseToSecurityHandlerTest.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You 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 org.opensaml.saml.common.binding.security.impl;
+
+import org.opensaml.core.xml.XMLObjectBaseTestCase;
+import org.opensaml.messaging.context.InOutOperationContext;
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.handler.MessageHandlerException;
+import org.opensaml.saml.saml1.core.Request;
+import org.opensaml.saml.saml1.core.Response;
+import org.opensaml.saml.saml2.core.ArtifactResolve;
+import org.opensaml.saml.saml2.core.ArtifactResponse;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+
+/**
+ *
+ */
+public class InResponseToSecurityHandlerTest extends XMLObjectBaseTestCase {
+    
+    private InOutOperationContext opContext;
+    
+    private InResponseToSecurityHandler handler;
+    
+    @BeforeClass
+    public void setup() throws ComponentInitializationException {
+        handler = new InResponseToSecurityHandler();
+        handler.initialize();
+        opContext = new InOutOperationContext<>(new MessageContext(), new MessageContext());
+    }
+    
+    @Test
+    public void testSAML2Match() throws MessageHandlerException {
+        ArtifactResolve request = buildXMLObject(ArtifactResolve.DEFAULT_ELEMENT_NAME);
+        request.setID("abc123");
+        opContext.getOutboundMessageContext().setMessage(request);
+        
+        ArtifactResponse response = buildXMLObject(ArtifactResponse.DEFAULT_ELEMENT_NAME);
+        response.setInResponseTo("abc123");
+        opContext.getInboundMessageContext().setMessage(response);
+        
+        handler.invoke(opContext.getInboundMessageContext());
+    }
+
+    @Test(expectedExceptions=MessageHandlerException.class)
+    public void testSAML2NonMatch() throws MessageHandlerException {
+        ArtifactResolve request = buildXMLObject(ArtifactResolve.DEFAULT_ELEMENT_NAME);
+        request.setID("abc123");
+        opContext.getOutboundMessageContext().setMessage(request);
+        
+        ArtifactResponse response = buildXMLObject(ArtifactResponse.DEFAULT_ELEMENT_NAME);
+        response.setInResponseTo("xyz456");
+        opContext.getInboundMessageContext().setMessage(response);
+        
+        handler.invoke(opContext.getInboundMessageContext());
+    }
+
+    @Test
+    public void testSAML1Match() throws MessageHandlerException {
+        Request request = buildXMLObject(Request.DEFAULT_ELEMENT_NAME);
+        request.setID("abc123");
+        opContext.getOutboundMessageContext().setMessage(request);
+        
+        Response response = buildXMLObject(Response.DEFAULT_ELEMENT_NAME);
+        response.setInResponseTo("abc123");
+        opContext.getInboundMessageContext().setMessage(response);
+        
+        handler.invoke(opContext.getInboundMessageContext());
+    }
+
+    @Test(expectedExceptions=MessageHandlerException.class)
+    public void testSAML1NonMatch() throws MessageHandlerException {
+        Request request = buildXMLObject(Request.DEFAULT_ELEMENT_NAME);
+        request.setID("abc123");
+        opContext.getOutboundMessageContext().setMessage(request);
+        
+        Response response = buildXMLObject(Response.DEFAULT_ELEMENT_NAME);
+        response.setInResponseTo("xyz456");
+        opContext.getInboundMessageContext().setMessage(response);
+        
+        handler.invoke(opContext.getInboundMessageContext());   
+    }
+
+}

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


More information about the commits mailing list