Fixed an IdP configuration schema bug.
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / idp / IdPResponder.java
index bc1fd28..b34edbe 100644 (file)
 package edu.internet2.middleware.shibboleth.idp;
 
 import java.io.IOException;
+import java.net.MalformedURLException;
 import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URLEncoder;
-import java.security.Principal;
-import java.security.cert.CertificateParsingException;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.List;
+import java.net.URL;
+import java.util.HashMap;
 import java.util.Random;
 
-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 org.apache.log4j.Logger;
 import org.apache.log4j.MDC;
-import org.apache.xml.security.exceptions.XMLSecurityException;
-import org.apache.xml.security.keys.KeyInfo;
-import org.apache.xml.security.signature.XMLSignature;
-import org.opensaml.InvalidCryptoException;
-import org.opensaml.SAMLAssertion;
-import org.opensaml.SAMLAttribute;
-import org.opensaml.SAMLAttributeDesignator;
-import org.opensaml.SAMLAttributeQuery;
-import org.opensaml.SAMLAttributeStatement;
-import org.opensaml.SAMLAudienceRestrictionCondition;
 import org.opensaml.SAMLBinding;
 import org.opensaml.SAMLBindingFactory;
-import org.opensaml.SAMLCondition;
 import org.opensaml.SAMLException;
-import org.opensaml.SAMLNameIdentifier;
 import org.opensaml.SAMLRequest;
 import org.opensaml.SAMLResponse;
-import org.opensaml.SAMLStatement;
-import org.opensaml.SAMLSubject;
 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.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.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.ArtifactMapperFactory;
 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;
-import edu.internet2.middleware.shibboleth.common.InvalidNameIdentifierException;
 import edu.internet2.middleware.shibboleth.common.NameIdentifierMapping;
 import edu.internet2.middleware.shibboleth.common.NameIdentifierMappingException;
 import edu.internet2.middleware.shibboleth.common.NameMapper;
 import edu.internet2.middleware.shibboleth.common.OriginConfig;
-import edu.internet2.middleware.shibboleth.common.RelyingParty;
 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.AttributeConsumerRole;
-import edu.internet2.middleware.shibboleth.metadata.Endpoint;
-import edu.internet2.middleware.shibboleth.metadata.KeyDescriptor;
-import edu.internet2.middleware.shibboleth.metadata.Provider;
-import edu.internet2.middleware.shibboleth.metadata.ProviderRole;
-import edu.internet2.middleware.shibboleth.metadata.SPProviderRole;
+import edu.internet2.middleware.shibboleth.metadata.Metadata;
+import edu.internet2.middleware.shibboleth.metadata.MetadataException;
 
 /**
  * Primary entry point for requests to the SAML IdP. Listens on multiple endpoints, routes requests to the appropriate
@@ -111,28 +75,20 @@ import edu.internet2.middleware.shibboleth.metadata.SPProviderRole;
  * @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 IdPConfig configuration;
+       private HashMap protocolHandlers = new HashMap();
+       private IdPProtocolSupport protocolSupport;
 
+       /*
+        * @see javax.servlet.GenericServlet#init()
+        */
        public void init() throws ServletException {
 
                super.init();
@@ -141,291 +97,194 @@ 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 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 name mappings
+                       NameMapper nameMapper = new NameMapper();
+                       NodeList itemElements = originConfig.getDocumentElement().getElementsByTagNameNS(
+                                       NameIdentifierMapping.mappingNamespace, "NameMapping");
 
-               // TODO do we need to check active endpoints to determine which
-               // components to load, for instance artifact repository, arp engine,
-               // attribute resolver
-
-               // 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));
 
-               // 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.");
-               }
+                       // Startup Attribute Resolver & ARP engine
+                       AttributeResolver resolver = null;
+                       ArpEngine arpEngine = null;
+                       try {
+                               resolver = new AttributeResolver(configuration);
 
-               // 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.");
-               }
+                               itemElements = originConfig.getDocumentElement().getElementsByTagNameNS(IdPConfig.configNameSpace,
+                                               "ReleasePolicyEngine");
 
-               try {
-                       // Startup Attribute Resolver
-                       AttributeResolver resolver = new AttributeResolver(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));
+                               }
 
-                       // Startup ARP Engine
-                       ArpEngine arpEngine = null;
-                       itemElements = originConfig.getDocumentElement().getElementsByTagNameNS(IdPConfig.originConfigNamespace,
-                                       "ReleasePolicyEngine");
+                       } 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.");
+                       }
 
