2 * The Shibboleth License, Version 1.
4 * University Corporation for Advanced Internet Development, Inc.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
11 * Redistributions of source code must retain the above copyright notice, this
12 * list of conditions and the following disclaimer.
14 * Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution, if any, must include
17 * the following acknowledgment: "This product includes software developed by
18 * the University Corporation for Advanced Internet Development
19 * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement
20 * may appear in the software itself, if and wherever such third-party
21 * acknowledgments normally appear.
23 * Neither the name of Shibboleth nor the names of its contributors, nor
24 * Internet2, nor the University Corporation for Advanced Internet Development,
25 * Inc., nor UCAID may be used to endorse or promote products derived from this
26 * software without specific prior written permission. For written permission,
27 * please contact shibboleth@shibboleth.org
29 * Products derived from this software may not be called Shibboleth, Internet2,
30 * UCAID, or the University Corporation for Advanced Internet Development, nor
31 * may Shibboleth appear in their name, without prior written permission of the
32 * University Corporation for Advanced Internet Development.
35 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
36 * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
38 * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK
39 * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE.
40 * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY
41 * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT,
42 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
43 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
44 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
47 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
50 package edu.internet2.middleware.shibboleth.aa.arp;
52 import java.io.FileInputStream;
53 import java.io.InputStream;
54 import java.net.MalformedURLException;
56 import java.net.URISyntaxException;
58 import java.security.Principal;
59 import java.util.Arrays;
60 import java.util.HashSet;
61 import java.util.Properties;
63 import junit.framework.TestCase;
65 import org.apache.log4j.BasicConfigurator;
66 import org.apache.xerces.parsers.DOMParser;
67 import org.xml.sax.InputSource;
70 * Validation suite for <code>Arp</code> processing.
72 * @ author Walter Hoehn(wassa@columbia.edu)
75 public class ArpTests extends TestCase {
77 public ArpTests(String name) {
79 BasicConfigurator.resetConfiguration();
80 BasicConfigurator.configure();
83 public static void main(String[] args) {
84 junit.textui.TestRunner.run(ArpTests.class);
85 BasicConfigurator.configure();
90 public void testArpMarshalling() {
92 //Test ARP description
94 InputStream inStream = new FileInputStream("test/arp1.xml");
95 DOMParser parser = new DOMParser();
96 parser.parse(new InputSource(inStream));
98 arp1.marshall(parser.getDocument().getDocumentElement());
100 "ARP Description not marshalled properly",
101 arp1.getDescription(),
102 "Simplest possible ARP.");
104 //Test Rule description
106 "ARP Rule Description not marshalled properly",
107 arp1.getAllRules()[0].getDescription(),
108 "Example Rule Description.");
109 } catch (Exception e) {
110 fail("Failed to marshall ARP: " + e);
113 //Test case where ARP description does not exist
115 InputStream inStream = new FileInputStream("test/arp2.xml");
116 DOMParser parser = new DOMParser();
117 parser.parse(new InputSource(inStream));
118 Arp arp2 = new Arp();
119 arp2.marshall(parser.getDocument().getDocumentElement());
120 assertNull("ARP Description not marshalled properly", arp2.getDescription());
122 //Test case where ARP Rule description does not exist
124 "ARP Rule Description not marshalled properly",
125 arp2.getAllRules()[0].getDescription());
126 } catch (Exception e) {
127 fail("Failed to marshall ARP.");
132 public void testMatchingFunctions() {
137 * Test Arp Engine function retrieval
140 //Lookup a function that doesn't exist
141 MatchFunction noFunction =
142 ArpEngine.lookupMatchFunction(new URI("urn:mace:shibboleth:arp:matchFunction:dummy"));
143 assertNull("ArpEngine did not return null on dummy function.", noFunction);
145 //Lookup some real functions
146 MatchFunction exactSharFunction =
147 ArpEngine.lookupMatchFunction(new URI("urn:mace:shibboleth:arp:matchFunction:exactShar"));
148 assertNotNull("ArpEngine did not properly load the Exact SHAR function.", exactSharFunction);
149 MatchFunction resourceTreeFunction =
150 ArpEngine.lookupMatchFunction(new URI("urn:mace:shibboleth:arp:matchFunction:resourceTree"));
152 "ArpEngine did not properly load the Resource Tree SHAR function.",
153 resourceTreeFunction);
154 MatchFunction regexFunction =
155 ArpEngine.lookupMatchFunction(new URI("urn:mace:shibboleth:arp:matchFunction:regexMatch"));
156 assertNotNull("ArpEngine did not properly load the Regex function.", regexFunction);
159 * Test the Exact SHAR function (requester)
163 "Exact SHAR function: false negative",
164 exactSharFunction.match("shar.example.edu", "shar.example.edu"));
166 "Exact SHAR function: false negative",
167 !exactSharFunction.match("shar.example.edu", "www.example.edu"));
169 "Exact SHAR function: false negative",
170 !exactSharFunction.match("example.edu", "shar.example.edu"));
172 //Make sure we properly handle bad input
174 exactSharFunction.match(null, null);
175 fail("Exact SHAR function seems to take improper input without throwing an exception.");
176 } catch (ArpException ie) {
177 //This is supposed to fail
181 * Test the Resource Tree function (resource)
184 URL requestURL1 = new URL("http://www.example.edu/test/");
185 URL requestURL2 = new URL("http://www.example.edu/test/index.html");
186 URL requestURL3 = new URL("http://www.example.edu/test2/index.html");
187 URL requestURL4 = new URL("http://www.example.edu/test2/index.html?test1=test1");
190 "Resource Tree function: false negative",
191 resourceTreeFunction.match("http://www.example.edu/", requestURL1));
193 "Resource Tree function: false positive",
194 !resourceTreeFunction.match("https://www.example.edu/", requestURL1));
196 "Resource Tree function: false negative",
197 resourceTreeFunction.match("http://www.example.edu:80/", requestURL1));
199 "Resource Tree function: false positive",
200 !resourceTreeFunction.match("http://www.example.edu:81/", requestURL1));
202 "Resource Tree function: false negative",
203 resourceTreeFunction.match("http://www.example.edu/test/", requestURL1));
205 "Resource Tree function: false negative",
206 resourceTreeFunction.match("http://www.example.edu/test/", requestURL2));
208 "Resource Tree function: false negative",
209 resourceTreeFunction.match("http://www.example.edu/", requestURL3));
211 "Resource Tree function: false positive",
212 !resourceTreeFunction.match("http://www.example.edu/test/", requestURL3));
214 "Resource Tree function: false negative",
215 resourceTreeFunction.match("http://www.example.edu/test2/index.html", requestURL3));
217 "Resource Tree function: false negative",
218 resourceTreeFunction.match("http://www.example.edu/test2/index.html", requestURL4));
220 "Resource Tree function: false negative",
221 resourceTreeFunction.match(
222 "http://www.example.edu/test2/index.html?test1=test1",
225 "Resource Tree function: false positive",
226 !resourceTreeFunction.match(
227 "http://www.example.edu/test2/index.html?test1=test1",
230 //Make sure we properly handle bad input
232 resourceTreeFunction.match(null, null);
233 fail("Resource Tree function seems to take improper input without throwing an exception.");
234 } catch (ArpException ie) {
235 //This is supposed to fail
238 resourceTreeFunction.match("Test", "Test");
239 fail("Resource Tree function seems to take improper input without throwing an exception.");
240 } catch (ArpException ie) {
241 //This is supposed to fail
245 * Test the Regex function (requester & resource)
248 //Try requester regexes
250 "Regex function: false negative",
251 regexFunction.match("^shar\\.example\\.edu$", "shar.example.edu"));
253 "Regex function: false negative",
254 regexFunction.match("^.*\\.example\\.edu$", "shar.example.edu"));
256 "Regex function: false negative",
257 regexFunction.match("^shar[1-9]?\\.example\\.edu$", "shar1.example.edu"));
258 assertTrue("Regex function: false negative", regexFunction.match(".*\\.edu", "shar.example.edu"));
260 "Regex function: false positive",
261 !regexFunction.match("^shar[1-9]\\.example\\.edu$", "shar.example.edu"));
263 "Regex function: false positive",
264 !regexFunction.match("^shar\\.example\\.edu$", "www.example.edu"));
266 "Regex function: false positive",
267 !regexFunction.match("^shar\\.example\\.edu$", "www.example.com"));
269 //Try resource regexes
271 "Regex function: false negative",
272 regexFunction.match("^http://www\\.example\\.edu/.*$", requestURL1));
274 "Regex function: false negative",
275 regexFunction.match("^http://www\\.example\\.edu/.*$", requestURL2));
277 "Regex function: false negative",
278 regexFunction.match("^http://.*\\.example\\.edu/.*$", requestURL2));
280 "Regex function: false negative",
281 regexFunction.match("^https?://.*\\.example\\.edu/.*$", requestURL2));
282 assertTrue("Regex function: false negative", regexFunction.match(".*", requestURL2));
284 "Regex function: false positive",
285 !regexFunction.match("^https?://.*\\.example\\.edu/$", requestURL2));
287 "Regex function: false positive",
288 !regexFunction.match("^https?://www\\.example\\.edu/test/$", requestURL3));
290 //Make sure we properly handle bad input
292 regexFunction.match(null, null);
293 fail("Regex function seems to take improper input without throwing an exception.");
294 } catch (ArpException ie) {
295 //This is supposed to fail
298 } catch (ArpException e) {
299 fail("Encountered a problem loading match function: " + e);
300 } catch (URISyntaxException e) {
301 fail("Unable to create URI from test string.");
302 } catch (MalformedURLException e) {
303 fail("Couldn't create test URLs: " + e);
308 public void testRepositories() {
314 //Make sure we fail if no Repository is specified
315 Properties props = new Properties();
317 ArpRepositoryFactory.getInstance(props);
318 } catch (ArpRepositoryException e) {
319 //This is supposed to fail
322 // Make sure we can create an Arp Repository
324 "edu.internet2.middleware.shibboleth.aa.arp.ArpRepository.implementation",
325 "edu.internet2.middleware.shibboleth.aa.arp.provider.MemoryArpRepository");
326 ArpRepository repository = null;
328 repository = ArpRepositoryFactory.getInstance(props);
329 } catch (ArpRepositoryException e) {
330 fail("Failed to create memory-based Arp Repository" + e);
332 assertNotNull("Failed to create memory-based Arp Repository: Factory returned null.", repository);
335 * Exercise the Memory Arp Repository
338 //Set/retrieve/remove a Site ARP
339 Arp siteArp1 = new Arp();
340 siteArp1.setDescription("Test Site Arp 1.");
342 repository.update(siteArp1);
344 "Memory Repository does not store and retrieve Site ARPs properly.",
346 repository.getSitePolicy());
347 repository.remove(repository.getSitePolicy());
348 assertNull("Memorty Repository does not properly delete Site ARPs.", repository.getSitePolicy());
349 } catch (ArpRepositoryException e) {
350 fail("Error adding Site ARP to Memory Repository.");
353 //Set/retrieve/delete some user ARPs
354 Arp userArp1 = new Arp();
355 userArp1.setDescription("Broken User Arp 1.");
357 repository.update(userArp1);
359 "Memory Repository does not store and retrieve User ARPs properly.",
360 (!userArp1.equals(repository.getUserPolicy(userArp1.getPrincipal()))));
361 } catch (ArpRepositoryException e) {
362 fail("Error adding User ARP to Memory Repository.");
365 Arp userArp2 = new Arp(new AAPrincipal("TestPrincipal"));
366 userArp2.setDescription("Test User Arp 2.");
368 repository.update(userArp2);
370 "Memory Repository does not store and retrieve User ARPs properly.",
372 repository.getUserPolicy(userArp2.getPrincipal()));
373 repository.remove(repository.getUserPolicy(userArp2.getPrincipal()));
375 "Memorty Repository does not properly delete User ARPs.",
376 repository.getUserPolicy(userArp2.getPrincipal()));
377 } catch (ArpRepositoryException e) {
378 fail("Error adding User ARP to Memory Repository.");
383 public void testPossibleReleaseSetComputation() {
384 Properties props = new Properties();
386 "edu.internet2.middleware.shibboleth.aa.arp.ArpRepository.implementation",
387 "edu.internet2.middleware.shibboleth.aa.arp.provider.MemoryArpRepository");
388 ArpRepository repository = null;
390 repository = ArpRepositoryFactory.getInstance(props);
391 } catch (ArpRepositoryException e) {
392 fail("Failed to create memory-based Arp Repository" + e);
396 Principal principal1 = new AAPrincipal("TestPrincipal");
397 URL url1 = new URL("http://www.example.edu/");
398 URI[] list1 = { new URI("urn:mace:eduPerson:1.0:eduPersonAffiliation")};
401 new URI("urn:mace:eduPerson:1.0:eduPersonAffiliation"),
402 new URI("urn:mace:eduPerson:1.0:eduPersonPrincipalName")};
403 URI[] list3 = new URI[0];
405 //Test with just a site ARP
406 InputStream inStream = new FileInputStream("test/arp1.xml");
407 DOMParser parser = new DOMParser();
408 parser.parse(new InputSource(inStream));
409 Arp arp1 = new Arp();
410 arp1.marshall(parser.getDocument().getDocumentElement());
411 repository.update(arp1);
412 ArpEngine engine = new ArpEngine(repository, props);
413 URI[] possibleAttributes =
414 engine.listPossibleReleaseAttributes(principal1, "shar.example.edu", url1);
415 assertTrue("Incorrectly computed possible release set.", Arrays.equals(possibleAttributes, list1));
417 //Test with site and user ARPs
418 inStream = new FileInputStream("test/arp7.xml");
419 parser.parse(new InputSource(inStream));
420 Arp arp7 = new Arp();
421 arp7.setPrincipal(principal1);
422 arp7.marshall(parser.getDocument().getDocumentElement());
423 repository.update(arp7);
424 possibleAttributes = engine.listPossibleReleaseAttributes(principal1, "shar.example.edu", url1);
425 assertTrue("Incorrectly computed possible release set.", Arrays.equals(possibleAttributes, list2));
427 //Ensure that explicit denies on any value are not in the release set
428 inStream = new FileInputStream("test/arp6.xml");
429 parser.parse(new InputSource(inStream));
430 Arp arp6 = new Arp();
431 arp6.setPrincipal(principal1);
432 arp6.marshall(parser.getDocument().getDocumentElement());
433 repository.update(arp6);
434 possibleAttributes = engine.listPossibleReleaseAttributes(principal1, "shar.example.edu", url1);
435 assertTrue("Incorrectly computed possible release set.", Arrays.equals(possibleAttributes, list3));
437 } catch (Exception e) {
439 fail("Failed to marshall ARP: " + e);
444 public void testArpApplication() {
445 Properties props = new Properties();
447 "edu.internet2.middleware.shibboleth.aa.arp.ArpRepository.implementation",
448 "edu.internet2.middleware.shibboleth.aa.arp.provider.MemoryArpRepository");
449 ArpRepository repository = null;
451 repository = ArpRepositoryFactory.getInstance(props);
452 } catch (ArpRepositoryException e) {
453 fail("Failed to create memory-based Arp Repository" + e);
456 Principal principal1 = new AAPrincipal("TestPrincipal");
457 URL url1 = new URL("http://www.example.edu/");
459 //Test with just a site ARP
460 InputStream inStream = new FileInputStream("test/arp1.xml");
461 DOMParser parser = new DOMParser();
462 parser.parse(new InputSource(inStream));
463 Arp arp1 = new Arp();
464 arp1.marshall(parser.getDocument().getDocumentElement());
465 repository.update(arp1);
466 ArpEngine engine = new ArpEngine(repository, props);
468 TestAttribute testAttribute1 =
470 "urn:mace:eduPerson:1.0:eduPersonAffiliation",
471 new Object[] { "member@example.edu", "faculty@example.edu" });
472 TestAttribute testAttribute2 =
474 "urn:mace:eduPerson:1.0:eduPersonPrincipalName",
475 new Object[] { "mehoehn@example.edu" });
476 ArpAttribute[] releaseAttributes =
477 engine.filterAttributes(
478 new ArpAttribute[] { testAttribute1, testAttribute2 },
483 "ARP not applied as expected.",
484 new HashSet(Arrays.asList(releaseAttributes)),
485 new HashSet(Arrays.asList(new ArpAttribute[] { testAttribute1 })));
487 //Test with site and user ARPs
488 inStream = new FileInputStream("test/arp7.xml");
489 parser.parse(new InputSource(inStream));
490 Arp arp7 = new Arp();
491 arp7.setPrincipal(principal1);
492 arp7.marshall(parser.getDocument().getDocumentElement());
493 repository.update(arp7);
495 engine.filterAttributes(
496 new ArpAttribute[] { testAttribute1, testAttribute2 },
501 "ARP not applied as expected.",
502 new HashSet(Arrays.asList(releaseAttributes)),
503 new HashSet(Arrays.asList(new ArpAttribute[] { testAttribute1, testAttribute2 })));
505 //Test with site and user ARPs
507 engine.filterAttributes(
508 new ArpAttribute[] { testAttribute1, testAttribute2 },
513 "ARP not applied as expected.",
514 new HashSet(Arrays.asList(releaseAttributes)),
515 new HashSet(Arrays.asList(new ArpAttribute[] { testAttribute1 })));
517 //Test with site and user ARPs
518 inStream = new FileInputStream("test/arp6.xml");
519 parser.parse(new InputSource(inStream));
520 Arp arp6 = new Arp();
521 arp6.setPrincipal(principal1);
522 arp6.marshall(parser.getDocument().getDocumentElement());
523 repository.update(arp6);
525 engine.filterAttributes(
526 new ArpAttribute[] { testAttribute1, testAttribute2 },
531 "ARP not applied as expected.",
532 new HashSet(Arrays.asList(releaseAttributes)),
535 } catch (Exception e) {
537 fail("Failed to apply ARPs: " + e);
541 public class TestAttribute implements ArpAttribute {
543 private Object[] values;
545 public TestAttribute(String name, Object[] values) {
547 this.values = values;
551 * @see edu.internet2.middleware.shibboleth.aa.arp.ArpAttribute#getName()
553 public String getName() {
558 * @see edu.internet2.middleware.shibboleth.aa.arp.ArpAttribute#getValues()
560 public Object[] getValues() {
565 * @see edu.internet2.middleware.shibboleth.aa.arp.ArpAttribute#setValues(Object[])
567 public void setValues(Object[] values) {
568 this.values = values;
571 public boolean equals(Object object) {
572 if (!(object instanceof TestAttribute)) {
576 return (new HashSet(Arrays.asList(values))).equals(
577 Arrays.asList(((TestAttribute) object).getValues()));