2 * Copyright [2005] [University Corporation for Advanced Internet Development, Inc.]
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package edu.internet2.middleware.shibboleth.integration;
19 import java.io.UnsupportedEncodingException;
20 import java.net.URLDecoder;
21 import java.util.Enumeration;
24 import javax.naming.directory.Attributes;
25 import javax.naming.directory.BasicAttribute;
26 import javax.servlet.http.HttpServletRequest;
28 import junit.framework.TestCase;
30 import org.apache.commons.codec.binary.Base64;
31 import org.apache.log4j.Level;
32 import org.opensaml.SAMLException;
34 import com.mockrunner.mock.web.MockHttpServletResponse;
36 import edu.internet2.middleware.shibboleth.idp.provider.ShibbolethV1SSOHandler;
37 import edu.internet2.middleware.shibboleth.resource.AuthenticationFilter;
38 import edu.internet2.middleware.shibboleth.resource.FilterUtil;
39 import edu.internet2.middleware.shibboleth.resource.FilterSupport.NewSessionData;
40 import edu.internet2.middleware.shibboleth.runner.ShibbolethRunner;
41 import edu.internet2.middleware.shibboleth.serviceprovider.AssertionConsumerServlet;
42 import edu.internet2.middleware.shibboleth.serviceprovider.ServiceProviderConfig;
43 import edu.internet2.middleware.shibboleth.serviceprovider.ServiceProviderContext;
44 import edu.internet2.middleware.shibboleth.serviceprovider.Session;
47 * A JUnit test case that exercises the IdP, SP, and Filter
48 * @author Howard Gilbert
50 public class IntegrationTest extends TestCase {
52 // Create some constants, both as parameters and to test responses
53 private static final String GIVENNAME = "Bozo";
54 public static final String SURNAME = "Clown";
55 private static final String TITLE = "clown";
56 public static final String AFFILIATION = "member";
57 public static final String SP_ENTITY = "https://sp.example.org/shibboleth";
58 public static final String POST_SHIRE = "https://sp.example.org/shibboleth-sp/Shibboleth.sso/SAML/POST";
59 public static final String ARTIFACT_SHIRE = "https://sp.example.org/shibboleth-sp/Shibboleth.sso/SAML/Artifact";
60 public static final String TARGET = "https://nonsense";
61 public static final String NETID = "BozoTClown";
62 public static final String APPLICATIONID = "default";
64 ShibbolethRunner runner;
65 ShibbolethRunner.IdpTestContext idp;
66 ShibbolethRunner.SPTestContext consumer;
67 ShibbolethRunner.AuthenticationFilterContext filter;
68 private NewSessionData newSessionData = new NewSessionData();
69 ServiceProviderContext context;
70 ServiceProviderConfig config;
76 protected void setUp() throws Exception {
79 // Static call to set Log4J appenders and levels
80 ShibbolethRunner.loglevel = Level.DEBUG;
81 ShibbolethRunner.setupLogging();
83 // Create the overall testing framework
84 runner = new ShibbolethRunner();
86 // Initialize the Idp, create the Mockrunner
87 // objects to do SSO, AA, and Artifact calls, and
88 // configure SAML to use the MockHTTPBindingProvider
89 runner.setIdpConfigFileName("/basicIdpHome/idpconfig.xml"); // default value
90 idp = runner.getIdp();
92 // Initialize the SP with the default config file.
93 runner.setSpConfigFileName("/basicSpHome/spconfig.xml"); // default value
95 // Use one of two forms to initialize the SP
96 // If only calling AssertionConsumerServlet.createSessionFromData directly
97 //runner.initializeSP();
98 // If calling AssertionConsumerServlet through MockRunner
99 consumer = ShibbolethRunner.consumer = runner.new SPTestContext();
101 context=ServiceProviderContext.getInstance();
102 config = context.getServiceProviderConfig();
104 // Initialize the Filter and create its separate
105 // Mockrunner simulated context.
106 filter= runner.getFilter();
107 // Note: If you are going to change the Filter init-param
108 // values, do it here before calling setUp()
111 newSessionData.applicationId=APPLICATIONID;
112 newSessionData.providerId=SP_ENTITY;
115 // Create the static collection of Attributes that are
116 // returned by the IdP for every principal.
117 // This could be done in each test, just as long as it
118 // is done before the SSO.
119 Attributes attributes = runner.getAttributesCollection();
120 attributes.put(new BasicAttribute("eduPersonAffiliation", AFFILIATION));
122 attributes.put(new BasicAttribute("eduPersonScopedAffiliation", AFFILIATION));
123 attributes.put(new BasicAttribute("title", TITLE));
124 attributes.put(new BasicAttribute("givenName", GIVENNAME));
125 attributes.put(new BasicAttribute("surname", SURNAME));
127 attributes.put(new BasicAttribute("unacceptable","nonsense"));
129 attributes.put(new BasicAttribute("unreleasable","foolishness"));
133 * Test the Post Profile, Attribute Push
134 * <p>Run SSO, call AssertionConsumerServlet directly, then Run Filter</p>
136 public void testAttributePush() throws SAMLException {
138 // Set the URL suffix that triggers SSO processing
139 idp.setRequestUrls("SSO");
141 // Add the WAYF/RM parameters
142 idp.testModule.addRequestParameter("target", TARGET);
143 idp.testModule.addRequestParameter("shire",POST_SHIRE);
144 idp.testModule.addRequestParameter("providerId", SP_ENTITY);
146 // Add a userid, as if provided by Basic Authentication or a Filter
147 idp.request.setRemoteUser(NETID);
149 // Force Attribute Push
150 ShibbolethV1SSOHandler.pushAttributeDefault=true;
153 idp.testModule.doGet();
156 * Sanity check: The IdP normally ends by transferring control to a
157 * JSP page that generates the FORM. However, we have not set up
158 * Mockrunner to perform the transfer, because the form would just
159 * create parsing work. Rather, the following code extracts the
160 * information from the request attributes that the JSP would have
161 * used as its source.
163 String bin64assertion = (String) idp.request.getAttribute("assertion");
164 String assertion = new String(Base64.decodeBase64(bin64assertion.getBytes()));
165 String handlerURL = (String) idp.request.getAttribute("shire");
166 String targetURL = (String) idp.request.getAttribute("target");
168 // Create the session directly without MockRunner
169 FilterUtil.sessionDataFromRequest(newSessionData,idp.request);
170 // there was no real redirect, so the next two fields are not
171 // in the places that sessionDataFromRequest expects.
172 newSessionData.SAMLResponse = bin64assertion;
173 newSessionData.target=targetURL;
174 newSessionData.handlerURL=handlerURL;
176 // Create the session, extract pushed Attributes
177 String sessionId = AssertionConsumerServlet.createSessionFromData(newSessionData);
179 // Now get what was created in case you want to test it.
180 Session session = context.getSessionManager().findSession(sessionId, APPLICATIONID);
181 checkSession(session);
183 // Pass the SessionId to the Filter, let it fetch the attributes
184 filter.testModule.addRequestParameter(AuthenticationFilter.SESSIONPARM, sessionId);
185 filter.setRequestUrls("test.txt");
186 filter.testModule.doFilter();
189 * Sanity Check: doFilter runs just the Filter itself. On
190 * input there was a Request and Response. When done, there
191 * will be a replacement Request object created by the Filter
192 * wrapping the original request and adding features.
199 * Verify correct operation of Filter and wrapped Request object,
200 * including attributes and headers.
202 private void checkFilter() {
203 // Get the Request Wrapper object created by the Filter
204 HttpServletRequest filteredRequest =
205 (HttpServletRequest) filter.testModule.getFilteredRequest();
207 assertEquals(NETID,filteredRequest.getRemoteUser());
208 assertEquals(NETID,filteredRequest.getHeader("REMOTE_USER"));
209 assertEquals(SURNAME,filteredRequest.getHeader("Shib-Person-surname"));
210 assertEquals(GIVENNAME,filteredRequest.getHeader("Shib-InetOrgPerson-givenName"));
211 assertEquals(TITLE,filteredRequest.getHeader("Shib-OrgPerson-title"));
213 Map attributes = (Map) filteredRequest.getAttribute(AuthenticationFilter.SHIB_ATTRIBUTES_PREFIX);
216 Enumeration headerNames = filteredRequest.getHeaderNames();
217 while (headerNames.hasMoreElements()) {
218 String name = (String) headerNames.nextElement();
219 String value = (String) filteredRequest.getHeader(name);
220 System.out.println(name+ "-"+value );
225 * Add Session object checking here.
227 private void checkSession(Session session) {
228 assertNotNull(session);
229 assertEquals(APPLICATIONID,session.getApplicationId());
236 * Test the Post Profile with Attribute Query
237 * <p>Run SSO, Run AssertionConsumerServlet, then Run Filter</p>
239 public void testAttributeQuery() throws SAMLException {
241 // Set the URL suffix that triggers SSO processing
242 idp.setRequestUrls("SSO");
244 // Add the WAYF/RM parameters
245 idp.testModule.addRequestParameter("target", TARGET);
246 idp.testModule.addRequestParameter("shire",POST_SHIRE);
247 idp.testModule.addRequestParameter("providerId", SP_ENTITY);
249 // Add a userid, as if provided by Basic Authentication or a Filter
250 idp.request.setRemoteUser(NETID);
252 // Block Attribute Push
253 ShibbolethV1SSOHandler.pushAttributeDefault=false;
256 idp.testModule.doGet();
258 String bin64assertion = (String) idp.request.getAttribute("assertion");
259 String assertion = new String(Base64.decodeBase64(bin64assertion.getBytes()));
260 String handlerURL = (String) idp.request.getAttribute("shire");
261 String targetURL = (String) idp.request.getAttribute("target");
263 // Simulate the POST to the SP Context using MockRunner
264 consumer.testModule.addRequestParameter("SAMLResponse",bin64assertion);
265 consumer.testModule.addRequestParameter("TARGET",targetURL);
266 consumer.setRequestUrls("Shibboleth.sso/SAML/POST");
267 consumer.testModule.doPost();
269 // Now check up on what the AssertionConsumerServlet did with the POST
270 MockHttpServletResponse response = consumer.response;
271 assertTrue(response.wasRedirectSent());
272 String redirectURL = response.getHeader("Location");
274 // The SessionId is on the end of the redirected URL
275 int pos = redirectURL.indexOf(AssertionConsumerServlet.SESSIONPARM);
277 String sessionId = redirectURL.substring(
278 pos+AssertionConsumerServlet.SESSIONPARM.length()+1);
280 // Now get what was created in case you want to test it.
281 Session session = context.getSessionManager().findSession(sessionId, APPLICATIONID);
282 checkSession(session);
284 // Pass the SessionId to the Filter, let it fetch the attributes
285 filter.testModule.addRequestParameter(AuthenticationFilter.SESSIONPARM, sessionId);
286 filter.setRequestUrls("test.txt"); // need any URL
287 filter.testModule.doFilter();
295 * <p>Run SSO, call AssertionConsumerServlet directly, then Run Filter</p>
297 public void testArtifact() throws SAMLException, UnsupportedEncodingException {
299 // Set the URL suffix that triggers SSO processing
300 idp.setRequestUrls("SSO");
302 // Add the WAYF/RM parameters
303 idp.testModule.addRequestParameter("target", TARGET);
304 idp.testModule.addRequestParameter("shire",ARTIFACT_SHIRE);
305 idp.testModule.addRequestParameter("providerId", SP_ENTITY);
307 // Add a userid, as if provided by Basic Authentication or a Filter
308 idp.request.setRemoteUser(NETID);
310 // Attribute Push is implied by Artifact
311 ShibbolethV1SSOHandler.pushAttributeDefault=false;
314 idp.testModule.doGet();
316 // Now check the response from the IdP
317 MockHttpServletResponse response = idp.response;
318 assertTrue(response.wasRedirectSent());
319 String redirectURL = response.getHeader("Location");
321 // The artifacts were appended to the end of the Redirect URL
322 String[] splits = redirectURL.split("\\&SAMLart=");
323 assertTrue(splits.length>1);
324 String[] artifactArray = new String[splits.length-1];
325 for (int i=0;i<artifactArray.length;i++) {
326 artifactArray[i]=URLDecoder.decode(splits[i+1],"UTF-8");
329 // Build the parameter for Session creation
330 FilterUtil.sessionDataFromRequest(newSessionData,idp.request);
331 newSessionData.SAMLArt=artifactArray;
332 newSessionData.target=TARGET;
333 newSessionData.handlerURL=ARTIFACT_SHIRE;
335 // Create the Session
336 // Under the covers, SAML will see the Artifact and fetch the Assertion
337 String sessionId = AssertionConsumerServlet.createSessionFromData(newSessionData);
340 // Now get what was created in case you want to test it.
341 Session session = context.getSessionManager().findSession(sessionId, APPLICATIONID);
342 checkSession(session);
345 // Pass the SessionId to the Filter, let it fetch the attributes
346 filter.testModule.addRequestParameter(AuthenticationFilter.SESSIONPARM, sessionId);
347 filter.setRequestUrls("test.txt"); // need any URL
348 filter.testModule.doFilter();