Added support for starttls and sasl EXTERNAL authentication. Hooked the existing...
authorwassa <wassa@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Wed, 26 Jan 2005 21:52:05 +0000 (21:52 +0000)
committerwassa <wassa@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Wed, 26 Jan 2005 21:52:05 +0000 (21:52 +0000)
git-svn-id: https://subversion.switch.ch/svn/shibboleth/java-idp/trunk@1220 ab3bd59b-922f-494d-bb5f-6f0a3c29deca

src/edu/internet2/middleware/shibboleth/aa/attrresolv/AttributeResolver.java
src/edu/internet2/middleware/shibboleth/aa/attrresolv/provider/JNDIDirectoryDataConnector.java
src/edu/internet2/middleware/shibboleth/common/Credentials.java
src/schemas/credentials.xsd
src/schemas/shibboleth-resolver-1.0.xsd

index 37fe0b3..32e2695 100644 (file)
@@ -114,6 +114,14 @@ public class AttributeResolver {
                                                } catch (IOException e) {
                                                        throw new SAXException("Could not load entity: " + e);
                                                }
+                                       } else if (systemId.endsWith("credentials.xsd")) {
+                                               try {
+                                                       return new InputSource(
+                                                               new ShibResource("/schemas/credentials.xsd", this.getClass())
+                                                                       .getInputStream());
+                                               } catch (IOException e) {
+                                                       throw new SAXException("Could not load entity: " + e);
+                                               }
                                        } else {
                                                return null;
                                        }
index a290d25..5473859 100644 (file)
 
 package edu.internet2.middleware.shibboleth.aa.attrresolv.provider;
 
+import java.io.IOException;
+import java.net.Socket;
+import java.security.GeneralSecurityException;
 import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.SecureRandom;
+import java.security.cert.X509Certificate;
 import java.util.Properties;
 
 import javax.naming.CommunicationException;
@@ -37,6 +43,14 @@ import javax.naming.directory.BasicAttribute;
 import javax.naming.directory.InitialDirContext;
 import javax.naming.directory.SearchControls;
 import javax.naming.directory.SearchResult;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+import javax.naming.ldap.StartTlsRequest;
+import javax.naming.ldap.StartTlsResponse;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.X509KeyManager;
 
 import org.apache.log4j.Logger;
 import org.w3c.dom.Element;
@@ -46,6 +60,8 @@ import edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeResolver;
 import edu.internet2.middleware.shibboleth.aa.attrresolv.DataConnectorPlugIn;
 import edu.internet2.middleware.shibboleth.aa.attrresolv.Dependencies;
 import edu.internet2.middleware.shibboleth.aa.attrresolv.ResolutionPlugInException;
