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