More re-architecting of the IdP servlet.
authorwassa <wassa@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Thu, 3 Mar 2005 22:06:36 +0000 (22:06 +0000)
committerwassa <wassa@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Thu, 3 Mar 2005 22:06:36 +0000 (22:06 +0000)
git-svn-id: https://subversion.switch.ch/svn/shibboleth/java-idp/trunk@1269 ab3bd59b-922f-494d-bb5f-6f0a3c29deca

src/edu/internet2/middleware/shibboleth/aa/AAResponder.java [deleted file]
src/edu/internet2/middleware/shibboleth/common/ServiceProviderMapper.java
src/edu/internet2/middleware/shibboleth/common/TargetFederationComponent.java [deleted file]
src/edu/internet2/middleware/shibboleth/idp/IdPResponder.java

diff --git a/src/edu/internet2/middleware/shibboleth/aa/AAResponder.java b/src/edu/internet2/middleware/shibboleth/aa/AAResponder.java
deleted file mode 100755 (executable)
index 8624a41..0000000
+++ /dev/null
@@ -1,154 +0,0 @@
-/* 
- * 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;
-
-/**
- *  Attribute Authority &amp; Release Policy
- *  Main logic that decides what to release 
- *
- * @author     Parviz Dousti (dousti@cmu.edu)
- * @author     Walter Hoehn (wassa@columbia.edu)
- */
-
-import java.net.URI;
-import java.net.URL;
-import java.security.Principal;
-
-import org.apache.log4j.Logger;
-import org.opensaml.SAMLAttribute;
-import org.opensaml.SAMLException;
-
-import edu.internet2.middleware.shibboleth.aa.arp.ArpEngine;
-import edu.internet2.middleware.shibboleth.aa.arp.ArpProcessingException;
-import edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeResolver;
-
-public class AAResponder {
-
-       private ArpEngine arpEngine;
-       private AttributeResolver resolver;
-       private static Logger log = Logger.getLogger(AAResponder.class.getName());
-
-       public AAResponder(ArpEngine arpEngine, AttributeResolver resolver) {
-
-               this.arpEngine = arpEngine;
-               this.resolver = resolver;
-       }
-
-       public SAMLAttribute[] getReleaseAttributes(Principal principal, String requester, URL resource)
-               throws AAException {
-
-               try {
-                       URI[] potentialAttributes = arpEngine.listPossibleReleaseAttributes(principal, requester, resource);
-                       return getReleaseAttributes(principal, requester, resource, potentialAttributes);
-
-               } catch (ArpProcessingException e) {
-                       log.error(
-                               "An error occurred while processing the ARPs for principal ("
-                                       + principal.getName()
-                                       + ") :"
-                                       + e.getMessage());
-                       throw new AAException("Error retrieving data for principal.");
-               }
-       }
-       
-       public SAMLAttribute[] getReleaseAttributes(
-               Principal principal,
-               String requester,
-               URL resource,
-               URI[] attributeNames)
-               throws AAException {
-
-               try {
-                       AAAttributeSet attributeSet = new AAAttributeSet();
-                       for (int i = 0; i < attributeNames.length; i++) {
-                               AAAttribute attribute = new AAAttribute(attributeNames[i].toString());
-                               attributeSet.add(attribute);
-                       }
-
-                       return resolveAttributes(principal, requester, resource, attributeSet);
-
-               } catch (SAMLException e) {
-                       log.error(
-                               "An error occurred while creating attributes for principal ("
-                                       + principal.getName()
-                                       + ") :"
-                                       + e.getMessage());
-                       throw new AAException("Error retrieving data for principal.");
-
-               } catch (ArpProcessingException e) {
-                       log.error(
-                               "An error occurred while processing the ARPs for principal ("
-                                       + principal.getName()
-                                       + ") :"
-                                       + e.getMessage());
-                       throw new AAException("Error retrieving data for principal.");
-               }
-       }
-
-       private SAMLAttribute[] resolveAttributes(
-               Principal principal,
-               String requester,
-               URL resource,
-               AAAttributeSet attributeSet)
-               throws ArpProcessingException {
-
-               resolver.resolveAttributes(principal, requester, attributeSet);
-               arpEngine.filterAttributes(attributeSet, principal, requester, resource);
-               return attributeSet.getAttributes();
-       }
-
-       /**
-        * Cleanup resources that won't be released when this object is garbage-collected
-        */
-       public void destroy() {
-               resolver.destroy();
-               arpEngine.destroy();
-       }
-}
index 4d53250..142e7ed 100644 (file)
@@ -58,9 +58,8 @@ public class ServiceProviderMapper {
        private NameMapper nameMapper;
 
        public ServiceProviderMapper(Element rawConfig, IdPConfig configuration, Credentials credentials,
-                       NameMapper nameMapper, Metadata metaData) throws ServiceProviderMapperException {
+                       NameMapper nameMapper) throws ServiceProviderMapperException {
 
-               this.metaData = metaData;
                this.configuration = configuration;
                this.credentials = credentials;
                this.nameMapper = nameMapper;
@@ -74,6 +73,10 @@ public class ServiceProviderMapper {
                verifyDefaultParty(configuration);
 
        }
+       
+       public void setMetadata(Metadata metadata) {
+               this.metaData = metadata;
+       }
 
        private IdPConfig getOriginConfig() {
 
@@ -131,19 +134,20 @@ public class ServiceProviderMapper {
 
        private RelyingParty findRelyingPartyByGroup(String providerIdFromTarget) {
 
+               if (metaData == null) { return null; }
+               
                EntityDescriptor provider = metaData.lookup(providerIdFromTarget);
                if (provider != null) {
                        EntitiesDescriptor parent = provider.getEntitiesDescriptor();
                        while (parent != null) {
                                if (relyingParties.containsKey(parent.getName())) {
                                        log.info("Found matching Relying Party for group (" + parent.getName() + ").");
-                                       return (RelyingParty)relyingParties.get(parent.getName());
+                                       return (RelyingParty) relyingParties.get(parent.getName());
                                } else {
-                                       log.debug("Provider is a member of group (" +
-                            parent.getName() +
-                            "), but no matching Relying Party was found.");
+                                       log.debug("Provider is a member of group (" + parent.getName()
+                                                       + "), but no matching Relying Party was found.");
                                }
-                parent = parent.getEntitiesDescriptor();
+                               parent = parent.getEntitiesDescriptor();
                        }
                }
                return null;
diff --git a/src/edu/internet2/middleware/shibboleth/common/TargetFederationComponent.java b/src/edu/internet2/middleware/shibboleth/common/TargetFederationComponent.java
deleted file mode 100644 (file)
index 86178a9..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * 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.common;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-
-import javax.servlet.http.HttpServlet;
-
-import org.apache.log4j.Logger;
-import org.opensaml.artifact.Artifact;
-import org.w3c.dom.Element;
-
-import edu.internet2.middleware.shibboleth.metadata.Metadata;
-import edu.internet2.middleware.shibboleth.metadata.MetadataException;
-import edu.internet2.middleware.shibboleth.metadata.EntityDescriptor;
-
-/**
- * @author Walter Hoehn (wassa@columbia.edu)
- */
-public class TargetFederationComponent extends HttpServlet implements Metadata {
-
-       private static Logger   log                     = Logger.getLogger(TargetFederationComponent.class.getName());
-
-       private ArrayList               fedMetadata     = new ArrayList();
-
-       protected void addFederationProvider(Element element) {
-               log.debug("Found Federation Provider configuration element.");
-               if (!element.getTagName().equals("FederationProvider")) {
-                       log.error("Error while attemtping to load Federation Provider.  Malformed provider specificaion.");
-                       return;
-               }
-
-               try {
-                       fedMetadata.add(FederationProviderFactory.loadProvider(element));
-               } catch (MetadataException e) {
-                       log.error("Unable to load Federation Provider.  Skipping...");
-               }
-       }
-
-       protected int providerCount() {
-               return fedMetadata.size();
-       }
-
-       public EntityDescriptor lookup(String providerId) {
-
-               Iterator iterator = fedMetadata.iterator();
-               while (iterator.hasNext()) {
-                       EntityDescriptor provider = ((Metadata) iterator.next()).lookup(providerId);
-                       if (provider != null) {
-                               return provider;
-                       }
-               }
-               return null;
-       }
-
-    public EntityDescriptor lookup(Artifact artifact) {
-        Iterator iterator = fedMetadata.iterator();
-        while (iterator.hasNext()) {
-            EntityDescriptor provider = ((Metadata) iterator.next()).lookup(artifact);
-            if (provider != null) {
-                return provider;
-            }
-        }
-        return null;
-    }
-}
-
-class FederationProviderFactory {
-
-       private static Logger   log     = Logger.getLogger(FederationProviderFactory.class.getName());
-
-       public static Metadata loadProvider(Element e) throws MetadataException {
-
-               String className = e.getAttribute("type");
-               if (className == null || className.equals("")) {
-                       log.error("Federation Provider requires specification of the attribute \"type\".");
-                       throw new MetadataException("Failed to initialize Federation Provider.");
-               } else {
-                       try {
-                               Class[] params = {Class.forName("org.w3c.dom.Element"),};
-                               return (Metadata) Class.forName(className).getConstructor(params).newInstance(new Object[]{e});
-                       } catch (Exception loaderException) {
-                               log.error("Failed to load Federation Provider implementation class: " + loaderException);
-                Throwable cause = loaderException.getCause();
-                while (cause != null) {
-                                       log.error("caused by: " + cause);
-                    cause = cause.getCause();
-                }
-                               throw new MetadataException("Failed to initialize Federation Provider.");
-                       }
-               }
-       }
-}
index 97163c9..8741fbf 100644 (file)
@@ -28,6 +28,7 @@ package edu.internet2.middleware.shibboleth.idp;
 import java.io.IOException;
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.net.URL;
 import java.net.URLEncoder;
 import java.security.Principal;
 import java.security.cert.CertificateParsingException;
@@ -46,6 +47,7 @@ import javax.security.auth.x500.X500Principal;
 import javax.servlet.RequestDispatcher;
 import javax.servlet.ServletException;
 import javax.servlet.UnavailableException;
+import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.xml.namespace.QName;
@@ -74,20 +76,20 @@ import org.opensaml.SAMLRequest;
 import org.opensaml.SAMLResponse;
 import org.opensaml.SAMLStatement;
 import org.opensaml.SAMLSubject;
+import org.opensaml.artifact.Artifact;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.NodeList;
 
 import sun.misc.BASE64Decoder;
+import edu.internet2.middleware.shibboleth.aa.AAAttribute;
+import edu.internet2.middleware.shibboleth.aa.AAAttributeSet;
 import edu.internet2.middleware.shibboleth.aa.AAException;
-import edu.internet2.middleware.shibboleth.aa.AAResponder;
 import edu.internet2.middleware.shibboleth.aa.arp.ArpEngine;
 import edu.internet2.middleware.shibboleth.aa.arp.ArpException;
+import edu.internet2.middleware.shibboleth.aa.arp.ArpProcessingException;
 import edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeResolver;
 import edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeResolverException;
-import edu.internet2.middleware.shibboleth.artifact.ArtifactMapper;
-import edu.internet2.middleware.shibboleth.artifact.ArtifactMapping;
-import edu.internet2.middleware.shibboleth.artifact.provider.MemoryArtifactMapper;
 import edu.internet2.middleware.shibboleth.common.AuthNPrincipal;
 import edu.internet2.middleware.shibboleth.common.Credential;
 import edu.internet2.middleware.shibboleth.common.Credentials;
@@ -101,8 +103,12 @@ import edu.internet2.middleware.shibboleth.common.ServiceProviderMapper;
 import edu.internet2.middleware.shibboleth.common.ServiceProviderMapperException;
 import edu.internet2.middleware.shibboleth.common.ShibBrowserProfile;
 import edu.internet2.middleware.shibboleth.common.ShibbolethConfigurationException;
-import edu.internet2.middleware.shibboleth.common.TargetFederationComponent;
-import edu.internet2.middleware.shibboleth.metadata.*;
+import edu.internet2.middleware.shibboleth.metadata.Endpoint;
+import edu.internet2.middleware.shibboleth.metadata.EntityDescriptor;
+import edu.internet2.middleware.shibboleth.metadata.KeyDescriptor;
+import edu.internet2.middleware.shibboleth.metadata.Metadata;
+import edu.internet2.middleware.shibboleth.metadata.MetadataException;
+import edu.internet2.middleware.shibboleth.metadata.SPSSODescriptor;
 
 /**
  * Primary entry point for requests to the SAML IdP. Listens on multiple endpoints, routes requests to the appropriate
@@ -111,27 +117,16 @@ import edu.internet2.middleware.shibboleth.metadata.*;
  * @author Walter Hoehn
  */
 
-public class IdPResponder extends TargetFederationComponent {
-
-       // TODO Maybe should rethink the inheritance here, since there is only one
-       // servlet
-       
-       //TODO idp config should accept new or old element <ShibbolethOriginConfiguration/> or <IdPConfig/>
+public class IdPResponder extends HttpServlet {
 
        private static Logger transactionLog = Logger.getLogger("Shibboleth-TRANSACTION");
        private static Logger log = Logger.getLogger(IdPResponder.class.getName());
        private static Random idgen = new Random();
-
        private SAMLBinding binding;
        private Semaphore throttle;
-       private ArtifactMapper artifactMapper;
-       private SSOProfileHandler[] profileHandlers;
        private IdPConfig configuration;
-       private NameMapper nameMapper;
-       private ServiceProviderMapper spMapper;
-
-       // TODO Need to rename, rework, and init
-       private AAResponder responder;
+       private ProtocolHandler[] protocolHandlers;
+       private ProtocolSupport protocolSupport;
 
        public void init() throws ServletException {
 
@@ -141,134 +136,103 @@ public class IdPResponder extends TargetFederationComponent {
 
                try {
                        binding = SAMLBindingFactory.getInstance(SAMLBinding.SOAP);
-                       nameMapper = new NameMapper();
-                       // TODO this needs to be pluggable
-                       artifactMapper = new MemoryArtifactMapper();
-                       loadConfiguration();
-                       
-                       
-                       //TODO figure out how this stuff should be configured
-                       // if (configuration.eAuthSupport()) {
-                       if (false) {    
-                       log.debug("Starting with Shibboleth & eAuth protocol handling enabled.");
-                       //      profileHandlers = new SSOProfileHandler[]{new ShibbolethProfileHandler(),
-                               //              new EAuthProfileHandler(configuration.getCsid())};
-                       } else {
-                               log.debug("Starting with Shibboleth protocol handling enabled.");
-                               profileHandlers = new SSOProfileHandler[]{new ShibbolethProfileHandler()};
-                       }
-                       
-                       
-                       
-                       
-                       
-                       
-                       
-                       //TODO what should we do with the throttle now!!!  default???
-                       
-                       //Load a semaphore that throttles how many requests the IdP will handle at once
-                       throttle = new Semaphore(configuration.getMaxThreads());
-                       
-                       log.info("Identity Provider initialization complete.");
-
-               } catch (ShibbolethConfigurationException ae) {
-                       log.fatal("The Identity Provider could not be initialized: " + ae);
-                       throw new UnavailableException("Identity Provider failed to initialize.");
-               } catch (SAMLException se) {
-                       log.fatal("SAML SOAP binding could not be loaded: " + se);
-                       throw new UnavailableException("Identity Provider failed to initialize.");
-               }
-       }
 
-       private void loadConfiguration() throws ShibbolethConfigurationException {
+                       Document originConfig = OriginConfig.getOriginConfig(this.getServletContext());
 
-               Document originConfig = OriginConfig.getOriginConfig(this.getServletContext());
+                       // Load global configuration properties
+                       configuration = new IdPConfig(originConfig.getDocumentElement());
 
-               // TODO I think some of the failure cases here are different than in the
-               // HS, so when the loadConfiguration() is unified, that must be taken
-               // into account
+                       // Load a semaphore that throttles how many requests the IdP will handle at once
+                       throttle = new Semaphore(configuration.getMaxThreads());
 
-               // TODO do we need to check active endpoints to determine which
-               // components to load, for instance artifact repository, arp engine,
-               // attribute resolver
+                       // Load name mappings
+                       NameMapper nameMapper = new NameMapper();
+                       NodeList itemElements = originConfig.getDocumentElement().getElementsByTagNameNS(
+                                       NameIdentifierMapping.mappingNamespace, "NameMapping");
 
-               // Load global configuration properties
-               configuration = new IdPConfig(originConfig.getDocumentElement());
+                       for (int i = 0; i < itemElements.getLength(); i++) {
+                               try {
+                                       nameMapper.addNameMapping((Element) itemElements.item(i));
+                               } catch (NameIdentifierMappingException e) {
+                                       log.error("Name Identifier mapping could not be loaded: " + e);
+                               }
+                       }
 
-               // Load name mappings
-               NodeList itemElements = originConfig.getDocumentElement().getElementsByTagNameNS(
-                               NameIdentifierMapping.mappingNamespace, "NameMapping");
+                       // Load signing credentials
+                       itemElements = originConfig.getDocumentElement().getElementsByTagNameNS(Credentials.credentialsNamespace,
+                                       "Credentials");
+                       if (itemElements.getLength() < 1) {
+                               log.error("No credentials specified.");
+                       }
+                       if (itemElements.getLength() > 1) {
+                               log.error("Multiple Credentials specifications found, using first.");
+                       }
+                       Credentials credentials = new Credentials((Element) itemElements.item(0));
 
-               for (int i = 0; i < itemElements.getLength(); i++) {
+                       // Load relying party config
+                       ServiceProviderMapper spMapper;
                        try {
-                               nameMapper.addNameMapping((Element) itemElements.item(i));
-                       } catch (NameIdentifierMappingException e) {
-                               log.error("Name Identifier mapping could not be loaded: " + e);
+                               spMapper = new ServiceProviderMapper(originConfig.getDocumentElement(), configuration, credentials,
+                                               nameMapper);
+                       } catch (ServiceProviderMapperException e) {
+                               log.error("Could not load Identity Provider configuration: " + e);
+                               throw new ShibbolethConfigurationException("Could not load Identity Provider configuration.");
                        }
-               }
 
-               // Load signing credentials
-               itemElements = originConfig.getDocumentElement().getElementsByTagNameNS(Credentials.credentialsNamespace,
-                               "Credentials");
-               if (itemElements.getLength() < 1) {
-                       log.error("No credentials specified.");
-               }
-               if (itemElements.getLength() > 1) {
-                       log.error("Multiple Credentials specifications found, using first.");
-               }
-               Credentials credentials = new Credentials((Element) itemElements.item(0));
+                       // Startup Attribute Resolver & ARP engine
+                       AttributeResolver resolver = null;
+                       ArpEngine arpEngine = null;
+                       try {
+                               resolver = new AttributeResolver(configuration);
 
-               // Load metadata
-               itemElements = originConfig.getDocumentElement().getElementsByTagNameNS(IdPConfig.originConfigNamespace,
-                               "FederationProvider");
-               for (int i = 0; i < itemElements.getLength(); i++) {
-                       addFederationProvider((Element) itemElements.item(i));
-               }
-               if (providerCount() < 1) {
-                       log.error("No Federation Provider metadata loaded.");
-                       throw new ShibbolethConfigurationException("Could not load federation metadata.");
-               }
+                               itemElements = originConfig.getDocumentElement().getElementsByTagNameNS(
+                                               IdPConfig.originConfigNamespace, "ReleasePolicyEngine");
 
-               // Load relying party config
-               try {
-                       spMapper = new ServiceProviderMapper(originConfig.getDocumentElement(), configuration, credentials,
-                                       nameMapper, this);
-               } catch (ServiceProviderMapperException e) {
-                       log.error("Could not load Identity Provider configuration: " + e);
-                       throw new ShibbolethConfigurationException("Could not load Identity Provider configuration.");
-               }
+                               if (itemElements.getLength() > 1) {
+                                       log.warn("Encountered multiple <ReleasePolicyEngine> configuration elements.  Using first...");
+                               }
+                               if (itemElements.getLength() < 1) {
+                                       arpEngine = new ArpEngine();
+                               } else {
+                                       arpEngine = new ArpEngine((Element) itemElements.item(0));
+                               }
 
-               try {
-                       // Startup Attribute Resolver
-                       AttributeResolver resolver = new AttributeResolver(configuration);
+                       } catch (ArpException ae) {
+                               log.fatal("The Identity Provider could not be initialized "
+                                               + "due to a problem with the ARP Engine configuration: " + ae);
+                               throw new ShibbolethConfigurationException("Could not load ARP Engine.");
+                       } catch (AttributeResolverException ne) {
+                               log.fatal("The Identity Provider could not be initialized due "
+                                               + "to a problem with the Attribute Resolver configuration: " + ne);
+                               throw new ShibbolethConfigurationException("Could not load Attribute Resolver.");
+                       }
 
-                       // Startup ARP Engine
-                       ArpEngine arpEngine = null;
-                       itemElements = originConfig.getDocumentElement().getElementsByTagNameNS(IdPConfig.originConfigNamespace,
-                                       "ReleasePolicyEngine");
+                       // Load protocol handlers and support library
+                       protocolSupport = new ProtocolSupport(configuration, transactionLog, nameMapper, spMapper, arpEngine,
+                                       resolver);
+                       log.debug("Starting with Shibboleth v1 protocol handling enabled.");
+                       protocolHandlers = new ProtocolHandler[]{new Shibbolethv1SSOHandler()};
 
-                       if (itemElements.getLength() > 1) {
-                               log.warn("Encountered multiple <ReleasePolicyEngine> configuration elements.  Using first...");
+                       // Load metadata
+                       itemElements = originConfig.getDocumentElement().getElementsByTagNameNS(IdPConfig.originConfigNamespace,
+                                       "FederationProvider");
+                       for (int i = 0; i < itemElements.getLength(); i++) {
+                               protocolSupport.addFederationProvider((Element) itemElements.item(i));
                        }
-                       if (itemElements.getLength() < 1) {
-                               arpEngine = new ArpEngine();
-                       } else {
-                               arpEngine = new ArpEngine((Element) itemElements.item(0));
+                       if (protocolSupport.providerCount() < 1) {
+                               log.error("No Federation Provider metadata loaded.");
+                               throw new ShibbolethConfigurationException("Could not load federation metadata.");
                        }
 
-                       // Startup responder
-                       responder = new AAResponder(arpEngine, resolver);
-
-               } catch (ArpException ae) {
-                       log.fatal("The Identity Provider could not be initialized "
-                                       + "due to a problem with the ARP Engine configuration: " + ae);
-                       throw new ShibbolethConfigurationException("Could not load ARP Engine.");
-               } catch (AttributeResolverException ne) {
-                       log.fatal("The Identity Provider could not be initialized due "
-                                       + "to a problem with the Attribute Resolver configuration: " + ne);
-                       throw new ShibbolethConfigurationException("Could not load Attribute Resolver.");
-               }
+                       log.info("Identity Provider initialization complete.");
 
+               } catch (ShibbolethConfigurationException ae) {
+                       log.fatal("The Identity Provider could not be initialized: " + ae);
+                       throw new UnavailableException("Identity Provider failed to initialize.");
+               } catch (SAMLException se) {
+                       log.fatal("SAML SOAP binding could not be loaded: " + se);
+                       throw new UnavailableException("Identity Provider failed to initialize.");
+               }
        }
 
        public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
@@ -276,147 +240,27 @@ public class IdPResponder extends TargetFederationComponent {
                MDC.put("serviceId", "[IdP] " + idgen.nextInt());
                MDC.put("remoteAddr", request.getRemoteAddr());
                log.debug("Recieved a request via GET.");
-               log.info("Handling authN request.");
 
                try {
+                       // TODO this throttle should probably just wrap signing operations...
                        throttle.enter();
 
-                       // Ensure that we have the required data from the servlet container
-                       validateEngineData(request);
-
-                       // Determine which profile of SAML we are responding to (at this point, Shib vs. EAuth)
-                       SSOProfileHandler activeHandler = null;
-                       for (int i = 0; i < profileHandlers.length; i++) {
-                               if (profileHandlers[i].validForRequest(request)) {
-                                       activeHandler = profileHandlers[i];
+                       // Determine which protocol we are responding to (at this point, Shibv1 vs. EAuth)
+                       ProtocolHandler activeHandler = null;
+                       for (int i = 0; i < protocolHandlers.length; i++) {
+                               if (protocolHandlers[i].validForRequest(request)) {
+                                       activeHandler = protocolHandlers[i];
                                        break;
                                }
                        }
+
                        if (activeHandler == null) { throw new InvalidClientDataException(
                                        "The request did not contain sufficient parameter data to determine the protocol."); }
 
-                       // Run profile specific preprocessing
-                       if (activeHandler.preProcessHook(request, response)) { return; }
-
-                       // Get the authN info
-                       String username = configuration.getAuthHeaderName().equalsIgnoreCase("REMOTE_USER") ? request
-                                       .getRemoteUser() : request.getHeader(configuration.getAuthHeaderName());
-
-                       // Select the appropriate Relying Party configuration for the request
-                       RelyingParty relyingParty = null;
-                       String remoteProviderId = activeHandler.getRemoteProviderId(request);
-                       // If the target did not send a Provider Id, then assume it is a Shib
-                       // 1.1 or older target
-                       if (remoteProviderId == null) {
-                               relyingParty = spMapper.getLegacyRelyingParty();
-                       } else if (remoteProviderId.equals("")) {
-                               throw new InvalidClientDataException("Invalid service provider id.");
-                       } else {
-                               log.debug("Remote provider has identified itself as: (" + remoteProviderId + ").");
-                               relyingParty = spMapper.getRelyingParty(remoteProviderId);
-                       }
-
-                       // Grab the metadata for the provider
-                       EntityDescriptor provider = lookup(relyingParty.getProviderId());
-
-                       // Use profile-specific method for determining the acceptance URL
-                       String acceptanceURL = activeHandler.getAcceptanceURL(request, relyingParty, provider);
-
-                       // Make sure that the selected relying party configuration is appropriate for this
-                       // acceptance URL
-                       if (!relyingParty.isLegacyProvider()) {
-
-                               if (provider == null) {
-                                       log.info("No metadata found for provider: (" + relyingParty.getProviderId() + ").");
-                                       relyingParty = spMapper.getRelyingParty(null);
-
-                               } else {
-
-                                       if (isValidAssertionConsumerURL(provider, acceptanceURL)) {
-                                               log.info("Supplied consumer URL validated for this provider.");
-                                       } else {
-                                               log.error("Assertion consumer service URL (" + acceptanceURL + ") is NOT valid for provider ("
-                                                               + relyingParty.getProviderId() + ").");
-                                               throw new InvalidClientDataException("Invalid assertion consumer service URL.");
-                                       }
-                               }
-                       }
-
-                       // Create SAML Name Identifier
-                       SAMLNameIdentifier nameId = nameMapper.getNameIdentifierName(relyingParty.getHSNameFormatId(),
-                                       new AuthNPrincipal(username), relyingParty, relyingParty.getIdentityProvider());
-
-                       String authenticationMethod = request.getHeader("SAMLAuthenticationMethod");
-                       if (authenticationMethod == null || authenticationMethod.equals("")) {
-                               authenticationMethod = relyingParty.getDefaultAuthMethod().toString();
-                               log.debug("User was authenticated via the default method for this relying party ("
-                                               + authenticationMethod + ").");
-                       } else {
-                               log.debug("User was authenticated via the method (" + authenticationMethod + ").");
-                       }
-
-                       // We might someday want to provide a mechanism for the authenticator to specify the auth time
-                       SAMLAssertion[] assertions = activeHandler.processHook(request, relyingParty, provider, nameId,
-                                       authenticationMethod, new Date(System.currentTimeMillis()));
-
-                       // TODO do assertion signing here
-
-                       // SAML Artifact profile
-                       if (useArtifactProfile(provider, acceptanceURL)) {
-                               log.debug("Responding with Artifact profile.");
-
-                               // Create artifacts for each assertion
-                               ArrayList artifacts = new ArrayList();
-                               for (int i = 0; i < assertions.length; i++) {
-                                       artifacts.add(artifactMapper.generateArtifact(assertions[i], relyingParty));
-                               }
-
-                               // Assemble the query string
-                               StringBuffer destination = new StringBuffer(acceptanceURL);
-                               destination.append("?TARGET=");
-                               destination.append(URLEncoder.encode(activeHandler.getSAMLTargetParameter(request, relyingParty,
-                                               provider), "UTF-8"));
-                               Iterator iterator = artifacts.iterator();
-                               StringBuffer artifactBuffer = new StringBuffer(); // Buffer for the transaction log
-                               while (iterator.hasNext()) {
-                                       destination.append("&SAMLart=");
-                                       String artifact = (String) iterator.next();
-                                       destination.append(URLEncoder.encode(artifact, "UTF-8"));
-                                       artifactBuffer.append("(" + artifact + ")");
-                               }
-                               log.debug("Redirecting to (" + destination.toString() + ").");
-                               response.sendRedirect(destination.toString()); // Redirect to the artifact receiver
-
-                               transactionLog.info("Assertion artifact(s) (" + artifactBuffer.toString() + ") issued to provider ("
-                                               + relyingParty.getIdentityProvider().getProviderId() + ") on behalf of principal (" + username
-                                               + "). Name Identifier: (" + nameId.getName() + "). Name Identifier Format: ("
-                                               + nameId.getFormat() + ").");
-
-                               // SAML POST profile
-                       } else {
-                               log.debug("Responding with POST profile.");
-                               request.setAttribute("acceptanceURL", acceptanceURL);
-                               request.setAttribute("target", activeHandler.getSAMLTargetParameter(request, relyingParty, provider));
-
-                               SAMLResponse samlResponse = new SAMLResponse(null, acceptanceURL, Arrays.asList(assertions), null);
-                               addSignatures(samlResponse, relyingParty, provider, true);
-                               createPOSTForm(request, response, samlResponse.toBase64());
-
-                               // Make transaction log entry
-                               if (relyingParty.isLegacyProvider()) {
-                                       transactionLog.info("Authentication assertion issued to legacy provider (SHIRE: "
-                                                       + request.getParameter("shire") + ") on behalf of principal (" + username
-                                                       + ") for resource (" + request.getParameter("target") + "). Name Identifier: ("
-                                                       + nameId.getName() + "). Name Identifier Format: (" + nameId.getFormat() + ").");
-                               } else {
-                                       transactionLog.info("Authentication assertion issued to provider ("
-                                                       + relyingParty.getIdentityProvider().getProviderId() + ") on behalf of principal ("
-                                                       + username + "). Name Identifier: (" + nameId.getName() + "). Name Identifier Format: ("
-                                                       + nameId.getFormat() + ").");
-                               }
-                       }
+                       log.info("Processing " + activeHandler.getHandlerName() + " request.");
+                       // Pass request to the appropriate handler
+                       activeHandler.processRequest(request, response, protocolSupport);
 
-                       // TODO profile specific error handling
                } catch (NameIdentifierMappingException ex) {
                        log.error(ex);
                        handleSSOError(request, response, ex);
@@ -436,7 +280,6 @@ public class IdPResponder extends TargetFederationComponent {
                } finally {
                        throttle.exit();
                }
-
        }
 
        public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
@@ -445,6 +288,24 @@ public class IdPResponder extends TargetFederationComponent {
                MDC.put("remoteAddr", request.getRemoteAddr());
                log.debug("Recieved a request via POST.");
 
+               // Determine which protocol we are responding to (at this point, Shibv1 vs. EAuth)
+               ProtocolHandler activeHandler = null;
+               for (int i = 0; i < protocolHandlers.length; i++) {
+                       if (protocolHandlers[i].validForRequest(request)) {
+                               activeHandler = protocolHandlers[i];
+                               break;
+                       }
+               }
+
+               // TODO some other type of error here
+               /*
+                * if (activeHandler == null) { throw new InvalidClientDataException( "The request did not contain sufficient
+                * parameter data to determine the protocol."); }
+                */
+               log.info("Processing " + activeHandler.getHandlerName() + " request.");
+               // Pass request to the appropriate handler
+       //      activeHandler.processRequest(request, response, protocolSupport);
+
                // Parse SOAP request and marshall SAML request object
                SAMLRequest samlRequest = null;
                try {
@@ -475,7 +336,8 @@ public class IdPResponder extends TargetFederationComponent {
                        if (artifacts.hasNext()) {
                                artifacts = null; // get rid of the iterator
                                log.info("Recieved a request to dereference an assertion artifact.");
-                               processArtifactDereference(samlRequest, request, response);
+
+                               // processArtifactDereference(samlRequest, request, response);
                                return;
                        }
 
@@ -577,7 +439,8 @@ public class IdPResponder extends TargetFederationComponent {
                                                then, Collections.singleton(condition), null, Collections.singleton(statement));
 
                                samlResponse = new SAMLResponse(samlRequest.getId(), null, Collections.singleton(sAssertion), exception);
-                               addSignatures(samlResponse, relyingParty, lookup(relyingParty.getProviderId()), false);
+                               ProtocolSupport.addSignatures(samlResponse, relyingParty, protocolSupport.lookup(relyingParty
+                                               .getProviderId()), false);
                        }
                } catch (SAMLException se) {
                        ourSE = se;
@@ -633,7 +496,7 @@ public class IdPResponder extends TargetFederationComponent {
                } else {
 
                        // Identify a Relying Party
-                       relyingParty = spMapper.getRelyingParty(attributeQuery.getResource());
+                       relyingParty = protocolSupport.getServiceProviderMapper().getRelyingParty(attributeQuery.getResource());
 
                        try {
                                effectiveName = getEffectiveName(request, relyingParty);
@@ -646,7 +509,7 @@ public class IdPResponder extends TargetFederationComponent {
 
                if (effectiveName == null) {
                        log.debug("Using default Relying Party for unauthenticated provider.");
-                       relyingParty = spMapper.getRelyingParty(null);
+                       relyingParty = protocolSupport.getServiceProviderMapper().getRelyingParty(null);
                }
 
                // Fail if we can't honor SAML Subject Confirmation
@@ -661,8 +524,8 @@ public class IdPResponder extends TargetFederationComponent {
                }
 
                // Map Subject to local principal
-               Principal principal = nameMapper.getPrincipal(attributeQuery.getSubject().getName(), relyingParty, relyingParty
-                               .getIdentityProvider());
+               Principal principal = protocolSupport.getNameMapper().getPrincipal(attributeQuery.getSubject().getName(),
+                               relyingParty, relyingParty.getIdentityProvider());
                log.info("Request is for principal (" + principal.getName() + ").");
 
                SAMLAttribute[] attrs;
@@ -681,11 +544,11 @@ public class IdPResponder extends TargetFederationComponent {
                                }
                        }
 
-                       attrs = responder.getReleaseAttributes(principal, effectiveName, null, (URI[]) requestedAttrs
+                       attrs = protocolSupport.getReleaseAttributes(principal, effectiveName, null, (URI[]) requestedAttrs
                                        .toArray(new URI[0]));
                } else {
                        log.info("Request does not designate specific attributes, resolving all available.");
-                       attrs = responder.getReleaseAttributes(principal, effectiveName, null);
+                       attrs = protocolSupport.getReleaseAttributes(principal, effectiveName, null);
                }
 
                log.info("Found " + attrs.length + " attribute(s) for " + principal.getName());
@@ -712,91 +575,41 @@ public class IdPResponder extends TargetFederationComponent {
 
        }
 
-       private void processArtifactDereference(SAMLRequest samlRequest, HttpServletRequest request,
-                       HttpServletResponse response) throws SAMLException, IOException {
-
-               // TODO validate that the endpoint is valid for the request type
-               // TODO how about signatures on artifact dereferencing
-
-               // Pull credential from request
-               X509Certificate credential = getCredentialFromProvider(request);
-               if (credential == null || credential.getSubjectX500Principal().getName(X500Principal.RFC2253).equals("")) {
-                       // The spec says that mutual authentication is required for the
-                       // artifact profile
-                       log.info("Request is from an unauthenticated service provider.");
-                       throw new SAMLException(SAMLException.REQUESTER,
-                                       "SAML Artifacts cannot be dereferenced for unauthenticated requesters.");
-               }
-
-               log.info("Request contains credential: (" + credential.getSubjectX500Principal().getName(X500Principal.RFC2253)
-                               + ").");
-
-               ArrayList assertions = new ArrayList();
-               Iterator artifacts = samlRequest.getArtifacts();
-
-               int queriedArtifacts = 0;
-               StringBuffer dereferencedArtifacts = new StringBuffer(); // for
-               // transaction
-               // log
-               while (artifacts.hasNext()) {
-                       queriedArtifacts++;
-                       String artifact = (String) artifacts.next();
-                       log.debug("Attempting to dereference artifact: (" + artifact + ").");
-                       ArtifactMapping mapping = artifactMapper.recoverAssertion(artifact);
-                       if (mapping != null) {
-                               SAMLAssertion assertion = mapping.getAssertion();
-
-                               // See if we have metadata for this provider
-                               EntityDescriptor provider = lookup(mapping.getServiceProviderId());
-                               if (provider == null) {
-                                       log.info("No metadata found for provider: (" + mapping.getServiceProviderId() + ").");
-                                       throw new SAMLException(SAMLException.REQUESTER, "Invalid service provider.");
-                               }
-
-                               // Make sure that the suppplied credential is valid for the
-                               // provider to which the artifact was issued
-                               if (!isValidCredential(provider, credential)) {
-                                       log.error("Supplied credential ("
-                                                       + credential.getSubjectX500Principal().getName(X500Principal.RFC2253)
-                                                       + ") is NOT valid for provider (" + mapping.getServiceProviderId()
-                                                       + "), to whom this artifact was issued.");
-                                       throw new SAMLException(SAMLException.REQUESTER, "Invalid credential.");
-                               }
-
-                               log.debug("Supplied credential validated for the provider to which this artifact was issued.");
-
-                               assertions.add(assertion);
-                               dereferencedArtifacts.append("(" + artifact + ")");
-                       }
-               }
-
-               // The spec requires that if any artifacts are dereferenced, they must
-               // all be dereferenced
-               if (assertions.size() > 0 && assertions.size() != queriedArtifacts) { throw new SAMLException(
-                               SAMLException.REQUESTER, "Unable to successfully dereference all artifacts."); }
-
-               // Create and send response
-               // The spec says that we should send "success" in the case where no
-               // artifacts match
-               SAMLResponse samlResponse = new SAMLResponse(samlRequest.getId(), null, assertions, null);
-
-               if (log.isDebugEnabled()) {
-                       try {
-                               log.debug("Dumping generated SAML Response:"
-                                               + System.getProperty("line.separator")
-                                               + new String(new BASE64Decoder().decodeBuffer(new String(samlResponse.toBase64(), "ASCII")),
-                                                               "UTF8"));
-                       } catch (SAMLException e) {
-                               log.error("Encountered an error while decoding SAMLReponse for logging purposes.");
-                       } catch (IOException e) {
-                               log.error("Encountered an error while decoding SAMLReponse for logging purposes.");
-                       }
-               }
-
-               binding.respond(response, samlResponse, null);
-
-               transactionLog.info("Succesfully dereferenced the following artifacts: " + dereferencedArtifacts.toString());
-       }
+       /*
+        * private void processArtifactDereference(SAMLRequest samlRequest, HttpServletRequest request, HttpServletResponse
+        * response) throws SAMLException, IOException { // TODO validate that the endpoint is valid for the request type //
+        * TODO how about signatures on artifact dereferencing // Pull credential from request X509Certificate credential =
+        * getCredentialFromProvider(request); if (credential == null ||
+        * credential.getSubjectX500Principal().getName(X500Principal.RFC2253).equals("")) { // The spec says that mutual
+        * authentication is required for the // artifact profile log.info("Request is from an unauthenticated service
+        * provider."); throw new SAMLException(SAMLException.REQUESTER, "SAML Artifacts cannot be dereferenced for
+        * unauthenticated requesters."); } log.info("Request contains credential: (" +
+        * credential.getSubjectX500Principal().getName(X500Principal.RFC2253) + ")."); ArrayList assertions = new
+        * ArrayList(); Iterator artifacts = samlRequest.getArtifacts(); int queriedArtifacts = 0; StringBuffer
+        * dereferencedArtifacts = new StringBuffer(); // for // transaction // log while (artifacts.hasNext()) {
+        * queriedArtifacts++; String artifact = (String) artifacts.next(); log.debug("Attempting to dereference artifact: (" +
+        * artifact + ")."); ArtifactMapping mapping = artifactMapper.recoverAssertion(artifact); if (mapping != null) {
+        * SAMLAssertion assertion = mapping.getAssertion(); // See if we have metadata for this provider EntityDescriptor
+        * provider = lookup(mapping.getServiceProviderId()); if (provider == null) { log.info("No metadata found for
+        * provider: (" + mapping.getServiceProviderId() + ")."); throw new SAMLException(SAMLException.REQUESTER, "Invalid
+        * service provider."); } // Make sure that the suppplied credential is valid for the // provider to which the
+        * artifact was issued if (!isValidCredential(provider, credential)) { log.error("Supplied credential (" +
+        * credential.getSubjectX500Principal().getName(X500Principal.RFC2253) + ") is NOT valid for provider (" +
+        * mapping.getServiceProviderId() + "), to whom this artifact was issued."); throw new
+        * SAMLException(SAMLException.REQUESTER, "Invalid credential."); } log.debug("Supplied credential validated for the
+        * provider to which this artifact was issued."); assertions.add(assertion); dereferencedArtifacts.append("(" +
+        * artifact + ")"); } } // The spec requires that if any artifacts are dereferenced, they must // all be
+        * dereferenced if (assertions.size() > 0 && assertions.size() != queriedArtifacts) { throw new SAMLException(
+        * SAMLException.REQUESTER, "Unable to successfully dereference all artifacts."); } // Create and send response //
+        * The spec says that we should send "success" in the case where no // artifacts match SAMLResponse samlResponse =
+        * new SAMLResponse(samlRequest.getId(), null, assertions, null); if (log.isDebugEnabled()) { try {
+        * log.debug("Dumping generated SAML Response:" + System.getProperty("line.separator") + new String(new
+        * BASE64Decoder().decodeBuffer(new String(samlResponse.toBase64(), "ASCII")), "UTF8")); } catch (SAMLException e) {
+        * log.error("Encountered an error while decoding SAMLReponse for logging purposes."); } catch (IOException e) {
+        * log.error("Encountered an error while decoding SAMLReponse for logging purposes."); } } binding.respond(response,
+        * samlResponse, null); transactionLog.info("Succesfully dereferenced the following artifacts: " +
+        * dereferencedArtifacts.toString()); }
+        */
 
        private void sendSAMLFailureResponse(HttpServletResponse httpResponse, SAMLRequest samlRequest,
                        SAMLException exception) throws IOException {
@@ -854,7 +667,7 @@ public class IdPResponder extends TargetFederationComponent {
                        } else {
 
                                // See if we have metadata for this provider
-                               EntityDescriptor provider = lookup(relyingParty.getProviderId());
+                               EntityDescriptor provider = protocolSupport.lookup(relyingParty.getProviderId());
                                if (provider == null) {
                                        log.info("No metadata found for provider: (" + relyingParty.getProviderId() + ").");
                                        log.info("Treating remote provider as unauthenticated.");
@@ -877,110 +690,6 @@ public class IdPResponder extends TargetFederationComponent {
                }
        }
 
-       private static void addSignatures(SAMLResponse response, RelyingParty relyingParty, EntityDescriptor provider,
-                       boolean signResponse) throws SAMLException {
-
-               if (provider != null) {
-                       boolean signAssertions = false;
-
-                       SPSSODescriptor sp = provider.getSPSSODescriptor(org.opensaml.XML.SAML11_PROTOCOL_ENUM);
-                       if (sp == null) {
-                               log.info("Inappropriate metadata for provider: " + provider.getId() + ".  Expected SPSSODescriptor.");
-                       }
-                       if (sp.getWantAssertionsSigned()) {
-                               signAssertions = true;
-                       }
-
-                       if (signAssertions && relyingParty.getIdentityProvider().getSigningCredential() != null
-                                       && relyingParty.getIdentityProvider().getSigningCredential().getPrivateKey() != null) {
-
-                               Iterator assertions = response.getAssertions();
-
-                               while (assertions.hasNext()) {
-                                       SAMLAssertion assertion = (SAMLAssertion) assertions.next();
-                                       String assertionAlgorithm;
-                                       if (relyingParty.getIdentityProvider().getSigningCredential().getCredentialType() == Credential.RSA) {
-                                               assertionAlgorithm = XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1;
-                                       } else if (relyingParty.getIdentityProvider().getSigningCredential().getCredentialType() == Credential.DSA) {
-                                               assertionAlgorithm = XMLSignature.ALGO_ID_SIGNATURE_DSA;
-                                       } else {
-                                               throw new InvalidCryptoException(SAMLException.RESPONDER,
-                                                               "The Shibboleth IdP currently only supports signing with RSA and DSA keys.");
-                                       }
-
-                                       assertion.sign(assertionAlgorithm, relyingParty.getIdentityProvider().getSigningCredential()
-                                                       .getPrivateKey(), Arrays.asList(relyingParty.getIdentityProvider().getSigningCredential()
-                                                       .getX509CertificateChain()));
-                               }
-                       }
-               }
-
-               // Sign the response, if appropriate
-               if (signResponse && relyingParty.getIdentityProvider().getSigningCredential() != null
-                               && relyingParty.getIdentityProvider().getSigningCredential().getPrivateKey() != null) {
-
-                       String responseAlgorithm;
-                       if (relyingParty.getIdentityProvider().getSigningCredential().getCredentialType() == Credential.RSA) {
-                               responseAlgorithm = XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1;
-                       } else if (relyingParty.getIdentityProvider().getSigningCredential().getCredentialType() == Credential.DSA) {
-                               responseAlgorithm = XMLSignature.ALGO_ID_SIGNATURE_DSA;
-                       } else {
-                               throw new InvalidCryptoException(SAMLException.RESPONDER,
-                                               "The Shibboleth IdP currently only supports signing with RSA and DSA keys.");
-                       }
-
-                       response.sign(responseAlgorithm, relyingParty.getIdentityProvider().getSigningCredential().getPrivateKey(),
-                                       Arrays.asList(relyingParty.getIdentityProvider().getSigningCredential().getX509CertificateChain()));
-               }
-       }
-
-       private static boolean useArtifactProfile(EntityDescriptor provider, String acceptanceURL) {
-
-               // Default to POST if we have no metadata
-               if (provider == null) { return false; }
-
-               // Default to POST if we have incomplete metadata
-        SPSSODescriptor sp = provider.getSPSSODescriptor(org.opensaml.XML.SAML11_PROTOCOL_ENUM);
-               if (sp == null) { return false; }
-
-        // TODO: This will actually favor artifact, since a given location could support
-        // both profiles. If that's not what we want, needs adjustment...
-               Iterator endpoints = sp.getAssertionConsumerServiceManager().getEndpoints();
-               while (endpoints.hasNext()) {
-            Endpoint ep = (Endpoint)endpoints.next();
-                       if (acceptanceURL.equals(ep.getLocation())
-                                       && SAMLBrowserProfile.PROFILE_ARTIFACT_URI.equals(ep.getBinding())) { return true; }
-               }
-
-        // Default to POST if we have incomplete metadata
-               return false;
-       }
-
-       private static void validateEngineData(HttpServletRequest req) throws InvalidClientDataException {
-
-               if ((req.getRemoteUser() == null) || (req.getRemoteUser().equals(""))) { throw new InvalidClientDataException(
-                               "Unable to authenticate remote user"); }
-               if ((req.getRemoteAddr() == null) || (req.getRemoteAddr().equals(""))) { throw new InvalidClientDataException(
-                               "Unable to obtain client address."); }
-       }
-
-       private static boolean isValidAssertionConsumerURL(EntityDescriptor provider, String shireURL)
-                       throws InvalidClientDataException {
-
-        SPSSODescriptor sp = provider.getSPSSODescriptor(org.opensaml.XML.SAML11_PROTOCOL_ENUM);
-               if (sp == null) {
-                       log.info("Inappropriate metadata for provider.");
-                       return false;
-               }
-
-               Iterator endpoints = sp.getAssertionConsumerServiceManager().getEndpoints();
-               while (endpoints.hasNext()) {
-                       if (shireURL.equals(((Endpoint)endpoints.next()).getLocation())) { return true; }
-               }
-               log.info("Supplied consumer URL not found in metadata.");
-               return false;
-       }
-
        private static X509Certificate getCredentialFromProvider(HttpServletRequest req) {
 
                X509Certificate[] certArray = (X509Certificate[]) req.getAttribute("javax.servlet.request.X509Certificate");
@@ -998,15 +707,14 @@ public class IdPResponder extends TargetFederationComponent {
                // TODO figure out what to do about this role business here
                Iterator descriptors = sp.getKeyDescriptors();
                while (descriptors.hasNext()) {
-                       KeyInfo keyInfo = ((KeyDescriptor)descriptors.next()).getKeyInfo();
+                       KeyInfo keyInfo = ((KeyDescriptor) descriptors.next()).getKeyInfo();
                        for (int l = 0; keyInfo.lengthKeyName() > l; l++) {
                                try {
 
                                        // First, try to match DN against metadata
                                        try {
                                                if (certificate.getSubjectX500Principal().getName(X500Principal.RFC2253).equals(
-                                                               new X500Principal(keyInfo.itemKeyName(l).getKeyName())
-                                                                               .getName(X500Principal.RFC2253))) {
+                                                               new X500Principal(keyInfo.itemKeyName(l).getKeyName()).getName(X500Principal.RFC2253))) {
                                                        log.debug("Matched against DN.");
                                                        return true;
                                                }
@@ -1022,8 +730,7 @@ public class IdPResponder extends TargetFederationComponent {
                                                if (altNames != null) {
                                                        for (Iterator nameIterator = altNames.iterator(); nameIterator.hasNext();) {
                                                                List altName = (List) nameIterator.next();
-                                                               if (altName.get(0).equals(new Integer(2))
-                                                                               || altName.get(0).equals(new Integer(6))) { // 2 is
+                                                               if (altName.get(0).equals(new Integer(2)) || altName.get(0).equals(new Integer(6))) { // 2 is
                                                                        // DNS,
                                                                        // 6 is
                                                                        // URI
@@ -1068,26 +775,7 @@ public class IdPResponder extends TargetFederationComponent {
                return true;
        }
 
-       private static void createPOSTForm(HttpServletRequest req, HttpServletResponse res, byte[] buf) throws IOException,
-                       ServletException {
-
-               // Hardcoded to ASCII to ensure Base64 encoding compatibility
-               req.setAttribute("assertion", new String(buf, "ASCII"));
-
-               if (log.isDebugEnabled()) {
-                       try {
-                               log.debug("Dumping generated SAML Response:" + System.getProperty("line.separator")
-                                               + new String(new BASE64Decoder().decodeBuffer(new String(buf, "ASCII")), "UTF8"));
-                       } catch (IOException e) {
-                               log.error("Encountered an error while decoding SAMLReponse for logging purposes.");
-                       }
-               }
-
-               // TODO rename from hs.jsp to more appropriate name
-               RequestDispatcher rd = req.getRequestDispatcher("/hs.jsp");
-               rd.forward(req, res);
-       }
-
+       // TODO this should be renamed
        private static void handleSSOError(HttpServletRequest req, HttpServletResponse res, Exception e)
                        throws ServletException, IOException {
 
@@ -1130,172 +818,596 @@ public class IdPResponder extends TargetFederationComponent {
                }
        }
 
-       abstract class SSOProfileHandler {
+}
 
-               abstract String getHandlerName();
+class InvalidClientDataException extends Exception {
 
-               abstract String getRemoteProviderId(HttpServletRequest req);
+       public InvalidClientDataException(String message) {
 
-               abstract boolean validForRequest(HttpServletRequest request);
+               super(message);
+       }
 
-               abstract boolean preProcessHook(HttpServletRequest request, HttpServletResponse response) throws IOException;
+}
 
-               abstract SAMLAssertion[] processHook(HttpServletRequest request, RelyingParty relyingParty, EntityDescriptor provider,
-                               SAMLNameIdentifier nameId, String authenticationMethod, Date authTime) throws SAMLException,
-                               IOException;
+class ProtocolSupport implements Metadata {
 
-               abstract String getSAMLTargetParameter(HttpServletRequest request, RelyingParty relyingParty, EntityDescriptor provider);
+       private static Logger log = Logger.getLogger(ProtocolSupport.class.getName());
+       private Logger transactionLog;
+       private IdPConfig config;
+       private ArrayList fedMetadata = new ArrayList();
+       private NameMapper nameMapper;
+       private ServiceProviderMapper spMapper;
+       private ArpEngine arpEngine;
+       private AttributeResolver resolver;
+
+       ProtocolSupport(IdPConfig config, Logger transactionLog, NameMapper nameMapper, ServiceProviderMapper spMapper,
+                       ArpEngine arpEngine, AttributeResolver resolver) {
+
+               this.transactionLog = transactionLog;
+               this.config = config;
+               this.nameMapper = nameMapper;
+               this.spMapper = spMapper;
+               spMapper.setMetadata(this);
+               this.arpEngine = arpEngine;
+               this.resolver = resolver;
+       }
+
+       public static void validateEngineData(HttpServletRequest req) throws InvalidClientDataException {
 
-               abstract String getAcceptanceURL(HttpServletRequest request, RelyingParty relyingParty, EntityDescriptor provider)
-                               throws InvalidClientDataException;
+               if ((req.getRemoteUser() == null) || (req.getRemoteUser().equals(""))) { throw new InvalidClientDataException(
+                               "Unable to authenticate remote user"); }
+               if ((req.getRemoteAddr() == null) || (req.getRemoteAddr().equals(""))) { throw new InvalidClientDataException(
+                               "Unable to obtain client address."); }
        }
-       class ShibbolethProfileHandler extends SSOProfileHandler {
 
-               //private static Logger log             = Logger.getLogger(ShibbolethProfileHandler.class.getName());
-               private final String    name    = "Shibboleth";
+       public Logger getTransactionLog() {
 
-               /*
-                * (non-Javadoc)
-                * 
-                * @see edu.internet2.middleware.shibboleth.hs.AuthNProfileHandler#getHandlerName()
-                */
-               String getHandlerName() {
-                       return name;
+               return transactionLog;
+       }
+
+       public IdPConfig getIdPConfig() {
+
+               return config;
+       }
+
+       public NameMapper getNameMapper() {
+
+               return nameMapper;
+       }
+
+       public ServiceProviderMapper getServiceProviderMapper() {
+
+               return spMapper;
+       }
+
+       public static void addSignatures(SAMLResponse response, RelyingParty relyingParty, EntityDescriptor provider,
+                       boolean signResponse) throws SAMLException {
+
+               if (provider != null) {
+                       boolean signAssertions = false;
+
+                       SPSSODescriptor sp = provider.getSPSSODescriptor(org.opensaml.XML.SAML11_PROTOCOL_ENUM);
+                       if (sp == null) {
+                               log.info("Inappropriate metadata for provider: " + provider.getId() + ".  Expected SPSSODescriptor.");
+                       }
+                       if (sp.getWantAssertionsSigned()) {
+                               signAssertions = true;
+                       }
+
+                       if (signAssertions && relyingParty.getIdentityProvider().getSigningCredential() != null
+                                       && relyingParty.getIdentityProvider().getSigningCredential().getPrivateKey() != null) {
+
+                               Iterator assertions = response.getAssertions();
+
+                               while (assertions.hasNext()) {
+                                       SAMLAssertion assertion = (SAMLAssertion) assertions.next();
+                                       String assertionAlgorithm;
+                                       if (relyingParty.getIdentityProvider().getSigningCredential().getCredentialType() == Credential.RSA) {
+                                               assertionAlgorithm = XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1;
+                                       } else if (relyingParty.getIdentityProvider().getSigningCredential().getCredentialType() == Credential.DSA) {
+                                               assertionAlgorithm = XMLSignature.ALGO_ID_SIGNATURE_DSA;
+                                       } else {
+                                               throw new InvalidCryptoException(SAMLException.RESPONDER,
+                                                               "The Shibboleth IdP currently only supports signing with RSA and DSA keys.");
+                                       }
+
+                                       assertion.sign(assertionAlgorithm, relyingParty.getIdentityProvider().getSigningCredential()
+                                                       .getPrivateKey(), Arrays.asList(relyingParty.getIdentityProvider().getSigningCredential()
+                                                       .getX509CertificateChain()));
+                               }
+                       }
                }
 
-               /*
-                * (non-Javadoc)
-                * 
-                * @see edu.internet2.middleware.shibboleth.hs.AuthNProfileHandler#validForRequest()
-                */
-               boolean validForRequest(HttpServletRequest request) {
+               // Sign the response, if appropriate
+               if (signResponse && relyingParty.getIdentityProvider().getSigningCredential() != null
+                               && relyingParty.getIdentityProvider().getSigningCredential().getPrivateKey() != null) {
 
-                       if (request.getParameter("target") != null && !request.getParameter("target").equals("")
-                                       && request.getParameter("shire") != null && !request.getParameter("shire").equals("")) {
-                               log.debug("Found (target) and (shire) parameters.  Request "
-                                               + "appears to be valid for the Shibboleth profile.");
-                               return true;
+                       String responseAlgorithm;
+                       if (relyingParty.getIdentityProvider().getSigningCredential().getCredentialType() == Credential.RSA) {
+                               responseAlgorithm = XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1;
+                       } else if (relyingParty.getIdentityProvider().getSigningCredential().getCredentialType() == Credential.DSA) {
+                               responseAlgorithm = XMLSignature.ALGO_ID_SIGNATURE_DSA;
                        } else {
-                               return false;
+                               throw new InvalidCryptoException(SAMLException.RESPONDER,
+                                               "The Shibboleth IdP currently only supports signing with RSA and DSA keys.");
                        }
+
+                       response.sign(responseAlgorithm, relyingParty.getIdentityProvider().getSigningCredential().getPrivateKey(),
+                                       Arrays.asList(relyingParty.getIdentityProvider().getSigningCredential().getX509CertificateChain()));
                }
+       }
 
-               /*
-                * (non-Javadoc)
-                * 
-                * @see edu.internet2.middleware.shibboleth.hs.AuthNProfileHandler#preProcessHook(javax.servlet.http.HttpServletRequest)
-                */
-               boolean preProcessHook(HttpServletRequest request, HttpServletResponse response) {
+       protected void addFederationProvider(Element element) {
+
+               log.debug("Found Federation Provider configuration element.");
+               if (!element.getTagName().equals("FederationProvider")) {
+                       log.error("Error while attemtping to load Federation Provider.  Malformed provider specificaion.");
+                       return;
+               }
+
+               try {
+                       fedMetadata.add(FederationProviderFactory.loadProvider(element));
+               } catch (MetadataException e) {
+                       log.error("Unable to load Federation Provider.  Skipping...");
+               }
+       }
+
+       public int providerCount() {
+
+               return fedMetadata.size();
+       }
+
+       public EntityDescriptor lookup(String providerId) {
+
+               Iterator iterator = fedMetadata.iterator();
+               while (iterator.hasNext()) {
+                       EntityDescriptor provider = ((Metadata) iterator.next()).lookup(providerId);
+                       if (provider != null) { return provider; }
+               }
+               return null;
+       }
+
+       public EntityDescriptor lookup(Artifact artifact) {
+
+               Iterator iterator = fedMetadata.iterator();
+               while (iterator.hasNext()) {
+                       EntityDescriptor provider = ((Metadata) iterator.next()).lookup(artifact);
+                       if (provider != null) { return provider; }
+               }
+               return null;
+       }
+
+       public SAMLAttribute[] getReleaseAttributes(Principal principal, String requester, URL resource) throws AAException {
+
+               try {
+                       URI[] potentialAttributes = arpEngine.listPossibleReleaseAttributes(principal, requester, resource);
+                       return getReleaseAttributes(principal, requester, resource, potentialAttributes);
+
+               } catch (ArpProcessingException e) {
+                       log.error("An error occurred while processing the ARPs for principal (" + principal.getName() + ") :"
+                                       + e.getMessage());
+                       throw new AAException("Error retrieving data for principal.");
+               }
+       }
+
+       public SAMLAttribute[] getReleaseAttributes(Principal principal, String requester, URL resource,
+                       URI[] attributeNames) throws AAException {
+
+               try {
+                       AAAttributeSet attributeSet = new AAAttributeSet();
+                       for (int i = 0; i < attributeNames.length; i++) {
+                               AAAttribute attribute = new AAAttribute(attributeNames[i].toString());
+                               attributeSet.add(attribute);
+                       }
+
+                       return resolveAttributes(principal, requester, resource, attributeSet);
+
+               } catch (SAMLException e) {
+                       log.error("An error occurred while creating attributes for principal (" + principal.getName() + ") :"
+                                       + e.getMessage());
+                       throw new AAException("Error retrieving data for principal.");
+
+               } catch (ArpProcessingException e) {
+                       log.error("An error occurred while processing the ARPs for principal (" + principal.getName() + ") :"
+                                       + e.getMessage());
+                       throw new AAException("Error retrieving data for principal.");
+               }
+       }
+
+       private SAMLAttribute[] resolveAttributes(Principal principal, String requester, URL resource,
+                       AAAttributeSet attributeSet) throws ArpProcessingException {
+
+               resolver.resolveAttributes(principal, requester, attributeSet);
+               arpEngine.filterAttributes(attributeSet, principal, requester, resource);
+               return attributeSet.getAttributes();
+       }
+
+       /**
+        * Cleanup resources that won't be released when this object is garbage-collected
+        */
+       public void destroy() {
+
+               resolver.destroy();
+               arpEngine.destroy();
+       }
+}
+
+class Shibbolethv1SSOHandler extends ProtocolHandler {
+
+       private static Logger log = Logger.getLogger(Shibbolethv1SSOHandler.class.getName());
+
+       /*
+        * (non-Javadoc)
+        * 
+        * @see edu.internet2.middleware.shibboleth.idp.IdPResponder.ProtocolHandler#validForRequest(javax.servlet.http.HttpServletRequest)
+        */
+       boolean validForRequest(HttpServletRequest request) {
+
+               if (request.getParameter("target") != null && !request.getParameter("target").equals("")
+                               && request.getParameter("shire") != null && !request.getParameter("shire").equals("")) {
+                       log.debug("Found (target) and (shire) parameters.  Request "
+                                       + "appears to be valid for the Shibboleth v1 profile.");
+                       return true;
+               } else {
                        return false;
                }
+       }
 
-               /*
-                * (non-Javadoc)
-                * 
-                * @see edu.internet2.middleware.shibboleth.hs.AuthNProfileHandler#getRemoteProviderId(javax.servlet.http.HttpServletRequest)
-                */
-               String getRemoteProviderId(HttpServletRequest req) {
-                       return req.getParameter("providerId");
+       /*
+        * (non-Javadoc)
+        * 
+        * @see edu.internet2.middleware.shibboleth.idp.IdPResponder.ProtocolHandler#processRequest(javax.servlet.http.HttpServletRequest,
+        *      javax.servlet.http.HttpServletResponse)
+        */
+       public void processRequest(HttpServletRequest request, HttpServletResponse response, ProtocolSupport support)
+                       throws InvalidClientDataException, NameIdentifierMappingException, SAMLException, IOException,
+                       ServletException {
+
+               // Ensure that we have the required data from the servlet container
+               ProtocolSupport.validateEngineData(request);
+
+               // Get the authN info
+               String username = support.getIdPConfig().getAuthHeaderName().equalsIgnoreCase("REMOTE_USER") ? request
+                               .getRemoteUser() : request.getHeader(support.getIdPConfig().getAuthHeaderName());
+
+               // Select the appropriate Relying Party configuration for the request
+               RelyingParty relyingParty = null;
+               String remoteProviderId = request.getParameter("providerId");
+
+               // If the target did not send a Provider Id, then assume it is a Shib
+               // 1.1 or older target
+               if (remoteProviderId == null) {
+                       relyingParty = support.getServiceProviderMapper().getLegacyRelyingParty();
+               } else if (remoteProviderId.equals("")) {
+                       throw new InvalidClientDataException("Invalid service provider id.");
+               } else {
+                       log.debug("Remote provider has identified itself as: (" + remoteProviderId + ").");
+                       relyingParty = support.getServiceProviderMapper().getRelyingParty(remoteProviderId);
                }
 
-               /*
-                * (non-Javadoc)
-                * 
-                * @see edu.internet2.middleware.shibboleth.hs.AuthNProfileHandler#processHook(javax.servlet.http.HttpServletRequest,
-                *      edu.internet2.middleware.shibboleth.hs.HSRelyingParty, org.opensaml.SAMLNameIdentifier, java.lang.String,
-                *      long)
-                */
-               SAMLAssertion[] processHook(HttpServletRequest request, RelyingParty relyingParty, EntityDescriptor provider,
-                               SAMLNameIdentifier nameId, String authenticationMethod, Date authTime) throws SAMLException, IOException {
-                       Document doc = org.opensaml.XML.parserPool.newDocument();
-
-                       //Determine audiences and issuer
-                       ArrayList audiences = new ArrayList();
-                       if (relyingParty.getProviderId() != null) {
-                               audiences.add(relyingParty.getProviderId());
+               // Grab the metadata for the provider
+               EntityDescriptor provider = support.lookup(relyingParty.getProviderId());
+
+               // Determine the acceptance URL
+               String acceptanceURL = request.getParameter("shire");
+
+               // Make sure that the selected relying party configuration is appropriate for this
+               // acceptance URL
+               if (!relyingParty.isLegacyProvider()) {
+
+                       if (provider == null) {
+                               log.info("No metadata found for provider: (" + relyingParty.getProviderId() + ").");
+                               relyingParty = support.getServiceProviderMapper().getRelyingParty(null);
+
+                       } else {
+
+                               if (isValidAssertionConsumerURL(provider, acceptanceURL)) {
+                                       log.info("Supplied consumer URL validated for this provider.");
+                               } else {
+                                       log.error("Assertion consumer service URL (" + acceptanceURL + ") is NOT valid for provider ("
+                                                       + relyingParty.getProviderId() + ").");
+                                       throw new InvalidClientDataException("Invalid assertion consumer service URL.");
+                               }
                        }
-                       if (relyingParty.getName() != null && !relyingParty.getName().equals(relyingParty.getProviderId())) {
-                               audiences.add(relyingParty.getName());
+               }
+
+               // Create SAML Name Identifier
+               SAMLNameIdentifier nameId = support.getNameMapper().getNameIdentifierName(relyingParty.getHSNameFormatId(),
+                               new AuthNPrincipal(username), relyingParty, relyingParty.getIdentityProvider());
+
+               String authenticationMethod = request.getHeader("SAMLAuthenticationMethod");
+               if (authenticationMethod == null || authenticationMethod.equals("")) {
+                       authenticationMethod = relyingParty.getDefaultAuthMethod().toString();
+                       log.debug("User was authenticated via the default method for this relying party (" + authenticationMethod
+                                       + ").");
+               } else {
+                       log.debug("User was authenticated via the method (" + authenticationMethod + ").");
+               }
+
+               // TODO change name!!!
+               // TODO We might someday want to provide a mechanism for the authenticator to specify the auth time
+               SAMLAssertion[] assertions = foo(request, relyingParty, provider, nameId, authenticationMethod, new Date(System
+                               .currentTimeMillis()));
+
+               // TODO do assertion signing for artifact stuff
+
+               // SAML Artifact profile
+               if (useArtifactProfile(provider, acceptanceURL)) {
+                       log.debug("Responding with Artifact profile.");
+
+                       // Create artifacts for each assertion
+                       ArrayList artifacts = new ArrayList();
+                       for (int i = 0; i < assertions.length; i++) {
+                               // TODO replace the artifact stuff here!!!
+                               // artifacts.add(artifactMapper.generateArtifact(assertions[i], relyingParty));
+                       }
+
+                       // Assemble the query string
+                       StringBuffer destination = new StringBuffer(acceptanceURL);
+                       destination.append("?TARGET=");
+                       destination.append(URLEncoder.encode(request.getParameter("target"), "UTF-8"));
+                       Iterator iterator = artifacts.iterator();
+                       StringBuffer artifactBuffer = new StringBuffer(); // Buffer for the transaction log
+                       while (iterator.hasNext()) {
+                               destination.append("&SAMLart=");
+                               String artifact = (String) iterator.next();
+                               destination.append(URLEncoder.encode(artifact, "UTF-8"));
+                               artifactBuffer.append("(" + artifact + ")");
                        }
+                       log.debug("Redirecting to (" + destination.toString() + ").");
+                       response.sendRedirect(destination.toString()); // Redirect to the artifact receiver
+
+                       support.getTransactionLog().info(
+                                       "Assertion artifact(s) (" + artifactBuffer.toString() + ") issued to provider ("
+                                                       + relyingParty.getIdentityProvider().getProviderId() + ") on behalf of principal ("
+                                                       + username + "). Name Identifier: (" + nameId.getName() + "). Name Identifier Format: ("
+                                                       + nameId.getFormat() + ").");
+
+                       // SAML POST profile
+               } else {
+                       log.debug("Responding with POST profile.");
+                       request.setAttribute("acceptanceURL", acceptanceURL);
+                       request.setAttribute("target", request.getParameter("target"));
+
+                       SAMLResponse samlResponse = new SAMLResponse(null, acceptanceURL, Arrays.asList(assertions), null);
+                       ProtocolSupport.addSignatures(samlResponse, relyingParty, provider, true);
+                       createPOSTForm(request, response, samlResponse.toBase64());
 
-                       String issuer = null;
+                       // Make transaction log entry
                        if (relyingParty.isLegacyProvider()) {
-//TODO figure this out
-                               /*
-                               log.debug("Service Provider is running Shibboleth <= 1.1.  Using old style issuer.");
-                               if (relyingParty.getIdentityProvider().getResponseSigningCredential() == null
-                                               || relyingParty.getIdentityProvider().getResponseSigningCredential().getX509Certificate() == null) { throw new SAMLException(
-                                               "Cannot serve legacy style assertions without an X509 certificate"); }
-                               issuer = ShibBrowserProfile.getHostNameFromDN(relyingParty.getIdentityProvider()
-                                               .getResponseSigningCredential().getX509Certificate().getSubjectX500Principal());
-                               if (issuer == null || issuer.equals("")) { throw new SAMLException(
-                                               "Error parsing certificate DN while determining legacy issuer name."); }
-                                               */
+                               support.getTransactionLog().info(
+                                               "Authentication assertion issued to legacy provider (SHIRE: " + request.getParameter("shire")
+                                                               + ") on behalf of principal (" + username + ") for resource ("
+                                                               + request.getParameter("target") + "). Name Identifier: (" + nameId.getName()
+                                                               + "). Name Identifier Format: (" + nameId.getFormat() + ").");
                        } else {
-                               issuer = relyingParty.getIdentityProvider().getProviderId();
+                               support.getTransactionLog().info(
+                                               "Authentication assertion issued to provider ("
+                                                               + relyingParty.getIdentityProvider().getProviderId() + ") on behalf of principal ("
+                                                               + username + "). Name Identifier: (" + nameId.getName()
+                                                               + "). Name Identifier Format: (" + nameId.getFormat() + ").");
                        }
+               }
 
-                       //For compatibility with pre-1.2 shibboleth targets, include a pointer to the AA
-                       ArrayList bindings = new ArrayList();
-                       if (relyingParty.isLegacyProvider()) {
+       }
 
-                               SAMLAuthorityBinding binding = new SAMLAuthorityBinding(SAMLBinding.SOAP, relyingParty
-                                               .getAAUrl().toString(), new QName(org.opensaml.XML.SAMLP_NS, "AttributeQuery"));
-                               bindings.add(binding);
-                       }
+       SAMLAssertion[] foo(HttpServletRequest request, RelyingParty relyingParty, EntityDescriptor provider,
+                       SAMLNameIdentifier nameId, String authenticationMethod, Date authTime) throws SAMLException, IOException {
 
-                       //Create the authN assertion
-                       Vector conditions = new Vector(1);
-                       if (audiences != null && audiences.size() > 0) conditions.add(new SAMLAudienceRestrictionCondition(audiences));
+               Document doc = org.opensaml.XML.parserPool.newDocument();
 
-                       String[] confirmationMethods = {SAMLSubject.CONF_BEARER};
-                       SAMLSubject subject = new SAMLSubject(nameId, Arrays.asList(confirmationMethods), null, null);
+               // Determine audiences and issuer
+               ArrayList audiences = new ArrayList();
+               if (relyingParty.getProviderId() != null) {
+                       audiences.add(relyingParty.getProviderId());
+               }
+               if (relyingParty.getName() != null && !relyingParty.getName().equals(relyingParty.getProviderId())) {
+                       audiences.add(relyingParty.getName());
+               }
 
-                       SAMLStatement[] statements = {new SAMLAuthenticationStatement(subject, authenticationMethod, authTime, request
-                                       .getRemoteAddr(), null, bindings)};
+               String issuer = null;
+               if (relyingParty.isLegacyProvider()) {
+                       // TODO figure this out
+                       /*
+                        * log.debug("Service Provider is running Shibboleth <= 1.1. Using old style issuer."); if
+                        * (relyingParty.getIdentityProvider().getResponseSigningCredential() == null ||
+                        * relyingParty.getIdentityProvider().getResponseSigningCredential().getX509Certificate() == null) { throw
+                        * new SAMLException( "Cannot serve legacy style assertions without an X509 certificate"); } issuer =
+                        * ShibBrowserProfile.getHostNameFromDN(relyingParty.getIdentityProvider()
+                        * .getResponseSigningCredential().getX509Certificate().getSubjectX500Principal()); if (issuer == null ||
+                        * issuer.equals("")) { throw new SAMLException( "Error parsing certificate DN while determining legacy
+                        * issuer name."); }
+                        */
+               } else {
+                       issuer = relyingParty.getIdentityProvider().getProviderId();
+               }
 
-                       SAMLAssertion[] assertions = {new SAMLAssertion(issuer, new Date(System.currentTimeMillis()), new Date(System
-                                       .currentTimeMillis() + 300000), conditions, null, Arrays.asList(statements))};
+               // For compatibility with pre-1.2 shibboleth targets, include a pointer to the AA
+               ArrayList bindings = new ArrayList();
+               if (relyingParty.isLegacyProvider()) {
 
-                       if (log.isDebugEnabled()) {
-                               log.debug("Dumping generated SAML Assertions:"
-                                               + System.getProperty("line.separator")
-                                               + new String(new BASE64Decoder().decodeBuffer(new String(assertions[0].toBase64(), "ASCII")),
-                                                               "UTF8"));
-                       }
+                       SAMLAuthorityBinding binding = new SAMLAuthorityBinding(SAMLBinding.SOAP, relyingParty.getAAUrl()
+                                       .toString(), new QName(org.opensaml.XML.SAMLP_NS, "AttributeQuery"));
+                       bindings.add(binding);
+               }
+
+               // Create the authN assertion
+               Vector conditions = new Vector(1);
+               if (audiences != null && audiences.size() > 0) conditions.add(new SAMLAudienceRestrictionCondition(audiences));
+
+               String[] confirmationMethods = {SAMLSubject.CONF_BEARER};
+               SAMLSubject subject = new SAMLSubject(nameId, Arrays.asList(confirmationMethods), null, null);
 
-                       return assertions;
+               SAMLStatement[] statements = {new SAMLAuthenticationStatement(subject, authenticationMethod, authTime, request
+                               .getRemoteAddr(), null, bindings)};
+
+               SAMLAssertion[] assertions = {new SAMLAssertion(issuer, new Date(System.currentTimeMillis()), new Date(System
+                               .currentTimeMillis() + 300000), conditions, null, Arrays.asList(statements))};
+
+               if (log.isDebugEnabled()) {
+                       log.debug("Dumping generated SAML Assertions:"
+                                       + System.getProperty("line.separator")
+                                       + new String(new BASE64Decoder().decodeBuffer(new String(assertions[0].toBase64(), "ASCII")),
+                                                       "UTF8"));
                }
 
-               /*
-                * (non-Javadoc)
-                * 
-                * @see edu.internet2.middleware.shibboleth.hs.AuthNProfileHandler#getSAMLTargetParameter(javax.servlet.http.HttpServletRequest,
-                *      edu.internet2.middleware.shibboleth.hs.HSRelyingParty)
-                */
-               String getSAMLTargetParameter(HttpServletRequest request, RelyingParty relyingParty, EntityDescriptor provider) {
-                       return request.getParameter("target");
+               return assertions;
+       }
+
+       /*
+        * (non-Javadoc)
+        * 
+        * @see edu.internet2.middleware.shibboleth.idp.IdPResponder.ProtocolHandler#getHandlerName()
+        */
+       String getHandlerName() {
+
+               // TODO Auto-generated method stub
+               return "Shibboleth-v1-SSO";
+       }
+
+       private static void createPOSTForm(HttpServletRequest req, HttpServletResponse res, byte[] buf) throws IOException,
+                       ServletException {
+
+               // Hardcoded to ASCII to ensure Base64 encoding compatibility
+               req.setAttribute("assertion", new String(buf, "ASCII"));
+
+               if (log.isDebugEnabled()) {
+                       try {
+                               log.debug("Dumping generated SAML Response:" + System.getProperty("line.separator")
+                                               + new String(new BASE64Decoder().decodeBuffer(new String(buf, "ASCII")), "UTF8"));
+                       } catch (IOException e) {
+                               log.error("Encountered an error while decoding SAMLReponse for logging purposes.");
+                       }
                }
 
-               /*
-                * (non-Javadoc)
-                * 
-                * @see edu.internet2.middleware.shibboleth.hs.AuthNProfileHandler#getAcceptanceURL(javax.servlet.http.HttpServletRequest,
-                *      edu.internet2.middleware.shibboleth.hs.HSRelyingParty)
-                */
-               String getAcceptanceURL(HttpServletRequest request, RelyingParty relyingParty, EntityDescriptor provider)
-                               throws InvalidClientDataException {
-                       return request.getParameter("shire");
+               // TODO rename from hs.jsp to more appropriate name
+               RequestDispatcher rd = req.getRequestDispatcher("/hs.jsp");
+               rd.forward(req, res);
+       }
+
+       private static boolean useArtifactProfile(EntityDescriptor provider, String acceptanceURL) {
+
+               // Default to POST if we have no metadata
+               if (provider == null) { return false; }
+
+               // Default to POST if we have incomplete metadata
+               SPSSODescriptor sp = provider.getSPSSODescriptor(org.opensaml.XML.SAML11_PROTOCOL_ENUM);
+               if (sp == null) { return false; }
+
+               // TODO: This will actually favor artifact, since a given location could support
+               // both profiles. If that's not what we want, needs adjustment...
+               Iterator endpoints = sp.getAssertionConsumerServiceManager().getEndpoints();
+               while (endpoints.hasNext()) {
+                       Endpoint ep = (Endpoint) endpoints.next();
+                       if (acceptanceURL.equals(ep.getLocation())
+                                       && SAMLBrowserProfile.PROFILE_ARTIFACT_URI.equals(ep.getBinding())) { return true; }
                }
+
+               // Default to POST if we have incomplete metadata
+               return false;
        }
 
+       private static boolean isValidAssertionConsumerURL(EntityDescriptor provider, String shireURL)
+                       throws InvalidClientDataException {
+
+               SPSSODescriptor sp = provider.getSPSSODescriptor(org.opensaml.XML.SAML11_PROTOCOL_ENUM);
+               if (sp == null) {
+                       log.info("Inappropriate metadata for provider.");
+                       return false;
+               }
+
+               Iterator endpoints = sp.getAssertionConsumerServiceManager().getEndpoints();
+               while (endpoints.hasNext()) {
+                       if (shireURL.equals(((Endpoint) endpoints.next()).getLocation())) { return true; }
+               }
+               log.info("Supplied consumer URL not found in metadata.");
+               return false;
+       }
 }
 
-class InvalidClientDataException extends Exception {
+class ArtifactQueryHandler extends ProtocolHandler {
 
-       public InvalidClientDataException(String message) {
+       /*
+        * (non-Javadoc)
+        * 
+        * @see edu.internet2.middleware.shibboleth.idp.ProtocolHandler#validForRequest(javax.servlet.http.HttpServletRequest)
+        */
+       boolean validForRequest(HttpServletRequest request) {
 
-               super(message);
+               // TODO Auto-generated method stub
+               return false;
        }
 
+       /*
+        * (non-Javadoc)
+        * 
+        * @see edu.internet2.middleware.shibboleth.idp.ProtocolHandler#getHandlerName()
+        */
+       String getHandlerName() {
+
+               // TODO change
+               return "foo";
+       }
+
+       /*
+        * (non-Javadoc)
+        * 
+        * @see edu.internet2.middleware.shibboleth.idp.ProtocolHandler#processRequest(javax.servlet.http.HttpServletRequest,
+        *      javax.servlet.http.HttpServletResponse, edu.internet2.middleware.shibboleth.idp.ProtocolSupport)
+        */
+       public void processRequest(HttpServletRequest request, HttpServletResponse response, ProtocolSupport support)
+                       throws SAMLException, InvalidClientDataException, NameIdentifierMappingException, IOException,
+                       ServletException {
+
+       // TODO Auto-generated method stub
+
+       }
+
+}
+
+// TODO should this name say something about SSO?
+
+abstract class ProtocolHandler {
+
+       abstract boolean validForRequest(HttpServletRequest request);
+
+       abstract String getHandlerName();
+
+       /**
+        * @param request
+        * @param response
+        * @throws ServletException
+        */
+       // TODO add javadoc
+       // TODO should the name identifier mapping exception really be thrown here or covered up?
+       public abstract void processRequest(HttpServletRequest request, HttpServletResponse response,
+                       ProtocolSupport support) throws SAMLException, InvalidClientDataException, NameIdentifierMappingException,
+                       IOException, ServletException;
+}
+
+class FederationProviderFactory {
+
+       private static Logger log = Logger.getLogger(FederationProviderFactory.class.getName());
+
+       public static Metadata loadProvider(Element e) throws MetadataException {
+
+               String className = e.getAttribute("type");
+               if (className == null || className.equals("")) {
+                       log.error("Federation Provider requires specification of the attribute \"type\".");
+                       throw new MetadataException("Failed to initialize Federation Provider.");
+               } else {
+                       try {
+                               Class[] params = {Class.forName("org.w3c.dom.Element"),};
+                               return (Metadata) Class.forName(className).getConstructor(params).newInstance(new Object[]{e});
+                       } catch (Exception loaderException) {
+                               log.error("Failed to load Federation Provider implementation class: " + loaderException);
+                               Throwable cause = loaderException.getCause();
+                               while (cause != null) {
+                                       log.error("caused by: " + cause);
+                                       cause = cause.getCause();
+                               }
+                               throw new MetadataException("Failed to initialize Federation Provider.");
+                       }
+               }
+       }
 }
\ No newline at end of file