+import edu.internet2.middleware.shibboleth.common.Credential;
+import edu.internet2.middleware.shibboleth.common.Credentials;
 
 /**
  * <code>DataConnectorPlugIn</code> implementation that utilizes a user-specified JNDI <code>DirContext</code> to
@@ -60,6 +76,9 @@ public class JNDIDirectoryDataConnector extends BaseDataConnector implements Dat
        protected Properties properties;
        protected SearchControls controls;
        protected String failover = null;
+       protected boolean startTls = false;
+       boolean useExternalAuth = false;
+       private SSLSocketFactory sslsf;
 
        /**
         * Constructs a DataConnector based on DOM configuration.
@@ -73,6 +92,14 @@ public class JNDIDirectoryDataConnector extends BaseDataConnector implements Dat
 
                super(e);
 
+               // Decide if we are using starttls
+               String tlsAttribute = e.getAttribute("useStartTls");
+               if (tlsAttribute != null && tlsAttribute.equalsIgnoreCase("TRUE")) {
+                       startTls = true;
+                       log.debug("Start TLS support enabled for connector.");
+               }
+
+               // Determine the search filter and controls
                NodeList searchNodes = e.getElementsByTagNameNS(AttributeResolver.resolverNamespace, "Search");
                if (searchNodes.getLength() != 1) {
                        log.error("JNDI Directory Data Connector requires a \"Search\" specification.");
@@ -90,6 +117,7 @@ public class JNDIDirectoryDataConnector extends BaseDataConnector implements Dat
 
                defineSearchControls(((Element) searchNodes.item(0)));
 
+               // Load JNDI properties
                NodeList propertyNodes = e.getElementsByTagNameNS(AttributeResolver.resolverNamespace, "Property");
                properties = new Properties(System.getProperties());
                for (int i = 0; propertyNodes.getLength() > i; i++) {
@@ -111,14 +139,66 @@ public class JNDIDirectoryDataConnector extends BaseDataConnector implements Dat
                        }
                }
 
-               //Fail-fast connection test
+               // Fail-fast connection test
                InitialDirContext context = null;
                try {
-                       context = new InitialDirContext(properties);
+                       if (!startTls) {
+                               try {
+                                       log.debug("Attempting to connect to JNDI directory source as a sanity check.");
+                                       context = initConnection();
+                               } catch (IOException ioe) {
+                                       log.error("Failed to startup directory context: " + ioe);
+                                       throw new ResolutionPlugInException("Failed to startup directory context.");
+                               }
+                       } else {
+                               // UGLY!
+                               // We can't do SASL EXTERNAL auth until we have a TLS session
+                               // So, we need to take this out of the environment and then stick it back in later
+                               if ("EXTERNAL".equals(properties.getProperty(Context.SECURITY_AUTHENTICATION))) {
+                                       useExternalAuth = true;
+                                       properties.remove(Context.SECURITY_AUTHENTICATION);
+                               }
+
+                               // If TLS credentials were supplied, load them and setup a KeyManager
+                               KeyManager keyManager = null;
+                               NodeList credNodes = e.getElementsByTagNameNS(Credentials.credentialsNamespace, "Credential");
+                               if (credNodes.getLength() > 0) {
+                                       log.debug("JNDI Directory Data Connector has a \"Credential\" specification.  "
+                                                       + "Loading credential...");
+                                       Credentials credentials = new Credentials((Element) credNodes.item(0));
+                                       Credential clientCred = credentials.getCredential();
+                                       if (clientCred == null) {
+                                               log.error("No credentials were loaded.");
+                                               throw new ResolutionPlugInException("Error loading credential.");
+                                       } else {
+                                               keyManager = new KeyManagerImpl(clientCred.getPrivateKey(), clientCred
+                                                               .getX509CertificateChain());
+                                       }
+                               }
+
+                               try {
+                                       // Setup a customized SSL socket factory that uses our implementation of KeyManager
+                                       // This factory will be used for all subsequent TLS negotiation
+                                       SSLContext sslc = SSLContext.getInstance("TLS");
+                                       sslc.init(new KeyManager[]{keyManager}, null, new SecureRandom());
+                                       sslsf = sslc.getSocketFactory();
+
+                                       log.debug("Attempting to connect to JNDI directory source as a sanity check.");
+                                       initConnection();
+                               } catch (GeneralSecurityException gse) {
+                                       log.error("Failed to startup directory context.  Error creating SSL socket: " + gse);
+                                       throw new ResolutionPlugInException("Failed to startup directory context.");
+
+                               } catch (IOException ioe) {
+                                       log.error("Failed to startup directory context.  Error negotiating Start TLS: " + ioe);
+                                       throw new ResolutionPlugInException("Failed to startup directory context.");
+                               }
+                       }
+
                        log.debug("JNDI Directory context activated.");
 
-               } catch (NamingException e1) {
-                       log.error("Failed to startup directory context: " + e1);
+               } catch (NamingException ne) {
+                       log.error("Failed to startup directory context: " + ne);
                        throw new ResolutionPlugInException("Failed to startup directory context.");
                } finally {
                        try {
@@ -214,18 +294,26 @@ public class JNDIDirectoryDataConnector extends BaseDataConnector implements Dat
                        throws ResolutionPlugInException {
 
                InitialDirContext context = null;
+               NamingEnumeration nEnumeration = null;
                try {
-                       context = new InitialDirContext(properties);
-                       NamingEnumeration nEnumeration = null;
-
                        try {
+                               context = initConnection();
                                nEnumeration = context
                                                .search("", searchFilter.replaceAll("%PRINCIPAL%", principal.getName()), controls);
+
+                               // If we get a failure during the init or query, attempt once to re-establish the connection
                        } 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());
+                               log.warn("Encountered a connection problem while querying for attributes.  Re-initializing "
+                                               + "JNDI context and retrying...");
+                               context = initConnection();
+                               nEnumeration = context
+                                               .search("", searchFilter.replaceAll("%PRINCIPAL%", principal.getName()), controls);
+                       } catch (IOException e) {
+                               log.debug(e);
+                               log.warn("Encountered a connection problem while querying for attributes.  Re-initializing "
+                                               + "JNDI context and retrying...");
+                               context = initConnection();
                                nEnumeration = context
                                                .search("", searchFilter.replaceAll("%PRINCIPAL%", principal.getName()), controls);
                        }
@@ -243,7 +331,7 @@ public class JNDIDirectoryDataConnector extends BaseDataConnector implements Dat
                                throw new ResolutionPlugInException("Cannot disambiguate data for this principal.");
                        }
 
-                       //For Sun's ldap provider only, construct the dn of the returned entry and manually add that as an
+                       // For Sun's ldap provider only, construct the dn of the returned entry and manually add that as an
                        // attribute
                        if (context.getEnvironment().get(Context.INITIAL_CONTEXT_FACTORY)
                                        .equals("com.sun.jndi.ldap.LdapCtxFactory")) {
@@ -257,6 +345,11 @@ public class JNDIDirectoryDataConnector extends BaseDataConnector implements Dat
                        log.error("An error occurred while retieving data for principal (" + principal.getName() + ") :"
                                        + e.getMessage());
                        throw new ResolutionPlugInException("Error retrieving data for principal.");
+               } catch (IOException e) {
+                       log.error("An error occurred while retieving data for principal (" + principal.getName() + ") :"
+                                       + e.getMessage());
+                       throw new ResolutionPlugInException("Error retrieving data for principal.");
+
                } finally {
                        try {
                                if (context != null) {
@@ -267,4 +360,73 @@ public class JNDIDirectoryDataConnector extends BaseDataConnector implements Dat
                        }
                }
        }
+
+       private InitialDirContext initConnection() throws NamingException, IOException, ResolutionPlugInException {
+
+               InitialDirContext context;
+               if (!startTls) {
+                       context = new InitialDirContext(properties);
+
+               } else {
+                       context = new InitialLdapContext(properties, null);
+                       if (!(context instanceof LdapContext)) {
+                               log.error("Directory context does not appear to be an implementation of LdapContext.  "
+                                               + "This is required for startTls.");
+                               throw new ResolutionPlugInException("Start TLS is only supported for implementations of LdapContext.");
+                       }
+                       StartTlsResponse tls = (StartTlsResponse) ((LdapContext) context).extendedOperation(new StartTlsRequest());
+                       tls.negotiate(sslsf);
+                       if (useExternalAuth) {
+                               context.addToEnvironment(Context.SECURITY_AUTHENTICATION, "EXTERNAL");
+                       }
+               }
+               return context;
+       }
+}
+
+/**
+ * Implementation of <code>X509KeyManager</code> that always uses a hard-coded client certificate.
+ */
+
+class KeyManagerImpl implements X509KeyManager {
+
+       private PrivateKey key;
+       private X509Certificate[] chain;
+
+       KeyManagerImpl(PrivateKey key, X509Certificate[] chain) {
+
+               this.key = key;
+               this.chain = chain;
+       }
+
+       public String[] getClientAliases(String arg0, Principal[] arg1) {
+
+               return new String[]{"default"};
+       }
+
+       public String chooseClientAlias(String[] arg0, Principal[] arg1, Socket arg2) {
+
+               return "default";
+       }
+
+       public String[] getServerAliases(String arg0, Principal[] arg1) {
+
+               return null;
+       }
+
+       public String chooseServerAlias(String arg0, Principal[] arg1, Socket arg2) {
+
+               return null;
+       }
+
+       public X509Certificate[] getCertificateChain(String arg0) {
+
+               return chain;
+       }
+
+       public PrivateKey getPrivateKey(String arg0) {
+
+               return key;
+       }
+
 }
