Import of attribute resolver code.
authorwassa <wassa@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Mon, 7 Apr 2003 18:00:59 +0000 (18:00 +0000)
committerwassa <wassa@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Mon, 7 Apr 2003 18:00:59 +0000 (18:00 +0000)
git-svn-id: https://subversion.switch.ch/svn/shibboleth/java-idp/trunk@551 ab3bd59b-922f-494d-bb5f-6f0a3c29deca

25 files changed:
src/conf/resolver.xml [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/aa/attrresolv/AttributeDefinitionPlugIn.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/aa/attrresolv/AttributeResolver.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/aa/attrresolv/AttributeResolverException.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/aa/attrresolv/DataConnectorPlugIn.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/aa/attrresolv/Dependencies.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/aa/attrresolv/ResolutionPlugIn.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/aa/attrresolv/ResolutionPlugInException.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/aa/attrresolv/ResolutionPlugInFactory.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/aa/attrresolv/ResolverAttribute.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/aa/attrresolv/ResolverAttributeSet.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/aa/attrresolv/ResolverCache.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/BaseAttributeDefinition.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/BaseResolutionPlugIn.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/CustomAttributeDefinition.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/CustomDataConnector.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/JNDIDirectoryDataConnector.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/SampleConnector.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/ScopedStringValueHandler.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/ShibAttribute.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/ShibAttributeSet.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/SimpleAttributeDefinition.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/ValueHandler.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/ValueHandlerException.java [new file with mode: 0644]
src/schemas/shibboleth-resolver-1.0.xsd [new file with mode: 0644]

diff --git a/src/conf/resolver.xml b/src/conf/resolver.xml
new file mode 100644 (file)
index 0000000..ca80014
--- /dev/null
@@ -0,0 +1,48 @@
+<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="urn:mace:eduPerson:1.0:eduPersonPrincipalName" smartScope="columbia.edu" cacheTime="1800000">
+               <DataConnectorDependency requires="directory"/>
+       </SimpleAttributeDefinition>
+       
+       <SimpleAttributeDefinition id="urn:mace:eduPerson:1.0:eduPersonNickName" lifeTime="1800000" cacheTime="1800000">
+               <DataConnectorDependency requires="directory"/>
+       </SimpleAttributeDefinition>
+       
+       <SimpleAttributeDefinition id="urn:mace:eduPerson:1.0:eduPersonEntitlement" lifeTime="300000" cacheTime="300000">
+               <DataConnectorDependency requires="echo"/>
+       </SimpleAttributeDefinition>
+       
+       <SimpleAttributeDefinition id="urn:mace:eduPerson:1.0:eduPersonAffiliation" lifeTime="1800000" cacheTime="1800000">
+               <DataConnectorDependency requires="directory"/>
+       </SimpleAttributeDefinition>
+       
+       <SimpleAttributeDefinition id="urn:mace:eduPerson:1.0:eduPersonPrimaryAffiliation" cacheTime="1800000">
+               <DataConnectorDependency requires="directory"/>
+       </SimpleAttributeDefinition>
+       
+       <SimpleAttributeDefinition id="urn:mace:eduPerson:1.0:eduPersonScopedAffiliation" smartScope="example.com" cacheTime="1800000">
+               <AttributeDependency requires="urn:mace:eduPerson:1.0:eduPersonAffiliation"/>
+       </SimpleAttributeDefinition>
+       
+       <SimpleAttributeDefinition id="urn:mace:rfc2079:labeledURI" cacheTime="1800000">
+               <DataConnectorDependency requires="directory"/>
+       </SimpleAttributeDefinition>
+       
+       <CustomAttributeDefinition id="urn:mace:example:homepage" 
+                       class="edu.internet2.middleware.shibboleth.aa.attrresolv.provider.SimpleAttributeDefinition" 
+                       cacheTime="1800000" 
+                       sourceName="labeledURI">
+               <DataConnectorDependency requires="directory"/>
+       </CustomAttributeDefinition>
+       
+       <CustomDataConnector id="echo" cacheTime="1800000"
+               class="edu.internet2.middleware.shibboleth.aa.attrresolv.provider.SampleConnector" />
+       
+       <JNDIDirectoryDataConnector id="directory" cacheTime="1800000">
+               <Search filter="cn=%PRINCIPAL%">
+                       <Controls />
+               </Search>
+               <Property name="java.naming.factory.initial" value="com.sun.jndi.ldap.LdapCtxFactory" />
+               <Property name="java.naming.provider.url" value="ldap://therock.cc.columbia.edu/dc=test,dc=com" />
+       </JNDIDirectoryDataConnector>
+</AttributeResolver>
\ No newline at end of file
diff --git a/src/edu/internet2/middleware/shibboleth/aa/attrresolv/AttributeDefinitionPlugIn.java b/src/edu/internet2/middleware/shibboleth/aa/attrresolv/AttributeDefinitionPlugIn.java
new file mode 100644 (file)
index 0000000..78b6726
--- /dev/null
@@ -0,0 +1,91 @@
+/* 
+ * The Shibboleth License, Version 1. 
+ * Copyright (c) 2002 
+ * University Corporation for Advanced Internet Development, Inc. 
+ * All rights reserved
+ * 
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials provided with the distribution, if any, must include 
+ * the following acknowledgment: "This product includes software developed by 
+ * the University Corporation for Advanced Internet Development 
+ * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
+ * may appear in the software itself, if and wherever such third-party 
+ * acknowledgments normally appear.
+ * 
+ * Neither the name of Shibboleth nor the names of its contributors, nor 
+ * Internet2, nor the University Corporation for Advanced Internet Development, 
+ * Inc., nor UCAID may be used to endorse or promote products derived from this 
+ * software without specific prior written permission. For written permission, 
+ * please contact shibboleth@shibboleth.org
+ * 
+ * Products derived from this software may not be called Shibboleth, Internet2, 
+ * UCAID, or the University Corporation for Advanced Internet Development, nor 
+ * may Shibboleth appear in their name, without prior written permission of the 
+ * University Corporation for Advanced Internet Development.
+ * 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
+ * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
+ * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
+ * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package edu.internet2.middleware.shibboleth.aa.attrresolv;
+
+import java.security.Principal;
+
+/**
+ * Defines an Attribute Definition PlugIn for the AA Attribute Resolver.  Such plugins can
+ * be realized at runtime by the resolver and subsequently resolved in conjunction with other
+ * dependant plugins.
+ * 
+ * @author Walter Hoehn (wassa@columbia.edu)
+ */
+
+public interface AttributeDefinitionPlugIn extends ResolutionPlugIn {
+
+       /**
+        * Resolves the values of an attribute.
+        * 
+        * @param attribute The attribute to be resolved
+        * @param principal The principal for which the attribute should be resolved
+        * @param requester The name of the entity making the resolution request
+        * @param depends Resolution dependencies
+        * @throws ResolutionPlugInException
+        */
+       public void resolve(ResolverAttribute attribute, Principal principal, String requester, Dependencies depends)
+               throws ResolutionPlugInException;
+
+       /**
+        * Returns an array containing the names of the attribute
+        * definitions that this definition depends upon for resolution. 
+        * 
+        * @return String[] an array of Ids
+        */
+       public String[] getAttributeDefinitionDependencyIds();
+       
+       /**
+        * Returns an array containining the names of the connectors
+        * that this definition depends upon for resolution. 
+        * 
+        * @return String[] an array of Ids
+        */
+       public String[] getDataConnectorDependencyIds();
+}
diff --git a/src/edu/internet2/middleware/shibboleth/aa/attrresolv/AttributeResolver.java b/src/edu/internet2/middleware/shibboleth/aa/attrresolv/AttributeResolver.java
new file mode 100644 (file)
index 0000000..dd30f20
--- /dev/null
@@ -0,0 +1,530 @@
+/* 
+ * The Shibboleth License, Version 1. 
+ * Copyright (c) 2002 
+ * University Corporation for Advanced Internet Development, Inc. 
+ * All rights reserved
+ * 
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials provided with the distribution, if any, must include 
+ * the following acknowledgment: "This product includes software developed by 
+ * the University Corporation for Advanced Internet Development 
+ * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
+ * may appear in the software itself, if and wherever such third-party 
+ * acknowledgments normally appear.
+ * 
+ * Neither the name of Shibboleth nor the names of its contributors, nor 
+ * Internet2, nor the University Corporation for Advanced Internet Development, 
+ * Inc., nor UCAID may be used to endorse or promote products derived from this 
+ * software without specific prior written permission. For written permission, 
+ * please contact shibboleth@shibboleth.org
+ * 
+ * Products derived from this software may not be called Shibboleth, Internet2, 
+ * UCAID, or the University Corporation for Advanced Internet Development, nor 
+ * may Shibboleth appear in their name, without prior written permission of the 
+ * University Corporation for Advanced Internet Development.
+ * 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
+ * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
+ * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
+ * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package edu.internet2.middleware.shibboleth.aa.attrresolv;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.naming.directory.Attributes;
+
+import org.apache.log4j.BasicConfigurator;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.xerces.parsers.DOMParser;
+import org.apache.xml.serialize.OutputFormat;
+import org.apache.xml.serialize.XMLSerializer;
+import org.opensaml.SAMLException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+import sun.security.acl.PrincipalImpl;
+import edu.internet2.middleware.shibboleth.aa.attrresolv.ResolverAttributeSet.ResolverAttributeIterator;
+import edu.internet2.middleware.shibboleth.aa.attrresolv.provider.ShibAttribute;
+import edu.internet2.middleware.shibboleth.aa.attrresolv.provider.ShibAttributeSet;
+import edu.internet2.middleware.shibboleth.aa.attrresolv.provider.ValueHandler;
+import edu.internet2.middleware.shibboleth.aa.attrresolv.provider.ShibAttributeSet.ShibAttributeIterator;
+import edu.internet2.middleware.shibboleth.common.ShibResource;
+import edu.internet2.middleware.shibboleth.common.ShibResource.ResourceNotAvailableException;
+
+/**
+ * An engine for obtaining attribute values for specified principals.  Attributes values are
+ * resolved using a directed graph of pluggable attribute definitions and data connectors.
+ *   
+ * @author Walter Hoehn (wassa@columbia.edu)
+ *
+ */
+
+public class AttributeResolver {
+
+       private static Logger log = Logger.getLogger(AttributeResolver.class.getName());
+       private HashMap plugIns = new HashMap();
+       private ResolverCache resolverCache = new ResolverCache();
+       public static final String resolverNamespace = "urn:mace:shibboleth:resolver:1.0";
+
+       public AttributeResolver(Properties properties) throws AttributeResolverException {
+               if (properties
+                       .getProperty("edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeResolver.ResolverConfig")
+                       == null) {
+                       log.error("No Attribute Resolver configuration file specified.");
+                       throw new AttributeResolverException("No Attribute Resolver configuration file specified.");
+               }
+
+               String configFile =
+                       properties.getProperty(
+                               "edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeResolver.ResolverConfig");
+
+               try {
+                       ShibResource config = new ShibResource(configFile);
+                       DOMParser parser = new DOMParser();
+                       parser.setFeature("http://xml.org/sax/features/validation", true);
+                       parser.setFeature("http://apache.org/xml/features/validation/schema", true);
+                       parser.setEntityResolver(new EntityResolver() {
+                               public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
+                                       if (systemId.endsWith("shibboleth-resolver-1.0.xsd")) {
+                                               InputStream stream;
+                                               try {
+                                                       return new InputSource(
+                                                               new ShibResource(
+                                                                       new File("shibboleth-resolver-1.0.xsd").toURL().toString(),
+                                                                       this.getClass())
+                                                                       .getInputStream());
+                                               } catch (IOException e) {
+                                                       throw new SAXException("Could not load entity: " + e);
+                                               }
+                                       } else {
+                                               return null;
+                                       }
+                               }
+                       });
+
+                       parser.setErrorHandler(new ErrorHandler() {
+                               public void error(SAXParseException arg0) throws SAXException {
+                                       throw new SAXException("Error parsing xml file: " + arg0);
+                               }
+                               public void fatalError(SAXParseException arg0) throws SAXException {
+                                       throw new SAXException("Error parsing xml file: " + arg0);
+                               }
+                               public void warning(SAXParseException arg0) throws SAXException {
+                                       throw new SAXException("Error parsing xml file: " + arg0);
+                               }
+                       });
+                       parser.parse(new InputSource(config.getInputStream()));
+                       loadConfig(parser.getDocument());
+
+               } catch (ResourceNotAvailableException e) {
+                       log.error("No Attribute Resolver configuration could be loaded from (" + configFile + "): " + e);
+                       throw new AttributeResolverException("No Attribute Resolver configuration found.");
+               } catch (SAXException e) {
+                       log.error("Error parsing Attribute Resolver Configuration file: " + e);
+                       throw new AttributeResolverException("Error parsing Attribute Resolver Configuration file.");
+               } catch (IOException e) {
+                       log.error("Error reading Attribute Resolver Configuration file: " + e);
+                       throw new AttributeResolverException("Error reading Attribute Resolver Configuration file.");
+               }
+       }
+
+       private void loadConfig(Document document) throws AttributeResolverException {
+
+               log.info("Configuring Attribute Resolver.");
+               if (!document.getDocumentElement().getTagName().equals("AttributeResolver")) {
+                       log.error("Configuration must include <AttributeResolver> as the root node.");
+                       throw new AttributeResolverException("Cannot load Attribute Resolver.");
+               }
+
+               NodeList plugInNodes =
+                       document.getElementsByTagNameNS(resolverNamespace, "AttributeResolver").item(0).getChildNodes();
+               if (plugInNodes.getLength() <= 0) {
+                       log.error("Configuration inclues no PlugIn definitions.");
+                       throw new AttributeResolverException("Cannot load Attribute Resolver.");
+               }
+               for (int i = 0; plugInNodes.getLength() > i; i++) {
+                       if (plugInNodes.item(i).getNodeType() == Node.ELEMENT_NODE) {
+                               try {
+                                       log.info("Found a PlugIn. Loading...");
+                                       ResolutionPlugIn plugIn = ResolutionPlugInFactory.createPlugIn((Element) plugInNodes.item(i));
+                                       registerPlugIn(plugIn, plugIn.getId());
+                               } catch (DuplicatePlugInException dpe) {
+                                       log.warn("Skipping PlugIn: " + dpe.getMessage());
+                               } catch (ClassCastException cce) {
+                                       log.error("Problem realizing PlugIn configuration" + cce.getMessage());
+                               } catch (AttributeResolverException are) {
+                                       log.warn("Skipping PlugIn: " + ((Element) plugInNodes.item(i)).getAttribute("id"));
+                               }
+                       }
+               }
+
+               verifyPlugIns();
+               log.info("Configuration complete.");
+       }
+
+       private void verifyPlugIns() {
+               //TODO Maybe this should detect loops in the directed graph
+
+               log.info("Verifying PlugIn graph consitency.");
+               Set inconsistent = new HashSet();
+               Iterator registered = plugIns.keySet().iterator();
+
+               while (registered.hasNext()) {
+                       ResolutionPlugIn plugIn = lookupPlugIn((String) registered.next());
+                       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()) {
+                                       if (!plugIns.containsKey(dependsIt.next())) {
+                                               log.error(
+                                                       "The PlugIn ("
+                                                               + plugIn.getId()
+                                                               + ") is inconsistent.  It depends on a PlugIn that is not registered.");
+                                               inconsistent.add(plugIn.getId());
+                                       }
+                               }
+                       }
+               }
+
+               if (!inconsistent.isEmpty()) {
+                       log.info("Unloading inconsistent PlugIns.");
+                       Iterator inconsistentIt = inconsistent.iterator();
+                       while (inconsistentIt.hasNext()) {
+                               plugIns.remove(inconsistentIt.next());
+                       }
+               }
+       }
+
+       private void registerPlugIn(ResolutionPlugIn connector, String id) throws DuplicatePlugInException {
+
+               if (plugIns.containsKey(id)) {
+                       log.error("A PlugIn is already registered with the Id (" + id + ").");
+                       throw new DuplicatePlugInException("Found a duplicate PlugIn Id.");
+               }
+               plugIns.put(id, connector);
+               log.info("Registered PlugIn: (" + id + ")");
+
+       }
+
+       private ResolutionPlugIn lookupPlugIn(String id) {
+               return (ResolutionPlugIn) plugIns.get(id);
+       }
+
+       public static void main(String[] args) {
+
+               BasicConfigurator.configure();
+               Logger.getRootLogger().setLevel(Level.WARN);
+
+               try {
+                       Properties props = new Properties();
+                       File file = new File("testconfig.xml");
+
+                       props.setProperty(
+                               "edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeResolver.ResolverConfig",
+                               file.toURL().toString());
+                       AttributeResolver ar = new AttributeResolver(props);
+                       for (int j = 0; j < 2; j++) {
+                               System.out.println("Resolving pass: " + (j + 1));
+                               ShibAttributeSet attributes = new ShibAttributeSet();
+                               if (j == 1) {
+                                       attributes.add(new ShibAttribute("urn:mace:eduPerson:1.0:eduPersonPrincipalName"));
+                               }
+                               attributes.add(new ShibAttribute("urn:mace:eduPerson:1.0:eduPersonNickName"));
+                               attributes.add(new ShibAttribute("urn:mace:eduPerson:1.0:eduPersonPrimaryAffiliation"));
+                               attributes.add(new ShibAttribute("urn:mace:eduPerson:1.0:eduPersonScopedAffiliation"));
+                               attributes.add(new ShibAttribute("urn:mace:eduPerson:1.0:eduPersonAffiliation"));
+                               attributes.add(new ShibAttribute("urn:mace:eduPerson:1.0:eduPersonEntitlement"));
+                               attributes.add(new ShibAttribute("urn:mace:rfc2079:labeledURI"));
+
+                               ar.resolveAttributes(new PrincipalImpl("mytestuser"), "shar.example.edu", attributes);
+                               ShibAttributeIterator iterator = attributes.shibAttributeIterator();
+                               while (iterator.hasNext()) {
+                                       ShibAttribute attribute = iterator.nextShibAttribute();
+                                       System.out.println(attribute.getName());
+                                       System.out.println("LifeTime: " + attribute.getLifetime());
+                                       System.out.println("\t" + " values:");
+                                       for (Iterator attrIterator = attribute.getValues(); attrIterator.hasNext();) {
+                                               System.out.println("\t\t" + attrIterator.next().toString());
+                                       }
+                                       System.out.println("To DOM:");
+                                       Node dom = attribute.toDOM();
+                                       ByteArrayOutputStream xmlOut = new ByteArrayOutputStream();
+                                       new XMLSerializer(xmlOut, new OutputFormat()).serialize((Element) dom);
+                                       System.out.write(xmlOut.toByteArray());
+                                       System.out.println(System.getProperty("line.separator") + System.getProperty("line.separator"));
+                               }
+                       }
+
+               } catch (AttributeResolverException e) {
+                       log.error("Couldn't load attribute resolver: " + e.getMessage());
+               } catch (MalformedURLException e1) {
+                       e1.printStackTrace();
+               } catch (SAMLException se) {
+                       se.printStackTrace();
+               } catch (IOException e) {
+                       log.error("Couldn't load attribute resolver: " + e.getMessage());
+               }
+       }
+
+       /**
+        * Resolve a set of attributes for a particular principal and requester.
+        * 
+        * @param principal the <code>Principal</code> for which the attributes should be resolved
+        * @param requester the name of the requesting entity
+        * @param attributes the set of attributes to be resolved
+        */
+       public void resolveAttributes(Principal principal, String requester, ResolverAttributeSet attributes) {
+
+               HashMap requestCache = new HashMap();
+               ResolverAttributeIterator iterator = attributes.resolverAttributeIterator();
+
+               while (iterator.hasNext()) {
+                       ResolverAttribute attribute = iterator.nextResolverAttribute();
+                       try {
+                               if (lookupPlugIn(attribute.getName()) == null) {
+                                       log.warn("No PlugIn registered for attribute: (" + attribute.getName() + ")");
+                                       iterator.remove();
+                               } else {
+                                       log.info("Resolving attribute: (" + attribute.getName() + ")");
+                                       if (attribute.resolved()) {
+                                               log.debug(
+                                                       "Attribute ("
+                                                               + attribute.getName()
+                                                               + ") already resolved for this request.  No need for further resolution.");
+
+                                       } else {
+                                               resolveAttribute(attribute, principal, requester, requestCache, attributes);
+                                       }
+
+                                       if (!attribute.hasValues()) {
+                                               iterator.remove();
+                                       }
+                               }
+                       } catch (ResolutionPlugInException rpe) {
+                               log.error("Problem encountered while resolving attribute: (" + attribute.getName() + "): " + rpe);
+                               iterator.remove();
+                       }
+               }
+       }
+
+       private void resolveAttribute(
+               ResolverAttribute attribute,
+               Principal principal,
+               String requester,
+               Map requestCache,
+               ResolverAttributeSet requestedAttributes)
+               throws ResolutionPlugInException {
+
+               AttributeDefinitionPlugIn currentDefinition = (AttributeDefinitionPlugIn) lookupPlugIn(attribute.getName());
+
+               //Check to see if we have already resolved the attribute during this request
+               if (requestCache.containsKey(currentDefinition.getId())) {
+                       log.debug(
+                               "Attribute ("
+                                       + currentDefinition.getId()
+                                       + ") already resolved for this request, using cached version");
+                       attribute.resolveFromCached((ResolverAttribute) requestCache.get(currentDefinition.getId()));
+                       return;
+               }
+
+               //Check to see if we have a cached resolution for this attribute
+               if (currentDefinition.getTTL() > 0) {
+                       ResolverAttribute cachedAttribute =
+                               resolverCache.getResolvedAttribute(principal, currentDefinition.getId());
+                       if (cachedAttribute != null) {
+                               log.debug(
+                                       "Attribute ("
+                                               + currentDefinition.getId()
+                                               + ") resolution cached from a previous request, using cached version");
+                               attribute.resolveFromCached(cachedAttribute);
+                               return;
+                       }
+               }
+
+               //Resolve all attribute dependencies
+               String[] attributeDependencies = currentDefinition.getAttributeDefinitionDependencyIds();
+               Dependencies depends = new Dependencies();
+
+               boolean dependancyOnly = false;
+               for (int i = 0; attributeDependencies.length > i; i++) {
+                       log.debug(
+                               "Attribute (" + attribute.getName() + ") depends on attribute (" + attributeDependencies[i] + ").");
+                       ResolverAttribute dependant = requestedAttributes.getByName(attributeDependencies[i]);
+                       if (dependant == null) {
+                               dependancyOnly = true;
+                               dependant = new DependentOnlyResolutionAttribute(attributeDependencies[i]);
+                       }
+                       resolveAttribute(dependant, principal, requester, requestCache, requestedAttributes);
+                       depends.addAttributeResolution(attributeDependencies[i], dependant);
+
+               }
+
+               //Resolve all connector dependencies
+               String[] connectorDependencies = currentDefinition.getDataConnectorDependencyIds();
+               for (int i = 0; connectorDependencies.length > i; i++) {
+                       log.debug(
+                               "Attribute (" + attribute.getName() + ") depends on connector (" + connectorDependencies[i] + ").");
+                       //Check to see if we have already resolved the connector during this request
+                       if (requestCache.containsKey(connectorDependencies[i])) {
+                               log.debug(
+                                       "Connector ("
+                                               + connectorDependencies[i]
+                                               + ") already resolved for this request, using cached version");
+                               depends.addConnectorResolution(
+                                       connectorDependencies[i],
+                                       (Attributes) requestCache.get(connectorDependencies[i]));
+                       } else {
+                               //Check to see if we have a cached resolution for this attribute
+                               if (((DataConnectorPlugIn) lookupPlugIn(connectorDependencies[i])).getTTL() > 0) {
+                                       Attributes cachedAttributes =
+                                               resolverCache.getResolvedConnector(principal, connectorDependencies[i]);
+                                       if (cachedAttributes != null) {
+                                               log.debug(
+                                                       "Connector ("
+                                                               + connectorDependencies[i]
+                                                               + ") resolution cached from a previous request, using cached version");
+                                               depends.addConnectorResolution(connectorDependencies[i], cachedAttributes);
+                                       }
+                               }
+
+                               Attributes resolvedConnector =
+                                       ((DataConnectorPlugIn) lookupPlugIn(connectorDependencies[i])).resolve(principal);
+                               requestCache.put(connectorDependencies[i], resolvedConnector);
+                               depends.addConnectorResolution(connectorDependencies[i], resolvedConnector);
+
+                               //Add attribute resolution to cache
+                               if (((DataConnectorPlugIn) lookupPlugIn(connectorDependencies[i])).getTTL() > 0) {
+                                       resolverCache.cacheConnectorResolution(
+                                               principal,
+                                               connectorDependencies[i],
+                                               ((DataConnectorPlugIn) lookupPlugIn(connectorDependencies[i])).getTTL(),
+                                               resolvedConnector);
+                               }
+                       }
+               }
+
+               //Resolve the attribute
+               currentDefinition.resolve(attribute, principal, requester, depends);
+
+               //If necessary, cache for this request
+               if (dependancyOnly || !attribute.hasValues()) {
+                       requestCache.put(currentDefinition.getId(), attribute);
+               }
+
+               //Add attribute resolution to cache
+               if (currentDefinition.getTTL() > 0) {
+                       resolverCache.cacheAttributeResolution(
+                               principal,
+                               attribute.getName(),
+                               currentDefinition.getTTL(),
+                               attribute);
+               }
+       }
+
+       private class DuplicatePlugInException extends Exception {
+               public DuplicatePlugInException(String message) {
+                       super(message);
+               }
+       }
+       
+       class DependentOnlyResolutionAttribute implements ResolverAttribute {
+               String name;
+               ArrayList values = new ArrayList();
+               boolean resolved = false;
+
+               DependentOnlyResolutionAttribute(String name) {
+                       this.name = name;
+               }
+
+               public String getName() {
+                       return name;
+               }
+
+               public boolean resolved() {
+                       return resolved;
+               }
+
+               public void setResolved() {
+                       resolved = true;
+               }
+
+               public void resolveFromCached(ResolverAttribute attribute) {
+               }
+
+               public void setLifetime(long lifetime) {
+               }
+
+               public long getLifetime() {
+                       return 0;
+               }
+
+               public void addValue(Object value) {
+                       values.add(value);
+               }
+
+               public Iterator getValues() {
+                       return values.iterator();
+               }
+
+               public boolean hasValues() {
+                       if (values.isEmpty()) {
+                               return false;
+                       }
+                       return true;
+               }
+
+               public void registerValueHandler(ValueHandler handler) {
+               }
+
+               public ValueHandler getRegisteredValueHandler() {
+                       return null;
+               }
+       }
+
+}
diff --git a/src/edu/internet2/middleware/shibboleth/aa/attrresolv/AttributeResolverException.java b/src/edu/internet2/middleware/shibboleth/aa/attrresolv/AttributeResolverException.java
new file mode 100644 (file)
index 0000000..6bd100a
--- /dev/null
@@ -0,0 +1,65 @@
+/* 
+ * The Shibboleth License, Version 1. 
+ * Copyright (c) 2002 
+ * University Corporation for Advanced Internet Development, Inc. 
+ * All rights reserved
+ * 
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials provided with the distribution, if any, must include 
+ * the following acknowledgment: "This product includes software developed by 
+ * the University Corporation for Advanced Internet Development 
+ * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
+ * may appear in the software itself, if and wherever such third-party 
+ * acknowledgments normally appear.
+ * 
+ * Neither the name of Shibboleth nor the names of its contributors, nor 
+ * Internet2, nor the University Corporation for Advanced Internet Development, 
+ * Inc., nor UCAID may be used to endorse or promote products derived from this 
+ * software without specific prior written permission. For written permission, 
+ * please contact shibboleth@shibboleth.org
+ * 
+ * Products derived from this software may not be called Shibboleth, Internet2, 
+ * UCAID, or the University Corporation for Advanced Internet Development, nor 
+ * may Shibboleth appear in their name, without prior written permission of the 
+ * University Corporation for Advanced Internet Development.
+ * 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
+ * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
+ * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
+ * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package edu.internet2.middleware.shibboleth.aa.attrresolv;
+
+/**
+ * Signals that an error occurred while the <code>AttributeResolver</code> was attempting
+ * to resolve the specified attributes.
+ * 
+ * @author Walter Hoehn (wassa@columbia.edu)
+ */
+public class AttributeResolverException extends Exception {
+       
+       public AttributeResolverException(String message) {
+               super(message);
+       }
+
+}
+
diff --git a/src/edu/internet2/middleware/shibboleth/aa/attrresolv/DataConnectorPlugIn.java b/src/edu/internet2/middleware/shibboleth/aa/attrresolv/DataConnectorPlugIn.java
new file mode 100644 (file)
index 0000000..37a237c
--- /dev/null
@@ -0,0 +1,68 @@
+/* 
+ * The Shibboleth License, Version 1. 
+ * Copyright (c) 2002 
+ * University Corporation for Advanced Internet Development, Inc. 
+ * All rights reserved
+ * 
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials provided with the distribution, if any, must include 
+ * the following acknowledgment: "This product includes software developed by 
+ * the University Corporation for Advanced Internet Development 
+ * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
+ * may appear in the software itself, if and wherever such third-party 
+ * acknowledgments normally appear.
+ * 
+ * Neither the name of Shibboleth nor the names of its contributors, nor 
+ * Internet2, nor the University Corporation for Advanced Internet Development, 
+ * Inc., nor UCAID may be used to endorse or promote products derived from this 
+ * software without specific prior written permission. For written permission, 
+ * please contact shibboleth@shibboleth.org
+ * 
+ * Products derived from this software may not be called Shibboleth, Internet2, 
+ * UCAID, or the University Corporation for Advanced Internet Development, nor 
+ * may Shibboleth appear in their name, without prior written permission of the 
+ * University Corporation for Advanced Internet Development.
+ * 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
+ * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
+ * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
+ * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package edu.internet2.middleware.shibboleth.aa.attrresolv;
+
+import java.security.Principal;
+
+import javax.naming.directory.Attributes;
+
+/**
+ * Defines a Data Connector PlugIn for the AA Attribute Resolver.  Such plugins represent
+ * connection points to external data sources from which attribute data is gathered.
+ * 
+ * @author Walter Hoehn (wassa@columbia.edu)
+ */
+
+public interface DataConnectorPlugIn extends ResolutionPlugIn {
+       
+       public Attributes resolve(Principal principal) throws ResolutionPlugInException;
+
+}
+
diff --git a/src/edu/internet2/middleware/shibboleth/aa/attrresolv/Dependencies.java b/src/edu/internet2/middleware/shibboleth/aa/attrresolv/Dependencies.java
new file mode 100644 (file)
index 0000000..0dc0435
--- /dev/null
@@ -0,0 +1,90 @@
+/* 
+ * The Shibboleth License, Version 1. 
+ * Copyright (c) 2002 
+ * University Corporation for Advanced Internet Development, Inc. 
+ * All rights reserved
+ * 
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials provided with the distribution, if any, must include 
+ * the following acknowledgment: "This product includes software developed by 
+ * the University Corporation for Advanced Internet Development 
+ * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
+ * may appear in the software itself, if and wherever such third-party 
+ * acknowledgments normally appear.
+ * 
+ * Neither the name of Shibboleth nor the names of its contributors, nor 
+ * Internet2, nor the University Corporation for Advanced Internet Development, 
+ * Inc., nor UCAID may be used to endorse or promote products derived from this 
+ * software without specific prior written permission. For written permission, 
+ * please contact shibboleth@shibboleth.org
+ * 
+ * Products derived from this software may not be called Shibboleth, Internet2, 
+ * UCAID, or the University Corporation for Advanced Internet Development, nor 
+ * may Shibboleth appear in their name, without prior written permission of the 
+ * University Corporation for Advanced Internet Development.
+ * 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
+ * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
+ * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
+ * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package edu.internet2.middleware.shibboleth.aa.attrresolv;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.naming.directory.Attributes;
+
+/**
+ * Contains resolved <code>ResolutionPlugIn</code> objects that a particular Attribute Definition PlugIn
+ * relies upon for its resolution.
+ * 
+ * @author Walter Hoehn (wassa@columbia.edu)
+ */
+public class Dependencies {
+
+       protected Map resolvedAttributeDependencies = new HashMap();
+       protected Map resolvedConnectorDependencies = new HashMap();
+
+       public Attributes getConnectorResolution(String id) {
+               if (resolvedConnectorDependencies.containsKey(id)) {
+                       return (Attributes) resolvedConnectorDependencies.get(id);
+               } else {
+                       return null;
+               }
+       }
+
+       void addConnectorResolution(String id, Attributes attrs) {
+               resolvedConnectorDependencies.put(id, attrs);
+       }
+
+       public ResolverAttribute getAttributeResolution(String id) {
+               if (resolvedAttributeDependencies.containsKey(id)) {
+                       return (ResolverAttribute) resolvedAttributeDependencies.get(id);
+               } else {
+                       return null;
+               }
+       }
+       void addAttributeResolution(String id, ResolverAttribute attr) {
+               resolvedAttributeDependencies.put(id, attr);
+       }
+}
diff --git a/src/edu/internet2/middleware/shibboleth/aa/attrresolv/ResolutionPlugIn.java b/src/edu/internet2/middleware/shibboleth/aa/attrresolv/ResolutionPlugIn.java
new file mode 100644 (file)
index 0000000..771339d
--- /dev/null
@@ -0,0 +1,65 @@
+/* 
+ * The Shibboleth License, Version 1. 
+ * Copyright (c) 2002 
+ * University Corporation for Advanced Internet Development, Inc. 
+ * All rights reserved
+ * 
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials provided with the distribution, if any, must include 
+ * the following acknowledgment: "This product includes software developed by 
+ * the University Corporation for Advanced Internet Development 
+ * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
+ * may appear in the software itself, if and wherever such third-party 
+ * acknowledgments normally appear.
+ * 
+ * Neither the name of Shibboleth nor the names of its contributors, nor 
+ * Internet2, nor the University Corporation for Advanced Internet Development, 
+ * Inc., nor UCAID may be used to endorse or promote products derived from this 
+ * software without specific prior written permission. For written permission, 
+ * please contact shibboleth@shibboleth.org
+ * 
+ * Products derived from this software may not be called Shibboleth, Internet2, 
+ * UCAID, or the University Corporation for Advanced Internet Development, nor 
+ * may Shibboleth appear in their name, without prior written permission of the 
+ * University Corporation for Advanced Internet Development.
+ * 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
+ * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
+ * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
+ * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package edu.internet2.middleware.shibboleth.aa.attrresolv;
+
+/**
+ * Defines a plugin for the AA Attribute Resolver.  Such plugins can
+ * be realized at runtime by the resolver.
+ * 
+ * @author Walter Hoehn
+ *
+ */
+public interface ResolutionPlugIn {
+       
+       public String getId();
+       public long getTTL();
+
+}
+
diff --git a/src/edu/internet2/middleware/shibboleth/aa/attrresolv/ResolutionPlugInException.java b/src/edu/internet2/middleware/shibboleth/aa/attrresolv/ResolutionPlugInException.java
new file mode 100644 (file)
index 0000000..6f790de
--- /dev/null
@@ -0,0 +1,64 @@
+/* 
+ * The Shibboleth License, Version 1. 
+ * Copyright (c) 2002 
+ * University Corporation for Advanced Internet Development, Inc. 
+ * All rights reserved
+ * 
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials provided with the distribution, if any, must include 
+ * the following acknowledgment: "This product includes software developed by 
+ * the University Corporation for Advanced Internet Development 
+ * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
+ * may appear in the software itself, if and wherever such third-party 
+ * acknowledgments normally appear.
+ * 
+ * Neither the name of Shibboleth nor the names of its contributors, nor 
+ * Internet2, nor the University Corporation for Advanced Internet Development, 
+ * Inc., nor UCAID may be used to endorse or promote products derived from this 
+ * software without specific prior written permission. For written permission, 
+ * please contact shibboleth@shibboleth.org
+ * 
+ * Products derived from this software may not be called Shibboleth, Internet2, 
+ * UCAID, or the University Corporation for Advanced Internet Development, nor 
+ * may Shibboleth appear in their name, without prior written permission of the 
+ * University Corporation for Advanced Internet Development.
+ * 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
+ * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
+ * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
+ * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package edu.internet2.middleware.shibboleth.aa.attrresolv;
+
+/**
+ * Signals that an error occurred while constucting or resolving a <code>ResolutionPlugIn</code>.
+ * 
+ * @author Walter Hoehn (wassa@columbia.edu)
+ */
+public class ResolutionPlugInException extends AttributeResolverException {
+
+       public ResolutionPlugInException(String message) {
+               super(message);
+       }
+
+
+}
diff --git a/src/edu/internet2/middleware/shibboleth/aa/attrresolv/ResolutionPlugInFactory.java b/src/edu/internet2/middleware/shibboleth/aa/attrresolv/ResolutionPlugInFactory.java
new file mode 100644 (file)
index 0000000..f278e40
--- /dev/null
@@ -0,0 +1,93 @@
+/* 
+ * The Shibboleth License, Version 1. 
+ * Copyright (c) 2002 
+ * University Corporation for Advanced Internet Development, Inc. 
+ * All rights reserved
+ * 
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials provided with the distribution, if any, must include 
+ * the following acknowledgment: "This product includes software developed by 
+ * the University Corporation for Advanced Internet Development 
+ * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
+ * may appear in the software itself, if and wherever such third-party 
+ * acknowledgments normally appear.
+ * 
+ * Neither the name of Shibboleth nor the names of its contributors, nor 
+ * Internet2, nor the University Corporation for Advanced Internet Development, 
+ * Inc., nor UCAID may be used to endorse or promote products derived from this 
+ * software without specific prior written permission. For written permission, 
+ * please contact shibboleth@shibboleth.org
+ * 
+ * Products derived from this software may not be called Shibboleth, Internet2, 
+ * UCAID, or the University Corporation for Advanced Internet Development, nor 
+ * may Shibboleth appear in their name, without prior written permission of the 
+ * University Corporation for Advanced Internet Development.
+ * 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
+ * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
+ * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
+ * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package edu.internet2.middleware.shibboleth.aa.attrresolv;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Element;
+
+import edu.internet2.middleware.shibboleth.aa.attrresolv.provider.CustomAttributeDefinition;
+import edu.internet2.middleware.shibboleth.aa.attrresolv.provider.CustomDataConnector;
+import edu.internet2.middleware.shibboleth.aa.attrresolv.provider.JNDIDirectoryDataConnector;
+import edu.internet2.middleware.shibboleth.aa.attrresolv.provider.SimpleAttributeDefinition;
+
+/**
+ * Factory that instanciates Resolution PlugIns based on Resolver configuration elements.
+ * 
+ * @author Walter Hoehn (wassa@columbia.edu)
+ *
+ */
+
+public class ResolutionPlugInFactory {
+
+       private static Logger log = Logger.getLogger(ResolutionPlugInFactory.class.getName());
+
+       public static ResolutionPlugIn createPlugIn(Element e) throws AttributeResolverException {
+
+               if (e.getTagName().equals("CustomDataConnector")) {
+                       return new CustomDataConnector(e);
+               }
+
+               if (e.getTagName().equals("CustomAttributeDefinition")) {
+                       return new CustomAttributeDefinition(e);
+               }
+
+               if (e.getTagName().equals("SimpleAttributeDefinition")) {
+                       return new SimpleAttributeDefinition(e);
+               }
+
+               if (e.getTagName().equals("JNDIDirectoryDataConnector")) {
+                       return new JNDIDirectoryDataConnector(e);
+               }
+
+               log.error("Unrecognized PlugIn type: " + e.getTagName());
+               throw new AttributeResolverException("Failed to initialize PlugIn.");
+       }
+
+}
diff --git a/src/edu/internet2/middleware/shibboleth/aa/attrresolv/ResolverAttribute.java b/src/edu/internet2/middleware/shibboleth/aa/attrresolv/ResolverAttribute.java
new file mode 100644 (file)
index 0000000..cbc47f0
--- /dev/null
@@ -0,0 +1,75 @@
+/* 
+ * The Shibboleth License, Version 1. 
+ * Copyright (c) 2002 
+ * University Corporation for Advanced Internet Development, Inc. 
+ * All rights reserved
+ * 
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials provided with the distribution, if any, must include 
+ * the following acknowledgment: "This product includes software developed by 
+ * the University Corporation for Advanced Internet Development 
+ * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
+ * may appear in the software itself, if and wherever such third-party 
+ * acknowledgments normally appear.
+ * 
+ * Neither the name of Shibboleth nor the names of its contributors, nor 
+ * Internet2, nor the University Corporation for Advanced Internet Development, 
+ * Inc., nor UCAID may be used to endorse or promote products derived from this 
+ * software without specific prior written permission. For written permission, 
+ * please contact shibboleth@shibboleth.org
+ * 
+ * Products derived from this software may not be called Shibboleth, Internet2, 
+ * UCAID, or the University Corporation for Advanced Internet Development, nor 
+ * may Shibboleth appear in their name, without prior written permission of the 
+ * University Corporation for Advanced Internet Development.
+ * 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
+ * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
+ * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
+ * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package edu.internet2.middleware.shibboleth.aa.attrresolv;
+
+import java.util.Iterator;
+
+import edu.internet2.middleware.shibboleth.aa.attrresolv.provider.ValueHandler;
+
+/**
+ * Defines an attribute that can be resolved by the <code>AttributeResolver</code>.
+ * 
+ * @author Walter Hoehn (wassa@columbia.edu)
+ */
+
+public interface ResolverAttribute {
+
+       public String getName();
+       public boolean resolved();
+       public void setResolved();
+       public void resolveFromCached(ResolverAttribute attribute);
+       public void setLifetime(long lifetime);
+       public long getLifetime();      
+       public void addValue(Object value);
+       public Iterator getValues();
+       public boolean hasValues();
+       public void registerValueHandler(ValueHandler handler);
+       public ValueHandler getRegisteredValueHandler();
+}
diff --git a/src/edu/internet2/middleware/shibboleth/aa/attrresolv/ResolverAttributeSet.java b/src/edu/internet2/middleware/shibboleth/aa/attrresolv/ResolverAttributeSet.java
new file mode 100644 (file)
index 0000000..234655e
--- /dev/null
@@ -0,0 +1,66 @@
+/* 
+ * The Shibboleth License, Version 1. 
+ * Copyright (c) 2002 
+ * University Corporation for Advanced Internet Development, Inc. 
+ * All rights reserved
+ * 
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials provided with the distribution, if any, must include 
+ * the following acknowledgment: "This product includes software developed by 
+ * the University Corporation for Advanced Internet Development 
+ * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
+ * may appear in the software itself, if and wherever such third-party 
+ * acknowledgments normally appear.
+ * 
+ * Neither the name of Shibboleth nor the names of its contributors, nor 
+ * Internet2, nor the University Corporation for Advanced Internet Development, 
+ * Inc., nor UCAID may be used to endorse or promote products derived from this 
+ * software without specific prior written permission. For written permission, 
+ * please contact shibboleth@shibboleth.org
+ * 
+ * Products derived from this software may not be called Shibboleth, Internet2, 
+ * UCAID, or the University Corporation for Advanced Internet Development, nor 
+ * may Shibboleth appear in their name, without prior written permission of the 
+ * University Corporation for Advanced Internet Development.
+ * 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
+ * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
+ * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
+ * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package edu.internet2.middleware.shibboleth.aa.attrresolv;
+/**
+ * Defines a set of attributes that can be resolved by the <code>AttributeResolver</code>.
+ * 
+ * @author Walter Hoehn (wassa@columbia.edu)
+ */
+public interface ResolverAttributeSet {
+
+       public ResolverAttributeIterator resolverAttributeIterator();
+       public ResolverAttribute getByName(String name);
+       
+       public interface ResolverAttributeIterator {
+               public boolean hasNext();
+               public ResolverAttribute nextResolverAttribute();
+               public void remove();
+       }
+}
diff --git a/src/edu/internet2/middleware/shibboleth/aa/attrresolv/ResolverCache.java b/src/edu/internet2/middleware/shibboleth/aa/attrresolv/ResolverCache.java
new file mode 100644 (file)
index 0000000..9eb280c
--- /dev/null
@@ -0,0 +1,332 @@
+/* 
+ * The Shibboleth License, Version 1. 
+ * Copyright (c) 2002 
+ * University Corporation for Advanced Internet Development, Inc. 
+ * All rights reserved
+ * 
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials provided with the distribution, if any, must include 
+ * the following acknowledgment: "This product includes software developed by 
+ * the University Corporation for Advanced Internet Development 
+ * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
+ * may appear in the software itself, if and wherever such third-party 
+ * acknowledgments normally appear.
+ * 
+ * Neither the name of Shibboleth nor the names of its contributors, nor 
+ * Internet2, nor the University Corporation for Advanced Internet Development, 
+ * Inc., nor UCAID may be used to endorse or promote products derived from this 
+ * software without specific prior written permission. For written permission, 
+ * please contact shibboleth@shibboleth.org
+ * 
+ * Products derived from this software may not be called Shibboleth, Internet2, 
+ * UCAID, or the University Corporation for Advanced Internet Development, nor 
+ * may Shibboleth appear in their name, without prior written permission of the 
+ * University Corporation for Advanced Internet Development.
+ * 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
+ * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
+ * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
+ * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package edu.internet2.middleware.shibboleth.aa.attrresolv;
+
+import java.security.Principal;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.naming.directory.Attributes;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Rudimentary mechanism for caching objects created by the various
+ * Resolution PlugIns.
+ * 
+ * @author Walter Hoehn (wassa@columbia.edu)
+ *
+ */
+public class ResolverCache {
+
+       private static Logger log = Logger.getLogger(ResolverCache.class.getName());
+       //Hashtable handles synchronization for us
+       private Hashtable attributeDataCache = new Hashtable();
+       private Hashtable connectorDataCache = new Hashtable();
+       private Cleaner cleaner = new Cleaner(Thread.currentThread());
+
+       ResolverCache() {
+               log.info("Initializing the Attribute Resolver cache.");
+       }
+
+       void cacheConnectorResolution(Principal principal, String plugInId, long cacheLength, Attributes toCache) {
+
+               if (principal != null && cacheLength > 0 && plugInId != null && !plugInId.equals("") && toCache != null) {
+                       log.debug("Adding resolved Connector data to Attribute Resolver cache.");
+                       connectorDataCache.put(
+                               new CacheKey(principal, plugInId),
+                               new CacheObject(toCache, System.currentTimeMillis() + cacheLength));
+
+               } else {
+                       log.error("Attempted to add bad data to Attribute Resolver cache.");
+               }
+       }
+
+       void cacheAttributeResolution(Principal principal, String plugInId, long cacheLength, ResolverAttribute toCache) {
+
+               if (principal != null && cacheLength > 0 && plugInId != null && !plugInId.equals("") && toCache != null) {
+                       log.debug("Adding resolved Attribute data to Attribute Resolver cache.");
+                       attributeDataCache.put(
+                               new CacheKey(principal, plugInId),
+                               new CacheObject(toCache, System.currentTimeMillis() + cacheLength));
+
+               } else {
+                       log.error("Attempted to add bad data to Attribute Resolver cache.");
+               }
+       }
+
+       ResolverAttribute getResolvedAttribute(Principal principal, String plugInId) {
+
+               log.debug("Searching Cache for resolved attribute.");
+               Object object = attributeDataCache.get(new CacheKey(principal, plugInId));
+               if (object == null) {
+                       log.debug("No match found.");
+                       return null;
+               } else {
+                       CacheObject cacheObject = (CacheObject) object;
+                       if (cacheObject.isExpired()) {
+                               deleteAttributeResolution(new CacheKey(principal, plugInId));
+                               log.debug("Cached entry is expired.");
+                               return null;
+                       } else {
+                               log.debug("Located cached entry.");
+                               return (ResolverAttribute) cacheObject.getCached();
+                       }
+               }
+       }
+
+       Attributes getResolvedConnector(Principal principal, String plugInId) {
+
+               log.debug("Searching Cache for resolved connector.");
+               Object object = connectorDataCache.get(new CacheKey(principal, plugInId));
+               if (object == null) {
+                       log.debug("No match found.");
+                       return null;
+               } else {
+                       CacheObject cacheObject = (CacheObject) object;
+                       if (cacheObject.isExpired()) {
+                               deleteConnectorResolution(new CacheKey(principal, plugInId));
+                               log.debug("Cached entry is expired.");
+                               return null;
+                       } else {
+                               log.debug("Located cached entry.");
+                               return (Attributes) cacheObject.getCached();
+                       }
+               }
+       }
+
+       private void deleteAttributeResolution(CacheKey cacheKey) {
+               synchronized (attributeDataCache) {
+                       Object object = attributeDataCache.get(cacheKey);
+                       if (object != null) {
+                               CacheObject cacheObject = (CacheObject) object;
+                               if (cacheObject.isExpired()) {
+                                       attributeDataCache.remove(cacheKey);
+                               }
+                       }
+
+               }
+       }
+
+       private void deleteConnectorResolution(CacheKey cacheKey) {
+               synchronized (connectorDataCache) {
+                       Object object = connectorDataCache.get(cacheKey);
+                       if (object != null) {
+                               CacheObject cacheObject = (CacheObject) object;
+                               if (cacheObject.isExpired()) {
+                                       connectorDataCache.remove(cacheKey);
+                               }
+                       }
+
+               }
+       }
+
+       /**
+        * @see java.lang.Object#finalize()
+        */
+       protected void finalize() throws Throwable {
+               super.finalize();
+               synchronized (cleaner) {
+                       cleaner.shutdown = true;
+                       cleaner.interrupt();
+               }
+       }
+
+       private class CacheObject {
+
+               Object object;
+               long expiration;
+
+               private CacheObject(Object object, long expiration) {
+                       this.object = object;
+                       this.expiration = expiration;
+               }
+
+               private Object getCached() {
+                       return object;
+               }
+
+               private boolean isExpired() {
+                       if (System.currentTimeMillis() > expiration) {
+                               return true;
+                       } else {
+                               return false;
+                       }
+               }
+       }
+
+       private class CacheKey {
+
+               private Principal principal;
+               private String plugInId;
+
+               private CacheKey(Principal principal, String plugInId) {
+                       if (principal == null || plugInId == null) {
+                               throw new IllegalArgumentException("Cannot use null value in as cache key.");
+                       }
+                       this.principal = principal;
+                       this.plugInId = plugInId;
+               }
+               /**
+                * @see java.lang.Object#equals(Object)
+                */
+               public boolean equals(Object object) {
+                       if (object == null || !(object instanceof CacheKey)) {
+                               return false;
+                       }
+                       if (!plugInId.equals(((CacheKey) object).getPlugInId())) {
+                               return false;
+                       }
+                       if (!principal.equals(((CacheKey) object).getPrincipal())) {
+                               return false;
+                       }
+                       return true;
+               }
+
+               /**
+                * Method getPlugInId.
+                * @return Object
+                */
+               private String getPlugInId() {
+                       return plugInId;
+               }
+
+               /**
+                * Method getPrincipal.
+                * @return Object
+                */
+               private Principal getPrincipal() {
+                       return principal;
+               }
+
+               /**
+                * @see java.lang.Object#hashCode()
+                */
+               public int hashCode() {
+                       return (principal.hashCode() + ":" + plugInId.hashCode()).hashCode();
+               }
+
+       }
+
+       private class Cleaner extends Thread {
+
+               private boolean shutdown = false;
+               private Thread master;
+
+               public Cleaner(Thread master) {
+                       super();
+                       log.debug("Starting Resolver Cache cleanup thread.");
+                       this.master = master;
+                       setDaemon(true);
+                       start();
+               }
+
+               public void run() {
+                       try {
+                               sleep(5 * 60 * 1000);
+                       } catch (InterruptedException e) {
+                               log.debug("Resolver Cache Cleanup interrupted.");
+                       }
+
+                       while (true) {
+                               try {
+                                       if (!master.isAlive()) {
+                                               shutdown = true;
+                                       }
+                                       if (shutdown) {
+                                               log.debug("Stopping Resolver cache cleanup thread.");
+                                               return;
+                                       }
+
+                                       log.debug("Resolver Cache cleanup thread searching cache for stale entries.");
+                                       Hashtable[] caches = { attributeDataCache, connectorDataCache };
+
+                                       for (int i = 0; i < caches.length; i++) {
+                                               Set stale = new HashSet();
+                                               synchronized (caches[i]) {
+                                                       Set keySet = caches[i].keySet();
+                                                       Iterator cachedAttributes = keySet.iterator();
+                                                       while (cachedAttributes.hasNext()) {
+                                                               CacheKey key = (CacheKey) cachedAttributes.next();
+                                                               CacheObject object = (CacheObject) caches[i].get(key);
+                                                               if (object != null && object.isExpired()) {
+                                                                       log.debug("Found a stale resolution in the cache.");
+                                                                       stale.add(key);
+                                                               }
+                                                       }
+                                               }
+
+                                               synchronized (caches[i]) {
+                                                       if (!stale.isEmpty()) {
+                                                               Iterator stales = stale.iterator();
+                                                               while (stales.hasNext()) {
+                                                                       log.debug("Expiring stale Resolutions from the Cache.");
+                                                                       Object removed = caches[i].remove(stales.next());
+                                                                       if (removed != null) {
+                                                                               log.debug("Entry expired.");
+                                                                       } else {
+                                                                               log.debug("Couldn't expire entry.  Not found in cache.");
+                                                                       }
+                                                               }
+                                                       }
+                                               }
+                                       }
+
+                                       sleep(5 * 60 * 1000);
+
+                               } catch (InterruptedException e) {
+                                       log.debug("Resolver Cache Cleanup interrupted.");
+                               }
+                       }
+               }
+       }
+}
diff --git a/src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/BaseAttributeDefinition.java b/src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/BaseAttributeDefinition.java
new file mode 100644 (file)
index 0000000..d5a00bd
--- /dev/null
@@ -0,0 +1,144 @@
+/* 
+ * The Shibboleth License, Version 1. 
+ * Copyright (c) 2002 
+ * University Corporation for Advanced Internet Development, Inc. 
+ * All rights reserved
+ * 
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials provided with the distribution, if any, must include 
+ * the following acknowledgment: "This product includes software developed by 
+ * the University Corporation for Advanced Internet Development 
+ * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
+ * may appear in the software itself, if and wherever such third-party 
+ * acknowledgments normally appear.
+ * 
+ * Neither the name of Shibboleth nor the names of its contributors, nor 
+ * Internet2, nor the University Corporation for Advanced Internet Development, 
+ * Inc., nor UCAID may be used to endorse or promote products derived from this 
+ * software without specific prior written permission. For written permission, 
+ * please contact shibboleth@shibboleth.org
+ * 
+ * Products derived from this software may not be called Shibboleth, Internet2, 
+ * UCAID, or the University Corporation for Advanced Internet Development, nor 
+ * may Shibboleth appear in their name, without prior written permission of the 
+ * University Corporation for Advanced Internet Development.
+ * 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
+ * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
+ * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
+ * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package edu.internet2.middleware.shibboleth.aa.attrresolv.provider;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeDefinitionPlugIn;
+import edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeResolver;
+import edu.internet2.middleware.shibboleth.aa.attrresolv.ResolutionPlugInException;
+
+/**
+ * Base class for Attribute Definition PlugIns.  Provides basic functionality such as 
+ * dependency mapping.  Subclasses must provide resolution logic.
+ * 
+ * @author Walter Hoehn (wassa@columbia.edu)
+ */
+
+public abstract class BaseAttributeDefinition extends BaseResolutionPlugIn implements AttributeDefinitionPlugIn {
+       
+       protected long lifeTime = -1;
+       private static Logger log = Logger.getLogger(BaseAttributeDefinition.class.getName());
+       protected Set connectorDependencyIds = new HashSet();
+       protected Set attributeDependencyIds = new HashSet();
+
+       protected BaseAttributeDefinition(Element e) throws ResolutionPlugInException {
+
+               super(e);
+
+               String lifeTimeSpec = e.getAttribute("lifeTime");
+               if (lifeTimeSpec != null && !lifeTimeSpec.equals("")) {
+                       try {
+                               lifeTime = Long.valueOf(lifeTimeSpec).longValue();
+                               log.debug("Explicit lifetime set for attribute (" + getId() + ").  Lifetime: (" + lifeTime + ").");
+                       } catch (NumberFormatException nfe) {
+                               log.error("Bad value for attribute (lifeTime) for Attribute Definition (" + getId() + ").");
+                       }
+               }
+
+               NodeList connectorNodes = e.getElementsByTagNameNS(AttributeResolver.resolverNamespace, "DataConnectorDependency");
+
+               for (int i = 0; connectorNodes.getLength() > i; i++) {
+                       Element connector = (Element) connectorNodes.item(i);
+                       String connectorName = connector.getAttribute("requires");
+                       if (connectorName != null && !connectorName.equals("")) {
+                               addDataConnectorDependencyId(connectorName);
+                       } else {
+                               log.error("Data Connector dependency must be accomanied by a \"requires\" attribute.");
+                               throw new ResolutionPlugInException("Failed to initialize Attribute PlugIn.");
+                       }
+               }
+
+               NodeList attributeNodes = e.getElementsByTagNameNS(AttributeResolver.resolverNamespace, "AttributeDependency");
+               for (int i = 0; attributeNodes.getLength() > i; i++) {
+                       Element attribute = (Element) attributeNodes.item(i);
+                       String attributeName = attribute.getAttribute("requires");
+                       if (attributeName != null && !attributeName.equals("")) {
+                               addAttributeDefinitionDependencyId(attributeName);
+                       } else {
+                               log.error("Attribute Definition dependency must be accomanied by a \"requires\" attribute.");
+                               throw new ResolutionPlugInException("Failed to initialize Attribute PlugIn.");
+                       }
+               }
+
+               if (connectorNodes.getLength() == 0 && attributeNodes.getLength() == 0) {
+                       log.warn("Attribute " + getId() + " has no registered dependencies.");
+               }
+
+       }
+
+       protected void addDataConnectorDependencyId(String id) {
+               connectorDependencyIds.add(id);
+       }
+
+       protected void addAttributeDefinitionDependencyId(String id) {
+               attributeDependencyIds.add(id);
+       }
+
+       /**
+        * @see edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeDefinitionPlugIn#getAttributeDependencyIds()
+        */
+       public String[] getAttributeDefinitionDependencyIds() {
+               return (String[]) attributeDependencyIds.toArray(new String[0]);
+       }
+
+       /**
+        * @see edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeDefinitionPlugIn#getConnectorDependencyIds()
+        */
+       public String[] getDataConnectorDependencyIds() {
+               return (String[]) connectorDependencyIds.toArray(new String[0]);
+       }
+
+}
diff --git a/src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/BaseResolutionPlugIn.java b/src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/BaseResolutionPlugIn.java
new file mode 100644 (file)
index 0000000..6aaad84
--- /dev/null
@@ -0,0 +1,96 @@
+/* 
+ * The Shibboleth License, Version 1. 
+ * Copyright (c) 2002 
+ * University Corporation for Advanced Internet Development, Inc. 
+ * All rights reserved
+ * 
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials provided with the distribution, if any, must include 
+ * the following acknowledgment: "This product includes software developed by 
+ * the University Corporation for Advanced Internet Development 
+ * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
+ * may appear in the software itself, if and wherever such third-party 
+ * acknowledgments normally appear.
+ * 
+ * Neither the name of Shibboleth nor the names of its contributors, nor 
+ * Internet2, nor the University Corporation for Advanced Internet Development, 
+ * Inc., nor UCAID may be used to endorse or promote products derived from this 
+ * software without specific prior written permission. For written permission, 
+ * please contact shibboleth@shibboleth.org
+ * 
+ * Products derived from this software may not be called Shibboleth, Internet2, 
+ * UCAID, or the University Corporation for Advanced Internet Development, nor 
+ * may Shibboleth appear in their name, without prior written permission of the 
+ * University Corporation for Advanced Internet Development.
+ * 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
+ * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
+ * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
+ * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package edu.internet2.middleware.shibboleth.aa.attrresolv.provider;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Element;
+
+import edu.internet2.middleware.shibboleth.aa.attrresolv.ResolutionPlugInException;
+
+/**
+ * Base class for Resolution PlugIns, both <code>AttributeDefinitionPlugIn</code> & <code>DataConnectorPlugIn</code>
+ * 
+ * @author Walter Hoehn (wassa@columbia.edu)
+ */
+
+public abstract class BaseResolutionPlugIn {
+
+       private static Logger log = Logger.getLogger(BaseResolutionPlugIn.class.getName());
+       protected String id;
+       protected long ttl = 0;
+
+       protected BaseResolutionPlugIn(Element e) throws ResolutionPlugInException {
+               String id = e.getAttribute("id");
+               if (id == null || id.equals("")) {
+                       log.error("Attribute \"id\" required to configure plugin.");
+                       throw new ResolutionPlugInException("Failed to initialize Resolution PlugIn.");
+               }
+               this.id = id;
+
+               String cacheTime = e.getAttribute("cacheTime");
+               if (cacheTime != null && !cacheTime.equals("")) {
+                       try {
+                               this.ttl = Long.parseLong(cacheTime);
+                       } catch (NumberFormatException nfe) {
+                               log.error("Attribute \"cacheTime\" must be an integer between 0 and " + Long.MAX_VALUE + ".");
+                               throw new ResolutionPlugInException("Failed to initialize Resolution PlugIn.");
+                       }
+               }
+       }
+
+       public String getId() {
+               return id;
+       }
+
+       public long getTTL() {
+               return ttl;
+       }
+
+}
diff --git a/src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/CustomAttributeDefinition.java b/src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/CustomAttributeDefinition.java
new file mode 100644 (file)
index 0000000..d16ea91
--- /dev/null
@@ -0,0 +1,137 @@
+/* 
+ * The Shibboleth License, Version 1. 
+ * Copyright (c) 2002 
+ * University Corporation for Advanced Internet Development, Inc. 
+ * All rights reserved
+ * 
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials provided with the distribution, if any, must include 
+ * the following acknowledgment: "This product includes software developed by 
+ * the University Corporation for Advanced Internet Development 
+ * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
+ * may appear in the software itself, if and wherever such third-party 
+ * acknowledgments normally appear.
+ * 
+ * Neither the name of Shibboleth nor the names of its contributors, nor 
+ * Internet2, nor the University Corporation for Advanced Internet Development, 
+ * Inc., nor UCAID may be used to endorse or promote products derived from this 
+ * software without specific prior written permission. For written permission, 
+ * please contact shibboleth@shibboleth.org
+ * 
+ * Products derived from this software may not be called Shibboleth, Internet2, 
+ * UCAID, or the University Corporation for Advanced Internet Development, nor 
+ * may Shibboleth appear in their name, without prior written permission of the 
+ * University Corporation for Advanced Internet Development.
+ * 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
+ * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
+ * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
+ * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package edu.internet2.middleware.shibboleth.aa.attrresolv.provider;
+
+import java.security.Principal;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Element;
+
+import edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeDefinitionPlugIn;
+import edu.internet2.middleware.shibboleth.aa.attrresolv.Dependencies;
+import edu.internet2.middleware.shibboleth.aa.attrresolv.ResolutionPlugIn;
+import edu.internet2.middleware.shibboleth.aa.attrresolv.ResolutionPlugInException;
+import edu.internet2.middleware.shibboleth.aa.attrresolv.ResolverAttribute;
+
+/**
+ * 
+ * Wrapper class for custom <code>AttributeDefinitionPlugIn</code> implementations.
+ * 
+ * @author Walter Hoehn (wassa@columbia.edu)
+ *
+ */
+public class CustomAttributeDefinition implements ResolutionPlugIn, AttributeDefinitionPlugIn {
+
+       private static Logger log = Logger.getLogger(CustomAttributeDefinition.class.getName());
+       private AttributeDefinitionPlugIn custom;
+
+       public CustomAttributeDefinition(Element e) throws ResolutionPlugInException {
+               if (!e.getTagName().equals("CustomAttributeDefinition")) {
+                       log.error("Incorrect attribute definition configuration: expected <CustomAttributeDefinition> .");
+                       throw new ResolutionPlugInException("Failed to initialize Attribute Definition PlugIn.");
+               }
+
+               String className = e.getAttribute("class");
+               if (className == null || className.equals("")) {
+                       log.error("Custom Attribute Definition requires specification of the attribute \"class\".");
+                       throw new ResolutionPlugInException("Failed to initialize Attribute Definition PlugIn.");
+               } else {
+                       try {
+                               Class[] params = { Class.forName("org.w3c.dom.Element"), };
+                               Object[] passElement = { e };
+                               custom =
+                                       (AttributeDefinitionPlugIn) Class.forName(className).getConstructor(params).newInstance(
+                                               passElement);
+                       } catch (Exception loaderException) {
+                               log.error(
+                                       "Failed to load Custom Attribute Definition PlugIn implementation class: "
+                                               + loaderException.getMessage());
+                               throw new ResolutionPlugInException("Failed to initialize Attribute Definition PlugIn.");
+                       }
+               }
+       }
+
+       /**
+        * @see edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeDefinitionPlugIn#resolve(edu.internet2.middleware.shibboleth.aa.attrresolv.ResolverAttribute, java.security.Principal, java.lang.String, edu.internet2.middleware.shibboleth.aa.attrresolv.Dependencies)
+        */
+       public void resolve(ResolverAttribute attribute, Principal principal, String requester, Dependencies depends)
+               throws ResolutionPlugInException {
+               custom.resolve(attribute, principal, requester, depends);
+       }
+
+       /**
+        * @see edu.internet2.middleware.shibboleth.aa.attrresolv.BaseResolutionPlugIn#getId()
+        */
+       public String getId() {
+               return custom.getId();
+       }
+
+       /**
+        * @see edu.internet2.middleware.shibboleth.aa.attrresolv.provider.BaseResolutionPlugIn#getTTL()
+        */
+       public long getTTL() {
+               return custom.getTTL();
+       }
+
+       /**
+        * @see edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeDefinitionPlugIn#getAttributeDefinitionDependencyIds()
+        */
+       public String[] getAttributeDefinitionDependencyIds() {
+               return custom.getAttributeDefinitionDependencyIds();
+       }
+
+       /**
+        * @see edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeDefinitionPlugIn#getDataConnectorDependencyIds()
+        */
+       public String[] getDataConnectorDependencyIds() {
+               return custom.getDataConnectorDependencyIds();
+       }
+
+}
diff --git a/src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/CustomDataConnector.java b/src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/CustomDataConnector.java
new file mode 100644 (file)
index 0000000..8c6b405
--- /dev/null
@@ -0,0 +1,118 @@
+/* 
+ * The Shibboleth License, Version 1. 
+ * Copyright (c) 2002 
+ * University Corporation for Advanced Internet Development, Inc. 
+ * All rights reserved
+ * 
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials provided with the distribution, if any, must include 
+ * the following acknowledgment: "This product includes software developed by 
+ * the University Corporation for Advanced Internet Development 
+ * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
+ * may appear in the software itself, if and wherever such third-party 
+ * acknowledgments normally appear.
+ * 
+ * Neither the name of Shibboleth nor the names of its contributors, nor 
+ * Internet2, nor the University Corporation for Advanced Internet Development, 
+ * Inc., nor UCAID may be used to endorse or promote products derived from this 
+ * software without specific prior written permission. For written permission, 
+ * please contact shibboleth@shibboleth.org
+ * 
+ * Products derived from this software may not be called Shibboleth, Internet2, 
+ * UCAID, or the University Corporation for Advanced Internet Development, nor 
+ * may Shibboleth appear in their name, without prior written permission of the 
+ * University Corporation for Advanced Internet Development.
+ * 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
+ * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
+ * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
+ * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package edu.internet2.middleware.shibboleth.aa.attrresolv.provider;
+
+import java.security.Principal;
+
+import javax.naming.directory.Attributes;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Element;
+
+import edu.internet2.middleware.shibboleth.aa.attrresolv.DataConnectorPlugIn;
+import edu.internet2.middleware.shibboleth.aa.attrresolv.ResolutionPlugInException;
+
+/**
+ * 
+ * Wrapper class for custom <code>DataConnectorPlugIn</code> implementations.
+ * 
+ * @author Walter Hoehn (wassa@columbia.edu)
+ *
+ */
+public class CustomDataConnector implements DataConnectorPlugIn {
+
+       private static Logger log = Logger.getLogger(CustomDataConnector.class.getName());
+       private DataConnectorPlugIn custom;
+
+       public CustomDataConnector(Element e) throws ResolutionPlugInException {
+               if (!e.getTagName().equals("CustomDataConnector")) {
+                       log.error("Incorrect connector configuration: expected <CustomDataConnector> .");
+                       throw new ResolutionPlugInException("Failed to initialize Connector PlugIn.");
+               }
+
+               String className = e.getAttribute("class");
+               if (className == null || className.equals("")) {
+                       log.error("Custom Data Connector requires specification of attributes \"class\".");
+                       throw new ResolutionPlugInException("Failed to initialize Connector PlugIn.");
+               } else {
+                       try {
+                               Class[] params = { Class.forName("org.w3c.dom.Element"), };
+                               Object[] passElement = { e };
+                               custom = (DataConnectorPlugIn) Class.forName(className).getConstructor(params).newInstance(passElement);
+                       } catch (Exception loaderException) {
+                               log.error(
+                                       "Failed to load Custom Connector PlugIn implementation class: " + loaderException.getMessage());
+                               throw new ResolutionPlugInException("Failed to initialize Connector PlugIn.");
+                       }
+               }
+       }
+
+       /**
+        * @see edu.internet2.middleware.shibboleth.aa.attrresolv.DataConnectorPlugIn#resolve(Principal)
+        */
+       public Attributes resolve(Principal principal) throws ResolutionPlugInException {
+               return custom.resolve(principal);
+       }
+
+       /**
+        * @see edu.internet2.middleware.shibboleth.aa.attrresolv.BaseResolutionPlugIn#getId()
+        */
+       public String getId() {
+               return custom.getId();
+       }
+
+       /**
+        * @see edu.internet2.middleware.shibboleth.aa.attrresolv.provider.BaseResolutionPlugIn#getTTL()
+        */
+       public long getTTL() {
+               return custom.getTTL();
+       }
+
+}
diff --git a/src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/JNDIDirectoryDataConnector.java b/src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/JNDIDirectoryDataConnector.java
new file mode 100644 (file)
index 0000000..dd94f32
--- /dev/null
@@ -0,0 +1,241 @@
+/* 
+ * The Shibboleth License, Version 1. 
+ * Copyright (c) 2002 
+ * University Corporation for Advanced Internet Development, Inc. 
+ * All rights reserved
+ * 
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials provided with the distribution, if any, must include 
+ * the following acknowledgment: "This product includes software developed by 
+ * the University Corporation for Advanced Internet Development 
+ * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
+ * may appear in the software itself, if and wherever such third-party 
+ * acknowledgments normally appear.
+ * 
+ * Neither the name of Shibboleth nor the names of its contributors, nor 
+ * Internet2, nor the University Corporation for Advanced Internet Development, 
+ * Inc., nor UCAID may be used to endorse or promote products derived from this 
+ * software without specific prior written permission. For written permission, 
+ * please contact shibboleth@shibboleth.org
+ * 
+ * Products derived from this software may not be called Shibboleth, Internet2, 
+ * UCAID, or the University Corporation for Advanced Internet Development, nor 
+ * may Shibboleth appear in their name, without prior written permission of the 
+ * University Corporation for Advanced Internet Development.
+ * 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
+ * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
+ * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
+ * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package edu.internet2.middleware.shibboleth.aa.attrresolv.provider;
+
+import java.security.Principal;
+import java.util.Hashtable;
+
+import javax.naming.CommunicationException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.InitialDirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeResolver;
+import edu.internet2.middleware.shibboleth.aa.attrresolv.DataConnectorPlugIn;
+import edu.internet2.middleware.shibboleth.aa.attrresolv.ResolutionPlugInException;
+
+/**
+ * <code>DataConnectorPlugIn</code> implementation that utilizes a user specified JNDI 
+ * <code>DirContext</code> to retrieve attribute data.
+ * 
+ * @author Walter Hoehn (wassa@columbia.edu)
+ *
+ */
+public class JNDIDirectoryDataConnector extends BaseResolutionPlugIn implements DataConnectorPlugIn {
+
+       private static Logger log = Logger.getLogger(JNDIDirectoryDataConnector.class.getName());
+       protected String searchFilter;
+       protected InitialDirContext context;
+       protected SearchControls controls;
+
+       public JNDIDirectoryDataConnector(Element e) throws ResolutionPlugInException {
+
+               super(e);
+
+               NodeList searchNodes = e.getElementsByTagNameNS(AttributeResolver.resolverNamespace, "Search");
+               if (searchNodes.getLength() != 1) {
+                       log.error("JNDI Directory Data Connector requires a \"Search\" specification.");
+                       throw new ResolutionPlugInException("JNDI Directory Data Connector requires a \"Search\" specification.");
+               }
+
+               String searchFilterSpec = ((Element) searchNodes.item(0)).getAttribute("filter");
+               if (searchFilterSpec != null && !searchFilterSpec.equals("")) {
+                       searchFilter = searchFilterSpec;
+                       log.debug("Search Filter: (" + searchFilter + ").");
+               } else {
+                       log.error("Search spec requires a filter attribute.");
+                       throw new ResolutionPlugInException("Search spec requires a filter attribute.");
+               }
+
+               defineSearchControls(((Element) searchNodes.item(0)));
+
+               NodeList propertyNodes = e.getElementsByTagNameNS(AttributeResolver.resolverNamespace, "Property");
+               Hashtable env = new Hashtable();
+               for (int i = 0; propertyNodes.getLength() > i; i++) {
+                       Element property = (Element) propertyNodes.item(i);
+                       String propName = property.getAttribute("name");
+                       String propValue = property.getAttribute("value");
+                       if (propName != null && !propName.equals("") && propValue != null && !propValue.equals("")) {
+                               env.put(propName, propValue);
+                               log.debug("Property: (" + propName + ").");
+                               log.debug("   Value: (" + propValue + ").");
+                       } else {
+                               log.error("Property is malformed.");
+                               throw new ResolutionPlugInException("Property is malformed.");
+                       }
+               }
+               try {
+                       context = new InitialDirContext(env);
+                       log.debug("JNDI Directory context activated.");
+               } catch (NamingException e1) {
+                       log.error("Failed to startup directory context: " + e1);
+                       throw new ResolutionPlugInException("Failed to startup directory context.");
+               }
+
+       }
+
+       protected void defineSearchControls(Element searchNode) {
+
+               controls = new SearchControls();
+
+               NodeList controlNodes = searchNode.getElementsByTagNameNS(AttributeResolver.resolverNamespace, "Controls");
+               if (controlNodes.getLength() < 1) {
+                       log.debug("No Search Control spec found.");
+               } else {
+                       if (controlNodes.getLength() > 1) {
+                               log.error("Found multiple Search Control specs for a Connector.  Ignoring all but the first.");
+                       }
+
+                       String searchScopeSpec = ((Element) controlNodes.item(0)).getAttribute("searchScope");
+                       if (searchScopeSpec != null && !searchScopeSpec.equals("")) {
+                               if (searchScopeSpec.equals("OBJECT_SCOPE")) {
+                                       controls.setSearchScope(SearchControls.OBJECT_SCOPE);
+                               } else if (searchScopeSpec.equals("ONELEVEL_SCOPE")) {
+                                       controls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
+                               } else if (searchScopeSpec.equals("SUBTREE_SCOPE")) {
+                                       controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+                               } else {
+                                       try {
+                                               controls.setSearchScope(Integer.parseInt(searchScopeSpec));
+                                       } catch (NumberFormatException nfe) {
+                                               log.error("Control spec included an invalid (searchScope) attribute value.");
+                                       }
+                               }
+                       }
+
+                       String timeLimitSpec = ((Element) controlNodes.item(0)).getAttribute("timeLimit");
+                       if (timeLimitSpec != null && !timeLimitSpec.equals("")) {
+                               try {
+                                       System.err.println("hey");
+                                       controls.setTimeLimit(Integer.parseInt(timeLimitSpec));
+                               } catch (NumberFormatException nfe) {
+                                       log.error("Control spec included an invalid (timeLimit) attribute value.");
+                               }
+                       }
+
+                       String returningObjectsSpec = ((Element) controlNodes.item(0)).getAttribute("returningObjects");
+                       if (returningObjectsSpec != null && !returningObjectsSpec.equals("")) {
+                               controls.setReturningObjFlag(new Boolean(returningObjectsSpec).booleanValue());
+                       }
+
+                       String linkDereferencingSpec = ((Element) controlNodes.item(0)).getAttribute("linkDereferencing");
+                       if (linkDereferencingSpec != null && !linkDereferencingSpec.equals("")) {
+                               if (linkDereferencingSpec != null && !linkDereferencingSpec.equals("")) {
+                                       controls.setDerefLinkFlag(new Boolean(linkDereferencingSpec).booleanValue());
+                               }
+                       }
+
+                       String countLimitSpec = ((Element) controlNodes.item(0)).getAttribute("countLimit");
+                       if (countLimitSpec != null && !countLimitSpec.equals("")) {
+                               try {
+                                       controls.setCountLimit(Long.parseLong(countLimitSpec));
+                               } catch (NumberFormatException nfe) {
+                                       log.error("Control spec included an invalid (countLimit) attribute value.");
+                               }
+                       }
+               }
+
+               if (log.isDebugEnabled()) {
+                       log.debug("Search Control (searchScope): " + controls.getSearchScope());
+                       log.debug("Search Control (timeLimit): " + controls.getTimeLimit());
+                       log.debug("Search Control (returningObjects): " + controls.getReturningObjFlag());
+                       log.debug("Search Control (linkDereferencing): " + controls.getDerefLinkFlag());
+                       log.debug("Search Control (countLimit): " + controls.getCountLimit());
+               }
+       }
+
+       /**
+        * @see edu.internet2.middleware.shibboleth.aa.attrresolv.DataConnectorPlugIn#resolve(java.security.Principal)
+        */
+       public Attributes resolve(Principal principal) throws ResolutionPlugInException {
+               try {
+                       NamingEnumeration enum = null;
+                       try {
+                               enum = context.search("", searchFilter.replaceAll("%PRINCIPAL%", principal.getName()), controls);
+                       } catch (CommunicationException e) {
+                               log.debug(e);
+                               log.warn(
+                                       "Encountered a connection problem while querying for attributes.  Re-initializing JNDI context and retrying...");
+                               context = new InitialDirContext(context.getEnvironment());
+                               enum = context.search("", searchFilter.replaceAll("%PRINCIPAL%", principal.getName()), controls);
+                       }
+
+                       if (enum == null || !enum.hasMore()) {
+                               log.error("Could not locate a principal with the name (" + principal.getName() + ").");
+                               throw new ResolutionPlugInException("No data available for this principal.");
+                       }
+
+                       SearchResult result = (SearchResult) enum.next();
+                       Attributes attributes = result.getAttributes();
+
+                       if (enum.hasMore()) {
+                               log.error("Unable to disambiguate date for principal (" + principal.getName() + ") in search.");
+                               throw new ResolutionPlugInException("Cannot disambiguate data for this principal.");
+                       }
+                       return attributes;
+
+               } catch (NamingException e) {
+                       log.error(
+                               "An error occurred while retieving data for principal ("
+                                       + principal.getName()
+                                       + ") :"
+                                       + e.getMessage());
+                       throw new ResolutionPlugInException("Error retrieving data for principal.");
+               }
+       }
+
+}
diff --git a/src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/SampleConnector.java b/src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/SampleConnector.java
new file mode 100644 (file)
index 0000000..7c729d2
--- /dev/null
@@ -0,0 +1,94 @@
+/* 
+ * The Shibboleth License, Version 1. 
+ * Copyright (c) 2002 
+ * University Corporation for Advanced Internet Development, Inc. 
+ * All rights reserved
+ * 
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials provided with the distribution, if any, must include 
+ * the following acknowledgment: "This product includes software developed by 
+ * the University Corporation for Advanced Internet Development 
+ * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
+ * may appear in the software itself, if and wherever such third-party 
+ * acknowledgments normally appear.
+ * 
+ * Neither the name of Shibboleth nor the names of its contributors, nor 
+ * Internet2, nor the University Corporation for Advanced Internet Development, 
+ * Inc., nor UCAID may be used to endorse or promote products derived from this 
+ * software without specific prior written permission. For written permission, 
+ * please contact shibboleth@shibboleth.org
+ * 
+ * Products derived from this software may not be called Shibboleth, Internet2, 
+ * UCAID, or the University Corporation for Advanced Internet Development, nor 
+ * may Shibboleth appear in their name, without prior written permission of the 
+ * University Corporation for Advanced Internet Development.
+ * 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
+ * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
+ * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
+ * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package edu.internet2.middleware.shibboleth.aa.attrresolv.provider;
+
+import java.security.Principal;
+
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.BasicAttributes;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Element;
+
+import edu.internet2.middleware.shibboleth.aa.attrresolv.DataConnectorPlugIn;
+import edu.internet2.middleware.shibboleth.aa.attrresolv.ResolutionPlugInException;
+
+/**
+ * Sample <code>DataConnectorPlugIn</code> implementation.  Echos principal name
+ * in EPPN.
+ * 
+ * @author Walter Hoehn
+ *
+ */
+public class SampleConnector extends BaseResolutionPlugIn implements DataConnectorPlugIn {
+
+       private static Logger log = Logger.getLogger(SampleConnector.class.getName());
+
+       public SampleConnector(Element e) throws ResolutionPlugInException {
+               super(e);
+       }
+
+       /**
+        * @see edu.internet2.middleware.shibboleth.aa.attrresolv.DataConnectorPlugIn#resolve(Principal)
+        */
+       public Attributes resolve(Principal principal) {
+
+               log.debug("Resolving connector: (" + getId() + ")");
+               log.debug(getId() + " resolving for principal: (" + principal.getName() + ")");
+
+               BasicAttributes attributes = new BasicAttributes();
+               attributes.put(new BasicAttribute("eduPersonPrincipalName", principal.getName()));
+               attributes.put(new BasicAttribute("eduPersonAffiliation", "member"));
+               attributes.put(new BasicAttribute("eduPersonEntitlement", "urn:mace:example.edu:exampleEntitlement"));
+               return attributes;
+       }
+
+}
diff --git a/src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/ScopedStringValueHandler.java b/src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/ScopedStringValueHandler.java
new file mode 100644 (file)
index 0000000..aceeaa0
--- /dev/null
@@ -0,0 +1,120 @@
+/* 
+ * The Shibboleth License, Version 1. 
+ * Copyright (c) 2002 
+ * University Corporation for Advanced Internet Development, Inc. 
+ * All rights reserved
+ * 
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials provided with the distribution, if any, must include 
+ * the following acknowledgment: "This product includes software developed by 
+ * the University Corporation for Advanced Internet Development 
+ * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
+ * may appear in the software itself, if and wherever such third-party 
+ * acknowledgments normally appear.
+ * 
+ * Neither the name of Shibboleth nor the names of its contributors, nor 
+ * Internet2, nor the University Corporation for Advanced Internet Development, 
+ * Inc., nor UCAID may be used to endorse or promote products derived from this 
+ * software without specific prior written permission. For written permission, 
+ * please contact shibboleth@shibboleth.org
+ * 
+ * Products derived from this software may not be called Shibboleth, Internet2, 
+ * UCAID, or the University Corporation for Advanced Internet Development, nor 
+ * may Shibboleth appear in their name, without prior written permission of the 
+ * University Corporation for Advanced Internet Development.
+ * 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
+ * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
+ * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
+ * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package edu.internet2.middleware.shibboleth.aa.attrresolv.provider;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * <code>ValueHandler</code> implementation for String objects with a domain scope.  Includes logic to add
+ * a default scope to values that don't already include a scope.
+ *
+ * @author Walter Hoehn (wassa@columbia.edu)
+ */
+class ScopedStringValueHandler implements ValueHandler {
+
+       private static Logger log = Logger.getLogger(ScopedStringValueHandler.class.getName());
+       public String smartScope;
+
+       public ScopedStringValueHandler(String smartScope) {
+               this.smartScope = smartScope;
+       }
+
+       /**
+        * @see edu.internet2.middleware.shibboleth.aa.attrresolv.provider.ValueHandler#toDOM(org.w3c.dom.Element, java.lang.Object, org.w3c.dom.Document)
+        */
+       public void toDOM(Element valueElement, Object value, Document document) throws ValueHandlerException {
+
+               if (value instanceof String) {
+                       String raw = (String) value;
+                       int divider = raw.indexOf("@");
+                       if (divider > 0) {
+                               log.debug("Using scope (" + raw.substring(divider + 1) + ") for value.");
+                               valueElement.appendChild(document.createTextNode(raw.substring(0, divider)));
+                               valueElement.setAttributeNS(null, "Scope", raw.substring(divider + 1));
+                       } else {
+                               log.debug("Adding defult scope of (" + smartScope + ") to value.");
+                               valueElement.appendChild(document.createTextNode(raw));
+                               valueElement.setAttributeNS(null, "Scope", smartScope);
+                       }
+                       return;
+               }
+               throw new ValueHandlerException("ScopedStringValueHandler called for non-String object.");
+       }
+
+       /**
+        * @see edu.internet2.middleware.shibboleth.aa.attrresolv.provider.ValueHandler#getValues(java.util.Collection)
+        */
+       public Iterator getValues(Collection internalValues) {
+
+               ArrayList values = new ArrayList();
+               for (Iterator iterator = internalValues.iterator(); iterator.hasNext();) {
+                       Object value = iterator.next();
+                       if (value instanceof String) {
+                               String raw = (String) value;
+                               int divider = raw.indexOf("@");
+                               if (divider > 0) {
+                                       values.add(raw);
+                               } else {
+                                       values.add(raw + "@" + smartScope);
+                               }
+                               continue;
+                       }
+                       log.error("ScopedStringValueHandler called for non-String object.");
+               }
+               return values.iterator();
+       }
+
+}
diff --git a/src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/ShibAttribute.java b/src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/ShibAttribute.java
new file mode 100644 (file)
index 0000000..862167d
--- /dev/null
@@ -0,0 +1,223 @@
+/* 
+ * The Shibboleth License, Version 1. 
+ * Copyright (c) 2002 
+ * University Corporation for Advanced Internet Development, Inc. 
+ * All rights reserved
+ * 
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials provided with the distribution, if any, must include 
+ * the following acknowledgment: "This product includes software developed by 
+ * the University Corporation for Advanced Internet Development 
+ * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
+ * may appear in the software itself, if and wherever such third-party 
+ * acknowledgments normally appear.
+ * 
+ * Neither the name of Shibboleth nor the names of its contributors, nor 
+ * Internet2, nor the University Corporation for Advanced Internet Development, 
+ * Inc., nor UCAID may be used to endorse or promote products derived from this 
+ * software without specific prior written permission. For written permission, 
+ * please contact shibboleth@shibboleth.org
+ * 
+ * Products derived from this software may not be called Shibboleth, Internet2, 
+ * UCAID, or the University Corporation for Advanced Internet Development, nor 
+ * may Shibboleth appear in their name, without prior written permission of the 
+ * University Corporation for Advanced Internet Development.
+ * 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
+ * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
+ * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
+ * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package edu.internet2.middleware.shibboleth.aa.attrresolv.provider;
+
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.log4j.Logger;
+import org.opensaml.QName;
+import org.opensaml.SAMLAttribute;
+import org.opensaml.SAMLException;
+import org.opensaml.XML;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import edu.internet2.middleware.shibboleth.aa.attrresolv.ResolverAttribute;
+import edu.internet2.middleware.shibboleth.common.Constants;
+
+/**
+ * An attribute for which the Shibboleth Attribute Authority has been asked
+ * to provide an assertion.
+ * 
+ * @author Walter Hoehn (wassa@columbia.edu)
+ */
+public class ShibAttribute extends SAMLAttribute implements ResolverAttribute {
+
+       private static Logger log = Logger.getLogger(ShibAttribute.class.getName());
+       private boolean resolved = false;
+       private static long defaultLifetime = 1800000;
+       private ValueHandler valueHandler = new StringValueHandler();
+
+       public ShibAttribute(String name) throws SAMLException {
+               super(
+                       name,
+                       Constants.SHIB_ATTRIBUTE_NAMESPACE_URI,
+                       new QName("shibNameSpace", "shibLocalName"),
+                       defaultLifetime,
+                       null);
+       }
+
+       public boolean hasValues() {
+               if (values.isEmpty()) {
+                       return false;
+               }
+               return true;
+       }
+
+       public Iterator getValues() {
+               return valueHandler.getValues(values);
+       }
+
+       private void setValues(Object[] values) {
+               if (!this.values.isEmpty()) {
+                       this.values.clear();
+               }
+               this.values.addAll(Arrays.asList(values));
+       }
+
+       /**
+       * @see java.lang.Object#hashCode()
+       */
+       public int hashCode() {
+               int code = 0;
+               if (values != null) {
+                       Iterator iterator = values.iterator();
+                       while (iterator.hasNext()) {
+                               code += iterator.next().hashCode();
+                       }
+               }
+               return name.hashCode() + code;
+       }
+
+       /**
+        * @see edu.internet2.middleware.shibboleth.aa.attrresolv.ResolverAttribute#resolved()
+        */
+       public boolean resolved() {
+               return resolved;
+       }
+
+       /**
+        * @see edu.internet2.middleware.shibboleth.aa.attrresolv.ResolverAttribute#setResolved()
+        */
+       public void setResolved() {
+               resolved = true;
+       }
+
+       /**
+        * @see edu.internet2.middleware.shibboleth.aa.attrresolv.ResolverAttribute#resolveFromCached(edu.internet2.middleware.shibboleth.aa.attrresolv.ResolverAttribute)
+        */
+       public void resolveFromCached(ResolverAttribute attribute) {
+               resolved = true;
+               setLifetime(attribute.getLifetime());
+
+               if (!this.values.isEmpty()) {
+                       this.values.clear();
+               }
+               for (Iterator iterator = attribute.getValues(); iterator.hasNext();) {
+                       values.add(iterator.next());
+               }
+
+               registerValueHandler(attribute.getRegisteredValueHandler());
+       }
+
+       public void setLifetime(long lifetime) {
+               this.lifetime = lifetime;
+
+       }
+
+       public void addValue(Object value) {
+               values.add(value);
+       }
+
+       /**
+        * @see org.opensaml.SAMLObject#toDOM(org.w3c.dom.Document)
+        */
+       public Node toDOM(Document doc) {
+
+               Element attributeElement = doc.createElementNS(XML.SAML_NS, "Attribute");
+               attributeElement.setAttributeNS(null, "AttributeName", name);
+               attributeElement.setAttributeNS(null, "AttributeNamespace", namespace);
+
+               for (int i = 0; i < values.size(); i++) {
+
+                       attributeElement.setAttributeNS(XML.XMLNS_NS, "xmlns:typens", type.getNamespaceURI());
+
+                       Element valueElement = doc.createElementNS(XML.SAML_NS, "AttributeValue");
+                       valueElement.setAttributeNS(XML.XSI_NS, "xsi:type", "typens:" + type.getLocalName());
+
+                       try {
+                               valueHandler.toDOM(valueElement, values.get(i), doc);
+                               attributeElement.appendChild(valueElement);
+
+                       } catch (ValueHandlerException e) {
+                               log.error("Value Handler unable to convert value to DOM Node: " + e);
+                       }
+               }
+               return attributeElement;
+       }
+
+       /**
+        * @see edu.internet2.middleware.shibboleth.aa.attrresolv.ResolverAttribute#registerValueHandler(edu.internet2.middleware.shibboleth.aa.attrresolv.provider.ValueHandler)
+        */
+       public void registerValueHandler(ValueHandler handler) {
+               valueHandler = handler;
+       }
+
+       /**
+        * @see edu.internet2.middleware.shibboleth.aa.attrresolv.ResolverAttribute#getRegisteredValueHandler()
+        */
+       public ValueHandler getRegisteredValueHandler() {
+               return valueHandler;
+       }
+
+}
+
+/**
+ *  Default <code>ValueHandler</code> implementation.  Expects all values to be String objects.
+ *
+ * @author Walter Hoehn (wassa@columbia.edu)
+ */
+class StringValueHandler implements ValueHandler {
+
+       public void toDOM(Element valueElement, Object value, Document document) {
+               valueElement.appendChild(document.createTextNode(value.toString()));
+       }
+
+       public Iterator getValues(Collection internalValues) {
+               return internalValues.iterator();
+       }
+
+}
+
+
diff --git a/src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/ShibAttributeSet.java b/src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/ShibAttributeSet.java
new file mode 100644 (file)
index 0000000..1e76135
--- /dev/null
@@ -0,0 +1,111 @@
+/* 
+ * The Shibboleth License, Version 1. 
+ * Copyright (c) 2002 
+ * University Corporation for Advanced Internet Development, Inc. 
+ * All rights reserved
+ * 
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials provided with the distribution, if any, must include 
+ * the following acknowledgment: "This product includes software developed by 
+ * the University Corporation for Advanced Internet Development 
+ * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
+ * may appear in the software itself, if and wherever such third-party 
+ * acknowledgments normally appear.
+ * 
+ * Neither the name of Shibboleth nor the names of its contributors, nor 
+ * Internet2, nor the University Corporation for Advanced Internet Development, 
+ * Inc., nor UCAID may be used to endorse or promote products derived from this 
+ * software without specific prior written permission. For written permission, 
+ * please contact shibboleth@shibboleth.org
+ * 
+ * Products derived from this software may not be called Shibboleth, Internet2, 
+ * UCAID, or the University Corporation for Advanced Internet Development, nor 
+ * may Shibboleth appear in their name, without prior written permission of the 
+ * University Corporation for Advanced Internet Development.
+ * 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
+ * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
+ * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
+ * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package edu.internet2.middleware.shibboleth.aa.attrresolv.provider;
+
+import java.util.HashMap;
+import java.util.Iterator;
+
+import edu.internet2.middleware.shibboleth.aa.attrresolv.ResolverAttribute;
+import edu.internet2.middleware.shibboleth.aa.attrresolv.ResolverAttributeSet;
+
+/**
+ * A set of attributes for which the Shibboleth Attribute Authority has been asked
+ * to provide assertions.
+ * 
+ * @author Walter Hoehn (wassa@columbia.edu)
+ */
+public class ShibAttributeSet implements ResolverAttributeSet {
+
+       private HashMap attributes = new HashMap();
+
+       public void add(ShibAttribute attribute) {
+               attributes.put(attribute.getName(), attribute);
+       }
+
+       public ResolverAttribute getByName(String name) {
+               return (ResolverAttribute) attributes.get(name);
+       }
+
+       public ShibAttributeIterator shibAttributeIterator() {
+               return new ShibAttributeIterator(attributes.values().iterator());
+       }
+
+       public ResolverAttributeIterator resolverAttributeIterator() {
+               return shibAttributeIterator();
+       }
+
+       public class ShibAttributeIterator implements ResolverAttributeIterator {
+
+               private Iterator genericIterator;
+
+               private ShibAttributeIterator(Iterator iterator) {
+                       genericIterator = iterator;
+               }
+
+               public boolean hasNext() {
+                       return genericIterator.hasNext();
+               }
+
+               public ResolverAttribute nextResolverAttribute() {
+                       return nextShibAttribute();
+               }
+
+               public ShibAttribute nextShibAttribute() {
+                       return (ShibAttribute) genericIterator.next();
+               }
+
+               public void remove() {
+                       genericIterator.remove();
+               }
+
+       }
+
+}
+
diff --git a/src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/SimpleAttributeDefinition.java b/src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/SimpleAttributeDefinition.java
new file mode 100644 (file)
index 0000000..e8f12eb
--- /dev/null
@@ -0,0 +1,239 @@
+/* 
+ * The Shibboleth License, Version 1. 
+ * Copyright (c) 2002 
+ * University Corporation for Advanced Internet Development, Inc. 
+ * All rights reserved
+ * 
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials provided with the distribution, if any, must include 
+ * the following acknowledgment: "This product includes software developed by 
+ * the University Corporation for Advanced Internet Development 
+ * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
+ * may appear in the software itself, if and wherever such third-party 
+ * acknowledgments normally appear.
+ * 
+ * Neither the name of Shibboleth nor the names of its contributors, nor 
+ * Internet2, nor the University Corporation for Advanced Internet Development, 
+ * Inc., nor UCAID may be used to endorse or promote products derived from this 
+ * software without specific prior written permission. For written permission, 
+ * please contact shibboleth@shibboleth.org
+ * 
+ * Products derived from this software may not be called Shibboleth, Internet2, 
+ * UCAID, or the University Corporation for Advanced Internet Development, nor 
+ * may Shibboleth appear in their name, without prior written permission of the 
+ * University Corporation for Advanced Internet Development.
+ * 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
+ * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
+ * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
+ * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package edu.internet2.middleware.shibboleth.aa.attrresolv.provider;
+
+import java.security.Principal;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Element;
+
+import edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeDefinitionPlugIn;
+import edu.internet2.middleware.shibboleth.aa.attrresolv.Dependencies;
+import edu.internet2.middleware.shibboleth.aa.attrresolv.ResolutionPlugInException;
+import edu.internet2.middleware.shibboleth.aa.attrresolv.ResolverAttribute;
+
+/**
+ * Basic <code>AttributeDefinitionPlugIn</code> implementation.  Operates as a proxy for attributes
+ * gathered by Connectors.
+ * 
+ * @author Walter Hoehn (wassa@columbia.edu)
+ */
+public class SimpleAttributeDefinition extends BaseAttributeDefinition implements AttributeDefinitionPlugIn {
+
+       private static Logger log = Logger.getLogger(SimpleAttributeDefinition.class.getName());
+       private String connectorMapping;
+       private String smartScope;
+       private String schemaType;
+       private String schemaNamespace;
+       private ValueHandler valueHandler;
+
+       /**
+        * Constructor for SimpleAttributeDefinition.  Creates a PlugIn based on configuration
+        * information presented in a DOM Element.
+        */
+       public SimpleAttributeDefinition(Element e) throws ResolutionPlugInException {
+
+               super(e);
+
+               String sourceName = e.getAttribute("sourceName");
+               if (sourceName == null || sourceName.equals("")) {
+                       connectorMapping = getId().substring(getId().lastIndexOf(":") + 1);
+               } else {
+                       connectorMapping = sourceName;
+               }
+
+               log.debug("Mapping attribute to name (" + connectorMapping + ") in connector.");
+
+               String smartScopingSpec = e.getAttribute("smartScope");
+               if (smartScopingSpec != null && !smartScopingSpec.equals("")) {
+                       smartScope = smartScopingSpec;
+               }
+               if (smartScope != null) {
+                       log.debug("Smart Scope (" + smartScope + ") enabled for attribute (" + getId() + ").");
+               } else {
+                       log.debug("Smart Scoping disabled for attribute (" + getId() + ").");
+               }
+
+               String valueHandlerSpec = e.getAttribute("valueHandler");
+
+               if (valueHandlerSpec != null && !valueHandlerSpec.equals("")) {
+                       if (smartScope == null) {
+                               try {
+                                       Class handlerClass = Class.forName(valueHandlerSpec);
+                                       valueHandler = (ValueHandler) handlerClass.newInstance();
+                               } catch (ClassNotFoundException cnfe) {
+                                       log.error(
+                                               "Value Handler implementation specified for attribute ("
+                                                       + getId()
+                                                       + ") cannot be found: "
+                                                       + cnfe);
+                                       throw new ResolutionPlugInException(
+                                               "Value Handler implementation specified for attribute (" + getId() + ") cannot be found.");
+                               } catch (Exception oe) {
+                                       log.error(
+                                               "Value Handler implementation specified for attribute ("
+                                                       + getId()
+                                                       + ") coudl not be loaded: "
+                                                       + oe);
+                                       throw new ResolutionPlugInException(
+                                               "Value Handler implementation specified for attribute (" + getId() + ") could not be loaded.");
+                               }
+                       } else {
+                               log.error(
+                                       "Specification of \"valueHandler\' cannot be used in combination with \"smartScope\".  Ignoring Value Handler for attribute ("
+                                               + getId()
+                                               + ").");
+                       }
+               }
+
+               if (valueHandler != null) {
+                       log.debug("Custom Value Handler enabled for attribute (" + getId() + ").");
+               }
+
+       }
+
+       /**
+        * @see edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeDefinitionPlugIn#resolve(edu.internet2.middleware.shibboleth.aa.attrresolv.ResolverAttribute, java.security.Principal, java.lang.String, edu.internet2.middleware.shibboleth.aa.attrresolv.Dependencies)
+        */
+       public void resolve(ResolverAttribute attribute, Principal principal, String requester, Dependencies depends)
+               throws ResolutionPlugInException {
+               log.debug("Resolving attribute: (" + getId() + ")");
+               Set results = new HashSet();
+               if (!connectorDependencyIds.isEmpty()) {
+                       results.addAll(Arrays.asList(getValuesFromConnectors(depends)));
+               }
+
+               if (!attributeDependencyIds.isEmpty()) {
+                       results.addAll(Arrays.asList(getValuesFromAttributes(depends)));
+               }
+
+               if (lifeTime != -1) {
+                       attribute.setLifetime(lifeTime);
+               }
+
+               Iterator resultsIt = results.iterator();
+               while (resultsIt.hasNext()) {
+                       if (smartScope != null) {
+                               attribute.registerValueHandler(new ScopedStringValueHandler(smartScope));
+                       }
+                       if (smartScope == null && valueHandler != null) {
+                               attribute.registerValueHandler(valueHandler);
+                       }
+                       attribute.addValue(resultsIt.next());
+               }
+               attribute.setResolved();
+       }
+
+       protected Object[] getValuesFromAttributes(Dependencies depends) {
+
+               Set results = new HashSet();
+
+               Iterator attrDependIt = attributeDependencyIds.iterator();
+               while (attrDependIt.hasNext()) {
+                       ResolverAttribute attribute = depends.getAttributeResolution((String) attrDependIt.next());
+                       if (attribute != null) {
+                               log.debug("Found value(s) for attribute (" + getId() + ").");
+                               for (Iterator iterator = attribute.getValues(); iterator.hasNext();) {
+                                       results.add(iterator.next());
+                               }
+                       } else {
+                               log.error(
+                                       "An attribute dependency of attribute (" + getId() + ") was not included in the dependency chain.");
+                       }
+               }
+
+               if (results.isEmpty()) {
+                       log.debug("An attribute dependency of attribute (" + getId() + ") supplied no values.");
+               }
+               return results.toArray();
+       }
+
+       protected Object[] getValuesFromConnectors(Dependencies depends) {
+
+               Set results = new HashSet();
+
+               Iterator connectorDependIt = connectorDependencyIds.iterator();
+               while (connectorDependIt.hasNext()) {
+                       Attributes attrs = depends.getConnectorResolution((String) connectorDependIt.next());
+                       if (attrs != null) {
+                               Attribute attr = attrs.get(connectorMapping);
+                               if (attr != null) {
+                                       log.debug("Found value(s) for attribute (" + getId() + ").");
+                                       try {
+                                               NamingEnumeration valuesEnum = attr.getAll();
+                                               while (valuesEnum.hasMore()) {
+                                                       results.add(valuesEnum.next());
+                                               }
+                                       } catch (NamingException e) {
+                                               log.error(
+                                                       "An problem was encountered resolving the dependencies of attribute ("
+                                                               + getId()
+                                                               + "): "
+                                                               + e);
+                                       }
+                               }
+                       }
+               }
+
+               if (results.isEmpty()) {
+                       log.debug("A connector dependency of attribute (" + getId() + ") supplied no values.");
+               }
+               return results.toArray();
+       }
+}
diff --git a/src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/ValueHandler.java b/src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/ValueHandler.java
new file mode 100644 (file)
index 0000000..fc444a5
--- /dev/null
@@ -0,0 +1,75 @@
+/* 
+ * The Shibboleth License, Version 1. 
+ * Copyright (c) 2002 
+ * University Corporation for Advanced Internet Development, Inc. 
+ * All rights reserved
+ * 
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials provided with the distribution, if any, must include 
+ * the following acknowledgment: "This product includes software developed by 
+ * the University Corporation for Advanced Internet Development 
+ * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
+ * may appear in the software itself, if and wherever such third-party 
+ * acknowledgments normally appear.
+ * 
+ * Neither the name of Shibboleth nor the names of its contributors, nor 
+ * Internet2, nor the University Corporation for Advanced Internet Development, 
+ * Inc., nor UCAID may be used to endorse or promote products derived from this 
+ * software without specific prior written permission. For written permission, 
+ * please contact shibboleth@shibboleth.org
+ * 
+ * Products derived from this software may not be called Shibboleth, Internet2, 
+ * UCAID, or the University Corporation for Advanced Internet Development, nor 
+ * may Shibboleth appear in their name, without prior written permission of the 
+ * University Corporation for Advanced Internet Development.
+ * 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
+ * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
+ * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
+ * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package edu.internet2.middleware.shibboleth.aa.attrresolv.provider;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * Defines a handler for rendering attribute values.
+ * 
+ * @author Walter Hoehn (wassa@columbia.edu)
+ *
+ */
+public interface ValueHandler {
+       
+       /**
+        * Converts an attribute value to a series of DOM nodes.
+        * 
+        * @param valueElement the parent DOM node
+        * @param value the value object
+        * @param document the <code>Document</code> from which to create new nodes
+        */
+       public void toDOM(Element valueElement, Object value, Document document) throws ValueHandlerException;
+       public Iterator getValues(Collection internalValues);
+}
diff --git a/src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/ValueHandlerException.java b/src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/ValueHandlerException.java
new file mode 100644 (file)
index 0000000..33132ce
--- /dev/null
@@ -0,0 +1,63 @@
+/* 
+ * The Shibboleth License, Version 1. 
+ * Copyright (c) 2002 
+ * University Corporation for Advanced Internet Development, Inc. 
+ * All rights reserved
+ * 
+ * 
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * Redistributions of source code must retain the above copyright notice, this 
+ * list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright notice, 
+ * this list of conditions and the following disclaimer in the documentation 
+ * and/or other materials provided with the distribution, if any, must include 
+ * the following acknowledgment: "This product includes software developed by 
+ * the University Corporation for Advanced Internet Development 
+ * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
+ * may appear in the software itself, if and wherever such third-party 
+ * acknowledgments normally appear.
+ * 
+ * Neither the name of Shibboleth nor the names of its contributors, nor 
+ * Internet2, nor the University Corporation for Advanced Internet Development, 
+ * Inc., nor UCAID may be used to endorse or promote products derived from this 
+ * software without specific prior written permission. For written permission, 
+ * please contact shibboleth@shibboleth.org
+ * 
+ * Products derived from this software may not be called Shibboleth, Internet2, 
+ * UCAID, or the University Corporation for Advanced Internet Development, nor 
+ * may Shibboleth appear in their name, without prior written permission of the 
+ * University Corporation for Advanced Internet Development.
+ * 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+ * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
+ * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
+ * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
+ * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package edu.internet2.middleware.shibboleth.aa.attrresolv.provider;
+
+/**
+ * Signals that an error occurred while rendering an attribute's value(s).
+ * 
+ * @author Walter Hoehn (wassa@columbia.edu)
+ *
+ */
+public class ValueHandlerException extends Exception {
+
+       public ValueHandlerException(String message) {
+               super(message);
+       }
+}
diff --git a/src/schemas/shibboleth-resolver-1.0.xsd b/src/schemas/shibboleth-resolver-1.0.xsd
new file mode 100644 (file)
index 0000000..8525528
--- /dev/null
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema targetNamespace="urn:mace:shibboleth:resolver:1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:resolver="urn:mace:shibboleth:resolver:1.0" elementFormDefault="qualified" attributeFormDefault="unqualified">
+       <xs:element name="AttributeResolver">
+               <xs:complexType>
+                       <xs:choice minOccurs="1" maxOccurs="unbounded">
+                               <xs:element name="SimpleAttributeDefinition" minOccurs="0" maxOccurs="unbounded">
+                                       <xs:complexType>
+                                               <xs:complexContent>
+                                                       <xs:extension base="resolver:BaseAttributeDefinition">
+                                                               <xs:attribute name="sourceName" type="xs:string" use="optional"/>
+                                                               <xs:attribute name="smartScope" type="xs:string" use="optional"/>
+                                                               <xs:attribute name="valueHandler" type="xs:string" use="optional"/>
+                                                       </xs:extension>
+                                               </xs:complexContent>
+                                       </xs:complexType>
+                               </xs:element>
+                               <xs:element name="CustomAttributeDefinition" minOccurs="0" maxOccurs="unbounded">
+                                       <xs:complexType>
+                                               <xs:complexContent>
+                                                       <xs:extension base="resolver:BaseAttributeDefinition">
+                                                               <xs:sequence minOccurs="0" maxOccurs="unbounded">
+                                                                       <xs:any namespace="##any" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+                                                               </xs:sequence>
+                                                               <xs:attribute name="class" type="xs:string" use="required"/>
+                                                               <xs:anyAttribute namespace="##any" processContents="lax"/>
+                                                       </xs:extension>
+                                               </xs:complexContent>
+                                       </xs:complexType>
+                               </xs:element>
+                               <xs:element name="CustomDataConnector" minOccurs="0" maxOccurs="unbounded">
+                                       <xs:complexType>
+                                               <xs:complexContent>
+                                                       <xs:extension base="resolver:BaseResolutionPlugIn">
+                                                               <xs:sequence minOccurs="0" maxOccurs="unbounded">
+                                                                       <xs:any namespace="##any" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+                                                               </xs:sequence>
+                                                               <xs:attribute name="class" type="xs:string" use="required"/>
+                                                               <xs:anyAttribute namespace="##any" processContents="lax"/>
+                                                       </xs:extension>
+                                               </xs:complexContent>
+                                       </xs:complexType>
+                               </xs:element>
+                               <xs:element name="JNDIDirectoryDataConnector" minOccurs="0" maxOccurs="unbounded">
+                                       <xs:complexType>
+                                               <xs:complexContent>
+                                                       <xs:extension base="resolver:BaseResolutionPlugIn">
+                                                               <xs:sequence>
+                                                                       <xs:element name="Search">
+                                                                               <xs:complexType>
+                                                                                       <xs:sequence minOccurs="0">
+                                                                                               <xs:element name="Controls">
+                                                                                                       <xs:complexType>
+                                                                                                               <xs:attribute name="searchScope" type="xs:string" use="optional"/>
+                                                                                                               <xs:attribute name="timeLimit" type="xs:int" use="optional"/>
+                                                                                                               <xs:attribute name="returningObjects" type="xs:boolean" use="optional"/>
+                                                                                                               <xs:attribute name="linkDereferencing" type="xs:boolean" use="optional"/>
+                                                                                                               <xs:attribute name="countLimit" type="xs:long" use="optional"/>
+                                                                                                       </xs:complexType>
+                                                                                               </xs:element>
+                                                                                       </xs:sequence>
+                                                                                       <xs:attribute name="filter" type="xs:string" use="required"/>
+                                                                               </xs:complexType>
+                                                                       </xs:element>
+                                                                       <xs:sequence>
+                                                                               <xs:element name="Property" maxOccurs="unbounded">
+                                                                                       <xs:complexType>
+                                                                                               <xs:attribute name="name" type="xs:string" use="required"/>
+                                                                                               <xs:attribute name="value" type="xs:string" use="required"/>
+                                                                                       </xs:complexType>
+                                                                               </xs:element>
+                                                                       </xs:sequence>
+                                                               </xs:sequence>
+                                                       </xs:extension>
+                                               </xs:complexContent>
+                                       </xs:complexType>
+                               </xs:element>
+                       </xs:choice>
+               </xs:complexType>
+       </xs:element>
+       <xs:complexType name="BaseAttributeDefinition">
+               <xs:complexContent>
+                       <xs:extension base="resolver:BaseResolutionPlugIn">
+                               <xs:choice minOccurs="0" maxOccurs="unbounded">
+                                       <xs:element name="DataConnectorDependency">
+                                               <xs:complexType>
+                                                       <xs:attribute name="requires" type="xs:string" use="required"/>
+                                               </xs:complexType>
+                                       </xs:element>
+                                       <xs:element name="AttributeDependency">
+                                               <xs:complexType>
+                                                       <xs:attribute name="requires" type="xs:string" use="optional"/>
+                                               </xs:complexType>
+                                       </xs:element>
+                               </xs:choice>
+                               <xs:attribute name="lifeTime" type="xs:long" use="optional"/>
+                       </xs:extension>
+               </xs:complexContent>
+       </xs:complexType>
+       <xs:complexType name="BaseResolutionPlugIn">
+               <xs:attribute name="id" type="xs:string" use="required"/>
+               <xs:attribute name="cacheTime" type="xs:string" use="optional"/>
+       </xs:complexType>
+</xs:schema>