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 IdPConfig configuration;
- private HashMap protocolHandlers = new HashMap();
- private IdPProtocolSupport protocolSupport;
+ 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 IdPConfig configuration;
+ private HashMap protocolHandlers = new HashMap();
+ private IdPProtocolSupport protocolSupport;
/*
* @see javax.servlet.GenericServlet#init()
MDC.put("serviceId", "[IdP] " + idgen.nextInt());
MDC.put("remoteAddr", request.getRemoteAddr());
- log.debug("Recieved a request via GET.");
+ log.debug("Recieved a request via GET for endpoint (" + request.getRequestURI() + ").");
try {
// TODO this throttle should probably just wrap signing operations...
throttle.enter();
- // Determine which protocol we are responding to (at this point, Shibv1 vs. EAuth)
- IdPProtocolHandler activeHandler = null;
- activeHandler = (IdPProtocolHandler) protocolHandlers.get(request.getRequestURI());
-
- if (activeHandler == null) { throw new InvalidClientDataException(
- "The request did not contain sufficient parameter data to determine the protocol."); }
+ // Determine which protocol we are responding to (at this point normally Shibv1 vs. EAuth)
+ IdPProtocolHandler activeHandler = (IdPProtocolHandler) protocolHandlers.get(request.getRequestURI());
+ if (activeHandler == null) {
+ log.error("No protocol handler registered for endpoint (" + request.getRequestURI() + ").");
+ throw new SAMLException("Request submitted to an invalid endpoint.");
+ }
// Pass request to the appropriate handler
log.info("Processing " + activeHandler.getHandlerName() + " request.");
- activeHandler.processRequest(request, response, null, protocolSupport);
+ 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 hmmm... there is kind of an assupmtion here that we don't get a response... how to handle?
- } catch (InvalidClientDataException ex) {
- log.error(ex);
- displayBrowserError(request, response, ex);
- return;
} catch (SAMLException ex) {
log.error(ex);
displayBrowserError(request, response, ex);
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 endpoint (" + request.getRequestURI() + ").");
// Parse SOAP request and marshall SAML request object
SAMLRequest samlRequest = null;
}
// Determine which protocol handler is active for this endpoint
- IdPProtocolHandler activeHandler = null;
- activeHandler = (IdPProtocolHandler) protocolHandlers.get(request.getRequestURI());
+ IdPProtocolHandler activeHandler = (IdPProtocolHandler) protocolHandlers.get(request.getRequestURI());
if (activeHandler == null) {
- log.fatal("No protocol handler registered for endpoint (" + request.getRequestURI() + ").");
- throw new SAMLException("Invalid request endpoint.");
+ log.error("No protocol handler registered for endpoint (" + request.getRequestURI() + ").");
+ throw new SAMLException("Request submitted to an invalid endpoint.");
}
// Pass request to the appropriate handler and respond
log.info("Processing " + activeHandler.getHandlerName() + " request.");
- try {
- SAMLResponse samlResponse = activeHandler.processRequest(request, response, samlRequest,
- protocolSupport);
- binding.respond(response, samlResponse, null);
- } catch (InvalidClientDataException e1) {
- // TODO throw SAML Exception here
- e1.printStackTrace();
- }
+ SAMLResponse samlResponse = activeHandler.processRequest(request, response, samlRequest, protocolSupport);
+ binding.respond(response, samlResponse, null);
} catch (SAMLException e) {
- log.error("Error while processing request: " + e);
- try {
- // TODO pass thru SAML Exception
- 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.");
- }
+ sendFailureToSAMLBinding(response, samlRequest, e);
}
-
}
- 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);
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.");
+ 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);
}
private class Semaphore {
- private int value;
+ private int value;
public Semaphore(int value) {
class FederationProviderFactory {
- private static Logger log = Logger.getLogger(FederationProviderFactory.class.getName());
+ private static Logger log = Logger.getLogger(FederationProviderFactory.class.getName());
public static Metadata loadProvider(Element e) throws MetadataException {
/*
- * Created on Mar 10, 2005
- *
- * TODO To change the template for this generated file go to
- * Window - Preferences - Java - Code Generation - Code and Comments
+ * 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.idp.provider;
import java.io.IOException;
import edu.internet2.middleware.shibboleth.metadata.EntityDescriptor;
import edu.internet2.middleware.shibboleth.metadata.SPSSODescriptor;
-
/**
* @author Walter Hoehn
*/
public class ShibbolethV1SSOHandler implements IdPProtocolHandler {
- private static Logger log = Logger.getLogger(ShibbolethV1SSOHandler.class.getName());
-
- /*
- * (non-Javadoc)
- *
- * @see edu.internet2.middleware.shibboleth.idp.IdPResponder.ProtocolHandler#validForRequest(javax.servlet.http.HttpServletRequest)
- */
- // TODO move this into the process method
- 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;
- }
- }
+ private static Logger log = Logger.getLogger(ShibbolethV1SSOHandler.class.getName());
/*
- * (non-Javadoc)
- *
* @see edu.internet2.middleware.shibboleth.idp.IdPResponder.ProtocolHandler#processRequest(javax.servlet.http.HttpServletRequest,
* javax.servlet.http.HttpServletResponse)
*/
public SAMLResponse processRequest(HttpServletRequest request, HttpServletResponse response,
- SAMLRequest samlRequest, IdPProtocolSupport support) throws InvalidClientDataException, SAMLException,
- ServletException, IOException {
-
- // TODO make sure the saml request is null for now
-
- // Ensure that we have the required data from the servlet container
- IdPProtocolSupport.validateEngineData(request);
-
- // Get the authN info
- String username = support.getIdPConfig().getAuthHeaderName().equalsIgnoreCase("REMOTE_USER") ? request
- .getRemoteUser() : request.getHeader(support.getIdPConfig().getAuthHeaderName());
+ SAMLRequest samlRequest, IdPProtocolSupport support) throws SAMLException, ServletException, IOException {
- // Select the appropriate Relying Party configuration for the request
- RelyingParty relyingParty = null;
- String remoteProviderId = request.getParameter("providerId");
+ //TODO attribute push?
- // 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);
+ if (request != null) {
+ log.error("Protocol Handler received a SAML Request, but is unable to handle it.");
+ throw new SAMLException(SAMLException.RESPONDER, "General error processing request.");
}
+ try {
+ // Ensure that we have the required data from the servlet container
+ IdPProtocolSupport.validateEngineData(request);
+ validateShibSpecificData(request);
+
+ // Get the authN info
+ String username = support.getIdPConfig().getAuthHeaderName().equalsIgnoreCase("REMOTE_USER") ? request
+ .getRemoteUser() : request.getHeader(support.getIdPConfig().getAuthHeaderName());
+ if ((username == null) || (username.equals(""))) { throw new InvalidClientDataException(
+ "Unable to authenticate remote user"); }
+
+ // 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);
+ }
- // Grab the metadata for the provider
- EntityDescriptor provider = support.lookup(relyingParty.getProviderId());
-
- // Determine the acceptance URL
- String acceptanceURL = request.getParameter("shire");
+ // Grab the metadata for the provider
+ EntityDescriptor provider = support.lookup(relyingParty.getProviderId());
- // Make sure that the selected relying party configuration is appropriate for this
- // acceptance URL
- if (!relyingParty.isLegacyProvider()) {
+ // Determine the acceptance URL
+ String acceptanceURL = request.getParameter("shire");
- if (provider == null) {
- log.info("No metadata found for provider: (" + relyingParty.getProviderId() + ").");
- relyingParty = support.getServiceProviderMapper().getRelyingParty(null);
+ // Make sure that the selected relying party configuration is appropriate for this
+ // acceptance URL
+ if (!relyingParty.isLegacyProvider()) {
- } else {
+ if (provider == null) {
+ log.info("No metadata found for provider: (" + relyingParty.getProviderId() + ").");
+ relyingParty = support.getServiceProviderMapper().getRelyingParty(null);
- 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 (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;
- try {
- nameId = support.getNameMapper().getNameIdentifierName(relyingParty.getHSNameFormatId(),
- new AuthNPrincipal(username), relyingParty, relyingParty.getIdentityProvider());
- } catch (NameIdentifierMappingException e) {
- log.error("Error converting principal to SAML Name Identifier: " + e);
- throw new SAMLException("Error converting principal to SAML Name Identifier.", e);
- }
- 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 SAML Name Identifier
+ SAMLNameIdentifier nameId;
+ try {
+ nameId = support.getNameMapper().getNameIdentifierName(relyingParty.getHSNameFormatId(),
+ new AuthNPrincipal(username), relyingParty, relyingParty.getIdentityProvider());
+ } catch (NameIdentifierMappingException e) {
+ log.error("Error converting principal to SAML Name Identifier: " + e);
+ throw new SAMLException("Error converting principal to SAML Name Identifier.", e);
+ }
- // 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));
+ 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 + ").");
}
- // Assemble the query string
- StringBuffer destination = new StringBuffer(acceptanceURL);
- destination.append("?TARGET=");
+ // TODO Provide a mechanism for the authenticator to specify the auth time
+ SAMLAssertion[] assertions = generateAssertion(request, relyingParty, provider, nameId,
+ authenticationMethod, new Date(System.currentTimeMillis()));
- destination.append(URLEncoder.encode(request.getParameter("target"), "UTF-8"));
+ // TODO do assertion signing for artifact stuff
- Iterator iterator = artifacts.iterator();
- StringBuffer artifactBuffer = new StringBuffer(); // Buffer for the transaction log
- while (iterator.hasNext()) {
- destination.append("&SAMLart=");
- String artifact = (String) iterator.next();
+ // SAML Artifact profile
+ if (useArtifactProfile(provider, acceptanceURL)) {
+ log.debug("Responding with Artifact profile.");
- destination.append(URLEncoder.encode(artifact, "UTF-8"));
- artifactBuffer.append("(" + artifact + ")");
+ // 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));
+ }
- }
- log.debug("Redirecting to (" + destination.toString() + ").");
- response.sendRedirect(destination.toString()); // Redirect to the artifact receiver
+ // Assemble the query string
+ StringBuffer destination = new StringBuffer(acceptanceURL);
+ destination.append("?TARGET=");
- 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() + ").");
+ destination.append(URLEncoder.encode(request.getParameter("target"), "UTF-8"));
- // SAML POST profile
- } else {
- log.debug("Responding with POST profile.");
- request.setAttribute("acceptanceURL", acceptanceURL);
- request.setAttribute("target", request.getParameter("target"));
+ Iterator iterator = artifacts.iterator();
+ StringBuffer artifactBuffer = new StringBuffer(); // Buffer for the transaction log
+ while (iterator.hasNext()) {
+ destination.append("&SAMLart=");
+ String artifact = (String) iterator.next();
- SAMLResponse samlResponse = new SAMLResponse(null, acceptanceURL, Arrays.asList(assertions), null);
- IdPProtocolSupport.addSignatures(samlResponse, relyingParty, provider, true);
- createPOSTForm(request, response, samlResponse.toBase64());
+ 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
- // Make transaction log entry
- if (relyingParty.isLegacyProvider()) {
- 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 {
support.getTransactionLog().info(
- "Authentication assertion issued to provider ("
+ "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);
+ IdPProtocolSupport.addSignatures(samlResponse, relyingParty, provider, true);
+ createPOSTForm(request, response, samlResponse.toBase64());
+
+ // Make transaction log entry
+ if (relyingParty.isLegacyProvider()) {
+ 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 {
+ 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() + ").");
+ }
}
+ } catch (InvalidClientDataException e) {
+ throw new SAMLException(SAMLException.RESPONDER, e.getMessage());
}
return null;
}
- SAMLAssertion[] foo(HttpServletRequest request, RelyingParty relyingParty, EntityDescriptor provider,
- SAMLNameIdentifier nameId, String authenticationMethod, Date authTime) throws SAMLException, IOException {
+ private SAMLAssertion[] generateAssertion(HttpServletRequest request, RelyingParty relyingParty,
+ EntityDescriptor provider, SAMLNameIdentifier nameId, String authenticationMethod, Date authTime)
+ throws SAMLException, IOException {
Document doc = org.opensaml.XML.parserPool.newDocument();
return "Shibboleth v1.x SSO";
}
+ private void validateShibSpecificData(HttpServletRequest request) throws InvalidClientDataException {
+
+ if (request.getParameter("target") == null || request.getParameter("target").equals("")) { throw new InvalidClientDataException(
+ "Invalid data from Service Provider: no target URL received."); }
+ if ((request.getParameter("shire") == null) || (request.getParameter("shire").equals(""))) { throw new InvalidClientDataException(
+ "Invalid data from Service Provider: No acceptance URL received."); }
+ }
+
private static void createPOSTForm(HttpServletRequest req, HttpServletResponse res, byte[] buf) throws IOException,
ServletException {
log.info("Supplied consumer URL not found in metadata.");
return false;
}
-}
+}
\ No newline at end of file