\ No newline at end of file
index b29639e..0f19d02 100644 (file)
@@ -95,6 +95,7 @@ public class Credentials {
 
        private static Logger log = Logger.getLogger(Credentials.class.getName());
        private Hashtable data = new Hashtable();
+       private boolean singleMode = false;
 
         /**
          * Creates credentials based on XML configuration.
@@ -102,9 +103,9 @@ public class Credentials {
          */
        public Credentials(Element e) {
 
-               if (!e.getLocalName().equals("Credentials")) {
-                       throw new IllegalArgumentException();
-               }
+               if (e != null && e.getLocalName().equals("Credential")) {
+                       singleMode = true;
+               } else if (e == null || !e.getLocalName().equals("Credentials")) { throw new IllegalArgumentException(); }
 
                NodeList resolverNodes = e.getChildNodes();
                if (resolverNodes.getLength() <= 0) {
@@ -115,10 +116,13 @@ public class Credentials {
                for (int i = 0; resolverNodes.getLength() > i; i++) {
                        if (resolverNodes.item(i).getNodeType() == Node.ELEMENT_NODE) {
                                try {
-
                                        String credentialId = ((Element) resolverNodes.item(i)).getAttribute("Id");
                                        if (credentialId == null || credentialId.equals("")) {
-                                               log.error("Found credential that was not labeled with a unique \"Id\" attribute. Skipping.");
+                                               if (singleMode) {
+                                                       credentialId = "SINGLE";
+                                               } else {
+                                                       log.error("Found credential that was not labeled with a unique \"Id\" attribute. Skipping.");
+                                               }
                                        }
 
                                        if (data.containsKey(credentialId)) {
@@ -150,6 +154,11 @@ public class Credentials {
 
                return (Credential) data.get(identifier);
        }
+       
+       public Credential getCredential() {
+
+               return (Credential) data.values().iterator().next();
+       }
 
        static class CredentialFactory {
 
@@ -210,12 +219,6 @@ class FileCredentialResolver implements CredentialResolver {
                        throw new CredentialFactoryException("Failed to initialize Credential Resolver.");
                }
 
-               String id = e.getAttribute("Id");
-               if (id == null || id.equals("")) {
-                       log.error("Credential Resolvers require specification of the attribute \"Id\".");
-                       throw new CredentialFactoryException("Failed to initialize Credential Resolver.");
-               }
-
                //Load the key
                String keyFormat = getKeyFormat(e);
                String keyPath = getKeyPath(e);
@@ -1370,12 +1373,6 @@ class KeystoreCredentialResolver implements CredentialResolver {
                        throw new CredentialFactoryException("Failed to initialize Credential Resolver.");
                }
 
-               String id = e.getAttribute("Id");
-               if (id == null || id.equals("")) {
-                       log.error("Credential Resolvers require specification of the attribute \"Id\".");
-                       throw new CredentialFactoryException("Failed to initialize Credential Resolver.");
-               }
-
                String keyStoreType = e.getAttribute("storeType");
                if (keyStoreType == null || keyStoreType.equals("")) {
                        log.debug("Using default store type for credential.");
@@ -1566,12 +1563,6 @@ class CustomCredentialResolver implements CredentialResolver {
                        throw new CredentialFactoryException("Failed to initialize Credential Resolver.");
                }
 
-               String id = e.getAttribute("id");
-               if (id == null || id.equals("")) {
-                       log.error("Credential Resolvers require specification of the attribute \"id\".");
-                       throw new CredentialFactoryException("Failed to initialize Credential Resolver.");
-               }
-
                String className = e.getAttribute("Class");
                if (className == null || className.equals("")) {
                        log.error("Custom Credential Resolver requires specification of the attribute \"Class\".");
index 31352fd..65a61ae 100644 (file)
@@ -1,77 +1,34 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- edited with XMLSPY v2004 rel. 2 U (http://www.xmlspy.com) by Walter F Hoehn, Jr (Columbia University in the City of New York) -->
-<xs:schema targetNamespace="urn:mace:shibboleth:credentials:1.0" xmlns:credentials="urn:mace:shibboleth:credentials:1.0" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" elementFormDefault="qualified" attributeFormDefault="unqualified">
-       <xs:import namespace="http://www.w3.org/2000/09/xmldsig#" schemaLocation="xmldsig-core-schema.xsd"/>
+<xs:schema targetNamespace="urn:mace:shibboleth:credentials:1.0" 
+       xmlns:credentials="urn:mace:shibboleth:credentials:1.0" 
+       xmlns:ds="http://www.w3.org/2000/09/xmldsig#" 
+       xmlns:xs="http://www.w3.org/2001/XMLSchema" 
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
+       elementFormDefault="qualified" attributeFormDefault="unqualified">
+       <xs:import namespace="http://www.w3.org/2000/09/xmldsig#" 
+               schemaLocation="xmldsig-core-schema.xsd"/>
        <xs:element name="Credentials">
                <xs:complexType>
-                       <xs:choice maxOccurs="unbounded">
-                               <xs:element name="KeyStoreResolver">
-                                       <xs:annotation>
-                                               <xs:documentation>Pulls credentials from a Java keystore.</xs:documentation>
-                                       </xs:annotation>
-                                       <xs:complexType>
-                                               <xs:complexContent>
-                                                       <xs:extension base="credentials:BaseCredentialFinder">
-                                                               <xs:sequence>
-                                                                       <xs:element name="Path" type="xs:string"/>
-                                                                       <xs:element name="KeyAlias" type="xs:string"/>
-                                                                       <xs:element name="CertAlias" type="xs:string" minOccurs="0"/>
-                                                                       <xs:element name="StorePassword" type="xs:string"/>
-                                                                       <xs:element name="KeyPassword" type="xs:string" minOccurs="0"/>
-                                                               </xs:sequence>
-                                                               <xs:attribute name="storeType" type="xs:string" use="optional" default="JKS"/>
-                                                       </xs:extension>
-                                               </xs:complexContent>
-                                       </xs:complexType>
-                               </xs:element>
-                               <xs:element name="FileResolver">
-                                       <xs:annotation>
-                                               <xs:documentation>Pulls credentials from files.</xs:documentation>
-                                       </xs:annotation>
-                                       <xs:complexType>
-                                               <xs:complexContent>
-                                                       <xs:extension base="credentials:BaseCredentialFinder">
-                                                               <xs:sequence>
-                                                                       <xs:element name="Key">
-                                                                               <xs:complexType>
-                                                                                       <xs:sequence>
-                                                                                               <xs:element name="Path" type="xs:string"/>
-                                                                                       </xs:sequence>
-                                                                                       <xs:attribute name="format" type="credentials:FormatType" use="optional" default="PEM"/>
-                                                                                       <xs:attribute name="password" type="xs:string" use="optional"/>
-                                                                               </xs:complexType>
-                                                                       </xs:element>
-                                                                       <xs:element name="Certificate" minOccurs="0">
-                                                                               <xs:complexType>
-                                                                                       <xs:sequence>
-                                                                                               <xs:element name="Path" type="xs:string"/>
-                                                                                               <xs:element name="CAPath" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
-                                                                                       </xs:sequence>
-                                                                                       <xs:attribute name="format" type="credentials:FormatType" use="optional" default="PEM"/>
-                                                                                       <xs:attribute name="password" type="xs:string" use="optional"/>
-                                                                               </xs:complexType>
-                                                                       </xs:element>
-                                                               </xs:sequence>
-                                                       </xs:extension>
-                                               </xs:complexContent>
-                                       </xs:complexType>
-                               </xs:element>
-                               <xs:element name="CustomResolver">
-                                       <xs:annotation>
-                                               <xs:documentation>Allows for specification of a Java class that loads credentials from a custom storage mechanism. </xs:documentation>
-                                       </xs:annotation>
-                                       <xs:complexType>
-                                               <xs:complexContent>
-                                                       <xs:extension base="credentials:BaseCredentialFinder">
-                                                               <xs:sequence>
-                                                                       <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"/>
-                                                       </xs:extension>
-                                               </xs:complexContent>
-                                       </xs:complexType>
-                               </xs:element>
+                       <xs:choice minOccurs="1" maxOccurs="unbounded">
+                               <xs:element name="KeyStoreResolver" 
+                                       type="credentials:KeyStoreResolverType"/>
+                               <xs:element name="FileResolver" 
+                                       type="credentials:FileResolverType"/>
+                               <xs:element name="CustomResolver" 
+                                       type="credentials:CustomResolverType"/>
+                       </xs:choice>
+                       <xs:anyAttribute namespace="##any" processContents="lax"/>
+               </xs:complexType>
+       </xs:element>
+               <xs:element name="Credential">
+               <xs:complexType>
+                       <xs:choice minOccurs="1" maxOccurs="1">
+                               <xs:element name="KeyStoreResolver" 
+                                       type="credentials:KeyStoreResolverType"/>
+                               <xs:element name="FileResolver" 
+                                       type="credentials:FileResolverType"/>
+                               <xs:element name="CustomResolver" 
+                                       type="credentials:CustomResolverType"/>
                        </xs:choice>
                        <xs:anyAttribute namespace="##any" processContents="lax"/>
                </xs:complexType>
                </xs:restriction>
        </xs:simpleType>
        <xs:complexType name="BaseCredentialFinder">
-               <xs:attribute name="Id" type="xs:string" use="required"/>
+               <xs:attribute name="Id" type="xs:string" use="optional"/>
+       </xs:complexType>
+       <xs:complexType name="KeyStoreResolverType">
+               <xs:annotation>
+                       <xs:documentation>Pulls credentials from a Java 
+                               keystore.</xs:documentation>
+               </xs:annotation>
+               <xs:complexContent>
+                       <xs:extension base="credentials:BaseCredentialFinder">
+                               <xs:sequence>
+                                       <xs:element name="Path" type="xs:string"/>
+                                       <xs:element name="KeyAlias" type="xs:string"/>
+                                       <xs:element name="CertAlias" type="xs:string" 
+                                               minOccurs="0"/>
+                                       <xs:element name="StorePassword" type="xs:string"/>
+                                       <xs:element name="KeyPassword" type="xs:string" 
+                                               minOccurs="0"/>
+                               </xs:sequence>
+                               <xs:attribute name="storeType" type="xs:string" use="optional" 
+                                       default="JKS"/>
+                       </xs:extension>
+               </xs:complexContent>
+       </xs:complexType>
+       <xs:complexType name ="FileResolverType">
+               <xs:annotation>
+                       <xs:documentation>Pulls credentials from files.</xs:documentation>
+               </xs:annotation>
+               <xs:complexContent>
+                       <xs:extension base="credentials:BaseCredentialFinder">
+                               <xs:sequence>
+                                       <xs:element name="Key">
+                                               <xs:complexType>
+                                                       <xs:sequence>
+                                                               <xs:element name="Path" type="xs:string"/>
+                                                       </xs:sequence>
+                                                       <xs:attribute name="format" 
+                                                               type="credentials:FormatType" use="optional" 
+                                                               default="PEM"/>
+                                                       <xs:attribute name="password" type="xs:string" 
+                                                               use="optional"/>
+                                               </xs:complexType>
+                                       </xs:element>
+                                       <xs:element name="Certificate" minOccurs="0">
+                                               <xs:complexType>
+                                                       <xs:sequence>
+                                                               <xs:element name="Path" type="xs:string"/>
+                                                               <xs:element name="CAPath" type="xs:string" 
+                                                                       minOccurs="0" maxOccurs="unbounded"/>
+                                                       </xs:sequence>
+                                                       <xs:attribute name="format" 
+                                                               type="credentials:FormatType" use="optional" 
+                                                               default="PEM"/>
+                                                       <xs:attribute name="password" type="xs:string" 
+                                                               use="optional"/>
+                                               </xs:complexType>
+                                       </xs:element>
+                               </xs:sequence>
+                       </xs:extension>
+               </xs:complexContent>
+       </xs:complexType>
+       <xs:complexType name="CustomResolverType">
+               <xs:annotation>
+                       <xs:documentation>Allows for specification of a Java class that 
+                               loads credentials from a custom storage mechanism. 
+                               </xs:documentation>
+               </xs:annotation>
+               <xs:complexContent>
+                       <xs:extension base="credentials:BaseCredentialFinder">
+                               <xs:sequence>
+                                       <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"/>
+                       </xs:extension>
+               </xs:complexContent>
        </xs:complexType>
 </xs:schema>
index 2f078e3..b9677c5 100644 (file)
 <?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:schema targetNamespace="urn:mace:shibboleth:resolver:1.0" 
+       xmlns:xs="http://www.w3.org/2001/XMLSchema" 
+       xmlns:resolver="urn:mace:shibboleth:resolver:1.0" 
+       xmlns:credentials="urn:mace:shibboleth:credentials:1.0" 
+       elementFormDefault="qualified" attributeFormDefault="unqualified">
+       <xs:import namespace="urn:mace:shibboleth:credentials:1.0" 
+               schemaLocation="credentials.xsd"/>
        <xs:element name="AttributeResolver">
                <xs:complexType>
                        <xs:choice minOccurs="1" maxOccurs="unbounded">
-                               <xs:element name="SimpleAttributeDefinition" minOccurs="0" 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:attribute name="allowEmpty" type="xs:boolean" use="optional"/>
-                                                               <xs:attribute name="downCase" type="xs:boolean" use="optional"/>
+                                                       <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:attribute name="allowEmpty" 
+                                                                       type="xs:boolean" use="optional"/>
+                                                               <xs:attribute name="downCase" type="xs:boolean" 
+                                                                       use="optional"/>
                                                        </xs:extension>
                                                </xs:complexContent>
                                        </xs:complexType>
                                </xs:element>
-                               <xs:element name="PersistentIDAttributeDefinition" minOccurs="0" maxOccurs="unbounded">
+                               <xs:element name="PersistentIDAttributeDefinition" 
+                                       minOccurs="0" maxOccurs="unbounded">
                                        <xs:complexType>
                                                <xs:complexContent>
-                                                       <xs:extension base="resolver:BaseAttributeDefinition">
+                                                       <xs:extension 
+                                                               base="resolver:BaseAttributeDefinition">
                                                                <xs:sequence>
                                                                        <xs:element name="Salt">
                                                                                <xs:complexType>
                                                                                        <xs:simpleContent>
                                                                                                <xs:extension base="xs:string">
-                                                                                                       <xs:attribute name="keyStorePath" type="xs:string" use="optional"/>
-                                                                                                       <xs:attribute name="keyStoreKeyAlias" type="xs:string" use="optional"/>
-                                                                                                       <xs:attribute name="keyStorePassword" type="xs:string" use="optional"/>
-                                                                                                       <xs:attribute name="keyStoreKeyPassword" type="xs:string" use="optional"/>
+                                                                                                       <xs:attribute 
+                                                                                                               name="keyStorePath" 
+                                                                                                               type="xs:string" 
+                                                                                                               use="optional"/>
+                                                                                                       <xs:attribute 
+                                                                                                               name="keyStoreKeyAlias" 
+                                                                                                               type="xs:string" 
+                                                                                                               use="optional"/>
+                                                                                                       <xs:attribute 
+                                                                                                               name="keyStorePassword" 
+                                                                                                               type="xs:string" 
+                                                                                                               use="optional"/>
+                                                                                                       <xs:attribute 
+                                                                                                               name="keyStoreKeyPassword" 
+                                                                                                               type="xs:string" 
+                                                                                                               use="optional"/>
                                                                                                </xs:extension>
                                                                                        </xs:simpleContent>
                                                                                </xs:complexType>
                                                                        </xs:element>
                                                                </xs:sequence>
-                                                               <xs:attribute name="sourceName" type="xs:string" use="optional"/>
-                                                               <xs:attribute name="scope" type="xs:string" use="required"/>
+                                                               <xs:attribute name="sourceName" 
+                                                                       type="xs:string" use="optional"/>
+                                                               <xs:attribute name="scope" type="xs:string" 
+                                                                       use="required"/>
                                                        </xs:extension>
                                                </xs:complexContent>
                                        </xs:complexType>
                                </xs:element>
-                               <xs:element name="CustomAttributeDefinition" minOccurs="0" maxOccurs="unbounded">
+                               <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: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: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:element name="CustomDataConnector" minOccurs="0" 
+                                       maxOccurs="unbounded">
                                        <xs:complexType>
                                                <xs:complexContent>
                                                        <xs:extension base="resolver:BaseDataConnector">
                                                                <xs:sequence>
-                                                                       <xs:any namespace="##any" processContents="lax" 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: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:element name="JNDIDirectoryDataConnector" minOccurs="0" 
+                                       maxOccurs="unbounded">
                                        <xs:complexType>
                                                <xs:complexContent>
                                                        <xs:extension base="resolver:BaseDataConnector">
                                                                <xs:sequence>
+                                                                       <xs:element ref="credentials:Credential" 
+                                                                               minOccurs="0" maxOccurs="1"/>
                                                                        <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: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:attribute name="filter" 
+                                                                                               type="xs:string" 
+                                                                                               use="required"/>
                                                                                </xs:complexType>
                                                                        </xs:element>
-                                                                       <xs:element name="Property" maxOccurs="unbounded">
+                                                                       <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:attribute name="name" 
+                                                                                               type="xs:string" 
+                                                                                               use="required"/>
+                                                                                       <xs:attribute name="value" 
+                                                                                               type="xs:string" 
+                                                                                               use="required"/>
                                                                                </xs:complexType>
                                                                        </xs:element>
                                                                </xs:sequence>
+                                                               <xs:attribute name="useStartTls" 
+                                                                       type="xs:boolean" use="optional" 
+                                                                       default="false"/>
                                                        </xs:extension>
                                                </xs:complexContent>
                                        </xs:complexType>
                                </xs:element>
-                               <xs:element name="JDBCDataConnector" minOccurs="0" maxOccurs="unbounded">
+                               <xs:element name="JDBCDataConnector" minOccurs="0" 
+                                       maxOccurs="unbounded">
                                        <xs:complexType>
                                                <xs:complexContent>
                                                        <xs:extension base="resolver:BaseDataConnector">
                                                                <xs:sequence>
                                                                        <xs:element name="Query" type="xs:string"/>
-                                                                       <xs:element name="AttributeExtractor" minOccurs="0">
+                                                                       <xs:element name="AttributeExtractor" 
+                                                                               minOccurs="0">
                                                                                <xs:complexType>
-                                                                                       <xs:attribute name="class" type="xs:string" use="required"/>
+                                                                                       <xs:attribute name="class" 
+                                                                                               type="xs:string" 
+                                                                                               use="required"/>
                                                                                </xs:complexType>
                                                                        </xs:element>
-                                                                       <xs:element name="StatementCreator" minOccurs="0">
+                                                                       <xs:element name="StatementCreator" 
+                                                                               minOccurs="0">
                                                                                <xs:complexType>
-                                                                                       <xs:sequence minOccurs="0" maxOccurs="unbounded">
-                                                                                               <xs:any namespace="##any" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+                                                                                       <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:attribute name="class" 
+                                                                                               type="xs:string" 
+                                                                                               use="required"/>
+                                                                                       <xs:anyAttribute namespace="##any" 
+                                                                                               processContents="lax"/>
                                                                                </xs:complexType>
                                                                        </xs:element>
-                                                                       <xs:element name="Property" minOccurs="0" maxOccurs="unbounded">
+                                                                       <xs:element name="Property" minOccurs="0" 
+                                                                               maxOccurs="unbounded">
                                                                                <xs:complexType>
-                                                                                       <xs:attribute name="name" type="xs:string" use="required"/>
-                                                                                       <xs:attribute name="value" type="xs:string" use="required"/>
+                                                                                       <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:attribute name="dbURL" type="xs:string" use="required"/>
-                                                               <xs:attribute name="dbDriver" type="xs:string" use="optional"/>
-                                                               <xs:attribute name="validationQuery" type="xs:string" use="optional" default="select 1"/>
-                                                               <xs:attribute name="maxActive" type="xs:integer" use="optional"/>
-                                                               <xs:attribute name="maxIdle" type="xs:integer" use="optional"/>
-                                                               <xs:attribute name="maxWait" type="xs:integer" use="optional"/>
-                                                               <xs:attribute name="minResultSet" type="xs:integer" use="optional"/>
-                                                               <xs:attribute name="maxResultSet" type="xs:integer" use="optional"/>
-                                                               <xs:attribute name="retryInterval" type="xs:integer" use="optional"/>
+                                                               <xs:attribute name="dbURL" type="xs:string" 
+                                                                       use="required"/>
+                                                               <xs:attribute name="dbDriver" type="xs:string" 
+                                                                       use="optional"/>
+                                                               <xs:attribute name="validationQuery" 
+                                                                       type="xs:string" use="optional" 
+                                                                       default="select 1"/>
+                                                               <xs:attribute name="maxActive" 
+                                                                       type="xs:integer" use="optional"/>
+                                                               <xs:attribute name="maxIdle" type="xs:integer" 
+                                                                       use="optional"/>
+                                                               <xs:attribute name="maxWait" type="xs:integer" 
+                                                                       use="optional"/>
+                                                               <xs:attribute name="minResultSet" 
+                                                                       type="xs:integer" use="optional"/>
+                                                               <xs:attribute name="maxResultSet" 
+                                                                       type="xs:integer" use="optional"/>
+                                                               <xs:attribute name="retryInterval" 
+                                                                       type="xs:integer" use="optional"/>
                                                        </xs:extension>
                                                </xs:complexContent>
                                        </xs:complexType>
                                <xs:sequence>
                                        <xs:element name="FailoverDependency" minOccurs="0">
                                                <xs:complexType>
-                                                       <xs:attribute name="requires" type="xs:string" use="required"/>
+                                                       <xs:attribute name="requires" type="xs:string" 
+                                                               use="required"/>
                                                </xs:complexType>
                                        </xs:element>
                                </xs:sequence>
                <xs:choice minOccurs="0" maxOccurs="unbounded">
                        <xs:element name="DataConnectorDependency">
                                <xs:complexType>
-                                       <xs:attribute name="requires" type="xs:string" use="required"/>
+                                       <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="required"/>
+                                       <xs:attribute name="requires" type="xs:string" 
+                                               use="required"/>
                                </xs:complexType>
                        </xs:element>
                </xs:choice>
                <xs:attribute name="id" type="xs:string" use="required"/>
                <xs:attribute name="cacheTime" type="xs:string" use="optional"/>
-               <xs:attribute name="propagateErrors" type="xs:boolean" use="optional" default="true"/>
+               <xs:attribute name="propagateErrors" type="xs:boolean" use="optional" 
+                       default="true"/>
        </xs:complexType>
 </xs:schema>