Basic SAML 1 artifact resolution unit test
authorlajoie <lajoie@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Sun, 16 Sep 2007 18:05:39 +0000 (18:05 +0000)
committerlajoie <lajoie@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Sun, 16 Sep 2007 18:05:39 +0000 (18:05 +0000)
git-svn-id: https://subversion.switch.ch/svn/shibboleth/java-idp/trunk@2399 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
test/data/conf1/internal.xml
test/data/conf1/relying-party.xml
test/edu/internet2/middleware/shibboleth/idp/system/conf1/SAML1ArtifactResolutionTest.java [new file with mode: 0644]
test/edu/internet2/middleware/shibboleth/idp/system/conf1/SAML1AttributeQueryTestCase.java
test/log4j.xml

index 99d5f6f..524db0e 100644 (file)
@@ -18,6 +18,7 @@ package edu.internet2.middleware.shibboleth.idp.profile.saml1;
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 
 import org.apache.log4j.Logger;
 import org.joda.time.DateTime;
@@ -28,8 +29,8 @@ import org.opensaml.common.binding.artifact.SAMLArtifactMap.SAMLArtifactMapEntry
 import org.opensaml.common.binding.decoding.SAMLMessageDecoder;
 import org.opensaml.common.xml.SAMLConstants;
 import org.opensaml.saml1.binding.SAML1ArtifactMessageContext;
-import org.opensaml.saml1.binding.artifact.AbstractSAML1Artifact;
 import org.opensaml.saml1.core.Assertion;
+import org.opensaml.saml1.core.AssertionArtifact;
 import org.opensaml.saml1.core.NameIdentifier;
 import org.opensaml.saml1.core.Request;
 import org.opensaml.saml1.core.Response;
