More work fleshing out the protocol handlers.
authorwassa <wassa@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Mon, 14 Mar 2005 17:26:35 +0000 (17:26 +0000)
committerwassa <wassa@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Mon, 14 Mar 2005 17:26:35 +0000 (17:26 +0000)
git-svn-id: https://subversion.switch.ch/svn/shibboleth/java-idp/trunk@1291 ab3bd59b-922f-494d-bb5f-6f0a3c29deca

src/edu/internet2/middleware/shibboleth/idp/IdPResponder.java
src/edu/internet2/middleware/shibboleth/idp/provider/E_AuthSSOHandler.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/idp/provider/SAMLv1_AttributeQueryHandler.java
src/edu/internet2/middleware/shibboleth/idp/provider/ShibbolethV1SSOHandler.java

index 90d89ad..8a03d69 100644 (file)
@@ -75,14 +75,14 @@ import edu.internet2.middleware.shibboleth.metadata.MetadataException;
 
 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()
@@ -336,7 +336,7 @@ public class IdPResponder extends HttpServlet {
 
        private class Semaphore {
 
-               private int     value;
+               private int value;
 
                public Semaphore(int value) {
 
@@ -366,7 +366,7 @@ public class IdPResponder extends HttpServlet {
 
 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 {
 
diff --git a/src/edu/internet2/middleware/shibboleth/idp/provider/E_AuthSSOHandler.java b/src/edu/internet2/middleware/shibboleth/idp/provider/E_AuthSSOHandler.java
new file mode 100644 (file)
index 0000000..a49f80f
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Vector;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.log4j.Logger;
+import org.opensaml.SAMLAssertion;
+import org.opensaml.SAMLAttribute;
+import org.opensaml.SAMLAttributeStatement;
+import org.opensaml.SAMLAudienceRestrictionCondition;
+import org.opensaml.SAMLAuthenticationStatement;
+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 sun.misc.BASE64Decoder;
+import edu.internet2.middleware.shibboleth.common.RelyingParty;
+import edu.internet2.middleware.shibboleth.idp.IdPProtocolHandler;
+import edu.internet2.middleware.shibboleth.idp.IdPProtocolSupport;
+import edu.internet2.middleware.shibboleth.idp.InvalidClientDataException;
+
+/**
+ * @author Walter Hoehn
+ */
+public class E_AuthSSOHandler implements IdPProtocolHandler {
+
+       private static Logger log = Logger.getLogger(E_AuthSSOHandler.class.getName());
+       private final String name = "EAuth";
+       private final String eAuthPortal = "http://eauth.firstgov.gov/service/select";
+       private final String eAuthFed = "urn:mace:shibboleth:eAuthFed";
+       private String csid;
+
+       //TODO validate that the target wants artifact, since it is required for this profile
+       //TODO validate that we aren't using signatures
+       //TODO validate that we are using the right nameIdentifier format
+       //TODO more robust attribute values before we ship
+
+       /*
+        * @see edu.internet2.middleware.shibboleth.idp.IdPProtocolHandler#getHandlerName()
+        */
+       public String getHandlerName() {
+
+               return "E-Authentication SSO";
+       }
+
+       /*
+        * @see edu.internet2.middleware.shibboleth.idp.IdPProtocolHandler#processRequest(javax.servlet.http.HttpServletRequest,
+        *      javax.servlet.http.HttpServletResponse, org.opensaml.SAMLRequest,
+        *      edu.internet2.middleware.shibboleth.idp.IdPProtocolSupport)
+        */
+       public SAMLResponse processRequest(HttpServletRequest request, HttpServletResponse response,
+                       SAMLRequest samlRequest, IdPProtocolSupport support) throws SAMLException, IOException, ServletException {
+
+               // Sanity check
+               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.");
+               }
+
+               //If no aaid is specified, redirect to the eAuth portal
+               if (request.getParameter("aaid") == null || request.getParameter("aaid").equals("")) {
+                       log.debug("Received an E-Authentication request with no (aaid) parameter.  "
+                                       + "Redirecting to the E-Authentication portal.");
+                       response.sendRedirect(eAuthPortal + "?csid=" + csid);
+                       return null;
+               }
+
+               //FUTURE at some point this needs to be integrated with SAML2 session reset
+               //If session reset was requested, delete the session and re-direct back
+               //Note, this only works with servler form-auth
+               String reAuth = request.getParameter("sessionreset");
+               if (reAuth != null && reAuth.equals("1")) {
+                       log.debug("E-Authebtication session reset requested.");
+                       Cookie session = new Cookie("JSESSIONID", null);
+                       session.setMaxAge(0);
+                       response.addCookie(session);
+
+                       response.sendRedirect(request.getRequestURI()
+                                       + (request.getQueryString() != null ? "?"
+                                                       + request.getQueryString().replaceAll("(^sessionreset=1&?|&?sessionreset=1)", "") : ""));
+                       return null;
+               }
+
+               try {
+                       IdPProtocolSupport.validateEngineData(request);
+               } catch (InvalidClientDataException e1) {
+                       // TODO Auto-generated catch block
+               }
+
+               //TODO figure this out
+               RelyingParty relyingParty = null;
+               SAMLNameIdentifier nameId = null;
+               String authenticationMethod = null;
+               Date authTime = null;
+
+               Document doc = org.opensaml.XML.parserPool.newDocument();
+               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());
+               }
+               String issuer = relyingParty.getIdentityProvider().getProviderId();
+               Vector conditions = new Vector(1);
+               if (audiences != null && audiences.size() > 0) {
+                       conditions.add(new SAMLAudienceRestrictionCondition(audiences));
+               }
+
+               //TODO need to pull this out into the generic artifact handling
+               String[] confirmationMethods = {SAMLSubject.CONF_ARTIFACT};
+               SAMLSubject subject = new SAMLSubject(nameId, Arrays.asList(confirmationMethods), null, null);
+               //TODO pull from authN system? or make configurable
+               ArrayList attributes = new ArrayList();
+               attributes.add(new SAMLAttribute("assuranceLevel", "http://eauthentication.gsa.gov/federated/attribute", null,
+                               0, Arrays.asList(new String[]{"2"})));
+
+               //TODO Hack Alert!!!
+               // Pull attributes from AA
+               String hackFullName = null;
+               if (nameId.getName().startsWith("uid=tomcat")) {
+                       hackFullName = "Tomcat Test User";
+               } else if (nameId.getName().startsWith("uid=nfaut")) {
+                       hackFullName = "Nathan Faut";
+               } else if (nameId.getName().startsWith("uid=wassa")) {
+                       hackFullName = "Walter F. Hoehn, Jr.";
+               } else if (nameId.getName().startsWith("uid=mtebo")) {
+                       hackFullName = "Matt Tebo";
+               } else if (nameId.getName().startsWith("uid=dblanchard")) {
+                       hackFullName = "Deb Blanchard";
+               } else if (nameId.getName().startsWith("uid=rweiser")) {
+                       hackFullName = "Russ Weiser";
+               } else if (nameId.getName().startsWith("uid=scarmody")) {
+                       hackFullName = "Steven Carmody";
+               }
+               attributes.add(new SAMLAttribute("commonName", "http://eauthentication.gsa.gov/federated/attribute", null, 0,
+                               Arrays.asList(new String[]{hackFullName})));
+               attributes.add(new SAMLAttribute("csid", "http://eauthentication.gsa.gov/federated/attribute", null, 0, Arrays
+                               .asList(new String[]{csid})));
+               try {
+                       SAMLStatement[] statements = {
+                                       new SAMLAuthenticationStatement(subject, authenticationMethod, authTime, request.getRemoteAddr(),
+                                                       null, null), new SAMLAttributeStatement((SAMLSubject) subject.clone(), attributes)};
+                       SAMLAssertion[] assertions = {new SAMLAssertion(issuer, new Date(System.currentTimeMillis()), new Date(
+                                       System.currentTimeMillis() + 300000), conditions, null, Arrays.asList(statements))};
+                       if (log.isDebugEnabled()) {
+                               log.debug("Dumping generated SAML Assertions:"
+                                               + System.getProperty("line.separator")
+                                               + new String(new BASE64Decoder().decodeBuffer(new String(assertions[0].toBase64(), "ASCII")),
+                                                               "UTF8"));
+                       }
+                       return null;
+               } catch (CloneNotSupportedException e) { //TODO handle return null; } }
+
+               }
+
+               return null;
+
+       }
+
+       /*
+        * EAuthProfileHandler(String csid) throws ShibbolethConfigurationException { if (csid == null) { throw new
+        * ShibbolethConfigurationException( "EAuth support is enabled, but no (csid) parameter has been configured."); }
+        * this.csid = csid; }
+        */
+
+       /*
+        * String getRemoteProviderId(HttpServletRequest req) { return req.getParameter("aaid"); }
+        */
+
+       /*
+        * String getSAMLTargetParameter(HttpServletRequest request, RelyingParty relyingParty, Provider provider) {
+        * ProviderRole[] roles = provider.getRoles(); if (roles.length == 0) { log.error("Inappropriate metadata for EAuth
+        * provider."); return null; } for (int i = 0; roles.length > i; i++) { if (roles[i] instanceof SPProviderRole) {
+        * return ((SPProviderRole) roles[i]).getTarget(); } } log.error("Inappropriate metadata for EAuth provider.");
+        * return null; }
+        */
+
+       /*
+        * internet2.middleware.shibboleth.hs.HSRelyingParty) String getAcceptanceURL(HttpServletRequest request,
+        * HSRelyingParty relyingParty, Provider provider) throws InvalidClientDataException { //The EAuth profile requires
+        * metadata, since the assertion consumer is not supplied as a request parameter if (provider == null) {
+        * log.error("Unkown requesting service provider (" + relyingParty.getProviderId() + ")."); throw new
+        * InvalidClientDataException("Unkown requesting service provider."); } //Pull the consumer URL from the metadata
+        * ProviderRole[] roles = provider.getRoles(); if (roles.length == 0) { log.info("Inappropriate metadata for
+        * provider: no roles specified."); throw new InvalidClientDataException("Invalid metadata for requesting service
+        * provider."); } for (int i = 0; roles.length > i; i++) { if (roles[i] instanceof SPProviderRole) { Endpoint[]
+        * endpoints = ((SPProviderRole) roles[i]).getAssertionConsumerServiceURLs(); if (endpoints.length > 0) { return
+        * endpoints[0].getLocation(); } } } log.info("Inappropriate metadata for provider: no roles specified."); throw new
+        * InvalidClientDataException("Invalid metadata for requesting service provider."); }
+        */
+
+}
\ No newline at end of file
index d154767..c01586b 100644 (file)
@@ -297,6 +297,13 @@ public class SAMLv1_AttributeQueryHandler extends BaseServiceHandler implements
 
                        return samlResponse;
 
+               } catch (SAMLException e) {
+                       if (relyingParty.passThruErrors()) {
+                               throw new SAMLException(SAMLException.RESPONDER, "General error processing request.", e);
+                       } else {
+                               throw new SAMLException(SAMLException.RESPONDER, "General error processing request.");
+                       }
+
                } catch (InvalidNameIdentifierException e) {
                        log.error("Could not associate the request's subject with a principal: " + e);
                        if (relyingParty.passThruErrors()) {
@@ -307,17 +314,23 @@ public class SAMLv1_AttributeQueryHandler extends BaseServiceHandler implements
                        }
 
                } catch (NameIdentifierMappingException e) {
-                       // TODO Auto-generated catch block
-                       e.printStackTrace();
-                       return null;
+                       log.error("Encountered an error while mapping the name identifier from the request: " + e);
+                       if (relyingParty.passThruErrors()) {
+                               throw new SAMLException(SAMLException.RESPONDER, "General error processing request.", e);
+                       } else {
+                               throw new SAMLException(SAMLException.RESPONDER, "General error processing request.");
+                       }
+
                } catch (AAException e) {
-                       //TODO get rid of AAException, I think
-                       // TODO Auto-generated catch block
-                       e.printStackTrace();
-                       return null;
+                       log.error("Encountered an error while resolving resolving attributes: " + e);
+                       if (relyingParty.passThruErrors()) {
+                               throw new SAMLException(SAMLException.RESPONDER, "General error processing request.", e);
+                       } else {
+                               throw new SAMLException(SAMLException.RESPONDER, "General error processing request.");
+                       }
 
                } catch (CloneNotSupportedException e) {
-                       log.error("Could not associate the request subject with a principal: " + e);
+                       log.error("Encountered an error while cloning request subject for use in response: " + e);
                        if (relyingParty.passThruErrors()) {
                                throw new SAMLException(SAMLException.RESPONDER, "General error processing request.", e);
                        } else {
index 6605a16..65498ac 100644 (file)
@@ -59,6 +59,7 @@ import sun.misc.BASE64Decoder;
 import edu.internet2.middleware.shibboleth.common.AuthNPrincipal;
 import edu.internet2.middleware.shibboleth.common.NameIdentifierMappingException;
 import edu.internet2.middleware.shibboleth.common.RelyingParty;
+import edu.internet2.middleware.shibboleth.common.ShibBrowserProfile;
 import edu.internet2.middleware.shibboleth.idp.IdPProtocolHandler;
 import edu.internet2.middleware.shibboleth.idp.IdPProtocolSupport;
 import edu.internet2.middleware.shibboleth.idp.InvalidClientDataException;
@@ -72,7 +73,7 @@ import edu.internet2.middleware.shibboleth.metadata.SPSSODescriptor;
  */
 public class ShibbolethV1SSOHandler implements IdPProtocolHandler {
 
-       private static Logger   log     = Logger.getLogger(ShibbolethV1SSOHandler.class.getName());
+       private static Logger log = Logger.getLogger(ShibbolethV1SSOHandler.class.getName());
 
        /*
         * @see edu.internet2.middleware.shibboleth.idp.IdPResponder.ProtocolHandler#processRequest(javax.servlet.http.HttpServletRequest,
@@ -87,6 +88,7 @@ public class ShibbolethV1SSOHandler implements IdPProtocolHandler {
                        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);
@@ -236,7 +238,7 @@ public class ShibbolethV1SSOHandler implements IdPProtocolHandler {
 
                Document doc = org.opensaml.XML.parserPool.newDocument();
 
-               // Determine audiences and issuer
+               // Determine the correct audiences
                ArrayList audiences = new ArrayList();
                if (relyingParty.getProviderId() != null) {
                        audiences.add(relyingParty.getProviderId());
@@ -245,19 +247,19 @@ public class ShibbolethV1SSOHandler implements IdPProtocolHandler {
                        audiences.add(relyingParty.getName());
                }
 
+               // Determine the correct issuer
                String issuer = null;
                if (relyingParty.isLegacyProvider()) {
-                       // TODO figure this out
-                       /*
-                        * log.debug("Service Provider is running Shibboleth <= 1.1. Using old style issuer."); if
-                        * (relyingParty.getIdentityProvider().getResponseSigningCredential() == null ||
-                        * relyingParty.getIdentityProvider().getResponseSigningCredential().getX509Certificate() == null) { throw
-                        * new SAMLException( "Cannot serve legacy style assertions without an X509 certificate"); } issuer =
-                        * ShibBrowserProfile.getHostNameFromDN(relyingParty.getIdentityProvider()
-                        * .getResponseSigningCredential().getX509Certificate().getSubjectX500Principal()); if (issuer == null ||
-                        * issuer.equals("")) { throw new SAMLException( "Error parsing certificate DN while determining legacy
-                        * issuer name."); }
-                        */
+
+                       log.debug("Service Provider is running Shibboleth <= 1.1. Using old style issuer.");
+                       if (relyingParty.getIdentityProvider().getSigningCredential() == null
+                                       || relyingParty.getIdentityProvider().getSigningCredential().getX509Certificate() == null) { throw new SAMLException(
+                                       "Cannot serve legacy style assertions without an X509 certificate"); }
+                       issuer = ShibBrowserProfile.getHostNameFromDN(relyingParty.getIdentityProvider().getSigningCredential()
+                                       .getX509Certificate().getSubjectX500Principal());
+                       if (issuer == null || issuer.equals("")) { throw new SAMLException(
+                                       "Error parsing certificate DN while determining legacy issuer name."); }
+
                } else {
                        issuer = relyingParty.getIdentityProvider().getProviderId();
                }
@@ -338,11 +340,11 @@ public class ShibbolethV1SSOHandler implements IdPProtocolHandler {
                SPSSODescriptor sp = provider.getSPSSODescriptor(org.opensaml.XML.SAML11_PROTOCOL_ENUM);
                if (sp == null) { return false; }
 
-               // TODO: This will actually favor artifact, since a given location could support
-               // both profiles. If that's not what we want, needs adjustment...
+               // Look at the bindings.. prefer POST if we have multiple
                Iterator endpoints = sp.getAssertionConsumerServiceManager().getEndpoints();
                while (endpoints.hasNext()) {
                        Endpoint ep = (Endpoint) endpoints.next();
+                       if (acceptanceURL.equals(ep.getLocation()) && SAMLBrowserProfile.PROFILE_POST_URI.equals(ep.getBinding())) { return false; }
                        if (acceptanceURL.equals(ep.getLocation())
                                        && SAMLBrowserProfile.PROFILE_ARTIFACT_URI.equals(ep.getBinding())) { return true; }
                }