upgrade to mockrunner 0.3.6, expand integration test coverage
authorgilbert <gilbert@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Fri, 18 Nov 2005 20:52:39 +0000 (20:52 +0000)
committergilbert <gilbert@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Fri, 18 Nov 2005 20:52:39 +0000 (20:52 +0000)
git-svn-id: https://subversion.switch.ch/svn/shibboleth/java-idp/trunk@1911 ab3bd59b-922f-494d-bb5f-6f0a3c29deca

data/AttributePushAssertion.xml [new file with mode: 0644]
testlib/mockrunner-servlet.jar
tests/com/mockrunner/mock/web/MockPageContext.java [new file with mode: 0644]
tests/edu/internet2/middleware/shibboleth/integration/FileAssertionTest.java [new file with mode: 0644]
tests/edu/internet2/middleware/shibboleth/integration/FilterConsumerTest.java [new file with mode: 0644]
tests/edu/internet2/middleware/shibboleth/integration/IntegrationTest.java
tests/edu/internet2/middleware/shibboleth/runner/MadSignertest.java [new file with mode: 0644]
tests/edu/internet2/middleware/shibboleth/runner/ShibbolethRunner.java

diff --git a/data/AttributePushAssertion.xml b/data/AttributePushAssertion.xml
new file mode 100644 (file)
index 0000000..473a31a
--- /dev/null
@@ -0,0 +1,63 @@
+<Response xmlns="urn:oasis:names:tc:SAML:1.0:protocol" 
+       xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion" 
+       xmlns:samlp="urn:oasis:names:tc:SAML:1.0:protocol" 
+       xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
+       IssueInstant="2006-11-17T18:21:43.428Z" 
+       MajorVersion="1" MinorVersion="1" 
+       Recipient="https://sp.example.org/shibboleth-sp/Shibboleth.sso/SAML/POST" 
+       ResponseID="_87070ade79c83d58ff51a98535185d9c">
+       <Status><StatusCode Value="samlp:Success"></StatusCode></Status>
+       <Assertion xmlns="urn:oasis:names:tc:SAML:1.0:assertion" AssertionID="_9505a09e2cc9fa81dad54790204650b5" IssueInstant="2005-11-17T18:21:43.194Z" Issuer="https://idp.example.org/shibboleth" MajorVersion="1" MinorVersion="1">
+               <Conditions NotBefore="2005-11-17T18:21:43.194Z" NotOnOrAfter="2006-11-17T18:26:43.194Z">
+                       <AudienceRestrictionCondition>
+                               <Audience>https://sp.example.org/shibboleth</Audience>
+                               <Audience>urn:mace:shibboleth:examples</Audience>
+                       </AudienceRestrictionCondition>
+               </Conditions>
+               <AuthenticationStatement AuthenticationInstant="2005-11-17T18:21:43.178Z" AuthenticationMethod="urn:oasis:names:tc:SAML:1.0:am:unspecified">
+                       <Subject>
+                               <NameIdentifier Format="urn:mace:shibboleth:1.0:nameIdentifier" NameQualifier="https://idp.example.org/shibboleth">_2d83b26798d9315becc934817d93942b</NameIdentifier>
+                               <SubjectConfirmation>
+                                       <ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:bearer</ConfirmationMethod>
+                               </SubjectConfirmation>
+                       </Subject>
+                       <SubjectLocality IPAddress="127.0.0.1"></SubjectLocality>
+               </AuthenticationStatement>
+       </Assertion>
+       <Assertion xmlns="urn:oasis:names:tc:SAML:1.0:assertion" AssertionID="_234ed5c8b167c13a7eeebb0265dec432" IssueInstant="2005-11-17T18:21:43.428Z" Issuer="https://idp.example.org/shibboleth" MajorVersion="1" MinorVersion="1">
+               <Conditions NotBefore="2005-11-17T18:21:43.428Z" NotOnOrAfter="2006-11-17T18:51:43.428Z">
+                       <AudienceRestrictionCondition>
+                               <Audience>https://sp.example.org/shibboleth</Audience>
+                               <Audience>urn:mace:shibboleth:examples</Audience>
+                       </AudienceRestrictionCondition>
+               </Conditions>
+               <AttributeStatement>
+                       <Subject>
+                               <NameIdentifier Format="urn:mace:shibboleth:1.0:nameIdentifier" NameQualifier="https://idp.example.org/shibboleth">_2d83b26798d9315becc934817d93942b</NameIdentifier>
+                               <SubjectConfirmation><ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:bearer</ConfirmationMethod></SubjectConfirmation>
+                       </Subject>
+                       <Attribute AttributeName="urn:mace:dir:attribute-def:title" AttributeNamespace="urn:mace:shibboleth:1.0:attributeNamespace:uri">
+                               <AttributeValue>clown</AttributeValue>
+                       </Attribute>
+                       <Attribute AttributeName="urn:mace:dir:attribute-def:eduPersonScopedAffiliation" AttributeNamespace="urn:mace:shibboleth:1.0:attributeNamespace:uri">
+                               <AttributeValue Scope="example.org">member</AttributeValue>
+                       </Attribute>
+                       <Attribute AttributeName="urn:mace:dir:attribute-def:eduPersonAffiliation" AttributeNamespace="urn:mace:shibboleth:1.0:attributeNamespace:uri">
+                               <AttributeValue>member</AttributeValue>
+                       </Attribute>
+                       <Attribute AttributeName="urn:mace:dir:attribute-def:unacceptable" AttributeNamespace="urn:mace:shibboleth:1.0:attributeNamespace:uri">
+                               <AttributeValue>nonsense</AttributeValue>
+                       </Attribute>
+                       <Attribute AttributeName="urn:mace:dir:attribute-def:givenName" AttributeNamespace="urn:mace:shibboleth:1.0:attributeNamespace:uri">
+                               <AttributeValue>Bozo</AttributeValue>
+                       </Attribute>
+                       <Attribute AttributeName="urn:mace:dir:attribute-def:surname" AttributeNamespace="urn:mace:shibboleth:1.0:attributeNamespace:uri">
+                               <AttributeValue>Clown</AttributeValue>
+                       </Attribute>
+                       <Attribute AttributeName="urn:mace:dir:attribute-def:eduPersonPrincipalName" AttributeNamespace="urn:mace:shibboleth:1.0:attributeNamespace:uri">
+                               <AttributeValue Scope="example.org">BozoTClown</AttributeValue>
+                       </Attribute>
+               </AttributeStatement>
+       </Assertion>
+</Response>
index a4e1f37..b89e3db 100644 (file)
Binary files a/testlib/mockrunner-servlet.jar and b/testlib/mockrunner-servlet.jar differ
diff --git a/tests/com/mockrunner/mock/web/MockPageContext.java b/tests/com/mockrunner/mock/web/MockPageContext.java
new file mode 100644 (file)
index 0000000..bfe3769
--- /dev/null
@@ -0,0 +1,466 @@
+/*
+ * Modified copy of Mockrunner 0.3.6 class with JSP 2.0 dependencies 
+ * removed so Shibboleth project doesn't require newer Servlet API to 
+ * compile and test. (Howard Gilbert)
+ */
+package com.mockrunner.mock.web;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Stack;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import javax.servlet.jsp.JspWriter;
+import javax.servlet.jsp.PageContext;
+import javax.servlet.jsp.tagext.BodyContent;
+
+/**
+ * Mock implementation of <code>PageContext</code>.
+ */
+//Some methods of this class were copied from org.apache.struts.mock.MockPageContext
+//and modified
+public class MockPageContext extends PageContext
+{
+    protected ServletConfig config;
+    protected ServletRequest request;
+    protected ServletResponse response;
+    private JspWriter jspWriter;
+    private Stack outStack;
+    private Exception exception;
+    private Object page;
+    private HashMap attributes;
+//    private ExpressionEvaluator evaluator;
+//    private VariableResolver resolver;
+    
+    public MockPageContext()
+    {
+        this(null, null, null);
+    }
+
+    public MockPageContext(ServletConfig config, ServletRequest request, ServletResponse response)
+    {
+        this.config = config;
+        this.request = request;
+        this.response = response;
+        jspWriter = new MockJspWriter();
+        outStack = new Stack();
+        attributes = new HashMap();
+//        evaluator = new MockExpressionEvaluator();
+//        resolver = new MockVariableResolver();
+    }
+    
+    /**
+     * This method allows to set custom implementations
+     * of <code>JspWriter</code>. Per default, {@link MockJspWriter}
+     * is used.
+     * @param jspWriter the <code>JspWriter</code>
+     */
+    public void setJspWriter(JspWriter jspWriter)
+    {
+        this.jspWriter = jspWriter;
+    }
+    
+    public void setPage(Object page) 
+    {
+        this.page = page;
+    }
+    
+    public void setServletConfig(ServletConfig config)
+    {
+        this.config = config;
+    }
+    
+    public void setServletRequest(ServletRequest request)
+    {
+        this.request = request;
+    }
+    
+    public void setServletResponse(ServletResponse response)
+    {
+        this.response = response;
+    }
+    
+    public void setException(Exception exception) 
+    {
+        this.exception = exception;
+    }
+    
+    public Object findAttribute(String name) 
+    {
+        Object value = getAttribute(name, PageContext.PAGE_SCOPE);
+        if(value == null) 
+        {
+            value = getAttribute(name, PageContext.REQUEST_SCOPE);
+        }
+        if(value == null) 
+        {
+            value = getAttribute(name, PageContext.SESSION_SCOPE);
+        }
+        if(value == null) 
+        {
+            value = getAttribute(name, PageContext.APPLICATION_SCOPE);
+        }
+        return value;
+    }
+    
+    public Object getAttribute(String name) 
+    {
+        return getAttribute(name, PageContext.PAGE_SCOPE);
+    }
+
+    public Object getAttribute(String name, int scope) 
+    {
+        if(scope == PageContext.PAGE_SCOPE) 
+        {
+            return attributes.get(name);
+        } 
+        else if(scope == PageContext.REQUEST_SCOPE) 
+        {
+            if(null == request) return null;
+            return request.getAttribute(name);
+        } 
+        else if(scope == PageContext.SESSION_SCOPE) 
+        {
+            if(null == getSession()) return null;
+            return getSession().getAttribute(name);
+        } 
+        else if(scope == PageContext.APPLICATION_SCOPE) 
+        {
+            if(null == getServletContext()) return null;
+            return getServletContext().getAttribute(name);
+        } 
+        else 
+        {
+            throw new IllegalArgumentException("Invalid scope " + scope);
+        }
+    }
+    
+    public void removeAttribute(String name) 
+    {
+        int scope = getAttributesScope(name);
+        if (scope != 0) 
+        {
+            removeAttribute(name, scope);
+        }
+    }
+
+    public void removeAttribute(String name, int scope) 
+    {
+        if(scope == PageContext.PAGE_SCOPE) 
+        {
+            attributes.remove(name);
+        } 
+        else if(scope == PageContext.REQUEST_SCOPE) 
+        {
+            if(request != null) 
+            {
+                request.removeAttribute(name);
+            }
+        } 
+        else if(scope == PageContext.SESSION_SCOPE) 
+        {
+            if(getSession() != null) 
+            {
+                getSession().removeAttribute(name);
+            }
+        } 
+        else if(scope == PageContext.APPLICATION_SCOPE) 
+        {
+            if(getServletContext() != null) 
+            {
+                getServletContext().removeAttribute(name);
+            }
+        } 
+        else 
+        {
+            throw new IllegalArgumentException("Invalid scope " + scope);
+        }
+    }
+    
+    public void setAttribute(String name, Object value) 
+    {
+        setAttribute(name, value, PageContext.PAGE_SCOPE);
+    }
+
+
+    public void setAttribute(String name, Object value, int scope) 
+    {
+        if(scope == PageContext.PAGE_SCOPE) 
+        {
+            attributes.put(name, value);
+        } 
+        else if(scope == PageContext.REQUEST_SCOPE) 
+        {
+            if(request != null) 
+            {
+                request.setAttribute(name, value);
+            }
+        } 
+        else if(scope == PageContext.SESSION_SCOPE) 
+        {
+            if(getSession() != null) 
+            {
+                getSession().setAttribute(name, value);
+            }
+        } 
+        else if(scope == PageContext.APPLICATION_SCOPE) 
+        {
+            if(getServletContext() != null) 
+            {
+                getServletContext().setAttribute(name, value);
+            }
+        } 
+        else 
+        {
+            throw new IllegalArgumentException("Invalid scope " + scope);
+        }
+    }
+    
+    public int getAttributesScope(String name) 
+    {
+        if(getAttribute(name, PageContext.PAGE_SCOPE) != null) 
+        {
+            return PageContext.PAGE_SCOPE;
+        } 
+        else if(getAttribute(name, PageContext.REQUEST_SCOPE) != null) 
+        {
+            return PageContext.REQUEST_SCOPE;
+        } 
+        else if(getAttribute(name, PageContext.SESSION_SCOPE) != null)
+        {
+            return PageContext.SESSION_SCOPE;
+        } 
+        else if(getAttribute(name, PageContext.APPLICATION_SCOPE) != null) 
+        {
+            return PageContext.APPLICATION_SCOPE;
+        } 
+        return 0;
+    }
+    
+    public Enumeration getAttributeNamesInScope(int scope) 
+    {
+        if(scope == PageContext.PAGE_SCOPE) 
+        {
+            return new WrappedEnumeration(attributes.keySet().iterator());
+        } 
+        else if(scope == PageContext.REQUEST_SCOPE) 
+        {
+            if(request == null) return new NullEnumeration();
+            return request.getAttributeNames();
+        } 
+        else if(scope == PageContext.SESSION_SCOPE) 
+        {
+            if(getSession() == null) return new NullEnumeration();
+            return getSession().getAttributeNames();
+        } 
+        else if(scope == PageContext.APPLICATION_SCOPE) 
+        {
+            if(getServletContext() == null) return new NullEnumeration();
+            return getServletContext().getAttributeNames();
+        } 
+        else 
+        {
+            throw new IllegalArgumentException("Invalid scope " + scope);
+        }
+    }
+    
+    public JspWriter getOut()
+    {
+        return jspWriter;
+    }
+    
+    public Exception getException() 
+    {
+        return exception;
+    }
+    
+    public Object getPage() 
+    {
+        return page;
+    }
+
+    public ServletRequest getRequest() 
+    {
+        return request;
+    }
+
+    public ServletResponse getResponse() 
+    {
+        return response;
+    }
+
+    public ServletConfig getServletConfig() 
+    {
+        return config;
+    }
+
+    public ServletContext getServletContext() 
+    {
+        if(null == config) return null;
+        return config.getServletContext();
+    }
+
+
+    public HttpSession getSession() 
+    {
+        if(null == request) return null;
+        return ((HttpServletRequest)request).getSession();
+    }
+    
+    public void handlePageException(Exception exc) 
+    {
+        
+    }
+
+    public void handlePageException(Throwable thr) 
+    {
+    
+    }
+    
+    public void forward(String path) throws ServletException, IOException
+    {
+        if(null != request)
+        {
+            RequestDispatcher dispatcher = request.getRequestDispatcher(path);
+            if(null != dispatcher)
+            {
+                dispatcher.forward(request, response); 
+            }
+        }
+    }
+
+    public void include(String path) throws ServletException, IOException
+    {
+        if(null != request)
+        {
+            RequestDispatcher dispatcher = request.getRequestDispatcher(path);
+            if(null != dispatcher)
+            {
+                dispatcher.include(request, response); 
+            }
+        }
+    }
+    
+    public void include(String path, boolean flush) throws ServletException, IOException
+    {
+        if(flush)
+        {
+            jspWriter.flush();
+        }
+        include(path);
+    }
+
+    public void initialize(Servlet servlet, ServletRequest request,
+                           ServletResponse response, String errorPageURL,
+                           boolean needsSession, int bufferSize,
+                           boolean autoFlush) 
+    {
+        this.config = servlet.getServletConfig();
+        this.request = request;
+        this.response = response;
+        jspWriter = new MockJspWriter();
+        outStack = new Stack();
+        attributes = new HashMap();
+    }
+
+    public JspWriter popBody() 
+    {
+        jspWriter = (JspWriter)outStack.pop();
+        return jspWriter;
+    }
+    
+    public BodyContent pushBody() 
+    {
+        outStack.push(jspWriter);
+        jspWriter = new MockBodyContent(jspWriter);
+        return (BodyContent)jspWriter;
+    }
+    
+    public JspWriter pushBody(Writer writer)
+    {
+        outStack.push(jspWriter);
+        jspWriter = new MockJspWriter(writer);
+        return jspWriter;
+    }
+    
+    public void release() 
+    {
+        jspWriter = new MockJspWriter();
+        outStack = new Stack();
+    }
+    
+//    /**
+//     * Sets the expression evaluator. The default expression evaluator
+//     * is {@link MockExpressionEvaluator}.
+//     * @param evaluator the <code>ExpressionEvaluator</code>
+//     */
+//    public void setExpressionEvaluator(ExpressionEvaluator evaluator)
+//    {
+//        this.evaluator = evaluator;
+//    }
+//
+//    /**
+//     * Sets the variable resolver. The default variable resolver
+//     * is {@link MockVariableResolver}.
+//     * @param resolver the <code>VariableResolver</code>
+//     */
+//    public void setVariableResolver(VariableResolver resolver)
+//    {
+//        this.resolver = resolver;
+//    }
+//    
+//    public ExpressionEvaluator getExpressionEvaluator()
+//    {
+//        return evaluator;
+//    }
+//
+//    public VariableResolver getVariableResolver()
+//    {
+//        return resolver;
+//    }
+    
+    private class NullEnumeration implements Enumeration 
+    {
+        public boolean hasMoreElements() 
+        {
+            return false;
+        }
+
+        public Object nextElement() 
+        {
+            throw new NoSuchElementException();
+        }
+    }
+    
+    private class WrappedEnumeration implements Enumeration 
+    {
+        private Iterator iterator;
+        
+        public WrappedEnumeration(Iterator iterator) 
+        {
+            this.iterator = iterator;
+        }
+
+        public boolean hasMoreElements() 
+        {
+            return iterator.hasNext();
+        }
+
+        public Object nextElement() 
+        {
+            return iterator.next();
+        }
+    }
+}
diff --git a/tests/edu/internet2/middleware/shibboleth/integration/FileAssertionTest.java b/tests/edu/internet2/middleware/shibboleth/integration/FileAssertionTest.java
new file mode 100644 (file)
index 0000000..b4314db
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * Copyright [2005] [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.integration;
+
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
+import javax.servlet.http.HttpServletRequest;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.log4j.Level;
+import org.opensaml.SAMLResponse;
+
+import edu.internet2.middleware.shibboleth.resource.AuthenticationFilter;
+import edu.internet2.middleware.shibboleth.resource.FilterSupport.NewSessionData;
+import edu.internet2.middleware.shibboleth.runner.MadSignertest;
+import edu.internet2.middleware.shibboleth.runner.ShibbolethRunner;
+import edu.internet2.middleware.shibboleth.serviceprovider.AssertionConsumerServlet;
+import edu.internet2.middleware.shibboleth.serviceprovider.ServiceProviderConfig;
+import edu.internet2.middleware.shibboleth.serviceprovider.ServiceProviderContext;
+import edu.internet2.middleware.shibboleth.serviceprovider.Session;
+
+/**
+ * A JUnit test case that exercises the IdP, SP, and Filter
+ * @author Howard Gilbert
+ */
+public class FileAssertionTest extends TestCase {
+    
+    // Create some constants, both as parameters and to test responses
+    private static final String GIVENNAME = "Bozo";
+    public static final String SURNAME = "Clown";
+    private static final String TITLE = "clown";
+    public static final String AFFILIATION = "member";
+    public static final String SP_ENTITY = "https://sp.example.org/shibboleth";
+    public static final String POST_SHIRE = "https://sp.example.org/shibboleth-sp/Shibboleth.sso/SAML/POST";
+    public static final String ARTIFACT_SHIRE = "https://sp.example.org/shibboleth-sp/Shibboleth.sso/SAML/Artifact";
+    public static final String TARGET = "https://nonsense";
+    public static final String NETID = "BozoTClown";
+    public static final String APPLICATIONID = "default";
+    
+    ShibbolethRunner runner;
+    ShibbolethRunner.SPTestContext consumer;
+    ShibbolethRunner.AuthenticationFilterContext filter;
+    private NewSessionData newSessionData = new NewSessionData();
+    ServiceProviderContext context;
+    ServiceProviderConfig config;
+    
+    
+    /**
+     * TestCase setUp
+     */
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        // Static call to set Log4J appenders and levels
+        ShibbolethRunner.loglevel = Level.DEBUG;
+        ShibbolethRunner.setupLogging();
+        
+        // Create the overall testing framework
+        runner = new ShibbolethRunner();
+        
+        
+        // Initialize the SP with the default config file.
+        runner.setSpConfigFileName("/basicSpHome/spconfig.xml"); 
+        
+        // Use one of two forms to initialize the SP
+        // Only calling AssertionConsumerServlet.createSessionFromData
+            //runner.initializeSP(); 
+        // Using either MockRunner or direct call to SP
+            consumer = ShibbolethRunner.consumer = runner.new SPTestContext();
+        
+        context=ServiceProviderContext.getInstance();
+        config = context.getServiceProviderConfig();
+        
+        // Initialize the Filter and create its separate
+        // Mockrunner simulated context. 
+        filter= runner.getFilter();
+            // Note: If you are going to change the Filter init-param
+            // values, do it here before calling setUp()
+        filter.setUp();
+  
+        newSessionData.applicationId=APPLICATIONID;
+        newSessionData.providerId=SP_ENTITY;
+          
+        
+        // Create the static collection of Attributes that are 
+        // returned by the IdP for every principal.
+        // This could be done in each test, just as long as it
+        // is done before the SSO.
+        Attributes attributes = runner.getAttributesCollection();
+        attributes.put(new BasicAttribute("eduPersonAffiliation", AFFILIATION));
+        // scoped
+        attributes.put(new BasicAttribute("eduPersonScopedAffiliation", AFFILIATION));
+        attributes.put(new BasicAttribute("title", TITLE));
+        attributes.put(new BasicAttribute("givenName", GIVENNAME));
+        attributes.put(new BasicAttribute("surname", SURNAME));
+        // not in AAP
+        attributes.put(new BasicAttribute("unacceptable","nonsense"));
+        // not in ARP
+        attributes.put(new BasicAttribute("unreleasable","foolishness"));
+    }
+    
+    /**
+     * Test the Post Profile, Attribute Push
+     * <p>Run SSO, call AssertionConsumerServlet directly, then Run Filter</p>
+     */
+    public void testAttributePush() throws Exception {
+        
+        MadSignertest signer = new MadSignertest("src/conf/idp-example.jks","exampleorg");
+        SAMLResponse samlresponse = 
+            signer.signResponseFile("data/AttributePushAssertion.xml", 
+                    "tomcat", new Date());
+        
+        
+        String bin64assertion = new String(samlresponse.toBase64());
+        String assertion = new String(Base64.decodeBase64(bin64assertion.getBytes()));
+        
+        newSessionData.SAMLResponse = bin64assertion; 
+        newSessionData.target=TARGET;
+        newSessionData.handlerURL=POST_SHIRE;
+        
+        // Create the session, extract pushed Attributes 
+        String sessionId = AssertionConsumerServlet.createSessionFromData(newSessionData);
+        
+        // Now get what was created in case you want to test it.
+        Session session = context.getSessionManager().findSession(sessionId, APPLICATIONID);
+        checkSession(session);
+        
+        // Pass the SessionId to the Filter, let it fetch the attributes
+        filter.resetRequest("test.txt");
+        filter.testModule.addRequestParameter(AuthenticationFilter.SESSIONPARM, sessionId);
+        filter.request.setMethod("GET");
+        filter.testModule.doFilter();
+        
+            /*
+             * Sanity Check: doFilter runs just the Filter itself. On 
+             * input there was a Request and Response. When done, there
+             * will be a replacement Request object created by the Filter
+             * wrapping the original request and adding features.
+             */
+
+        checkFilter();
+    }
+    
+    /**
+     * Verify correct operation of Filter and wrapped Request object,
+     * including attributes and headers.
+     */
+    private void checkFilter() {
+        // Get the Request Wrapper object created by the Filter
+        HttpServletRequest filteredRequest = 
+            (HttpServletRequest) filter.testModule.getFilteredRequest();
+        
+        assertEquals(NETID,filteredRequest.getRemoteUser());
+        assertEquals(NETID,filteredRequest.getHeader("REMOTE_USER"));
+        assertEquals(SURNAME,filteredRequest.getHeader("Shib-Person-surname"));
+        assertEquals(GIVENNAME,filteredRequest.getHeader("Shib-InetOrgPerson-givenName"));
+        assertEquals(TITLE,filteredRequest.getHeader("Shib-OrgPerson-title"));
+        
+        Map attributes = (Map) filteredRequest.getAttribute(AuthenticationFilter.SHIB_ATTRIBUTES_PREFIX);
+        Iterator iterator = attributes.entrySet().iterator();
+        while (iterator.hasNext()) {
+            Map.Entry entry = (Map.Entry) iterator.next();
+            String key = (String) entry.getKey();
+            String value = (String) entry.getValue();
+            System.out.println(key+" : "+value);
+        }
+        
+        
+        Enumeration headerNames = filteredRequest.getHeaderNames();
+        while (headerNames.hasMoreElements()) {
+            String name = (String) headerNames.nextElement();
+            String value = (String) filteredRequest.getHeader(name);
+            System.out.println(name+ " : "+value );
+        }
+    }
+    
+    /**
+     * Add Session object checking here.
+     */
+    private void checkSession(Session session) {
+        assertNotNull(session);
+        assertEquals(APPLICATIONID,session.getApplicationId());
+        
+        
+        
+    }
+    
+}
diff --git a/tests/edu/internet2/middleware/shibboleth/integration/FilterConsumerTest.java b/tests/edu/internet2/middleware/shibboleth/integration/FilterConsumerTest.java
new file mode 100644 (file)
index 0000000..5f9f520
--- /dev/null
@@ -0,0 +1,432 @@
+/*
+ * Copyright [2005] [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.integration;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.log4j.Level;
+import org.opensaml.SAMLException;
+
+import com.mockrunner.mock.web.MockHttpServletResponse;
+
+import edu.internet2.middleware.shibboleth.idp.provider.ShibbolethV1SSOHandler;
+import edu.internet2.middleware.shibboleth.resource.AuthenticationFilter;
+import edu.internet2.middleware.shibboleth.resource.FilterSupport.NewSessionData;
+import edu.internet2.middleware.shibboleth.runner.ShibbolethRunner;
+import edu.internet2.middleware.shibboleth.serviceprovider.ServiceProviderConfig;
+import edu.internet2.middleware.shibboleth.serviceprovider.ServiceProviderContext;
+import edu.internet2.middleware.shibboleth.serviceprovider.Session;
+
+/**
+ * A JUnit test case that POSTs the Assertion to the Filter
+ * instead of the SP. The Filter then forwards the POST data
+ * to the SP to create the Session.
+ * @author Howard Gilbert
+ */
+public class FilterConsumerTest extends TestCase {
+    
+    // Create some constants, both as parameters and to test responses
+    private static final String GIVENNAME = "Bozo";
+    public static final String SURNAME = "Clown";
+    private static final String TITLE = "clown";
+    public static final String AFFILIATION = "member";
+    public static final String SP_ENTITY = "https://sp.example.org/shibboleth";
+    public static final String POST_HANDLER = "https://sp.example.org:9443/secure/Shibboleth.sso/SAML/POST";
+    public static final String ARTIFACT_HANDER = "https://sp.example.org:9443/secure/Shibboleth.sso/SAML/Artifact";
+    public static final String POST_SHIRE = "https://sp.example.org/shibboleth-sp/Shibboleth.sso/SAML/POST";
+    public static final String ARTIFACT_SHIRE = "https://sp.example.org/shibboleth-sp/Shibboleth.sso/SAML/Artifact";
+    public static final String TARGET = "https://nonsense";
+    public static final String NETID = "BozoTClown";
+    public static final String APPLICATIONID = "default";
+    
+    ShibbolethRunner runner;
+    ShibbolethRunner.IdpTestContext idp;
+    ShibbolethRunner.SPTestContext consumer;
+    ShibbolethRunner.AuthenticationFilterContext filter;
+    private NewSessionData newSessionData = new NewSessionData();
+    ServiceProviderContext context;
+    ServiceProviderConfig config;
+    
+    
+    /**
+     * TestCase setUp
+     */
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        // Static call to set Log4J appenders and levels
+        ShibbolethRunner.loglevel = Level.DEBUG;
+        ShibbolethRunner.setupLogging();
+        
+        // Create the overall testing framework
+        runner = new ShibbolethRunner();
+        
+        // Initialize the Idp, create the Mockrunner
+        // objects to do SSO, AA, and Artifact calls, and
+        // configure SAML to use the MockHTTPBindingProvider
+        runner.setIdpConfigFileName("/basicIdpHome/idpconfig.xml"); 
+        idp = runner.getIdp();
+        
+        // Initialize the SP with the default config file.
+        runner.setSpConfigFileName("/basicSpHome/spconfig.xml"); 
+        
+        // Use one of two forms to initialize the SP
+        // Only calling AssertionConsumerServlet.createSessionFromData
+            //runner.initializeSP(); 
+        // Using either MockRunner or direct call to SP
+            consumer = ShibbolethRunner.consumer = runner.new SPTestContext();
+        
+        context=ServiceProviderContext.getInstance();
+        config = context.getServiceProviderConfig();
+        
+        // Initialize the Filter and create its separate
+        // Mockrunner simulated context. 
+        filter= runner.getFilter();
+            // Note: If you are going to change the Filter init-param
+            // values, do it here before calling setUp()
+        filter.setUp();
+  
+        newSessionData.applicationId=APPLICATIONID;
+        newSessionData.providerId=SP_ENTITY;
+          
+        
+        // Create the static collection of Attributes that are 
+        // returned by the IdP for every principal.
+        // This could be done in each test, just as long as it
+        // is done before the SSO.
+        Attributes attributes = runner.getAttributesCollection();
+        attributes.put(new BasicAttribute("eduPersonAffiliation", AFFILIATION));
+        // scoped
+        attributes.put(new BasicAttribute("eduPersonScopedAffiliation", AFFILIATION));
+        attributes.put(new BasicAttribute("title", TITLE));
+        attributes.put(new BasicAttribute("givenName", GIVENNAME));
+        attributes.put(new BasicAttribute("surname", SURNAME));
+        // not in AAP
+        attributes.put(new BasicAttribute("unacceptable","nonsense"));
+        // not in ARP
+        attributes.put(new BasicAttribute("unreleasable","foolishness"));
+    }
+    
+    
+    /**
+     * Verify correct operation of Filter and wrapped Request object,
+     * including attributes and headers.
+     */
+    private void checkFilter() {
+        // Get the Request Wrapper object created by the Filter
+        HttpServletRequest filteredRequest = 
+            (HttpServletRequest) filter.testModule.getFilteredRequest();
+        
+        assertEquals(NETID,filteredRequest.getRemoteUser());
+        assertEquals(NETID,filteredRequest.getHeader("REMOTE_USER"));
+        assertEquals(SURNAME,filteredRequest.getHeader("Shib-Person-surname"));
+        assertEquals(GIVENNAME,filteredRequest.getHeader("Shib-InetOrgPerson-givenName"));
+        assertEquals(TITLE,filteredRequest.getHeader("Shib-OrgPerson-title"));
+        
+        Map attributes = (Map) filteredRequest.getAttribute(AuthenticationFilter.SHIB_ATTRIBUTES_PREFIX);
+        Iterator iterator = attributes.entrySet().iterator();
+        while (iterator.hasNext()) {
+            Map.Entry entry = (Map.Entry) iterator.next();
+            String key = (String) entry.getKey();
+            String value = (String) entry.getValue();
+            System.out.println(key+" : "+value);
+        }
+        
+        
+        Enumeration headerNames = filteredRequest.getHeaderNames();
+        while (headerNames.hasMoreElements()) {
+            String name = (String) headerNames.nextElement();
+            String value = (String) filteredRequest.getHeader(name);
+            System.out.println(name+ " : "+value );
+        }
+    }
+    
+    /**
+     * Add Session object checking here.
+     */
+    private void checkSession(Session session) {
+        assertNotNull(session);
+        assertEquals(APPLICATIONID,session.getApplicationId());
+        
+        
+        
+    }
+    
+    /**
+     * Test the Post Profile with Attribute Query
+     * <p>Run SSO, Run AssertionConsumerServlet, then Run Filter</p>
+     */
+    public void testAttributeQuery() throws SAMLException {
+        
+        // Set the URL suffix that triggers SSO processing
+        idp.resetRequest("SSO");
+        
+        // Add the WAYF/RM parameters
+        idp.testModule.addRequestParameter("target", TARGET);
+        idp.testModule.addRequestParameter("shire",POST_HANDLER);
+        idp.testModule.addRequestParameter("providerId", SP_ENTITY);
+        
+        // Add a userid, as if provided by Basic Authentication or a Filter
+        idp.request.setRemoteUser(NETID);
+        
+        // Block Attribute Push
+        ShibbolethV1SSOHandler.pushAttributeDefault=false;
+        
+        // Call the IdP 
+        idp.testModule.doGet();
+        
+        String bin64assertion = (String) idp.request.getAttribute("assertion");
+        String assertion = new String(Base64.decodeBase64(bin64assertion.getBytes()));
+        String handlerURL = (String) idp.request.getAttribute("shire");
+        String targetURL = (String) idp.request.getAttribute("target");
+
+        // Simulate the POST to the Filter Context using MockRunner
+        filter.resetRequest("Shibboleth.sso/SAML/POST");
+        filter.testModule.addRequestParameter("SAMLResponse",bin64assertion);
+        filter.testModule.addRequestParameter("TARGET",targetURL);
+        filter.request.setMethod("POST");
+        filter.testModule.doFilter();
+        
+        // Now check up on what the Filter did with the POST
+        MockHttpServletResponse response = filter.response;
+        assertTrue(response.wasRedirectSent());
+        Iterator cookies = response.getCookies().iterator();
+        String cookiename = AuthenticationFilter.getCookieName(APPLICATIONID);
+        String sessionId = null;
+        while (cookies.hasNext()) {
+            Cookie cookie = (Cookie) cookies.next();
+            if (cookie.getName().equals(cookiename)) {
+                sessionId = cookie.getValue();
+                filter.request.addCookie(cookie);
+                break;
+            }
+        }
+        assertNotNull(sessionId);
+
+
+        correctMockrunnerFilterRedirectBug();
+        
+        
+        // Now get what was created in case you want to test it.
+        Session session = context.getSessionManager().findSession(sessionId, APPLICATIONID);
+        checkSession(session);
+        
+        filter.resetRequest("test.txt"); // need any URL
+        filter.request.setMethod("GET");
+        filter.testModule.doFilter();
+        
+        checkFilter();
+        
+    }
+    
+    /**
+     * Test Filter Redirect with precreated Session, Post to Filter
+     */
+    public void testPrecreatedSessionFilterPost() 
+        throws SAMLException, UnsupportedEncodingException {
+        
+        // Simulate the Resource Get 
+        filter.resetRequest("test.txt");
+        filter.request.setMethod("GET");
+        filter.testModule.doFilter();
+        
+        // Now check up on what the Filter did
+        MockHttpServletResponse response = filter.response;
+        assertTrue(response.wasRedirectSent());
+        Iterator cookies = response.getCookies().iterator();
+        String cookiename = AuthenticationFilter.getCookieName(APPLICATIONID);
+        String sessionId = null;
+        Cookie sessionCookie = null;
+        while (cookies.hasNext()) {
+            Cookie cookie = (Cookie) cookies.next();
+            if (cookie.getName().equals(cookiename)) {
+                sessionId = cookie.getValue();
+                sessionCookie=cookie;
+                break;
+            }
+        }
+        assertNotNull(sessionId);
+        String redirectURL = response.getHeader("Location");
+        assertTrue(redirectURL.indexOf(
+                "target="+URLEncoder.encode(sessionId,"UTF-8"))>0);
+        
+        correctMockrunnerFilterRedirectBug();
+        
+        
+        // Set the URL suffix that triggers SSO processing
+        idp.resetRequest("SSO");
+        
+        // Add the WAYF/RM parameters
+        idp.testModule.addRequestParameter("target", sessionId);
+        idp.testModule.addRequestParameter("shire",POST_HANDLER);
+        idp.testModule.addRequestParameter("providerId", SP_ENTITY);
+        
+        // Add a userid, as if provided by Basic Authentication or a Filter
+        idp.request.setRemoteUser(NETID);
+        
+        // Block Attribute Push
+        ShibbolethV1SSOHandler.pushAttributeDefault=false;
+        
+        // Call the IdP 
+        idp.testModule.doGet();
+        
+        String bin64assertion = (String) idp.request.getAttribute("assertion");
+        String assertion = new String(Base64.decodeBase64(bin64assertion.getBytes()));
+        String handlerURL = (String) idp.request.getAttribute("shire");
+        String targetURL = (String) idp.request.getAttribute("target");
+
+        // Simulate the POST to the Filter Context using MockRunner
+        filter.resetRequest("Shibboleth.sso/SAML/POST");
+        filter.testModule.addRequestParameter("SAMLResponse",bin64assertion);
+        filter.testModule.addRequestParameter("TARGET",targetURL);
+        filter.request.setMethod("POST");
+        if (sessionCookie!=null)
+            filter.request.addCookie(sessionCookie);
+        filter.testModule.doFilter();
+        
+        // Now check up on what the Filter did with the POST
+        response = filter.response;
+        assertTrue(response.wasRedirectSent());
+        redirectURL = response.getHeader("Location");
+
+        correctMockrunnerFilterRedirectBug();
+        
+        
+        // Now get what was created in case you want to test it.
+        Session session = context.getSessionManager().findSession(sessionId, APPLICATIONID);
+        checkSession(session);
+        
+        filter.resetRequest("test.txt"); // need any URL
+        filter.request.setMethod("GET");
+        if (sessionCookie!=null)
+            filter.request.addCookie(sessionCookie);
+        filter.testModule.doFilter();
+        
+        checkFilter();
+        
+    }
+    
+    
+    
+    /**
+     * Test Filter Redirect with precreated Session, Post to SP
+     */
+    public void testPrecreatedSessionSPPost() 
+        throws SAMLException, UnsupportedEncodingException {
+        
+        // Simulate the Resource Get 
+        filter.resetRequest("test.txt");
+        filter.request.setMethod("GET");
+        filter.testModule.doFilter();
+        
+        // Now check up on what the Filter did
+        MockHttpServletResponse response = filter.response;
+        assertTrue(response.wasRedirectSent());
+        Iterator cookies = response.getCookies().iterator();
+        String cookiename = AuthenticationFilter.getCookieName(APPLICATIONID);
+        String sessionId = null;
+        Cookie sessionCookie = null;
+        while (cookies.hasNext()) {
+            Cookie cookie = (Cookie) cookies.next();
+            if (cookie.getName().equals(cookiename)) {
+                sessionId = cookie.getValue();
+                sessionCookie=cookie;
+                break;
+            }
+        }
+        assertNotNull(sessionId);
+        String redirectURL = response.getHeader("Location");
+        assertTrue(redirectURL.indexOf(
+                "target="+URLEncoder.encode(sessionId,"UTF-8"))>0);
+        
+        correctMockrunnerFilterRedirectBug();
+        
+        
+        // Set the URL suffix that triggers SSO processing
+        idp.resetRequest("SSO");
+        
+        // Add the WAYF/RM parameters
+        idp.testModule.addRequestParameter("target", sessionId);
+        idp.testModule.addRequestParameter("shire",POST_SHIRE);
+        idp.testModule.addRequestParameter("providerId", SP_ENTITY);
+        
+        // Add a userid, as if provided by Basic Authentication or a Filter
+        idp.request.setRemoteUser(NETID);
+        
+        // Block Attribute Push
+        ShibbolethV1SSOHandler.pushAttributeDefault=false;
+        
+        // Call the IdP 
+        idp.testModule.doGet();
+        
+        String bin64assertion = (String) idp.request.getAttribute("assertion");
+        String assertion = new String(Base64.decodeBase64(bin64assertion.getBytes()));
+        String handlerURL = (String) idp.request.getAttribute("shire");
+        String targetURL = (String) idp.request.getAttribute("target");
+
+        // Simulate the POST to the SP Context using MockRunner
+        consumer.resetRequest("Shibboleth.sso/SAML/POST");
+        consumer.testModule.addRequestParameter("SAMLResponse",bin64assertion);
+        consumer.testModule.addRequestParameter("TARGET",sessionId);
+        consumer.testModule.doPost();
+        
+        // Now check up on what the AssertionConsumerServlet did with the POST
+        response = consumer.response;
+        assertTrue(response.wasRedirectSent());
+        redirectURL = response.getHeader("Location");
+
+        
+        // Now get what was created in case you want to test it.
+        Session session = context.getSessionManager().findSession(sessionId, APPLICATIONID);
+        checkSession(session);
+        
+        filter.resetRequest("test.txt"); // need any URL
+        filter.request.setMethod("GET");
+        if (sessionCookie!=null)
+            filter.request.addCookie(sessionCookie);
+        filter.testModule.doFilter();
+        
+        checkFilter();
+        
+    }
+    
+    private void correctMockrunnerFilterRedirectBug() {
+        // Mockrunner 0.3.6 (and before) has a bug. 
+        // When a Filter ends with
+        // Redirect and does not run out the entire chain, a
+        // static interator is left hanging. This call circumvents
+        // the problem by finishing the empty chain. It does not
+        // actually call the Shibboleth Filter. It does nothing
+        // other than to reset the static iterator. The next 
+        // doFilter then starts over
+        filter.testModule.doFilter();
+    }
+    
+}
+
index f62702e..5795505 100644 (file)
@@ -19,6 +19,7 @@ package edu.internet2.middleware.shibboleth.integration;
 import java.io.UnsupportedEncodingException;
 import java.net.URLDecoder;
 import java.util.Enumeration;