@@ -61,6 +62,9 @@ public class ArtifactResolution extends AbstractSAML1ProfileHandler {
     /** Builder of Response objects. */
     private SAMLObjectBuilder<Response> responseBuilder;
 
+    /** Builder of assertion consumer service endpoints. */
+    private SAMLObjectBuilder<AssertionConsumerService> acsEndpointBuilder;
+
     /** Map artifacts to SAML messages. */
     private SAMLArtifactMap artifactMap;
 
@@ -75,6 +79,8 @@ public class ArtifactResolution extends AbstractSAML1ProfileHandler {
         artifactMap = map;
 
         responseBuilder = (SAMLObjectBuilder<Response>) getBuilderFactory().getBuilder(Response.DEFAULT_ELEMENT_NAME);
+        acsEndpointBuilder = (SAMLObjectBuilder<AssertionConsumerService>) getBuilderFactory().getBuilder(
+                AssertionConsumerService.DEFAULT_ELEMENT_NAME);
     }
 
     /** {@inheritDoc} */
@@ -101,6 +107,8 @@ public class ArtifactResolution extends AbstractSAML1ProfileHandler {
 
             checkSamlVersion(requestContext);
 
+            derferenceArtifacts(requestContext);
+
             // create the SAML response
             samlResponse = buildArtifactResponse(requestContext);
         } catch (ProfileException e) {
@@ -166,6 +174,7 @@ public class ArtifactResolution extends AbstractSAML1ProfileHandler {
                 String relyingPartyId = requestContext.getInboundMessageIssuer();
                 RelyingPartyConfiguration rpConfig = getRelyingPartyConfiguration(relyingPartyId);
                 requestContext.setRelyingPartyConfiguration(rpConfig);
+                requestContext.setPeerEntityEndpoint(selectEndpoint(requestContext));
 
                 String assertingPartyId = requestContext.getRelyingPartyConfiguration().getProviderId();
                 requestContext.setLocalEntityId(assertingPartyId);
@@ -200,48 +209,59 @@ public class ArtifactResolution extends AbstractSAML1ProfileHandler {
      * @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.SAML1_SOAP11_BINDING_URI)) {
+            endpoint = acsEndpointBuilder.buildObject();
+            endpoint.setBinding(SAMLConstants.SAML1_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;
     }
 
     /**
-     * Derferences the provided artifacts and stores them in the request context.
+     * Derferences the artifacts within the incomming request and stores them in the request context.
      * 
      * @param requestContext current request context
+     * 
+     * @throws ProfileException thrown if the incomming request does not contain any {@link AssertionArtifact}s.
      */
-    protected void derferenceArtifacts(ArtifactResolutionRequestContext requestContext) {
-        Collection<AbstractSAML1Artifact> artifacts = requestContext.getArtifacts();
-        if (artifacts != null) {
-            ArrayList<Assertion> assertions = new ArrayList<Assertion>();
-            SAMLArtifactMapEntry artifactEntry;
-
-            for (AbstractSAML1Artifact artifact : artifacts) {
-                artifactEntry = artifactMap.get(artifact.getArtifactBytes());
-                if (artifactEntry == null || artifactEntry.isExpired()) {
-                    log.error("Unknown artifact.");
-                }
+    protected void derferenceArtifacts(ArtifactResolutionRequestContext requestContext) throws ProfileException {
+        Request request = requestContext.getInboundSAMLMessage();
+        List<AssertionArtifact> assertionArtifacts = request.getAssertionArtifacts();
 
-                if (!artifactEntry.getIssuerId().equals(requestContext.getLocalEntityId())) {
-                    log.error("Artifact issuer mismatch.  Artifact issued by " + artifactEntry.getIssuerId()
-                            + " but IdP has entity ID of " + requestContext.getLocalEntityId());
-                }
+        if (assertionArtifacts == null || assertionArtifacts.size() == 0) {
+            log.error("No AssertionArtifacts available in request");
+            throw new ProfileException("No AssertionArtifacts available in request");
+        }
 
-                if (!artifactEntry.getRelyingPartyId().equals(requestContext.getInboundMessageIssuer())) {
-                    log.error("Artifact requester mismatch.  Artifact was issued to "
-                            + artifactEntry.getRelyingPartyId() + " but was resolve request came from "
-                            + requestContext.getInboundMessageIssuer());
-                }
-                artifactMap.remove(artifact.getArtifactBytes());
-                assertions.add((Assertion) artifactEntry.getSamlMessage());
+        ArrayList<Assertion> assertions = new ArrayList<Assertion>();
+        SAMLArtifactMapEntry artifactEntry;
+        for (AssertionArtifact assertionArtifact : assertionArtifacts) {
+            artifactEntry = artifactMap.get(assertionArtifact.getAssertionArtifact());
+            if (artifactEntry == null || artifactEntry.isExpired()) {
+                log.error("Unknown artifact.");
             }
-            requestContext.setReferencedAssertions(assertions);
+
+            if (!artifactEntry.getIssuerId().equals(requestContext.getLocalEntityId())) {
+                log.error("Artifact issuer mismatch.  Artifact issued by " + artifactEntry.getIssuerId()
+                        + " but IdP has entity ID of " + requestContext.getLocalEntityId());
+            }
+
+            artifactMap.remove(assertionArtifact.getAssertionArtifact());
+            assertions.add((Assertion) artifactEntry.getSamlMessage());
         }
+        
+        requestContext.setReferencedAssertions(assertions);
     }
 
     /**
@@ -275,27 +295,19 @@ public class ArtifactResolution extends AbstractSAML1ProfileHandler {
             SAML1ArtifactMessageContext<Request, Response, NameIdentifier> {
 
         /** Artifact to be resolved. */
-        private Collection<AbstractSAML1Artifact> artifacts;
+        private Collection<String> artifacts;
 
         /** Message referenced by the SAML artifact. */
         private Collection<Assertion> referencedAssertions;
 
-        /**
-         * Gets the artifacts to be resolved.
-         * 
-         * @return artifacts to be resolved
-         */
-        public Collection<AbstractSAML1Artifact> getArtifacts() {
+        /** {@inheritDoc} */
+        public Collection<String> getArtifacts() {
             return artifacts;
         }
 
-        /**
-         * Sets the artifacts to be resolved.
-         * 
-         * @param artifacts artifacts to be resolved
-         */
-        public void setArtifacts(Collection<AbstractSAML1Artifact> artifacts) {
-            this.artifacts = artifacts;
+        /** {@inheritDoc} */
+        public void setArtifacts(Collection<String> encodedArtifacts) {
+            this.artifacts = encodedArtifacts;
         }
 
         /**
index 8e9939e..8921af3 100644 (file)
@@ -99,7 +99,7 @@ public class ArtifactResolution extends AbstractSAML2ProfileHandler {
 
             checkSamlVersion(requestContext);
 
-            SAMLArtifactMapEntry artifactEntry = artifactMap.get(requestContext.getArtifact().getArtifactBytes());
+            SAMLArtifactMapEntry artifactEntry = artifactMap.get(requestContext.getArtifact().base64Encode());
             if (artifactEntry == null || artifactEntry.isExpired()) {
                 log.error("Unknown artifact.");
                 requestContext.setFailureStatus(buildStatus(StatusCode.SUCCESS_URI, StatusCode.REQUEST_DENIED_URI,
@@ -119,7 +119,7 @@ public class ArtifactResolution extends AbstractSAML2ProfileHandler {
                 requestContext.setFailureStatus(buildStatus(StatusCode.SUCCESS_URI, StatusCode.REQUEST_DENIED_URI,
                         "Artifact requester mismatch."));
             }
-            artifactMap.remove(requestContext.getArtifact().getArtifactBytes());
+            artifactMap.remove(requestContext.getArtifact().base64Encode());
             SAMLObject referencedMessage = artifactEntry.getSamlMessage();
             requestContext.setReferencedMessage(referencedMessage);
 
index 43f8eb8..a675662 100644 (file)
                 <value>urn:oasis:names:tc:SAML:1.0:profiles:browser-post</value>
             </key>
             <bean id="shibboleth.SAML1HttpPostDecoder" class="org.opensaml.saml1.binding.decoding.HTTPPostDecoder">
+                <constructor-arg ref="shibboleth.ArtifactMap" />
                 <constructor-arg ref="shibboleth.ParserPool" />
             </bean>
         </entry>
             </key>
             <bean id="shibboleth.SAML1HttpSoap11Decoder"
                 class="org.opensaml.saml1.binding.decoding.HTTPSOAP11Decoder">
+                <constructor-arg ref="shibboleth.ArtifactMap" />
                 <constructor-arg ref="shibboleth.ParserPool" />
             </bean>
         </entry>
index 516a8c2..67f0687 100644 (file)
@@ -27,7 +27,7 @@
     <DefaultRelyingParty provider="http://example.org/IdP" />
         
     <RelyingParty id="urn:example.org"
-                  provider="http://idp.example.org">
+                  provider="urn:example.org:idp1">
         <ProfileConfiguration xsi:type="saml:ShibbolethSSOProfile" />
         <ProfileConfiguration xsi:type="saml:SAML1AttributeQueryProfile" />
         <ProfileConfiguration xsi:type="saml:SAML1ArtifactResolutionProfile" />
                     <SingleSignOnService Binding="urn:mace:shibboleth:1.0:profiles:AuthnRequest" Location="https://example.org/myIdP"/>
                     <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://example.org/myIdP" />
                 </IDPSSODescriptor>
+                
+                <AttributeAuthorityDescriptor protocolSupportEnumeration="urn:mace:shibboleth:1.0 urn:oasis:names:tc:SAML:1.1:protocol urn:oasis:names:tc:SAML:2.0:protocol">
+                    <AttributeService Binding="urn:oasis:names:tc:SAML:1.0:bindings:SOAP-binding" Location="https://example.org/myIdP"/>
+                    <AttributeService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://example.org/myIdP"/>
+                    <NameIDFormat>urn:mace:shibboleth:1.0:nameIdentifier</NameIDFormat>
+                </AttributeAuthorityDescriptor>
             </EntityDescriptor>
             
             <EntityDescriptor entityID="urn:example.org:sp1">
diff --git a/test/edu/internet2/middleware/shibboleth/idp/system/conf1/SAML1ArtifactResolutionTest.java b/test/edu/internet2/middleware/shibboleth/idp/system/conf1/SAML1ArtifactResolutionTest.java
new file mode 100644 (file)
index 0000000..d635f7d
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * 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.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.saml1.binding.artifact.SAML1ArtifactType0002;
+import org.opensaml.saml1.core.Assertion;
+import org.opensaml.saml1.core.AssertionArtifact;
+import org.opensaml.saml1.core.Request;
+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.ProfileHandler;
+import edu.internet2.middleware.shibboleth.common.profile.ProfileHandlerManager;
+
+/**
+ * 
+ */
+public class SAML1ArtifactResolutionTest 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("/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("saml:Assertion"));
+    }
+
+    @SuppressWarnings("unchecked")
+    protected SAMLArtifactMapEntry stageArtifact(String relyingPartyId) throws Exception {
+        SAMLObjectBuilder<Assertion> assetionBuilder = (SAMLObjectBuilder<Assertion>) builderFactory
+                .getBuilder(Assertion.DEFAULT_ELEMENT_NAME);
+        Assertion assertion = assetionBuilder.buildObject();
+
+        SecureRandom handleGenerator = SecureRandom.getInstance("SHA1PRNG");
+        byte[] assertionHandle = new byte[20];
+        handleGenerator.nextBytes(assertionHandle);
+        SAML1ArtifactType0002 artifact = new SAML1ArtifactType0002(assertionHandle, relyingPartyId);
+
+        SAMLArtifactMap artifactMap = (SAMLArtifactMap) getApplicationContext().getBean("shibboleth.ArtifactMap");
+        artifactMap.put(artifact.base64Encode(), relyingPartyId, "urn:example.org:idp1", assertion);
+        return artifactMap.get(artifact.base64Encode());
+    }
+
+    @SuppressWarnings("unchecked")
+    protected String buildRequestMessage(String relyingPartyId, String artifact) throws Exception {
+        SAMLObjectBuilder<AssertionArtifact> assertionArtifactBuilder = (SAMLObjectBuilder<AssertionArtifact>) builderFactory
+                .getBuilder(AssertionArtifact.DEFAULT_ELEMENT_NAME);
+        AssertionArtifact aa = assertionArtifactBuilder.buildObject();
+        aa.setAssertionArtifact(artifact);
+
+        SAMLObjectBuilder<Request> requestBuilder = (SAMLObjectBuilder<Request>) builderFactory
+                .getBuilder(Request.DEFAULT_ELEMENT_NAME);
+        Request request = requestBuilder.buildObject();
+        request.setID("1");
+        request.setIssueInstant(new DateTime());
+        request.getAssertionArtifacts().add(aa);
+
+        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
index a83dd7a..ac1ad23 100644 (file)
@@ -18,9 +18,11 @@ package edu.internet2.middleware.shibboleth.idp.system.conf1;
 
 import java.io.StringWriter;
 
+import org.joda.time.DateTime;
 import org.opensaml.common.SAMLObjectBuilder;
 import org.opensaml.saml1.core.AttributeQuery;
 import org.opensaml.saml1.core.NameIdentifier;
+import org.opensaml.saml1.core.Request;
 import org.opensaml.saml1.core.Subject;
 import org.opensaml.ws.soap.common.SOAPObjectBuilder;
 import org.opensaml.ws.soap.soap11.Body;
@@ -136,10 +138,16 @@ public class SAML1AttributeQueryTestCase extends BaseConf1TestCase {
      */
     @SuppressWarnings("unchecked")
     protected String getSOAPMessage(AttributeQuery query) throws MarshallingException {
+        SAMLObjectBuilder<Request> requestBuilder = (SAMLObjectBuilder<Request>)builderFactory.getBuilder(Request.DEFAULT_ELEMENT_NAME);
+        Request request = requestBuilder.buildObject();
+        request.setQuery(query);
+        request.setIssueInstant(new DateTime());
+        request.setID("1");
+        
         SOAPObjectBuilder<Body> bodyBuilder = (SOAPObjectBuilder<Body>) builderFactory
                 .getBuilder(Body.DEFAULT_ELEMENT_NAME);
         Body body = bodyBuilder.buildObject();
-        body.getUnknownXMLObjects().add(query);
+        body.getUnknownXMLObjects().add(request);
 
         SOAPObjectBuilder<Envelope> envelopeBuilder = (SOAPObjectBuilder<Envelope>) builderFactory
                 .getBuilder(Envelope.DEFAULT_ELEMENT_NAME);
index 6c6e66c..d199613 100644 (file)
         <priority value="CRITICAL" />
     </category>
     
-
-    <category name="edu.internet2.middleware.shibboleth.idp">
-        <priority value="DEBUG" />
-    </category>
-
-    <category name="edu.internet2.middleware.shibboleth.common">
-        <priority value="DEBUG" />
+    <category name="edu.internet2.middleware.shibboleth">
+        <priority value="WARN" />
     </category>
 
-    <category name="org.opensaml.resource">
-        <priority value="DEBUG" />
+    <category name="org.opensaml">
+        <priority value="WARN" />
     </category>
 
     <category name="org.springframework">\r