SAML 2 artifact unit tests and associated bug fixes
authorlajoie <lajoie@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Tue, 18 Sep 2007 00:40:13 +0000 (00:40 +0000)
committerlajoie <lajoie@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Tue, 18 Sep 2007 00:40:13 +0000 (00:40 +0000)
git-svn-id: https://subversion.switch.ch/svn/shibboleth/java-idp/trunk@2401 ab3bd59b-922f-494d-bb5f-6f0a3c29deca

src/edu/internet2/middleware/shibboleth/idp/profile/saml1/ArtifactResolution.java
src/edu/internet2/middleware/shibboleth/idp/profile/saml2/ArtifactResolution.java
src/edu/internet2/middleware/shibboleth/idp/profile/saml2/AttributeQueryProfileHandler.java
test/edu/internet2/middleware/shibboleth/idp/system/conf1/SAML1ArtifactResolutionTest.java
test/edu/internet2/middleware/shibboleth/idp/system/conf1/SAML2ArtifactResolutionTest.java [new file with mode: 0644]

index 524db0e..a54c6ed 100644 (file)
@@ -95,7 +95,7 @@ public class ArtifactResolution extends AbstractSAML1ProfileHandler {
         ArtifactResolutionRequestContext requestContext = decodeRequest(inTransport, outTransport);
 
         try {
-            if (requestContext.getRelyingPartyConfiguration() == null) {
+            if (requestContext.getProfileConfiguration() == null) {
                 log.error("SAML 1 Artifact resolution profile is not configured for relying party "
                         + requestContext.getInboundMessageIssuer());
                 requestContext.setFailureStatus(buildStatus(StatusCode.SUCCESS, StatusCode.REQUEST_DENIED,
@@ -185,11 +185,13 @@ public class ArtifactResolution extends AbstractSAML1ProfileHandler {
 
                 ArtifactResolutionConfiguration profileConfig = (ArtifactResolutionConfiguration) rpConfig
                         .getProfileConfiguration(ArtifactResolutionConfiguration.PROFILE_ID);
-                requestContext.setProfileConfiguration(profileConfig);
-                if (profileConfig.getSigningCredential() != null) {
-                    requestContext.setOutboundSAMLMessageSigningCredential(profileConfig.getSigningCredential());
-                } else if (rpConfig.getDefaultSigningCredential() != null) {
-                    requestContext.setOutboundSAMLMessageSigningCredential(rpConfig.getDefaultSigningCredential());
+                if(profileConfig != null){
+                    requestContext.setProfileConfiguration(profileConfig);
+                    if (profileConfig.getSigningCredential() != null) {
+                        requestContext.setOutboundSAMLMessageSigningCredential(profileConfig.getSigningCredential());
+                    } else if (rpConfig.getDefaultSigningCredential() != null) {
+                        requestContext.setOutboundSAMLMessageSigningCredential(rpConfig.getDefaultSigningCredential());
+                    }
                 }
 
             } catch (MetadataProviderException e) {
index 8921af3..724268e 100644 (file)
@@ -26,7 +26,6 @@ import org.opensaml.common.binding.artifact.SAMLArtifactMap.SAMLArtifactMapEntry
 import org.opensaml.common.binding.decoding.SAMLMessageDecoder;
 import org.opensaml.common.xml.SAMLConstants;
 import org.opensaml.saml2.binding.SAML2ArtifactMessageContext;
-import org.opensaml.saml2.binding.artifact.AbstractSAML2Artifact;
 import org.opensaml.saml2.core.ArtifactResolve;
 import org.opensaml.saml2.core.ArtifactResponse;
 import org.opensaml.saml2.core.NameID;
@@ -60,6 +59,9 @@ public class ArtifactResolution extends AbstractSAML2ProfileHandler {
 
     /** Artifact response object builder. */
     private SAMLObjectBuilder<ArtifactResponse> responseBuilder;
+    
+    /** Builder of assertion consumer service endpoints. */
+    private SAMLObjectBuilder<AssertionConsumerService> acsEndpointBuilder;
 
     /**
      * Constructor.
@@ -73,6 +75,8 @@ public class ArtifactResolution extends AbstractSAML2ProfileHandler {
         
         responseBuilder = (SAMLObjectBuilder<ArtifactResponse>) getBuilderFactory().getBuilder(
                 ArtifactResponse.DEFAULT_ELEMENT_NAME);
+        acsEndpointBuilder = (SAMLObjectBuilder<AssertionConsumerService>) getBuilderFactory().getBuilder(
+                AssertionConsumerService.DEFAULT_ELEMENT_NAME);
     }
 
     /** {@inheritDoc} */
@@ -87,7 +91,7 @@ public class ArtifactResolution extends AbstractSAML2ProfileHandler {
         ArtifactResolutionRequestContext requestContext = decodeRequest(inTransport, outTransport);
 
         try {
-            if (requestContext.getRelyingPartyConfiguration() == null) {
+            if (requestContext.getProfileConfiguration() == null) {
                 log.error("SAML 2 Artifact Resolve profile is not configured for relying party "
                         + requestContext.getInboundMessageIssuer());
                 requestContext.setFailureStatus(buildStatus(StatusCode.SUCCESS_URI, StatusCode.REQUEST_DENIED_URI,
@@ -99,7 +103,7 @@ public class ArtifactResolution extends AbstractSAML2ProfileHandler {
 
             checkSamlVersion(requestContext);
 
-            SAMLArtifactMapEntry artifactEntry = artifactMap.get(requestContext.getArtifact().base64Encode());
+            SAMLArtifactMapEntry artifactEntry = artifactMap.get(requestContext.getArtifact());
             if (artifactEntry == null || artifactEntry.isExpired()) {
                 log.error("Unknown artifact.");
                 requestContext.setFailureStatus(buildStatus(StatusCode.SUCCESS_URI, StatusCode.REQUEST_DENIED_URI,
@@ -119,7 +123,7 @@ public class ArtifactResolution extends AbstractSAML2ProfileHandler {
                 requestContext.setFailureStatus(buildStatus(StatusCode.SUCCESS_URI, StatusCode.REQUEST_DENIED_URI,
                         "Artifact requester mismatch."));
             }
-            artifactMap.remove(requestContext.getArtifact().base64Encode());
+            artifactMap.remove(requestContext.getArtifact());
             SAMLObject referencedMessage = artifactEntry.getSamlMessage();
             requestContext.setReferencedMessage(referencedMessage);
 
@@ -186,9 +190,12 @@ public class ArtifactResolution extends AbstractSAML2ProfileHandler {
         } finally {
             // Set as much information as can be retrieved from the decoded message
             try {
+                requestContext.setArtifact(requestContext.getInboundSAMLMessage().getArtifact().getArtifact());
+                
                 String relyingPartyId = requestContext.getInboundMessageIssuer();
                 RelyingPartyConfiguration rpConfig = getRelyingPartyConfiguration(relyingPartyId);
                 requestContext.setRelyingPartyConfiguration(rpConfig);
+                requestContext.setPeerEntityEndpoint(selectEndpoint(requestContext));
 
                 String assertingPartyId = requestContext.getRelyingPartyConfiguration().getProviderId();
                 requestContext.setLocalEntityId(assertingPartyId);
@@ -199,11 +206,13 @@ public class ArtifactResolution extends AbstractSAML2ProfileHandler {
 
                 ArtifactResolutionConfiguration profileConfig = (ArtifactResolutionConfiguration) rpConfig
                         .getProfileConfiguration(ArtifactResolutionConfiguration.PROFILE_ID);
-                requestContext.setProfileConfiguration(profileConfig);
-                if (profileConfig.getSigningCredential() != null) {
-                    requestContext.setOutboundSAMLMessageSigningCredential(profileConfig.getSigningCredential());
-                } else if (rpConfig.getDefaultSigningCredential() != null) {
-                    requestContext.setOutboundSAMLMessageSigningCredential(rpConfig.getDefaultSigningCredential());
+                if(profileConfig != null){
+                    requestContext.setProfileConfiguration(profileConfig);
+                    if (profileConfig.getSigningCredential() != null) {
+                        requestContext.setOutboundSAMLMessageSigningCredential(profileConfig.getSigningCredential());
+                    } else if (rpConfig.getDefaultSigningCredential() != null) {
+                        requestContext.setOutboundSAMLMessageSigningCredential(rpConfig.getDefaultSigningCredential());
+                    }
                 }
 
             } catch (MetadataProviderException e) {
@@ -223,14 +232,23 @@ public class ArtifactResolution extends AbstractSAML2ProfileHandler {
      * @return Endpoint selected from the information provided in the request context
      */
     protected Endpoint selectEndpoint(ArtifactResolutionRequestContext requestContext) {
-        BasicEndpointSelector endpointSelector = new BasicEndpointSelector();
-        endpointSelector.setEndpointType(AssertionConsumerService.DEFAULT_ELEMENT_NAME);
-        endpointSelector.setMetadataProvider(getMetadataProvider());
-        endpointSelector.setEntityMetadata(requestContext.getPeerEntityMetadata());
-        endpointSelector.setEntityRoleMetadata(requestContext.getPeerEntityRoleMetadata());
-        endpointSelector.setSamlRequest(requestContext.getInboundSAMLMessage());
-        endpointSelector.getSupportedIssuerBindings().addAll(getSupportedOutboundBindings());
-        return endpointSelector.selectEndpoint();
+        Endpoint endpoint;
+
+        if (getInboundBinding().equals(SAMLConstants.SAML2_SOAP11_BINDING_URI)) {
+            endpoint = acsEndpointBuilder.buildObject();
+            endpoint.setBinding(SAMLConstants.SAML2_SOAP11_BINDING_URI);
+        } else {
+            BasicEndpointSelector endpointSelector = new BasicEndpointSelector();
+            endpointSelector.setEndpointType(AssertionConsumerService.DEFAULT_ELEMENT_NAME);
+            endpointSelector.setMetadataProvider(getMetadataProvider());
+            endpointSelector.setEntityMetadata(requestContext.getPeerEntityMetadata());
+            endpointSelector.setEntityRoleMetadata(requestContext.getPeerEntityRoleMetadata());
+            endpointSelector.setSamlRequest(requestContext.getInboundSAMLMessage());
+            endpointSelector.getSupportedIssuerBindings().addAll(getSupportedOutboundBindings());
+            endpoint = endpointSelector.selectEndpoint();
+        }
+        
+        return endpoint;
     }
 
     /**
@@ -282,26 +300,18 @@ public class ArtifactResolution extends AbstractSAML2ProfileHandler {
             implements SAML2ArtifactMessageContext<ArtifactResolve, ArtifactResponse, NameID> {
 
         /** Artifact to be resolved. */
-        private AbstractSAML2Artifact artifact;
+        private String artifact;
 
         /** Message referenced by the SAML artifact. */
         private SAMLObject referencedMessage;
 
-        /**
-         * Gets the artifact to be resolved.
-         * 
-         * @return artifact to be resolved
-         */
-        public AbstractSAML2Artifact getArtifact() {
+        /** {@inheritDoc} */
+        public String getArtifact() {
             return artifact;
         }
 
-        /**
-         * Sets the artifact to be resolved.
-         * 
-         * @param saml2Artifact artifact to be resolved
-         */
-        public void setArtifact(AbstractSAML2Artifact saml2Artifact) {
+        /** {@inheritDoc} */
+        public void setArtifact(String saml2Artifact) {
             this.artifact = saml2Artifact;
         }
 
index e85447b..7893ae8 100644 (file)
@@ -77,7 +77,8 @@ public class AttributeQueryProfileHandler extends AbstractSAML2ProfileHandler {
                 requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, StatusCode.REQUEST_DENIED_URI,
                         "SAML 2 Attribute Query profile is not configured for relying party "
                                 + requestContext.getInboundMessageIssuer()));
-                samlResponse = buildErrorResponse(requestContext);
+                throw new ProfileException("SAML 2 Attribute Query profile is not configured for relying party "
+                                + requestContext.getInboundMessageIssuer());
             }
 
             checkSamlVersion(requestContext);
index d635f7d..96fb848 100644 (file)
@@ -73,6 +73,32 @@ public class SAML1ArtifactResolutionTest extends BaseConf1TestCase {
         assertTrue(response.contains("samlp:Success"));
         assertTrue(response.contains("saml:Assertion"));
     }
+    
+    public void testWithoutConfiguration() throws Exception {
+        String relyingPartyId = "urn:example.org:BogusSP";
+        SAMLArtifactMapEntry artifactEntry = stageArtifact(relyingPartyId);
+        String soapMessage = buildRequestMessage(relyingPartyId, artifactEntry.getArtifact());
+
+        MockHttpServletRequest servletRequest = new MockHttpServletRequest();
+        servletRequest.setPathInfo("/saml1/SOAP/ArtifactResolution");
+        servletRequest.setContent(soapMessage.getBytes());
+
+        MockHttpServletResponse servletResponse = new MockHttpServletResponse();
+
+        ProfileHandlerManager handlerManager = (ProfileHandlerManager) getApplicationContext().getBean(
+                "shibboleth.HandlerManager");
+        ProfileHandler handler = handlerManager.getProfileHandler(servletRequest);
+        assertNotNull(handler);
+
+        // Process request
+        HTTPInTransport profileRequest = new HttpServletRequestAdapter(servletRequest);
+        HTTPOutTransport profileResponse = new HttpServletResponseAdapter(servletResponse);
+        handler.processRequest(profileRequest, profileResponse);
+
+        String response = servletResponse.getContentAsString();
+        assertTrue(response.contains("samlp:Success"));
+        assertTrue(response.contains("samlp:RequestDenied"));
+    }
 
     @SuppressWarnings("unchecked")
     protected SAMLArtifactMapEntry stageArtifact(String relyingPartyId) throws Exception {
diff --git a/test/edu/internet2/middleware/shibboleth/idp/system/conf1/SAML2ArtifactResolutionTest.java b/test/edu/internet2/middleware/shibboleth/idp/system/conf1/SAML2ArtifactResolutionTest.java
new file mode 100644 (file)
index 0000000..45f23e5
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * Copyright [2007] [University Corporation for Advanced Internet Development, Inc.]
+ *
+ * Licensed 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 edu.internet2.middleware.shibboleth.idp.system.conf1;
+
+import java.io.StringWriter;
+import java.security.MessageDigest;
+import java.security.SecureRandom;
+
+import org.joda.time.DateTime;
+import org.opensaml.common.SAMLObjectBuilder;
+import org.opensaml.common.binding.artifact.SAMLArtifactMap;
+import org.opensaml.common.binding.artifact.SAMLArtifactMap.SAMLArtifactMapEntry;
+import org.opensaml.saml2.binding.artifact.SAML2ArtifactType0004;
+import org.opensaml.saml2.core.Artifact;
+import org.opensaml.saml2.core.ArtifactResolve;
+import org.opensaml.saml2.core.Assertion;
+import org.opensaml.saml2.core.Issuer;
+import org.opensaml.saml2.core.Response;
+import org.opensaml.ws.soap.common.SOAPObjectBuilder;
+import org.opensaml.ws.soap.soap11.Body;
+import org.opensaml.ws.soap.soap11.Envelope;
+import org.opensaml.ws.transport.http.HTTPInTransport;
+import org.opensaml.ws.transport.http.HTTPOutTransport;
+import org.opensaml.ws.transport.http.HttpServletRequestAdapter;
+import org.opensaml.ws.transport.http.HttpServletResponseAdapter;
+import org.opensaml.xml.io.Marshaller;
+import org.opensaml.xml.util.XMLHelper;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+import org.w3c.dom.Element;
+
+import edu.internet2.middleware.shibboleth.common.profile.ProfileException;
+import edu.internet2.middleware.shibboleth.common.profile.ProfileHandler;
+import edu.internet2.middleware.shibboleth.common.profile.ProfileHandlerManager;
+
+/**
+ * 
+ */
+public class SAML2ArtifactResolutionTest extends BaseConf1TestCase {
+
+    public void testArtifactResolution() throws Exception {
+        String relyingPartyId = "urn:example.org:sp1";
+        SAMLArtifactMapEntry artifactEntry = stageArtifact(relyingPartyId);
+        String soapMessage = buildRequestMessage(relyingPartyId, artifactEntry.getArtifact());
+
+        MockHttpServletRequest servletRequest = new MockHttpServletRequest();
+        servletRequest.setPathInfo("/saml2/SOAP/ArtifactResolution");
+        servletRequest.setContent(soapMessage.getBytes());
+
+        MockHttpServletResponse servletResponse = new MockHttpServletResponse();
+
+        ProfileHandlerManager handlerManager = (ProfileHandlerManager) getApplicationContext().getBean(
+                "shibboleth.HandlerManager");
+        ProfileHandler handler = handlerManager.getProfileHandler(servletRequest);
+        assertNotNull(handler);
+
+        // Process request
+        HTTPInTransport profileRequest = new HttpServletRequestAdapter(servletRequest);
+        HTTPOutTransport profileResponse = new HttpServletResponseAdapter(servletResponse);
+        handler.processRequest(profileRequest, profileResponse);
+
+        String response = servletResponse.getContentAsString();
+        assertTrue(response.contains("samlp:ArtifactResponse"));
+        assertTrue(response.contains("urn:oasis:names:tc:SAML:2.0:status:Success"));
+        assertTrue(response.contains("saml:Assertion"));
+    }
+    
+    public void testWithoutConfiguration() throws Exception{
+        String relyingPartyId = "urn:example.org:BogusSP";
+        SAMLArtifactMapEntry artifactEntry = stageArtifact(relyingPartyId);
+        String soapMessage = buildRequestMessage(relyingPartyId, artifactEntry.getArtifact());
+
+        MockHttpServletRequest servletRequest = new MockHttpServletRequest();
+        servletRequest.setPathInfo("/saml2/SOAP/ArtifactResolution");
+        servletRequest.setContent(soapMessage.getBytes());
+
+        MockHttpServletResponse servletResponse = new MockHttpServletResponse();
+
+        ProfileHandlerManager handlerManager = (ProfileHandlerManager) getApplicationContext().getBean(
+                "shibboleth.HandlerManager");
+        ProfileHandler handler = handlerManager.getProfileHandler(servletRequest);
+        assertNotNull(handler);
+
+        // Process request
+        HTTPInTransport profileRequest = new HttpServletRequestAdapter(servletRequest);
+        HTTPOutTransport profileResponse = new HttpServletResponseAdapter(servletResponse);
+        handler.processRequest(profileRequest, profileResponse);
+        String response = servletResponse.getContentAsString();
+        assertTrue(response.contains("urn:oasis:names:tc:SAML:2.0:status:Success"));
+        assertTrue(response.contains("urn:oasis:names:tc:SAML:2.0:status:RequestDenied"));
+    }
+
+    @SuppressWarnings("unchecked")
+    protected SAMLArtifactMapEntry stageArtifact(String relyingPartyId) throws Exception {
+        SAMLObjectBuilder<Assertion> assetionBuilder = (SAMLObjectBuilder<Assertion>) builderFactory
+                .getBuilder(Assertion.DEFAULT_ELEMENT_NAME);
+        Assertion assertion = assetionBuilder.buildObject();
+
+        SAMLObjectBuilder<Response> responseBuilder = (SAMLObjectBuilder<Response>) builderFactory
+                .getBuilder(Response.DEFAULT_ELEMENT_NAME);
+        Response response = responseBuilder.buildObject();
+        response.getAssertions().add(assertion);
+
+        SecureRandom handleGenerator = SecureRandom.getInstance("SHA1PRNG");
+        byte[] endpointIndex = { 0, 1 };
+        MessageDigest sha1Digester = MessageDigest.getInstance("SHA-1");
+        byte[] source = sha1Digester.digest(relyingPartyId.getBytes());
+        byte[] assertionHandle = new byte[20];
+        handleGenerator.nextBytes(assertionHandle);
+        SAML2ArtifactType0004 artifact = new SAML2ArtifactType0004(endpointIndex, source, assertionHandle);
+
+        SAMLArtifactMap artifactMap = (SAMLArtifactMap) getApplicationContext().getBean("shibboleth.ArtifactMap");
+        artifactMap.put(artifact.base64Encode(), relyingPartyId, "urn:example.org:idp1", response);
+        return artifactMap.get(artifact.base64Encode());
+    }
+
+    @SuppressWarnings("unchecked")
+    protected String buildRequestMessage(String relyingPartyId, String artifact) throws Exception {
+        SAMLObjectBuilder<Artifact> artifactBuilder = (SAMLObjectBuilder<Artifact>) builderFactory
+                .getBuilder(Artifact.DEFAULT_ELEMENT_NAME);
+        Artifact samlArt = artifactBuilder.buildObject();
+        samlArt.setArtifact(artifact);
+
+        SAMLObjectBuilder<Issuer> issuerBuilder = (SAMLObjectBuilder<Issuer>) builderFactory
+                .getBuilder(Issuer.DEFAULT_ELEMENT_NAME);
+        Issuer issuer = issuerBuilder.buildObject();
+        issuer.setFormat(Issuer.ENTITY);
+        issuer.setValue(relyingPartyId);
+
+        SAMLObjectBuilder<ArtifactResolve> requestBuilder = (SAMLObjectBuilder<ArtifactResolve>) builderFactory
+                .getBuilder(ArtifactResolve.DEFAULT_ELEMENT_NAME);
+        ArtifactResolve request = requestBuilder.buildObject();
+        request.setID("1");
+        request.setIssuer(issuer);
+        request.setIssueInstant(new DateTime());
+        request.setArtifact(samlArt);
+
+        SOAPObjectBuilder<Body> bodyBuilder = (SOAPObjectBuilder<Body>) builderFactory
+                .getBuilder(Body.DEFAULT_ELEMENT_NAME);
+        Body body = bodyBuilder.buildObject();
+        body.getUnknownXMLObjects().add(request);
+
+        SOAPObjectBuilder<Envelope> envelopeBuilder = (SOAPObjectBuilder<Envelope>) builderFactory
+                .getBuilder(Envelope.DEFAULT_ELEMENT_NAME);
+        Envelope envelope = envelopeBuilder.buildObject();
+        envelope.setBody(body);
+
+        Marshaller marshaller = marshallerFactory.getMarshaller(envelope);
+        Element envelopeElem = marshaller.marshall(envelope);
+
+        StringWriter writer = new StringWriter();
+        XMLHelper.writeNode(envelopeElem, writer);
+        return writer.toString();
+    }
+}
\ No newline at end of file