+                       // Load artifact mapping implementation
+                       ArtifactMapper artifactMapper = null;
+                       itemElements = originConfig.getDocumentElement().getElementsByTagNameNS(IdPConfig.configNameSpace,
+                                       "ArtifactMapper");
                        if (itemElements.getLength() > 1) {
-                               log.warn("Encountered multiple <ReleasePolicyEngine> configuration elements.  Using first...");
+                               log.warn("Encountered multiple <ArtifactMapper/> configuration elements.  Using first...");
                        }
-                       if (itemElements.getLength() < 1) {
-                               arpEngine = new ArpEngine();
+                       if (itemElements.getLength() > 0) {
+                               artifactMapper = ArtifactMapperFactory.getInstance((Element) itemElements.item(0));
                        } else {
-                               arpEngine = new ArpEngine((Element) itemElements.item(0));
+                               log.debug("No Artifact Mapper configuration found.  Defaulting to Memory-based implementation.");
+                               artifactMapper = new MemoryArtifactMapper();
                        }
 
-                       // 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.");
-               }
+                       // Load protocol handlers and support library
+                       protocolSupport = new IdPProtocolSupport(configuration, transactionLog, nameMapper, spMapper, arpEngine,
+                                       resolver, artifactMapper);
+                       itemElements = originConfig.getDocumentElement().getElementsByTagNameNS(IdPConfig.configNameSpace,
+                                       "ProtocolHandler");
 
-       }
-
-       public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
-
-               MDC.put("serviceId", "[IdP] " + idgen.nextInt());
-               MDC.put("remoteAddr", request.getRemoteAddr());
-               log.debug("Recieved a request via GET.");
-               log.info("Handling authN request.");
-
-               try {
-                       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];
-                                       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());
+                       // Default if no handlers are specified
+                       if (itemElements.getLength() < 1) {
+                               // TODO work out defaulting
 
-                       // 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.");
+                               // If handlers were specified, load them and register them against their locations
                        } else {
-                               log.debug("Remote provider has identified itself as: (" + remoteProviderId + ").");
-                               relyingParty = spMapper.getRelyingParty(remoteProviderId);
-                       }
-
-                       // Grab the metadata for the provider
-                       Provider 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.");
+                               EACHHANDLER : for (int i = 0; i < itemElements.getLength(); i++) {
+                                       IdPProtocolHandler handler = ProtocolHandlerFactory.getInstance((Element) itemElements.item(i));
+                                       URI[] locations = handler.getLocations();
+                                       EACHLOCATION : for (int j = 0; j < locations.length; j++) {
+                                               if (protocolHandlers.containsKey(locations[j].toString())) {
+                                                       log.error("Multiple protocol handlers are registered to listen at ("
+                                                                       + locations[j]
+                                                                       + ").  Ignoring all except ("
+                                                                       + ((IdPProtocolHandler) protocolHandlers.get(locations[j].toString()))
+                                                                                       .getHandlerName() + ").");
+                                                       continue EACHLOCATION;
+                                               }
+                                               log.info("Registering handler (" + handler.getHandlerName() + ") to listen at (" + locations[j]
+                                                               + ").");
+                                               protocolHandlers.put(locations[j].toString(), handler);
                                        }
                                }
                        }
 
-                       // 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 + ").");
+                       // Load metadata
+                       itemElements = originConfig.getDocumentElement().getElementsByTagNameNS(IdPConfig.configNameSpace,
+                                       "MetadataProvider");
+                       for (int i = 0; i < itemElements.getLength(); i++) {
+                               protocolSupport.addMetadataProvider((Element) itemElements.item(i));
+                       }
+                       if (protocolSupport.providerCount() < 1) {
+                               log.error("No Metadata Provider metadata loaded.");
+                               throw new ShibbolethConfigurationException("Could not load SAML metadata.");
                        }
 
-                       // 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()));
+                       log.info("Identity Provider initialization complete.");
 
-                       // TODO do assertion signing here
+               } 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.");
+               }
+       }
 
