New structure to JUnit integration test to simplify Test files
authorgilbert <gilbert@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Wed, 2 Nov 2005 16:00:51 +0000 (16:00 +0000)
committergilbert <gilbert@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Wed, 2 Nov 2005 16:00:51 +0000 (16:00 +0000)
git-svn-id: https://subversion.switch.ch/svn/shibboleth/java-idp/trunk@1891 ab3bd59b-922f-494d-bb5f-6f0a3c29deca

tests/edu/internet2/middleware/shibboleth/integration/IntegrationTest.java [new file with mode: 0644]
tests/edu/internet2/middleware/shibboleth/runner/AttributeSourceForTests.java [new file with mode: 0644]
tests/edu/internet2/middleware/shibboleth/runner/MockHTTPBindingProvider.java [new file with mode: 0644]
tests/edu/internet2/middleware/shibboleth/runner/ShibbolethRunner.java [new file with mode: 0644]

diff --git a/tests/edu/internet2/middleware/shibboleth/integration/IntegrationTest.java b/tests/edu/internet2/middleware/shibboleth/integration/IntegrationTest.java
new file mode 100644 (file)
index 0000000..28e498b
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * 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.Enumeration;
+
+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.SAMLException;
+
+import edu.internet2.middleware.shibboleth.idp.provider.ShibbolethV1SSOHandler;
+import edu.internet2.middleware.shibboleth.resource.FilterUtil;
+import edu.internet2.middleware.shibboleth.resource.FilterSupport.NewSessionData;
+import edu.internet2.middleware.shibboleth.runner.ShibbolethRunner;
+import edu.internet2.middleware.shibboleth.serviceprovider.AssertionConsumerServlet;
+import edu.internet2.middleware.shibboleth.serviceprovider.ServiceProviderContext;
+import edu.internet2.middleware.shibboleth.serviceprovider.Session;
+import edu.internet2.middleware.shibboleth.serviceprovider.SessionManager;
+
+/**
+ * A JUnit test case that exercises the IdP, SP, and Filter
+ * @author Howard Gilbert
+ */
+public class IntegrationTest extends TestCase {
+    
+    ShibbolethRunner runner;
+    ShibbolethRunner.IdpTestContext idp;
+    ShibbolethRunner.AuthenticationFilterContext filter;
+    
+    
+    
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        // Static call to set Log4J appenders and levels
+        ShibbolethRunner.loglevel = Level.INFO;
+        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");
+        runner.initializeSP();
+        
+        // Initialize the Filter and create its separate
+        // Mockrunner simulated context. 
+        filter= runner.getFilter();
+        
+        // Make changes to Filter init-param values before setUp.
+        filter.setUp();
+        
+        // Create attributes to be returned from the IdP
+        // 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", "member"));
+        attributes.put(new BasicAttribute("title", "clown"));
+        attributes.put(new BasicAttribute("givenName", "bozo"));
+        attributes.put(new BasicAttribute("surname", "Clown"));
+    }
+    
+    
+    public void testAttributePush() throws SAMLException {
+        
+        // Set the URL suffix that triggers SSO processing
+        idp.setRequestUrls("SSO");
+        
+        // Add the WAYF/RM parameters
+        idp.testModule.addRequestParameter("target", "https://nonsense");
+        idp.testModule.addRequestParameter("shire","https://sp.example.org/Shibboleth.sso/SAML/POST");
+        idp.testModule.addRequestParameter("providerId", "https://sp.example.org/shibboleth");
+        
+        // Add a userid, as if provided by Basic Authentication or a Filter
+        idp.request.setRemoteUser("BozoTClown");
+        
+        // Force Attribute Push
+        ShibbolethV1SSOHandler.pushAttributeDefault=true;
+        
+        // Call the IdP 
+        idp.testModule.doGet();
+        
+        /*
+         * Sanity check: The IdP normally ends by transferring control to a
+         * JSP page that generates the FORM. However, we have not set up
+         * Mockrunner to perform the transfer, because the form would just
+         * create parsing work. Rather, the following code extracts the
+         * information from the request attributes that the JSP would have
+         * used as its source.
+         */
+        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");
+        
+        
+        // Build the parameter for Session creation
+        NewSessionData data = new NewSessionData();
+        FilterUtil.sessionDataFromRequest(data,idp.request);
+        data.applicationId="default";
+        data.handlerURL=handlerURL;
+        data.providerId="https://sp.example.org/shibboleth";
+        
+        // Create the session, extract pushed Attributes 
+        String sessionId = AssertionConsumerServlet.createSessionFromData(data);
+        
+        // Now get what was created in case you want to test it.
+        ServiceProviderContext context   = ServiceProviderContext.getInstance();
+        Session session = context.getSessionManager().findSession(sessionId, "default");
+        
+        // Pass the SessionId to the Filter, let it fetch the attributes
+        filter.testModule.addRequestParameter("ShibbolethSessionId", sessionId);
+        filter.setRequestUrls("test.txt");
+        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.
+         */
+        
+        // Get the Request Wrapper object created by the Filter
+        HttpServletRequest filteredRequest = 
+            (HttpServletRequest) filter.testModule.getFilteredRequest();
+        
+        // Now do something that uses Filter supplied logic
+        Enumeration headerNames = filteredRequest.getHeaderNames();
+        while (headerNames.hasMoreElements()) {
+            String name = (String) headerNames.nextElement();
+            String value = (String) filteredRequest.getHeader(name);
+            System.out.println(name+ "-"+value );
+        }
+        
+        
+    }
+    
+    public void testAttributeQuery() throws SAMLException {
+        
+        // Set the URL suffix that triggers SSO processing
+        idp.setRequestUrls("SSO");
+        
+        // Add the WAYF/RM parameters
+        idp.testModule.addRequestParameter("target", "https://nonsense");
+        idp.testModule.addRequestParameter("shire","https://sp.example.org/Shibboleth.sso/SAML/POST");
+        idp.testModule.addRequestParameter("providerId", "https://sp.example.org/shibboleth");
+        
+        // Add a userid, as if provided by Basic Authentication or a Filter
+        idp.request.setRemoteUser("BozoTClown");
+        
+        // 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");
+        
+        
+        // Build the parameter for Session creation
+        NewSessionData data = new NewSessionData();
+        FilterUtil.sessionDataFromRequest(data,idp.request);
+        data.applicationId="default";
+        data.handlerURL=handlerURL;
+        data.providerId="https://sp.example.org/shibboleth";
+        
+        // Create the Session
+        // Internally an AA Query will fetch the attributes through the 
+        // MockHTTPBindingProvider
+        String sessionId = AssertionConsumerServlet.createSessionFromData(data);
+        
+        
+        // Now get what was created in case you want to test it.
+        ServiceProviderContext context   = ServiceProviderContext.getInstance();
+        Session session = context.getSessionManager().findSession(sessionId, "default");
+        StringBuffer buffer = SessionManager.dumpAttributes(session);
+        System.out.println(buffer.toString());
+        
+        // Pass the SessionId to the Filter, let it fetch the attributes
+        filter.testModule.addRequestParameter("ShibbolethSessionId", sessionId);
+        filter.setRequestUrls("test.txt"); // need any URL
+        filter.testModule.doFilter();
+        
+        // Get the Request Wrapper object created by the Filter
+        HttpServletRequest filteredRequest = (HttpServletRequest) filter.testModule.getFilteredRequest();
+        
+        // Now do something that uses Filter supplied logic
+        Enumeration headerNames = filteredRequest.getHeaderNames();
+        while (headerNames.hasMoreElements()) {
+            String name = (String) headerNames.nextElement();
+            String value = (String) filteredRequest.getHeader(name);
+            System.out.println(name+ "-"+value );
+        }
+    }
+    
+    
+}
diff --git a/tests/edu/internet2/middleware/shibboleth/runner/AttributeSourceForTests.java b/tests/edu/internet2/middleware/shibboleth/runner/AttributeSourceForTests.java
new file mode 100644 (file)
index 0000000..9d57250
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * External class so it can be configured as a plugin in XML.
+ * Look for:
+ *  <CustomDataConnector id="jutest" class="edu.internet2.middleware.shibboleth.runner.AttributeSourceForTests"/>
+ * in resolver.xml
+ */
+
+/*
+ * 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.security.Principal;
+
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
+
+import org.w3c.dom.Element;
+
+import edu.internet2.middleware.shibboleth.aa.attrresolv.DataConnectorPlugIn;
+import edu.internet2.middleware.shibboleth.aa.attrresolv.Dependencies;
+import edu.internet2.middleware.shibboleth.aa.attrresolv.ResolutionPlugInException;
+import edu.internet2.middleware.shibboleth.aa.attrresolv.provider.BaseResolutionPlugIn;
+
+/**
+ * An in-memory Attribute source for JUnit tests. This class exposes
+ * a static collection to which a parent test case can add attributes.
+ * When the IdP requests attributes for any principal, the static 
+ * collection is returned and is then processed into SAML. This allows
+ * a Test Case to create attributes that pass or fail the ARP and AAP
+ * without a complex LDAP, JDBC, or file to produce them
+ * 
+ */
+public class AttributeSourceForTests 
+    extends BaseResolutionPlugIn 
+    implements DataConnectorPlugIn {
+
+    /**
+     * The test case adds Attributes to this collection, or can
+     * clear it and refill it.
+     */
+
+    public AttributeSourceForTests(Element e) throws ResolutionPlugInException {
+        super(e);
+    }
+
+    /*
+     * When called to return attributes for a particular principal,
+     * return the static collection plus one for the Principal name.
+      */
+    public Attributes resolve(Principal principal, String requester, String responder, Dependencies depends) {
+        ShibbolethRunner.attributes.put(new BasicAttribute("eduPersonPrincipalName", principal.getName()));
+        return ShibbolethRunner.attributes;
+    }
+
+    public String getFailoverDependencyId() {
+        return null;
+    }
+}
+
diff --git a/tests/edu/internet2/middleware/shibboleth/runner/MockHTTPBindingProvider.java b/tests/edu/internet2/middleware/shibboleth/runner/MockHTTPBindingProvider.java
new file mode 100644 (file)
index 0000000..fa95894
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * External class so it can be configured by String name to SAML.
+ * 
+ * Look for:
+ *     samlConfig = SAMLConfig.instance();
+ *     samlConfig.setDefaultBindingProvider(SAMLBinding.SOAP,"edu.internet2.middleware.shibboleth.runner.MockHTTPBindingProvider" );
+ * in ShibbolethRunner constructor.
+ */
+
+/*
+ * 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.BufferedReader;
+import java.io.StringReader;
+import java.net.MalformedURLException;
+
+import org.apache.xml.security.c14n.CanonicalizationException;
+import org.apache.xml.security.c14n.Canonicalizer;
+import org.apache.xml.security.c14n.InvalidCanonicalizerException;
+import org.opensaml.BindingException;
+import org.opensaml.SAMLBinding;
+import org.opensaml.SAMLConfig;
+import org.opensaml.SAMLException;
+import org.opensaml.SAMLRequest;
+import org.opensaml.SAMLResponse;
+import org.opensaml.XML;
+import org.opensaml.provider.SOAPHTTPBindingProvider;
+import org.w3c.dom.Element;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import edu.internet2.middleware.shibboleth.runner.ShibbolethRunner.IdpTestContext;
+
+
+/**
+ *  This is a replacement for SOAPHTTPBindingProvider in OpenSAML. While that
+ *  module builds a URL and URLConnection to send a request to a Web Server
+ *  hosting the IdP, this code generates a direct call to the AA or Artifact
+ *  Resolver through the IdP Servlet.
+ *  
+ *  <p>The ShibbolethRunner constructor sets this class name as the SAML 
+ *  default BindingProvider.</p>
+ */
+public class MockHTTPBindingProvider 
+    extends SOAPHTTPBindingProvider {
+    
+    
+    /** OpenSAML will construct this object. */
+    public MockHTTPBindingProvider(String binding, Element e) throws SAMLException {
+        super(binding, e);
+    }
+
+    /**
+     * Based on the Http version of this code, this method replaces the URL and
+     * URLConnection with operations on the Mock HttpRequest.
+     */
+    public SAMLResponse send(String endpoint, SAMLRequest request, Object callCtx)
+        throws SAMLException
+    {
+        try {
+            Element envelope = sendRequest(request, callCtx);
+            
+            IdpTestContext idp = ShibbolethRunner.idp;
+            
+            /*
+             * Prepare the Idp Mockrunner blocks for the Query
+             */
+            idp.request.setLocalPort(8443);
+            idp.request.setRequestURI(endpoint);
+            idp.request.setRequestURL(endpoint);
+            if (endpoint.endsWith("/AA")) {
+                idp.request.setServletPath("/shibboleth.idp/AA");
+            } else {
+                idp.request.setServletPath("/shibboleth.idp/Artifact");
+            }
+
+            idp.request.setContentType("text/xml; charset=UTF-8");
+            idp.request.setHeader("SOAPAction","http://www.oasis-open.org/committees/security");
+        
+             
+            
+            Canonicalizer c = Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_OMIT_COMMENTS);
+            byte[] bs = c.canonicalizeSubtree(envelope);
+            idp.request.setBodyContent(bs);
+
+            idp.testModule.doPost();
+            
+            String content_type=idp.response.getContentType();
+            
+            if (content_type == null || !content_type.startsWith("text/xml")) {
+                String outputStreamContent = idp.response.getOutputStreamContent();
+                StringReader outputreader = new StringReader(outputStreamContent);
+                BufferedReader reader=new BufferedReader(outputreader);
+                throw new BindingException(
+                    "MockHTTPBindingProvider.send() detected an invalid content type ("
+                        + (content_type!=null ? content_type : "none")
+                        + ") in the response.");
+            }
+            
+            envelope=XML.parserPool.parse(
+                    new InputSource(new StringReader(idp.response.getOutputStreamContent())),
+                    (request.getMinorVersion()>0) ? XML.parserPool.getSchemaSAML11() : XML.parserPool.getSchemaSAML10()
+                    ).getDocumentElement();
+            
+            SAMLResponse ret = recvResponse(envelope, callCtx);
+           
+            if (!ret.getInResponseTo().equals(request.getId())) {
+                throw new BindingException("MockHTTPBindingProvider.send() unable to match SAML InResponseTo value to request");
+            }
+            return ret;
+        }
+        catch (MalformedURLException ex) {
+            throw new SAMLException("SAMLSOAPBinding.send() detected a malformed URL in the binding provided", ex);
+        }
+        catch (SAXException ex) {
+            throw new SAMLException("SAMLSOAPBinding.send() caught an XML exception while parsing the response", ex);
+        }
+        catch (InvalidCanonicalizerException ex) {
+            throw new SAMLException("SAMLSOAPBinding.send() caught a C14N exception while serializing the request", ex);
+        }
+        catch (CanonicalizationException ex) {
+            throw new SAMLException("SAMLSOAPBinding.send() caught a C14N exception while serializing the request", ex);
+        }
+        catch (java.io.IOException ex) {
+            throw new SAMLException("SAMLSOAPBinding.send() caught an I/O exception", ex);
+        }
+        finally {
+        }
+    }
+}
+
diff --git a/tests/edu/internet2/middleware/shibboleth/runner/ShibbolethRunner.java b/tests/edu/internet2/middleware/shibboleth/runner/ShibbolethRunner.java
new file mode 100644 (file)
index 0000000..503839c
--- /dev/null
@@ -0,0 +1,429 @@
+/*
+ * 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 javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttributes;
+
+import org.apache.log4j.ConsoleAppender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.PatternLayout;
+import org.opensaml.SAMLBinding;
+import org.opensaml.SAMLConfig;
+
+import com.mockrunner.mock.web.MockFilterConfig;
+import com.mockrunner.mock.web.MockHttpServletRequest;
+import com.mockrunner.mock.web.MockHttpServletResponse;
+import com.mockrunner.mock.web.MockServletContext;
+import com.mockrunner.mock.web.WebMockObjectFactory;
+import com.mockrunner.servlet.ServletTestModule;
+
+import edu.internet2.middleware.shibboleth.common.ShibbolethConfigurationException;
+import edu.internet2.middleware.shibboleth.idp.IdPResponder;
+import edu.internet2.middleware.shibboleth.resource.AuthenticationFilter;
+import edu.internet2.middleware.shibboleth.resource.FilterSupport;
+import edu.internet2.middleware.shibboleth.resource.FilterSupport.RMAppInfo;
+import edu.internet2.middleware.shibboleth.serviceprovider.FilterSupportImpl;
+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.
+ * 
+ * <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>Look at *.integration.IntegrationTest for an example of use.</p>
+ * 
+ * @author Howard Gilbert.
+ */
+public class ShibbolethRunner {
+    
+    private static SAMLConfig samlConfig; 
+    
+    /**
+     * 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" );
+    }
+    
+    
+    /*
+     * Logging
+     * 
+     * For automated test cases that normally just work, you 
+     * probably want to leave the logging level to ERROR. However,
+     * if you are running a custom test case to discover the source
+     * of a problem, or when building a new test case, then you 
+     * may want to set the logging level to DEBUG.
+     * 
+     * You can change the loglevel variable from the test case
+     * code before calling setupLogging(). 
+     */
+    public static Level loglevel = Level.INFO;
+    
+    private static Logger clientLogger = Logger.getLogger("edu.internet2.middleware");
+    private static Logger initLogger = Logger.getLogger("shibboleth.init");
+    private static Logger samlLogger = Logger.getLogger("org.opensaml");
+    private static boolean manageLogs = false;
+    
+    /**
+     * You will almost always call setupLogging first, but it
+     * it not automatic in case you have exotic logging 
+     * requirements.
+     * 
+     * <p>Restriction: avoid any static initialization that generates
+     * log messages because this method can only be called after 
+     * static initialation.</p>
+     */
+    public static void setupLogging() {
+        manageLogs = true;
+        Logger root = Logger.getRootLogger();
+        Layout initLayout = new PatternLayout("%d{HH:mm} %-5p %m%n");
+        ConsoleAppender consoleAppender= new ConsoleAppender(initLayout,ConsoleAppender.SYSTEM_OUT);
+        root.addAppender(consoleAppender);
+        root.setLevel(Level.ERROR);
+        clientLogger.setLevel(loglevel);
+        initLogger.setLevel(loglevel);
+        samlLogger.setLevel(loglevel);
+    }
+    
+    /**
+     * Sometimes (as in IdP initialization) the logging levels
+     * get reset to some unintended level. This resets them
+     * to whatever we want for testing.
+     */
+    public static void resetLoggingLevels() {
+        if (!manageLogs) return;  // If setupLogging was never called.
+        clientLogger.setLevel(loglevel);
+        initLogger.setLevel(loglevel);
+        samlLogger.setLevel(loglevel);
+        
+    }
+    
+    
+    
+    /*
+     * 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.
+     * 
+     * @param spConfigFileName
+     */
+    public void setSpConfigFileName(String spConfigFileName) {
+        this.spConfigFileName = spConfigFileName;
+    }
+    
+    private static boolean SPinitialized = false; // don't do it twice
+    
+    /**
+     * Load an SP configuration file.
+     * @throws ShibbolethConfigurationException  if bad config file
+     */
+    public void initializeSP() 
+        throws ShibbolethConfigurationException{
+        if (SPinitialized) return;
+        SPinitialized=true;
+        
+        ServiceProviderContext context = ServiceProviderContext.getInstance();
+        context.initialize();
+        
+        ServiceProviderConfig config = new ServiceProviderConfig();
+        context.setServiceProviderConfig(config);
+        config.loadConfigObjects(spConfigFileName);
+    }
+    
+
+    
+    /*
+     * 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.
+     * 
+     * To direct the AA and Artifact queries back to the Idp object,
+     * a call to SAML sets up the MockHTTPBindingProvider to replace
+     * the normal HTTPBindingProvider. Thus instead of creating URL
+     * and sockets to talk to the IdP, a simulated Request object is
+     * configured and the IdP is called through MockRunner.
+     */
+    public String idpConfigFileName = "/basicIdpHome/idpconfig.xml";
+    public void setIdpConfigFileName(String idpConfigFileName) {
+        this.idpConfigFileName = idpConfigFileName;
+    } 
+    
+    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() {
+        if (idp==null) {
+            idp = new IdpTestContext();
+        }
+        return idp;
+    }
+    
+    
+    /**
+     * Establish initialized IdP and a set of MockRunner objects to
+     * process SSO, AA, and Artifact requests.
+     * 
+     * <p>The IdP is initialized when the IdpResponder servlet init() is 
+     * called. This establishes the static context of tables that 
+     * allow the IdP to issue a Subject and then respond when that
+     * Subject is returned in an Attribute Query.</p>
+     * 
+     * <p>This class creates the Mockrunner control blocks needed to 
+     * call the IdP and, by creating the IdP Servlet object, also 
+     * initializes an instance of the IdP. It depends on a configuration
+     * file located as a resource in the classpath, typically in the 
+     * /testresources directory of the project.</p>
+     */
+    public class IdpTestContext {
+        
+        
+        // The Factory creates the Request, Response, Session, etc.
+        public WebMockObjectFactory factory = new WebMockObjectFactory();
+        
+        // The TestModule runs the Servlet and Filter methods in the simulated container
+        public ServletTestModule testModule = new ServletTestModule(factory);
+        
+        // Now simulated Servlet API objects
+        public MockServletContext servletContext= factory.getMockServletContext();
+        public MockFilterConfig filterConfig= factory.getMockFilterConfig();
+        public MockHttpServletResponse response = factory.getMockResponse();
+        public MockHttpServletRequest request = factory.getMockRequest();
+        
+        
+        // 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
+         */
+        public IdpTestContext() {
+            this(idpConfigFileName);
+        }
+        
+        /**
+         * Construct using a specified IdP configuration file.
+         */
+        public IdpTestContext(String configFileName) {
+            
+            // ServletContext
+            servletContext.setServletContextName("dummy IdP Context");
+            servletContext.setInitParameter("IdPConfigFile", configFileName);
+            
+            
+            // 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);
+            
+            // Unchanging properties of the HttpServletRequest
+            request.setRemoteAddr("127.0.0.1");
+            request.setContextPath("/shibboleth-idp");
+            request.setProtocol("HTTP/1.1");
+            request.setScheme("https");
+            request.setServerName("idp.example.org");
+            request.setServerPort(443);
+            
+        }
+        
+        /**
+         * Set the fields of the request that depend on a suffix,
+         * normally SSO, AA, or Artifact
+         */
+        public void setRequestUrls(String suffix) {
+            request.setRequestURI("https://idp.example.org/shibboleth-idp/"+suffix);
+            request.setRequestURL("https://idp.example.org/shibboleth-idp/"+suffix);
+            request.setServletPath("/shibboleth.idp/"+suffix);
+            
+        }
+        
+    }
+
+    
+    
+    
+    
+    /*
+     * Here we keep a static reference to a Collection of Attributes. 
+     * 
+     * The Test can clear the collection and add attributes. When
+     * the IdP needs attributes, it treats this collection as the 
+     * starting point and processes them through ARP. When then get
+     * to the SP they go through AAP. So you can test the Attribute
+     * processing logic in both components by creating Attributes 
+     * with names and values that are accepted or rejected.
+     */
+    
+    public static BasicAttributes attributes = new BasicAttributes();
+    
+    /**
+     * The Test should obtain a reference to the Attribute collection and add
+     * such attributes as it wants the IdP to return for a Principal.
+     * @return Attributes collection
+     */
+    public Attributes getAttributesCollection() {
+        return attributes;
+    }
+    
+    
+    
+    /*
+     * 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.
+     * 
+     * The Filter depends on the SP and, once initialized, has a reference
+     * to FilterSupportImpl and through it the SP configuration and Sessions.
+     */
+    private AuthenticationFilterContext filter;
+    public AuthenticationFilterContext getFilter() throws ShibbolethConfigurationException {
+        if (filter==null)
+            filter=new AuthenticationFilterContext();
+        return filter;
+    }
+    
+    /**
+     * Create the MockRunning interface for running the ServletFilter.
+     * 
+     * <p>The SP must be initialized to provide parameters.</p>
+     *
+     */
+    public class AuthenticationFilterContext {
+        
+        // The Factory creates the Request, Response, Session, etc.
+        public WebMockObjectFactory factory = new WebMockObjectFactory();
+        
+        // The TestModule runs the Servlet and Filter methods in the simulated container
+        public ServletTestModule testModule = new ServletTestModule(factory);
+        
+        // Now simulated Servlet API objects
+        public MockServletContext servletContext= new MockServletContext();
+        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;
+        
+        // SP configuration objects
+        private FilterSupport service;
+        private RMAppInfo rmAppInfo;
+
+       public AuthenticationFilterContext() {
+            
+            // ServletContext (argument to Filters and Servlets)
+            servletContext.setServletContextName("dummy Servlet Context");
+            servletContext.setInitParameter("requireId", ".+/test.+");
+            
+            // The FilterConfig (argument to Filter init)
+            filterConfig.setupServletContext(servletContext);
+            filterConfig.setFilterName("Test Filter under JUnit");
+       }
+       
+       /**
+        * Call after any changes to Context init-param values to
+        * initialize the filter object and connect to the SP.
+        * 
+        * @throws ShibbolethConfigurationException from SP init.
+        */
+       public void setUp() throws ShibbolethConfigurationException {
+            
+            // Create instance of Filter class, add to chain, call its init()
+            filter = (AuthenticationFilter) testModule.createFilter(AuthenticationFilter.class);
+            
+            // Note: if the SP is already initialized, this noops.
+            initializeSP();
+            
+            // Plug an instance of FilterSupportImpl into the Filter
+            service = new FilterSupportImpl();
+            AuthenticationFilter.setFilterSupport(service);
+
+            // Get our own copy of SP Config info for Assert statements
+            rmAppInfo = service.getRMAppInfo("default");
+
+            request.setRemoteAddr("127.0.0.1");
+            request.setContextPath("/secure");
+            request.setProtocol("HTTP/1.1");
+            request.setScheme("https");
+            request.setServerName("sp.example.org");
+            request.setServerPort(9443);
+        }
+        
+        public void setRequestUrls(String suffix) {
+            request.setMethod("GET");
+            request.setRequestURI("http://sp.example.org:9443/secure/"+suffix);
+            request.setRequestURL("http://sp.example.org:9443/secure/"+suffix);
+            request.setServletPath("/secure/"+suffix);
+            
+        }
+    }
+    
+}