More jUnit tests for Attribute Resolver.
authorwassa <wassa@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Wed, 16 Apr 2003 20:54:07 +0000 (20:54 +0000)
committerwassa <wassa@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Wed, 16 Apr 2003 20:54:07 +0000 (20:54 +0000)
Resolver now does MUCH better sanity checking of the configuration on startup.

git-svn-id: https://subversion.switch.ch/svn/shibboleth/java-idp/trunk@570 ab3bd59b-922f-494d-bb5f-6f0a3c29deca

data/resolver3.xml [new file with mode: 0644]
data/resolver4.xml [new file with mode: 0644]
data/resolver5.xml [new file with mode: 0644]
data/resolver6.xml [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/aa/attrresolv/AttributeResolver.java
tests/edu/internet2/middleware/shibboleth/aa/attrresolv/ResolverTests.java

diff --git a/data/resolver3.xml b/data/resolver3.xml
new file mode 100644 (file)
index 0000000..171e455
--- /dev/null
@@ -0,0 +1 @@
+<AttributeResolver xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:mace:shibboleth:resolver:1.0" xsi:schemaLocation="urn:mace:shibboleth:resolver:1.0 shibboleth-resolver-1.0.xsd" />
\ No newline at end of file
diff --git a/data/resolver4.xml b/data/resolver4.xml
new file mode 100644 (file)
index 0000000..5ab9b97
--- /dev/null
@@ -0,0 +1,12 @@
+<AttributeResolver xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:mace:shibboleth:resolver:1.0" xsi:schemaLocation="urn:mace:shibboleth:resolver:1.0 shibboleth-resolver-1.0.xsd">
+       
+       <SimpleAttributeDefinition id="foo">
+               <DataConnectorDependency requires="directory"/>
+       </SimpleAttributeDefinition>
+       
+       <JNDIDirectoryDataConnector id="directory">
+               <Search filter="cn=%PRINCIPAL%" />
+               <Property name="java.naming.factory.initial" value="com.sun.jndi.ldap.LdapCtxFactory" />
+               <Property name="java.naming.provider.url" value="ldap://nonexistent/dc=test,dc=com" />
+       </JNDIDirectoryDataConnector>
+</AttributeResolver>
\ No newline at end of file
diff --git a/data/resolver5.xml b/data/resolver5.xml
new file mode 100644 (file)
index 0000000..1831490
--- /dev/null
@@ -0,0 +1,11 @@
+<AttributeResolver xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:mace:shibboleth:resolver:1.0" xsi:schemaLocation="urn:mace:shibboleth:resolver:1.0 shibboleth-resolver-1.0.xsd">
+       
+       <SimpleAttributeDefinition id="foo">
+               <AttributeDependency requires="bar"/>
+       </SimpleAttributeDefinition>
+       
+       <SimpleAttributeDefinition id="bar">
+               <AttributeDependency requires="foo"/>
+       </SimpleAttributeDefinition>
+       
+</AttributeResolver>
\ No newline at end of file
diff --git a/data/resolver6.xml b/data/resolver6.xml
new file mode 100644 (file)
index 0000000..a56c9da
--- /dev/null
@@ -0,0 +1,15 @@
+<AttributeResolver xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:mace:shibboleth:resolver:1.0" xsi:schemaLocation="urn:mace:shibboleth:resolver:1.0 shibboleth-resolver-1.0.xsd">
+       
+       <SimpleAttributeDefinition id="foo">
+               <AttributeDependency requires="bar"/>
+       </SimpleAttributeDefinition>
+       
+       <SimpleAttributeDefinition id="bar">
+               <AttributeDependency requires="shoe"/>
+       </SimpleAttributeDefinition>
+       
+       <SimpleAttributeDefinition id="shoe">
+               <AttributeDependency requires="foo"/>
+       </SimpleAttributeDefinition>
+       
+</AttributeResolver>
\ No newline at end of file
index ac3f4cc..29935f7 100644 (file)
@@ -119,8 +119,7 @@ public class AttributeResolver {
                                                InputStream stream;
                                                try {
                                                        return new InputSource(
-                                                               new ShibResource("/schemas/shibboleth-resolver-1.0.xsd",
-                                                                       this.getClass())
+                                                               new ShibResource("/schemas/shibboleth-resolver-1.0.xsd", this.getClass())
                                                                        .getInputStream());
                                                } catch (IOException e) {
                                                        throw new SAXException("Could not load entity: " + e);
@@ -191,36 +190,20 @@ public class AttributeResolver {
                log.info("Configuration complete.");
        }
 
-       private void verifyPlugIns() {
-               //TODO Maybe this should detect loops in the directed graph
-               //TODO this has to do better verification, if plugins are unloaded after dependants have been checked, we get null pointers
-
+       private void verifyPlugIns() throws AttributeResolverException {
+               
                log.info("Verifying PlugIn graph consitency.");
                Set inconsistent = new HashSet();
                Iterator registered = plugIns.keySet().iterator();
 
                while (registered.hasNext()) {
                        ResolutionPlugIn plugIn = lookupPlugIn((String) registered.next());
+                       log.debug("Checking PlugIn (" + plugIn.getId() + ") for consistency.");
                        if (plugIn instanceof AttributeDefinitionPlugIn) {
-                               log.debug("Checking PlugIn (" + plugIn.getId() + ") for consistency.");
-                               List depends = new ArrayList();
-                               depends.addAll(
-                                       Arrays.asList(((AttributeDefinitionPlugIn) plugIn).getAttributeDefinitionDependencyIds()));
-                               depends.addAll(Arrays.asList(((AttributeDefinitionPlugIn) plugIn).getDataConnectorDependencyIds()));
-                               Iterator dependsIt = depends.iterator();
-                               while (dependsIt.hasNext()) {
-                                       String key = (String) dependsIt.next();
-                                       if (!plugIns.containsKey(key)) {
-                                               log.error(
-                                                       "The PlugIn ("
-                                                               + plugIn.getId()
-                                                               + ") is inconsistent.  It depends on a PlugIn (" + key + ") that is not registered.");
-                                               inconsistent.add(plugIn.getId());
-                                       }
-                               }
+                               verifyPlugIn((AttributeDefinitionPlugIn) plugIn, new HashSet(), inconsistent);
                        }
                }
-
+               
                if (!inconsistent.isEmpty()) {
                        log.info("Unloading inconsistent PlugIns.");
                        Iterator inconsistentIt = inconsistent.iterator();
@@ -228,6 +211,91 @@ public class AttributeResolver {
                                plugIns.remove(inconsistentIt.next());
                        }
                }
+               
+               if (plugIns.size() < 1) {
+                       log.error("Failed to load any PlugIn definitions.");
+                       throw new AttributeResolverException("Cannot load Attribute Resolver.");
+               }
+
+       }
+
+       private void verifyPlugIn(AttributeDefinitionPlugIn plugIn, Set verifyChain, Set inconsistent) {
+
+               //Short-circuit if we have already found this PlugIn to be inconsistent
+               if (inconsistent.contains(plugIn.getId())) {
+                       return;
+               }
+
+               //Make sure that we don't have a circular dependency
+               if (verifyChain.contains(plugIn.getId())) {
+                       log.error(
+                               "The PlugIn (" + plugIn.getId() + ") is inconsistent.  It is involved in a circular dependency chain.");
+                       inconsistent.add(plugIn.getId());
+                       return;
+               }
+
+               //Make sure all dependent Data Connectors are registered
+               List depends = new ArrayList();
+               depends.addAll(Arrays.asList(plugIn.getDataConnectorDependencyIds()));
+               Iterator dependsIt = depends.iterator();
+               while (dependsIt.hasNext()) {
+                       String key = (String) dependsIt.next();
+                       if (!plugIns.containsKey(key)) {
+                               log.error(
+                                       "The PlugIn ("
+                                               + plugIn.getId()
+                                               + ") is inconsistent.  It depends on a PlugIn ("
+                                               + key
+                                               + ") that is not registered.");
+                               inconsistent.add(plugIn.getId());
+                               return;
+                       }
+               }
+
+               //Recursively go through all AttributeDefinition dependencies and make sure all are registered and consistent.
+               depends.clear();
+               depends.addAll(Arrays.asList(plugIn.getAttributeDefinitionDependencyIds()));
+               dependsIt = depends.iterator();
+               while (dependsIt.hasNext()) {
+                       String key = (String) dependsIt.next();
+
+                       if (!plugIns.containsKey(key)) {
+                               log.error(
+                                       "The PlugIn ("
+                                               + plugIn.getId()
+                                               + ") is inconsistent.  It depends on a PlugIn ("
+                                               + key
+                                               + ") that is not registered.");
+                               inconsistent.add(plugIn.getId());
+                               return;
+                       }
+
+                       ResolutionPlugIn dependent = lookupPlugIn(key);
+                       if (!(dependent instanceof AttributeDefinitionPlugIn)) {
+                               log.error(
+                                       "The PlugIn ("
+                                               + plugIn.getId()
+                                               + ") is inconsistent.  It depends on a PlugIn ("
+                                               + key
+                                               + ") that is mislabeled as an AttributeDefinitionPlugIn.");
+                               inconsistent.add(plugIn.getId());
+                               return;
+                       }
+                       
+                       verifyChain.add(plugIn.getId());
+                       verifyPlugIn((AttributeDefinitionPlugIn) dependent, verifyChain, inconsistent);
+
+                       if (inconsistent.contains(key)) {
+                               log.error(
+                                       "The PlugIn ("
+                                               + plugIn.getId()
+                                               + ") is inconsistent.  It depends on a PlugIn ("
+                                               + key
+                                               + ") that is not inconsistent.");
+                               inconsistent.add(plugIn.getId());
+                               return;
+                       }
+               }
        }
 
        private void registerPlugIn(ResolutionPlugIn connector, String id) throws DuplicatePlugInException {
@@ -405,7 +473,7 @@ public class AttributeResolver {
                        super(message);
                }
        }
-       
+
        class DependentOnlyResolutionAttribute implements ResolverAttribute {
                String name;
                ArrayList values = new ArrayList();
index 1401e72..25564ad 100644 (file)
@@ -152,9 +152,13 @@ public class ResolverTests extends TestCase {
                                //Attribute should have scope appended to connector output
                                new AAAttribute(
                                        "urn:mace:eduPerson:1.0:eduPersonPrincipalName",
-                                       new Object[] { "mytestuser@example.edu" }, new ScopedStringValueHandler("example.edu")),
+                                       new Object[] { "mytestuser@example.edu" },
+                                       new ScopedStringValueHandler("example.edu")),
                                //Attribute should retain scope from connector output
-                               new AAAttribute("foo", new Object[] { "bar@example.com" }, new ScopedStringValueHandler("example.edu"))
+                               new AAAttribute(
+                                       "foo",
+                                       new Object[] { "bar@example.com" },
+                                       new ScopedStringValueHandler("example.edu"))
                                });
 
                        ar.resolveAttributes(new PrincipalImpl("mytestuser"), "shar.example.edu", inputAttributes);
@@ -168,5 +172,77 @@ public class ResolverTests extends TestCase {
                        fail("Error creating SAML Attribute: " + e.getMessage());
                }
        }
+       
+       public void testExceptionForNoPlugIns() {
+
+               try {
+                       Properties props = new Properties();
+                       File file = new File("data/resolver3.xml");
+                       props.setProperty(
+                               "edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeResolver.ResolverConfig",
+                               file.toURL().toString());
+
+                       AttributeResolver ar = new AttributeResolver(props);
+                       fail("Attribute Resolver loaded even when no PlugIns were configured.");
+               } catch (AttributeResolverException e) {
+                       //This exception should be thrown, ignoring
+               } catch (MalformedURLException e) {
+                       fail("Error in test specification: " + e.getMessage());
+               }
+       }
+               
+       public void testExceptionForNoValidPlugIns() {
+
+               try {
+                       Properties props = new Properties();
+                       File file = new File("data/resolver4.xml");
+                       props.setProperty(
+                               "edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeResolver.ResolverConfig",
+                               file.toURL().toString());
+
+                       AttributeResolver ar = new AttributeResolver(props);
+                       fail("Attribute Resolver loaded even when no PlugIns were successfully registered.");
+               } catch (AttributeResolverException e) {
+                       //This exception should be thrown, ignoring
+               } catch (MalformedURLException e) {
+                       fail("Error in test specification: " + e.getMessage());
+               }
+       }
+               
+       public void testFailToLoadCircularDependencies() {
+
+               try {
+                       Properties props = new Properties();
+                       File file = new File("data/resolver5.xml");
+                       props.setProperty(
+                               "edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeResolver.ResolverConfig",
+                               file.toURL().toString());
+
+                       AttributeResolver ar = new AttributeResolver(props);
+                       fail("Attribute Resolver loaded even when no only PlugIns with circular dependencies were configured.");
+               } catch (AttributeResolverException e) {
+                       //This exception should be thrown, ignoring
+               } catch (MalformedURLException e) {
+                       fail("Error in test specification: " + e.getMessage());
+               }
+       }
+       
+       public void testFailToLoadCircularDependenciesDeeper() {
+
+                       try {
+                               Properties props = new Properties();
+                               File file = new File("data/resolver6.xml");
+                               props.setProperty(
+                                       "edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeResolver.ResolverConfig",
+                                       file.toURL().toString());
+
+                               AttributeResolver ar = new AttributeResolver(props);
+                               fail("Attribute Resolver loaded even when no only PlugIns with circular dependencies were configured.");
+                       } catch (AttributeResolverException e) {
+                               //This exception should be thrown, ignoring
+                       } catch (MalformedURLException e) {
+                               fail("Error in test specification: " + e.getMessage());
+                       }
+               }
 
 }