-                       // SAML Artifact profile
-                       if (useArtifactProfile(provider, acceptanceURL)) {
-                               log.debug("Responding with Artifact profile.");
+       /*
+        * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest,
+        *      javax.servlet.http.HttpServletResponse)
+        */
+       public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 
-                               // Create artifacts for each assertion
-                               ArrayList artifacts = new ArrayList();
-                               for (int i = 0; i < assertions.length; i++) {
-                                       artifacts.add(artifactMapper.generateArtifact(assertions[i], relyingParty));
-                               }
+               MDC.put("serviceId", "[IdP] " + idgen.nextInt());
+               MDC.put("remoteAddr", request.getRemoteAddr());
+               log.debug("Recieved a request via GET for location (" + request.getRequestURL() + ").");
 
-                               // 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 + ")");
+               try {
+                       // Determine which protocol we are responding to (at this point normally Shibv1 vs. EAuth)
+                       String requestURL = request.getRequestURL().toString();
+                       IdPProtocolHandler activeHandler = (IdPProtocolHandler) protocolHandlers.get(requestURL);
+                       if (activeHandler == null) {
+                               log.debug("No protocol handler registered for location (" + request.getRequestURL()
+                                               + ").  Attempting to match against relative path.");
+                               try {
+                                       activeHandler = (IdPProtocolHandler) protocolHandlers.get(new URL(requestURL).getPath());
+                               } catch (MalformedURLException e) {
+                                       // squelch, we will just fail to find a handler
                                }
-                               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());
+                       if (activeHandler == null) {
+                               log.error("No protocol handler registered for location (" + request.getRequestURL() + ").");
+                               throw new SAMLException("Request submitted to an invalid location.");
+                       }
 
-                               // 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() + ").");
-                               }
+                       // Pass request to the appropriate handler
+                       log.info("Processing " + activeHandler.getHandlerName() + " request.");
+                       if (activeHandler.processRequest(request, response, null, protocolSupport) != null) {
+                               // This shouldn't happen unless somebody configures a protocol handler incorrectly
+                               log.error("Protocol Handler returned a SAML Response, but there is no binding to handle it.");
+                               throw new SAMLException(SAMLException.RESPONDER, "General error processing request.");
                        }
 
-                       // TODO profile specific error handling
-               } catch (NameIdentifierMappingException ex) {
-                       log.error(ex);
-                       handleSSOError(request, response, ex);
-                       return;
-               } catch (InvalidClientDataException ex) {
-                       log.error(ex);
-                       handleSSOError(request, response, ex);
-                       return;
                } catch (SAMLException ex) {
                        log.error(ex);
-                       handleSSOError(request, response, ex);
-                       return;
-               } catch (InterruptedException ex) {
-                       log.error(ex);
-                       handleSSOError(request, response, ex);
+                       displayBrowserError(request, response, ex);
                        return;
-               } finally {
-                       throttle.exit();
                }
-
        }
 
+       /*
+        * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest,
+        *      javax.servlet.http.HttpServletResponse)
+        */
        public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 
                MDC.put("serviceId", "[IdP] " + idgen.nextInt());
                MDC.put("remoteAddr", request.getRemoteAddr());
-               log.debug("Recieved a request via POST.");
+               log.debug("Recieved a request via POST for location (" + request.getRequestURL() + ").");
 
                // Parse SOAP request and marshall SAML request object
                SAMLRequest samlRequest = null;
@@ -437,365 +296,47 @@ public class IdPResponder extends TargetFederationComponent {
                                throw new SAMLException("Invalid request data.");
                        }
 