+import java.util.Iterator;
 import java.util.Map;
 
 import javax.naming.directory.Attributes;
@@ -86,16 +87,16 @@ public class IntegrationTest extends TestCase {
         // Initialize the Idp, create the Mockrunner
         // objects to do SSO, AA, and Artifact calls, and
         // configure SAML to use the MockHTTPBindingProvider
-        runner.setIdpConfigFileName("/basicIdpHome/idpconfig.xml"); // default value
+        runner.setIdpConfigFileName("/basicIdpHome/idpconfig.xml"); 
         idp = runner.getIdp();
         
         // Initialize the SP with the default config file.
-        runner.setSpConfigFileName("/basicSpHome/spconfig.xml"); // default value
+        runner.setSpConfigFileName("/basicSpHome/spconfig.xml"); 
         
         // Use one of two forms to initialize the SP
-        // If only calling AssertionConsumerServlet.createSessionFromData directly
+        // Only calling AssertionConsumerServlet.createSessionFromData
             //runner.initializeSP(); 
-        // If calling AssertionConsumerServlet through MockRunner
+        // Using either MockRunner or direct call to SP
             consumer = ShibbolethRunner.consumer = runner.new SPTestContext();
         
         context=ServiceProviderContext.getInstance();
@@ -136,7 +137,7 @@ public class IntegrationTest extends TestCase {
     public void testAttributePush() throws SAMLException {
         
         // Set the URL suffix that triggers SSO processing
-        idp.setRequestUrls("SSO");
+        idp.resetRequest("SSO");
         
         // Add the WAYF/RM parameters
         idp.testModule.addRequestParameter("target", TARGET);
@@ -181,8 +182,9 @@ public class IntegrationTest extends TestCase {
         checkSession(session);
         
         // Pass the SessionId to the Filter, let it fetch the attributes
+        filter.resetRequest("test.txt");
         filter.testModule.addRequestParameter(AuthenticationFilter.SESSIONPARM, sessionId);
-        filter.setRequestUrls("test.txt");
+        filter.request.setMethod("GET");
         filter.testModule.doFilter();
         
             /*
@@ -211,13 +213,20 @@ public class IntegrationTest extends TestCase {
         assertEquals(TITLE,filteredRequest.getHeader("Shib-OrgPerson-title"));
         
         Map attributes = (Map) filteredRequest.getAttribute(AuthenticationFilter.SHIB_ATTRIBUTES_PREFIX);
+        Iterator iterator = attributes.entrySet().iterator();
+        while (iterator.hasNext()) {
+            Map.Entry entry = (Map.Entry) iterator.next();
+            String key = (String) entry.getKey();
+            String value = (String) entry.getValue();
+            System.out.println(key+" : "+value);
+        }
         
         
         Enumeration headerNames = filteredRequest.getHeaderNames();
         while (headerNames.hasMoreElements()) {
             String name = (String) headerNames.nextElement();
             String value = (String) filteredRequest.getHeader(name);
-            System.out.println(name+ "-"+value );
+            System.out.println(name+ " : "+value );
         }
     }
     
@@ -239,7 +248,7 @@ public class IntegrationTest extends TestCase {
     public void testAttributeQuery() throws SAMLException {
         
         // Set the URL suffix that triggers SSO processing
-        idp.setRequestUrls("SSO");
+        idp.resetRequest("SSO");
         
         // Add the WAYF/RM parameters
         idp.testModule.addRequestParameter("target", TARGET);
@@ -261,9 +270,9 @@ public class IntegrationTest extends TestCase {
         String targetURL = (String) idp.request.getAttribute("target");
 
         // Simulate the POST to the SP Context using MockRunner
+        consumer.resetRequest("Shibboleth.sso/SAML/POST");
         consumer.testModule.addRequestParameter("SAMLResponse",bin64assertion);
         consumer.testModule.addRequestParameter("TARGET",targetURL);
-        consumer.setRequestUrls("Shibboleth.sso/SAML/POST");
         consumer.testModule.doPost();
         
         // Now check up on what the AssertionConsumerServlet did with the POST
@@ -282,8 +291,9 @@ public class IntegrationTest extends TestCase {
         checkSession(session);
         
         // Pass the SessionId to the Filter, let it fetch the attributes
+        filter.resetRequest("test.txt"); // need any URL
         filter.testModule.addRequestParameter(AuthenticationFilter.SESSIONPARM, sessionId);
-        filter.setRequestUrls("test.txt"); // need any URL
+        filter.request.setMethod("GET");
         filter.testModule.doFilter();
         
         checkFilter();
@@ -297,7 +307,7 @@ public class IntegrationTest extends TestCase {
     public void testArtifact() throws SAMLException, UnsupportedEncodingException {
         
         // Set the URL suffix that triggers SSO processing
-        idp.setRequestUrls("SSO");
+        idp.resetRequest("SSO");
         
         // Add the WAYF/RM parameters
         idp.testModule.addRequestParameter("target", TARGET);
@@ -343,8 +353,9 @@ public class IntegrationTest extends TestCase {
         
         
         // Pass the SessionId to the Filter, let it fetch the attributes
+        filter.resetRequest("test.txt"); // need any URL
         filter.testModule.addRequestParameter(AuthenticationFilter.SESSIONPARM, sessionId);
-        filter.setRequestUrls("test.txt"); // need any URL
+        filter.request.setMethod("GET");
         filter.testModule.doFilter();
 
         checkFilter();
diff --git a/tests/edu/internet2/middleware/shibboleth/runner/MadSignertest.java b/tests/edu/internet2/middleware/shibboleth/runner/MadSignertest.java
new file mode 100644 (file)
index 0000000..dfc188e
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright [2005] [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.runner;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Iterator;
+
+import org.apache.xml.security.signature.XMLSignature;
+import org.opensaml.SAMLAssertion;
+import org.opensaml.SAMLResponse;
+
+/**
+ * Class that signs and resets the timestamps on SAML objects.
+ * 
+ * <p>SAML Responses and Assertions must be signed and they have
+ * expiration times that are very short. This makes static files
+ * of test cases hard to use. This class promiscuously signs 
+ * any static assertion in an XML file with credentials supplied
+ * in a JKS and it resets the timestamps. It is used to support
+ * JUnit testing where signed input is required.</p>
+ * 
+ * @author gilbert
+ *
+ */
+public class MadSignertest {
+
+    private KeyStore ks = null;
+    private char[] passwd;
+
+    /**
+     * Create a signer associated with a JKS file
+     * @param path The JKS file path
+     * @param password The password of the JKS file and all its Keys.
+     */
+    public MadSignertest(String path, String password) 
+        throws KeyStoreException, 
+            NoSuchAlgorithmException, 
+            CertificateException, 
+            FileNotFoundException, 
+            IOException {
+        passwd = password.toCharArray();
+        ks = KeyStore.getInstance("JKS");
+        ks.load(new FileInputStream(path), passwd);
+    }
+    
+    /**
+     * Sign the SAMLResponse in a test data xml file.
+     * @param path Path to the input XML file.
+     * @param alias Alias in the JKS of the signing key.
+     * @param now Date to use for timestamps
+     * @return SAMLResponse now signed
+     */
+    public SAMLResponse signResponseFile(String path, String alias, Date now) 
+        throws Exception {
+        InputStream in = new FileInputStream(path);
+        
+        if (now==null)
+            now = new Date();
+        
+        SAMLResponse r = new SAMLResponse(in);
+        
+        Iterator assertions = r.getAssertions();
+        while (assertions.hasNext()) {
+            SAMLAssertion assertion = (SAMLAssertion) assertions.next();
+            assertion.setIssueInstant(now);
+            assertion.setNotBefore(now);
+            assertion.setNotOnOrAfter(new Date(now.getTime() + 60000));
+            assertion.sign(
+                    XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1,
+                    ks.getKey(alias,passwd),
+                    Arrays.asList(ks.getCertificateChain(alias))
+                    );
+            
+        }
+        r.sign(
+                XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1,
+                ks.getKey(alias,passwd),
+                Arrays.asList(ks.getCertificateChain(alias))
+                );
+        
+        return r;
+    }
+    
+}
index e3f1abe..aff83e1 100644 (file)
@@ -43,11 +43,13 @@ import edu.internet2.middleware.shibboleth.serviceprovider.ServiceProviderConfig
 import edu.internet2.middleware.shibboleth.serviceprovider.ServiceProviderContext;
 
 /**
- * Initialize on request the IdP, SP, and Filter on behalf of a JUnit test.
+ * A JUnit Test support class for Shibboleth.
  * 
- * <p>An instance of this class is created by a JUnit test class, which
- * uses it to initialize and create MockRunner objects for testing the
- * Shibboleth components. This keeps the tests themselves simple.</p>
+ * <p>This class can create the Mockrunner control blocks to 
+ * interface to an instance of the SP and one or more instances
+ * of the IdP and Resource Filter. Each instance is initialized
+ * with its own set of configuration files. The test only needs
+ * to create the objects it needs.</p>
  * 
  * <p>Look at *.integration.IntegrationTest for an example of use.</p>
  * 
@@ -55,39 +57,34 @@ import edu.internet2.middleware.shibboleth.serviceprovider.ServiceProviderContex
  */
 public class ShibbolethRunner {
     
-    
-    public static int SERVER_PORT = 443;
-    public static int RESOURCE_SERVER_PORT = 9443;
-    public static String IDP_SERVER_NAME = "idp.example.org";
-    public static String SP_SERVER_NAME = "sp.example.org";
+    // Default values used to define each context
+    public static String REMOTE_ADDR = "192.168.0.99";
     public static String SCHEME = "https";
     public static String PROTOCOL = "HTTP/1.1";
+    public static int    SERVER_PORT = 443;
+    
+    // IdP
+    public static String IDP_SERVER_NAME = "idp.example.org";
     public static String IDP_CONTEXT_PATH = "/shibboleth-idp";
-    public static String SP_CONTEXT_PATH = "/shibboleth-sp";
-    public static String RESOURCE_CONTEXT_PATH = "/secure";
-    public static String REMOTE_ADDR = "127.0.0.1";
     public static String IDP_CONTEXT_URL = SCHEME+"://"+IDP_SERVER_NAME+IDP_CONTEXT_PATH+"/";
+    
+    // SP
+    public static String SP_SERVER_NAME = "sp.example.org";
+    public static String SP_CONTEXT_PATH = "/shibboleth-sp";
     public static String SP_CONTEXT_URL = SCHEME+"://"+SP_SERVER_NAME+SP_CONTEXT_PATH+"/";
+    
+    // Resource
+    public static int    RESOURCE_SERVER_PORT = 9443;
+    public static String RESOURCE_CONTEXT_PATH = "/secure";
     public static String RESOURCE_CONTEXT_URL = SCHEME+"://"+SP_SERVER_NAME+":"+RESOURCE_SERVER_PORT+RESOURCE_CONTEXT_PATH+"/";
     
     
-    private static SAMLConfig samlConfig; 
+    private static SAMLConfig samlConfig; // See constructor for use
 
     
     
-    /**
-     * Initialization logic goes here.
-     * <p>Reqires that Log4J already be configured.</p>
-     */
-    public ShibbolethRunner() {
-        
-        // Configure SAML to use the MockRunner interface to callback
-        // from the SP to the IdP instead of trying to use real HTTP.
-        samlConfig = SAMLConfig.instance();
-        samlConfig.setDefaultBindingProvider(SAMLBinding.SOAP,"edu.internet2.middleware.shibboleth.runner.MockHTTPBindingProvider" );
-    }
  
-    
+    /********************* Static Methods **********************/
     
     /*
      * Logging
@@ -151,23 +148,53 @@ public class ShibbolethRunner {
     
     
     
-    /*
+    
+    
+    /********************* Constructors ************************
+     * Initialization logic goes here.
+     * <p>Reqires that Log4J already be configured.</p>
+     */
+    public ShibbolethRunner() {
+        configureTestSAMLQueries();
+    }
+
+    /**
+     * SAML has a list of BindingProviders that access the IdP.
+     * Normally the SOAP HTTP BindingProvider is the default and
+     * it accesses the IdP by creating a URL socket. This code 
+     * replaces that default with a Mockrunner BindingProvider.
+     * So when the SP does an AA or Artifact Query, the IdP is
+     * called using its Mockrunner simulated Servlet context.
+     * 
+     * <p>Note: This method depends on a real IdP context created
+     * using the IdPTestContext. Use another method if you want
+     * to feed back pre-created test responses.</p>
+     */
+    private void configureTestSAMLQueries() {
+        samlConfig = SAMLConfig.instance();
+        samlConfig.setDefaultBindingProvider(SAMLBinding.SOAP,
+                "edu.internet2.middleware.shibboleth.runner.MockHTTPBindingProvider" );
+    }
+    
+    
+    
+    
+    
+    
+    
+    
+    /************************* Service Provider ********************
      * The SP is represented by an SPContext object and the objects
      * SessionManager, SPConfig, etc. chained off it. The context
      * is initialized and then the main configuration file is read
      * in to create the Config object.
-     * 
-     * The testing environment doesn't bother with MockRunner objects.
-     * The Servlet interface to the SP is a thin layer that only 
-     * translates between HTTP/HTML (the Request object) and method
-     * calls. So once initialized, it is just as easy to call the
-     * SessionManager and FilterSupportImpl directly.
      */
     
     private String spConfigFileName = "/basicSpHome/spconfig.xml";
     /**
      * If you are goint to change the SP Config File
-     * do it before calling initServiceProvider.
+     * do it before calling initServiceProvider or constructing
+     * an SPTestContext.
      * 
      * @param spConfigFileName
      */
@@ -179,11 +206,7 @@ public class ShibbolethRunner {
     public static SPTestContext consumer = null;
     
     /**
-     * Load an SP configuration file if you are not using SPTestContext.
-     * 
-     * <p>Calling this routine does not create a MockRunner context
-     * to call the AssertionConsumerServlet. However, you can still
-     * create sessions by directly calling SessionManager.</p>
+     * Initialize an instance of the SP context and configuration.
      * 
      * @throws ShibbolethConfigurationException  if bad config file
      */
@@ -207,7 +230,16 @@ public class ShibbolethRunner {
     
     
     /**
-     * A MockRunner context for the AssertionConsumerServlet.
+     * A MockRunner interface object for the AssertionConsumerServlet.
+     * 
+     * <p>The SP itself is a static set of objects initialized 
+     * under the ServiceProviderContext. There can be only one
+     * SP per ClassLoader, so there is no way to test multiple 
+     * SPs at the same time. However, SPs don't interact, so it
+     * doesn't matter.</p>
+     * 
+     * <p>If more than one SPTestContext object is created, they
+     * share the same SPContext objects.
      */
     public class SPTestContext {
 
@@ -223,26 +255,34 @@ public class ShibbolethRunner {
         public MockHttpServletResponse response = factory.getMockResponse();
         public MockHttpServletRequest request = factory.getMockRequest();
         
-        
-        // The Servlet object is created by Mockrunner
         public AssertionConsumerServlet spServlet;
         
         
         /**
          * Construct the related objects
+         * @throws ShibbolethConfigurationException 
          */
-        public SPTestContext() {
+        public SPTestContext() throws ShibbolethConfigurationException {
             
             // ServletContext
             servletContext.setServletContextName("dummy SP Context");
             servletContext.setInitParameter("ServiceProviderConfigFile", spConfigFileName);
             
+            // Create the Servlet object, but do not run its init()
+            // instead use the initializeSP() routine which does 
+            // the same initialize in the test environment
+            spServlet = new AssertionConsumerServlet();
+            testModule.setServlet(spServlet, false);
+            initializeSP();
             
-            // Create instance of Filter class, add to chain, call its init()
-            // NOTE: The SP reads its configuration file and initializes
-            // itself within this call.
-            spServlet = (AssertionConsumerServlet) testModule.createServlet(AssertionConsumerServlet.class);
-            SPinitialized=true;
+        }
+        
+        /**
+         * Set the fields of the request that depend on a suffix,
+         */
+        public void resetRequest(String suffix) {
+            request.resetAll();
+            response.resetAll();
             
             // Unchanging properties of the HttpServletRequest
             request.setRemoteAddr(REMOTE_ADDR);
@@ -252,12 +292,6 @@ public class ShibbolethRunner {
             request.setServerName(SP_SERVER_NAME);
             request.setServerPort(SERVER_PORT);
             
-        }
-        
-        /**
-         * Set the fields of the request that depend on a suffix,
-         */
-        public void setRequestUrls(String suffix) {
             request.setRequestURI(SP_CONTEXT_URL+suffix);
             request.setRequestURL(SP_CONTEXT_URL+suffix);
             request.setServletPath(SP_CONTEXT_PATH+"/"+suffix);
@@ -270,18 +304,16 @@ public class ShibbolethRunner {
     
     
     
-    /*
+    /************************ IdP ******************************
      * Setup the IdP interface object
      * 
-     * The IdP keeps its "context" of cached data and configured 
-     * objects internal rather than exposing it as a public object.
-     * The IdpTestContext object does the initialization and creates
-     * a set of MockRunner object through which the SSO, AA, and 
-     * Artifact requests can be generated.
-     * 
-     * The real IdP objects configure themselves when the Servlet
-     * init() method is called. The Configuration file name coded
-     * here is passed to the Servlet as a simulated context parameter.
+     * The IdP associates its "context" of cached data and configured 
+     * objects with the IdPResponder Servlet object. They are 
+     * initialized when the Servlet init() is called. It is 
+     * possible to create more than one IdpTestContext object
+     * representing different configuration files, or a new
+     * IdpTestContext can be created with fresh Mockrunner object
+     * on top of an existing initialized IdP.
      * 
      * To direct the AA and Artifact queries back to the Idp object,
      * a call to SAML sets up the MockHTTPBindingProvider to replace
@@ -294,11 +326,20 @@ public class ShibbolethRunner {
         this.idpConfigFileName = idpConfigFileName;
     } 
     
+    /**
+     * Although it is possible in theory to have more than one IdP 
+     * running in a TestCase, this one static IdpTestContext
+     * pointer tells the MockHTTPBindingProvider which IdP
+     * to use for AA and Artifact queries. If you have more
+     * that one IdP, the TestCase has to figure out how to swap this
+     * pointer between them.  
+     */
     public static IdpTestContext idp = null;
     
     /**
      * Initializes the IdP if necessary, then returns a 
      * pointer to the MockRunner interface object
+     * 
      * @return IdpTestContext with Mockrunner objects
      */
     public IdpTestContext getIdp() {
@@ -310,8 +351,14 @@ public class ShibbolethRunner {
     
     
     /**
-     * Establish initialized IdP and a set of MockRunner objects to
-     * process SSO, AA, and Artifact requests.
+     * A set of Mockrunner control blocks to call a newly initialized
+     * or previously created IdP. 
+     * 
+     * <p>By default, an IdpTestContext creates a new instance of the
+     * IdP using the current configuration file. However, if an 
+     * already intialized IdPResponder servlet is passed to the
+     * constructor, then new Mockrunner blocks are created but
+     * the existing IdP is reused.</p>
      * 
      * <p>The IdP is initialized when the IdpResponder servlet init() is 
      * called. This establishes the static context of tables that 
@@ -342,31 +389,48 @@ public class ShibbolethRunner {
         
         
         // The IdP Servlet that processes SSO, AA, and Artifact requests
-        // The object is created by Mockrunner
         public IdPResponder idpServlet;
         
         /**
-         * Construct with the default configuration file
+         * Construct new context and new IdP from the configuration file.
          */
         public IdpTestContext() {
-            this(idpConfigFileName);
+            this(null);
         }
         
         /**
-         * Construct using a specified IdP configuration file.
+         * Create a new Mockrunner context. If an previous
+         * IdP was initialized in a prior context, reuse it 
+         * and therefore only refresh the Mockrunner objects.
+         * Otherwise, initialize a new instance of the IdP.
          */
-        public IdpTestContext(String configFileName) {
+        public IdpTestContext(IdPResponder oldidp) {
             
             // ServletContext
             servletContext.setServletContextName("dummy IdP Context");
-            servletContext.setInitParameter("IdPConfigFile", configFileName);
+            servletContext.setInitParameter("IdPConfigFile", idpConfigFileName);
             
-            
-            // Create instance of Filter class, add to chain, call its init()
-            // NOTE: The IdP reads its configuration file and initializes
-            // itself within this call.
-            idpServlet = (IdPResponder) testModule.createServlet(IdPResponder.class);
+            if (oldidp==null) {
+                idpServlet = new IdPResponder();
+                // NOTE: The IdP reads its configuration file and initializes
+                // itself within this call.
+                testModule.setServlet(idpServlet,true);
             resetLoggingLevels();
+            } else {
+                // reuse an existing initialized servlet
+                idpServlet=oldidp;
+            }
+        }
+        
+        
+        /**
+         * Set the fields of the request that depend on a suffix,
+         * normally SSO, AA, or Artifact
+         */
+        public void resetRequest(String suffix) {
+            
+            request.resetAll();
+            response.resetAll();
             
             // Unchanging properties of the HttpServletRequest
             request.setRemoteAddr(REMOTE_ADDR);
@@ -376,13 +440,6 @@ public class ShibbolethRunner {
             request.setServerName(IDP_SERVER_NAME);
             request.setServerPort(SERVER_PORT);
             
-        }
-        
-        /**
-         * Set the fields of the request that depend on a suffix,
-         * normally SSO, AA, or Artifact
-         */
-        public void setRequestUrls(String suffix) {
             request.setRequestURI(IDP_CONTEXT_URL+suffix);
             request.setRequestURL(IDP_CONTEXT_URL+suffix);
             request.setServletPath(IDP_CONTEXT_PATH+"/"+suffix);
@@ -394,7 +451,7 @@ public class ShibbolethRunner {
     
     
     
-    /*
+    /********************** Attribute Source ***********************
      * Here we keep a static reference to a Collection of Attributes. 
      * 
      * The Test can clear the collection and add attributes. When
@@ -418,7 +475,9 @@ public class ShibbolethRunner {
     
     
     
-    /*
+    
+    
+    /*************************** Resource Manage Filter *****************
      * The Filter depends on a Servlet environment simulated by MockRunner.
      * We give it its own set of MockRunner blocks because in real life
      * it runs in a separate context from the SP or IdP.
@@ -436,7 +495,10 @@ public class ShibbolethRunner {
     /**
      * Create the MockRunning interface for running the ServletFilter.
      * 
-     * <p>The SP must be initialized to provide parameters.</p>
+     * <p>The AuthenticationFilter object itself contains no 
+     * meaningful state, so you can create multiple instances
+     * of this interface object to represent more than one
+     * Resource context being managed by the same SP.</p>
      *
      */
     public class AuthenticationFilterContext {
@@ -449,38 +511,23 @@ public class ShibbolethRunner {
         public ServletTestModule testModule = new ServletTestModule(factory);
         
         // Now simulated Servlet API objects
-        public MockServletContext servletContext= new MockServletContext();
+        public MockServletContext servletContext= factory.getMockServletContext();
         public MockFilterConfig filterConfig= factory.getMockFilterConfig();
         public MockHttpServletResponse response = factory.getMockResponse();
         public MockHttpServletRequest request = factory.getMockRequest();
         
-        /*
-         * The Missing Manual: There are three types of init-params in
-         * the web.xml. One applies to the Context as a whole. The other
-         * two are nested inside a <servlet> or <filter> and provide
-         * parameters specific to that particular object. If you do
-         * a factory.getMockServletContext() you get an object that corresponds
-         * to the web.xml configuration itself. However, rather than adding
-         * init-param collections to the MockServletConfig and MockFilterConfig,
-         * Mockrunner seems to chain a user-created MockServletContext object
-         * to them and use its init-params as the parameters fed back to the
-         * Filter or Servlet object. So when you see "new MockServletContext()"
-         * there is a pretty good reason to expect this will not be used as a
-         * real ServletContext but rather as a secondary control block to a 
-         * MockFilterConfig or MockServletConfig.
-         */
-        
         // Filter objects
         private AuthenticationFilter filter;
         
        public AuthenticationFilterContext() {
             
-            // ServletContext (argument to Filters and Servlets)
+            // Dummy web.xml for Resouce context
             servletContext.setServletContextName("dummy Servlet Context");
-            servletContext.setInitParameter("requireId", ".+/test.+");
             
-            // The FilterConfig (argument to Filter init)
-            filterConfig.setupServletContext(servletContext);
+            // Dummy <Filter> in dummy web.xml
+            MockServletContext filterParameters = new MockServletContext();
+            filterParameters.setInitParameter("requireId", ".+/test.+");
+            filterConfig.setupServletContext(filterParameters);
             filterConfig.setFilterName("Test Filter under JUnit");
        }
        
@@ -493,21 +540,24 @@ public class ShibbolethRunner {
        public void setUp() throws ShibbolethConfigurationException {
             
             // Create instance of Filter class, add to chain, call its init()
-            filter = (AuthenticationFilter) testModule.createFilter(AuthenticationFilter.class);
+            filter = new AuthenticationFilter();
+            testModule.addFilter(filter,true);
             
             // Note: if the SP is already initialized, this noops.
             initializeSP();
             
 
+        }
+        
+        public void resetRequest(String suffix) {
+            
             request.setRemoteAddr(REMOTE_ADDR);
             request.setContextPath(RESOURCE_CONTEXT_PATH);
             request.setProtocol(PROTOCOL);
             request.setScheme(SCHEME);
             request.setServerName(SP_SERVER_NAME);
             request.setServerPort(RESOURCE_SERVER_PORT);
-        }
-        
-        public void setRequestUrls(String suffix) {
+
             request.setMethod("GET");
             request.setRequestURI(RESOURCE_CONTEXT_URL+suffix);
             request.setRequestURL(RESOURCE_CONTEXT_URL+suffix);