From ea2d2c8af69a1056724580ef9edb527164cbbe85 Mon Sep 17 00:00:00 2001 From: gilbert Date: Wed, 2 Nov 2005 16:00:51 +0000 Subject: [PATCH] New structure to JUnit integration test to simplify Test files git-svn-id: https://subversion.switch.ch/svn/shibboleth/java-idp/trunk@1891 ab3bd59b-922f-494d-bb5f-6f0a3c29deca --- .../shibboleth/integration/IntegrationTest.java | 226 +++++++++++ .../shibboleth/runner/AttributeSourceForTests.java | 73 ++++ .../shibboleth/runner/MockHTTPBindingProvider.java | 146 +++++++ .../shibboleth/runner/ShibbolethRunner.java | 429 ++++++++++++++++++++ 4 files changed, 874 insertions(+) create mode 100644 tests/edu/internet2/middleware/shibboleth/integration/IntegrationTest.java create mode 100644 tests/edu/internet2/middleware/shibboleth/runner/AttributeSourceForTests.java create mode 100644 tests/edu/internet2/middleware/shibboleth/runner/MockHTTPBindingProvider.java create mode 100644 tests/edu/internet2/middleware/shibboleth/runner/ShibbolethRunner.java diff --git a/tests/edu/internet2/middleware/shibboleth/integration/IntegrationTest.java b/tests/edu/internet2/middleware/shibboleth/integration/IntegrationTest.java new file mode 100644 index 0000000..28e498b --- /dev/null +++ b/tests/edu/internet2/middleware/shibboleth/integration/IntegrationTest.java @@ -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 index 0000000..9d57250 --- /dev/null +++ b/tests/edu/internet2/middleware/shibboleth/runner/AttributeSourceForTests.java @@ -0,0 +1,73 @@ +/* + * External class so it can be configured as a plugin in XML. + * Look for: + * + * 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 index 0000000..fa95894 --- /dev/null +++ b/tests/edu/internet2/middleware/shibboleth/runner/MockHTTPBindingProvider.java @@ -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. + * + *

The ShibbolethRunner constructor sets this class name as the SAML + * default BindingProvider.

+ */ +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 index 0000000..503839c --- /dev/null +++ b/tests/edu/internet2/middleware/shibboleth/runner/ShibbolethRunner.java @@ -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. + * + *

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.

+ * + *

Look at *.integration.IntegrationTest for an example of use.

+ * + * @author Howard Gilbert. + */ +public class ShibbolethRunner { + + private static SAMLConfig samlConfig; + + /** + * Initialization logic goes here. + *

Reqires that Log4J already be configured.

+ */ + 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. + * + *

Restriction: avoid any static initialization that generates + * log messages because this method can only be called after + * static initialation.

+ */ + 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. + * + *

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.

+ * + *

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.

+ */ + 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. + * + *

The SP must be initialized to provide parameters.

+ * + */ + 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 or 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); + + } + } + +} -- 1.7.10.4