-                       // If we have DEBUGing turned on, dump out the request to the log
+                       // If we have DEBUG logging turned on, dump out the request to the log
                        // This takes some processing, so only do it if we need to
                        if (log.isDebugEnabled()) {
-                               try {
-                                       log.debug("Dumping generated SAML Request:"
-                                                       + System.getProperty("line.separator")
-                                                       + new String(new BASE64Decoder().decodeBuffer(new String(samlRequest.toBase64(), "ASCII")),
-                                                                       "UTF8"));
-                               } catch (SAMLException e) {
-                                       log.error("Encountered an error while decoding SAMLRequest for logging purposes.");
-                               } catch (IOException e) {
-                                       log.error("Encountered an error while decoding SAMLRequest for logging purposes.");
-                               }
-                       }
-
-                       // Determine the request type
-                       Iterator artifacts = samlRequest.getArtifacts();
-                       if (artifacts.hasNext()) {
-                               artifacts = null; // get rid of the iterator
-                               log.info("Recieved a request to dereference an assertion artifact.");
-                               processArtifactDereference(samlRequest, request, response);
-                               return;
-                       }
-
-                       if (samlRequest.getQuery() != null && (samlRequest.getQuery() instanceof SAMLAttributeQuery)) {
-                               log.info("Recieved an attribute query.");
-                               processAttributeQuery(samlRequest, request, response);
-                               return;
-                       }
-
-                       throw new SAMLException(SAMLException.REQUESTER,
-                                       "Identity Provider unable to respond to this SAML Request type.");
-
-               } catch (InvalidNameIdentifierException invalidNameE) {
-                       log.info("Could not associate the request subject with a principal: " + invalidNameE);
-                       try {
-                               // TODO once again, ifgure out passThruErrors
-                               if (false) {
-                                       // if (relyingParty.passThruErrors()) {
-                                       sendSAMLFailureResponse(response, samlRequest, new SAMLException(Arrays.asList(invalidNameE
-                                                       .getSAMLErrorCodes()), "The supplied Subject was unrecognized.", invalidNameE));
-
-                               } else {
-                                       sendSAMLFailureResponse(response, samlRequest, new SAMLException(Arrays.asList(invalidNameE
-                                                       .getSAMLErrorCodes()), "The supplied Subject was unrecognized."));
-                               }
-                               return;
-                       } catch (Exception ee) {
-                               log.fatal("Could not construct a SAML error response: " + ee);
-                               throw new ServletException("Identity Provider response failure.");
-                       }
-               } catch (Exception e) {
-                       log.error("Error while processing request: " + e);
-                       try {
-                               // TODO figure out how to implement the passThru error handling
-                               // below
-                               // if (relyingParty != null && relyingParty.passThruErrors()) {
-                               if (false) {
-                                       sendSAMLFailureResponse(response, samlRequest, new SAMLException(SAMLException.RESPONDER,
-                                                       "General error processing request.", e));
-                               } else if (configuration.passThruErrors()) {
-                                       sendSAMLFailureResponse(response, samlRequest, new SAMLException(SAMLException.RESPONDER,
-                                                       "General error processing request.", e));
-                               } else {
-                                       sendSAMLFailureResponse(response, samlRequest, new SAMLException(SAMLException.RESPONDER,
-                                                       "General error processing request."));
-                               }
-                               return;
-                       } catch (Exception ee) {
-                               log.fatal("Could not construct a SAML error response: " + ee);
-                               throw new ServletException("Identity Provider response failure.");
-                       }
-               }
-       }
-
-       // TODO this should be renamed, since it is now only one type of response
-       // that we can send
-       public void sendSAMLResponse(HttpServletResponse resp, SAMLAttribute[] attrs, SAMLRequest samlRequest,
-                       RelyingParty relyingParty, SAMLException exception) throws IOException {
-
-               SAMLException ourSE = null;
-               SAMLResponse samlResponse = null;
-
-               try {
-                       if (attrs == null || attrs.length == 0) {
-                               // No attribute found
-                               samlResponse = new SAMLResponse(samlRequest.getId(), null, null, exception);
-                       } else {
-
-                               SAMLAttributeQuery attributeQuery = (SAMLAttributeQuery) samlRequest.getQuery();
-
-                               // Reference requested subject
-                               SAMLSubject rSubject = (SAMLSubject) attributeQuery.getSubject().clone();
-
-                               // Set appropriate audience
-                               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());
-                               }
-                               SAMLCondition condition = new SAMLAudienceRestrictionCondition(audiences);
-
-                               // Put all attributes into an assertion
-                               SAMLStatement statement = new SAMLAttributeStatement(rSubject, Arrays.asList(attrs));
-
-                               // Set assertion expiration to longest attribute expiration
-                               long max = 0;
-                               for (int i = 0; i < attrs.length; i++) {
-                                       if (max < attrs[i].getLifetime()) {
-                                               max = attrs[i].getLifetime();
-                                       }
-                               }
-                               Date now = new Date();
-                               Date then = new Date(now.getTime() + (max * 1000)); // max is in
-                               // seconds
-
-                               SAMLAssertion sAssertion = new SAMLAssertion(relyingParty.getIdentityProvider().getProviderId(), now,
-                                               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);
+                               log.debug("Dumping generated SAML Request:" + System.getProperty("line.separator")
+                                               + samlRequest.toString());
                        }
