--- /dev/null
+/*
+ * 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 );
+ }
+ }
+
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
+
--- /dev/null
+/*
+ * 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 {
+ }
+ }
+}
+
--- /dev/null
+/*
+ * 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);
+
+ }
+ }
+
+}