-               } catch (SAMLException se) {
-                       ourSE = se;
-               } catch (CloneNotSupportedException ex) {
-                       ourSE = new SAMLException(SAMLException.RESPONDER, ex);
 
-               } finally {
-
-                       if (log.isDebugEnabled()) { // This takes some processing, so only do it if we need to
+                       // Determine which protocol handler is active for this endpoint
+                       String requestURL = request.getRequestURL().toString();
+                       IdPProtocolHandler activeHandler = (IdPProtocolHandler) protocolHandlers.get(requestURL);
+                       if (activeHandler == null) {
+                               log.debug("No protocol handler registered for location (" + request.getRequestURL()
+                                               + ").  Attempting to match against relative path.");
                                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.");
+                                       activeHandler = (IdPProtocolHandler) protocolHandlers.get(new URL(requestURL).getPath());
+                               } catch (MalformedURLException e) {
+                                       // squelch, we will just fail to find a handler
                                }
                        }
 
-                       try {
-                               binding.respond(resp, samlResponse, ourSE);
-                       } catch (SAMLException e) {
-                               log.error("Caught exception while responding to requester: " + e.getMessage());
-                               resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Error while responding.");
-                       }
-               }
-       }
-
-       // TODO get rid of this AAException thing
-       private void processAttributeQuery(SAMLRequest samlRequest, HttpServletRequest request, HttpServletResponse response)
-                       throws SAMLException, IOException, ServletException, AAException, InvalidNameIdentifierException,
-                       NameIdentifierMappingException {
-
-               // TODO validate that the endpoint is valid for the request type
-
-               RelyingParty relyingParty = null;
-
-               SAMLAttributeQuery attributeQuery = (SAMLAttributeQuery) samlRequest.getQuery();
-
-               if (!fromLegacyProvider(request)) {
-                       log.info("Remote provider has identified itself as: (" + attributeQuery.getResource() + ").");
-               }
-
-               // This is the requester name that will be passed to subsystems
-               String effectiveName = null;
-
-               X509Certificate credential = getCredentialFromProvider(request);
-               if (credential == null || credential.getSubjectX500Principal().getName(X500Principal.RFC2253).equals("")) {
-                       log.info("Request is from an unauthenticated service provider.");
-               } else {
-
-                       // Identify a Relying Party
-                       relyingParty = spMapper.getRelyingParty(attributeQuery.getResource());
+                       // Pass request to the appropriate handler and respond
+                       log.info("Processing " + activeHandler.getHandlerName() + " request.");
 
-                       try {
-                               effectiveName = getEffectiveName(request, relyingParty);
-                       } catch (InvalidProviderCredentialException ipc) {
-                               sendSAMLFailureResponse(response, samlRequest, new SAMLException(SAMLException.RESPONDER,
-                                               "Invalid credentials for request."));
-                               return;
-                       }
-               }
-
-               if (effectiveName == null) {
-                       log.debug("Using default Relying Party for unauthenticated provider.");
-                       relyingParty = spMapper.getRelyingParty(null);
-               }
+                       SAMLResponse samlResponse = activeHandler.processRequest(request, response, samlRequest, protocolSupport);
+                       binding.respond(response, samlResponse, null);
 
-               // Fail if we can't honor SAML Subject Confirmation
-               if (!fromLegacyProvider(request)) {
-                       Iterator iterator = attributeQuery.getSubject().getConfirmationMethods();
-                       boolean hasConfirmationMethod = false;
-                       while (iterator.hasNext()) {
-                               log.info("Request contains SAML Subject Confirmation method: (" + (String) iterator.next() + ").");
-                       }
-                       if (hasConfirmationMethod) { throw new SAMLException(SAMLException.REQUESTER,
-                                       "This SAML authority cannot honor requests containing the supplied SAML Subject Confirmation Method."); }
+               } catch (SAMLException e) {
+                       sendFailureToSAMLBinding(response, samlRequest, e);
                }
-
-               // Map Subject to local principal
-               Principal principal = nameMapper.getPrincipal(attributeQuery.getSubject().getName(), relyingParty, relyingParty
-                               .getIdentityProvider());
-               log.info("Request is for principal (" + principal.getName() + ").");
-
-               SAMLAttribute[] attrs;
-               Iterator requestedAttrsIterator = attributeQuery.getDesignators();
-               if (requestedAttrsIterator.hasNext()) {
-                       log.info("Request designates specific attributes, resolving this set.");
-                       ArrayList requestedAttrs = new ArrayList();
-                       while (requestedAttrsIterator.hasNext()) {
-                               SAMLAttributeDesignator attribute = (SAMLAttributeDesignator) requestedAttrsIterator.next();
-                               try {
-                                       log.debug("Designated attribute: (" + attribute.getName() + ")");
-                                       requestedAttrs.add(new URI(attribute.getName()));
-                               } catch (URISyntaxException use) {
-                                       log.error("Request designated an attribute name that does not conform to the required URI syntax ("
-                                                       + attribute.getName() + ").  Ignoring this attribute");
-                               }
-                       }
-
-                       attrs = responder.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);
-               }
-
-               log.info("Found " + attrs.length + " attribute(s) for " + principal.getName());
-               sendSAMLResponse(response, attrs, samlRequest, relyingParty, null);
-               log.info("Successfully responded about " + principal.getName());
-
-               if (effectiveName == null) {
-                       if (fromLegacyProvider(request)) {
-                               transactionLog.info("Attribute assertion issued to anonymous legacy provider at ("
-                                               + request.getRemoteAddr() + ") on behalf of principal (" + principal.getName() + ").");
-                       } else {
-                               transactionLog.info("Attribute assertion issued to anonymous provider at (" + request.getRemoteAddr()
-                                               + ") on behalf of principal (" + principal.getName() + ").");
-                       }
-               } else {
-                       if (fromLegacyProvider(request)) {
-                               transactionLog.info("Attribute assertion issued to legacy provider (" + effectiveName
-                                               + ") on behalf of principal (" + principal.getName() + ").");
-                       } else {
-                               transactionLog.info("Attribute assertion issued to provider (" + effectiveName
-                                               + ") on behalf of principal (" + principal.getName() + ").");
-                       }
-               }
-
-       }
-
-       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
-                               Provider 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 {
+       private void sendFailureToSAMLBinding(HttpServletResponse httpResponse, SAMLRequest samlRequest,
+                       SAMLException exception) throws ServletException {
 
+               log.error("Error while processing request: " + exception);
                try {
                        SAMLResponse samlResponse = new SAMLResponse((samlRequest != null) ? samlRequest.getId() : null, null,
                                        null, exception);
                        if (log.isDebugEnabled()) {
-                               try {
-                                       log.debug("Dumping generated SAML Error Response:"
-                                                       + System.getProperty("line.separator")
-                                                       + new String(
-                                                                       new BASE64Decoder().decodeBuffer(new String(samlResponse.toBase64(), "ASCII")),
-                                                                       "UTF8"));
-                               } catch (IOException e) {
-                                       log.error("Encountered an error while decoding SAMLReponse for logging purposes.");
-                               }
+                               log.debug("Dumping generated SAML Error Response:" + System.getProperty("line.separator")
+                                               + samlResponse.toString());
                        }
                        binding.respond(httpResponse, samlResponse, null);
                        log.debug("Returning SAML Error Response.");
@@ -804,356 +345,51 @@ public class IdPResponder extends TargetFederationComponent {
                                binding.respond(httpResponse, null, exception);
                        } catch (SAMLException e) {
                                log.error("Caught exception while responding to requester: " + e.getMessage());
-                               httpResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Error while responding.");
-                       }
-                       log.error("Identity Provider failed to make an error message: " + se);
-               }
-       }
-
-       private String getEffectiveName(HttpServletRequest req, RelyingParty relyingParty)
-                       throws InvalidProviderCredentialException {
-
-               // X500Principal credentialName = getCredentialName(req);
-               X509Certificate credential = getCredentialFromProvider(req);
-
-               if (credential == null || credential.getSubjectX500Principal().getName(X500Principal.RFC2253).equals("")) {
-                       log.info("Request is from an unauthenticated service provider.");
-                       return null;
-
-               } else {
-                       log.info("Request contains credential: ("
-                                       + credential.getSubjectX500Principal().getName(X500Principal.RFC2253) + ").");
-                       // Mockup old requester name for requests from < 1.2 targets
-                       if (fromLegacyProvider(req)) {
-                               String legacyName = ShibBrowserProfile.getHostNameFromDN(credential.getSubjectX500Principal());
-                               if (legacyName == null) {
-                                       log.error("Unable to extract legacy requester name from certificate subject.");
-                               }
-
-                               log.info("Request from legacy service provider: (" + legacyName + ").");
-                               return legacyName;
-
-                       } else {
-
-                               // See if we have metadata for this provider
-                               Provider provider = lookup(relyingParty.getProviderId());
-                               if (provider == null) {
-                                       log.info("No metadata found for provider: (" + relyingParty.getProviderId() + ").");
-                                       log.info("Treating remote provider as unauthenticated.");
-                                       return null;
-                               }
-
-                               // Make sure that the suppplied credential is valid for the
-                               // selected relying party
-                               if (isValidCredential(provider, credential)) {
-                                       log.info("Supplied credential validated for this provider.");
-                                       log.info("Request from service provider: (" + relyingParty.getProviderId() + ").");
-                                       return relyingParty.getProviderId();
-                               } else {
-                                       log.error("Supplied credential ("
-                                                       + credential.getSubjectX500Principal().getName(X500Principal.RFC2253)
-                                                       + ") is NOT valid for provider (" + relyingParty.getProviderId() + ").");
-                                       throw new InvalidProviderCredentialException("Invalid credential.");
-                               }
-                       }
-               }
-       }
-
-       private static void addSignatures(SAMLResponse response, RelyingParty relyingParty, Provider provider,
-                       boolean signResponse) throws SAMLException {
-
-               if (provider != null) {
-                       boolean signAssertions = false;
-
-                       ProviderRole[] roles = provider.getRoles();
-                       if (roles.length == 0) {
-                               log.info("Inappropriate metadata for provider: " + provider.getId() + ".  Expected SPSSODescriptor.");
-                       }
-                       for (int i = 0; roles.length > i; i++) {
-                               if (roles[i] instanceof SPProviderRole) {
-                                       if (((SPProviderRole) roles[i]).wantAssertionsSigned()) {
-                                               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(Provider provider, String acceptanceURL) {
-
-               // Default to POST if we have no metadata
-               if (provider == null) { return false; }
-
-               // Default to POST if we have incomplete metadata
-               ProviderRole[] roles = provider.getRoles();
-               if (roles.length == 0) { return false; }
-
-               for (int i = 0; roles.length > i; i++) {
-                       if (roles[i] instanceof SPProviderRole) {
-                               Endpoint[] endpoints = ((SPProviderRole) roles[i]).getAssertionConsumerServiceURLs();
-
-                               for (int j = 0; endpoints.length > j; j++) {
-                                       if (acceptanceURL.equals(endpoints[j].getLocation())
-                                                       && "urn:oasis:names:tc:SAML:1.0:profiles:artifact-01".equals(endpoints[j].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(Provider provider, String shireURL)
-                       throws InvalidClientDataException {
-
-               ProviderRole[] roles = provider.getRoles();
-               if (roles.length == 0) {
-                       log.info("Inappropriate metadata for provider.");
-                       return false;
-               }
-
-               for (int i = 0; roles.length > i; i++) {
-                       if (roles[i] instanceof SPProviderRole) {
-                               Endpoint[] endpoints = ((SPProviderRole) roles[i]).getAssertionConsumerServiceURLs();
-                               for (int j = 0; endpoints.length > j; j++) {
-                                       if (shireURL.equals(endpoints[j].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");
-               if (certArray != null && certArray.length > 0) { return certArray[0]; }
-               return null;
-       }
-
-       private static boolean isValidCredential(Provider provider, X509Certificate certificate) {
-
-               ProviderRole[] roles = provider.getRoles();
-               if (roles.length == 0) {
-                       log.info("Inappropriate metadata for provider.");
-                       return false;
-               }
-               // TODO figure out what to do about this role business here
-               for (int i = 0; roles.length > i; i++) {
-                       if (roles[i] instanceof AttributeConsumerRole) {
-                               KeyDescriptor[] descriptors = roles[i].getKeyDescriptors();
-                               for (int j = 0; descriptors.length > j; j++) {
-                                       KeyInfo[] keyInfo = descriptors[j].getKeyInfo();
-                                       for (int k = 0; keyInfo.length > k; k++) {
-                                               for (int l = 0; keyInfo[k].lengthKeyName() > l; l++) {
-                                                       try {
-
-                                                               // First, try to match DN against metadata
-                                                               try {
-                                                                       if (certificate.getSubjectX500Principal().getName(X500Principal.RFC2253).equals(
-                                                                                       new X500Principal(keyInfo[k].itemKeyName(l).getKeyName())
-                                                                                                       .getName(X500Principal.RFC2253))) {
-                                                                               log.debug("Matched against DN.");
-                                                                               return true;
-                                                                       }
-                                                               } catch (IllegalArgumentException iae) {
-                                                                       // squelch this runtime exception, since
-                                                                       // this might be a valid case
-                                                               }
-
-                                                               // If that doesn't work, we try matching against
-                                                               // some Subject Alt Names
-                                                               try {
-                                                                       Collection altNames = certificate.getSubjectAlternativeNames();
-                                                                       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
-                                                                                               // DNS,
-                                                                                               // 6 is
-                                                                                               // URI
-                                                                                               if (altName.get(1).equals(keyInfo[k].itemKeyName(l).getKeyName())) {
-                                                                                                       log.debug("Matched against SubjectAltName.");
-                                                                                                       return true;
-                                                                                               }
-                                                                                       }
-                                                                               }
-                                                                       }
-                                                               } catch (CertificateParsingException e1) {
-                                                                       log
-                                                                                       .error("Encountered an problem trying to extract Subject Alternate Name from supplied certificate: "
-                                                                                                       + e1);
-                                                               }
-
-                                                               // If that doesn't work, try to match using
-                                                               // SSL-style hostname matching
-                                                               if (ShibBrowserProfile.getHostNameFromDN(certificate.getSubjectX500Principal()).equals(
-                                                                               keyInfo[k].itemKeyName(l).getKeyName())) {
-                                                                       log.debug("Matched against hostname.");
-                                                                       return true;
-                                                               }
-
-                                                       } catch (XMLSecurityException e) {
-                                                               log.error("Encountered an error reading federation metadata: " + e);
-                                                       }
-                                               }
-                                       }
+                               try {
+                                       httpResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Error while responding.");
+                               } catch (IOException ee) {
+                                       log.fatal("Could not construct a SAML error response: " + ee);
+                                       throw new ServletException("Identity Provider response failure.");
                                }
                        }
+                       log.error("Identity Provider failed to make an error message: " + se);
                }
-               log.info("Supplied credential not found in metadata.");
-               return false;
-       }
-
-       private static boolean fromLegacyProvider(HttpServletRequest request) {
-
-               String version = request.getHeader("Shibboleth");
-               if (version != null) {
-                       log.debug("Request from Shibboleth version: " + version);
-                       return false;
-               }
-               log.debug("No version header found.");
-               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);
        }
 
-       private static void handleSSOError(HttpServletRequest req, HttpServletResponse res, Exception e)
+       private static void displayBrowserError(HttpServletRequest req, HttpServletResponse res, Exception e)
                        throws ServletException, IOException {
 
                req.setAttribute("errorText", e.toString());
                req.setAttribute("requestURL", req.getRequestURI().toString());
-               RequestDispatcher rd = req.getRequestDispatcher("/hserror.jsp");
-               // TODO rename hserror.jsp to a more appropriate name
+               RequestDispatcher rd = req.getRequestDispatcher("/IdPError.jsp");
                rd.forward(req, res);
        }
 
-       private class Semaphore {
-
-               private int value;
+}
 
-               public Semaphore(int value) {
+class MetadataProviderFactory {
 
-                       this.value = value;
-               }
+       private static Logger log = Logger.getLogger(MetadataProviderFactory.class.getName());
 
-               public synchronized void enter() throws InterruptedException {
+       public static Metadata loadProvider(Element e) throws MetadataException {
 
-                       --value;
-                       if (value < 0) {
-                               wait();
+               String className = e.getAttribute("type");
+               if (className == null || className.equals("")) {
+                       log.error("Metadata Provider requires specification of the attribute \"type\".");
+                       throw new MetadataException("Failed to initialize Metadata 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 Metadata 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 Metadata Provider.");
                        }
                }
-
-               public synchronized void exit() {
-
-                       ++value;
-                       notify();
-               }
-       }
-
-       private class InvalidProviderCredentialException extends Exception {
-
-               public InvalidProviderCredentialException(String message) {
-
-                       super(message);
-               }
-       }
-
-       abstract class SSOProfileHandler {
-
-               abstract String getHandlerName();
-
-               abstract String getRemoteProviderId(HttpServletRequest req);
-
-               abstract boolean validForRequest(HttpServletRequest request);
-
-               abstract boolean preProcessHook(HttpServletRequest request, HttpServletResponse response) throws IOException;
-
-               abstract SAMLAssertion[] processHook(HttpServletRequest request, RelyingParty relyingParty, Provider provider,
-                               SAMLNameIdentifier nameId, String authenticationMethod, Date authTime) throws SAMLException,
-                               IOException;
-
-               abstract String getSAMLTargetParameter(HttpServletRequest request, RelyingParty relyingParty, Provider provider);
-
-               abstract String getAcceptanceURL(HttpServletRequest request, RelyingParty relyingParty, Provider provider)
-                               throws InvalidClientDataException;
-       }
-
-}
-
-class InvalidClientDataException extends Exception {
-
-       public InvalidClientDataException(String message) {
-
-               super(message);
        }
-
 }
\ No newline at end of file