More work fleshing out the protocol handlers.
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / idp / provider / E_AuthSSOHandler.java
1 /*
2  * The Shibboleth License, Version 1. Copyright (c) 2002 University Corporation for Advanced Internet Development, Inc.
3  * All rights reserved Redistribution and use in source and binary forms, with or without modification, are permitted
4  * provided that the following conditions are met: Redistributions of source code must retain the above copyright
5  * notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above
6  * copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials
7  * provided with the distribution, if any, must include the following acknowledgment: "This product includes software
8  * developed by the University Corporation for Advanced Internet Development <http://www.ucaid.edu> Internet2 Project.
9  * Alternately, this acknowledegement may appear in the software itself, if and wherever such third-party
10  * acknowledgments normally appear. Neither the name of Shibboleth nor the names of its contributors, nor Internet2, nor
11  * the University Corporation for Advanced Internet Development, Inc., nor UCAID may be used to endorse or promote
12  * products derived from this software without specific prior written permission. For written permission, please contact
13  * shibboleth@shibboleth.org Products derived from this software may not be called Shibboleth, Internet2, UCAID, or the
14  * University Corporation for Advanced Internet Development, nor may Shibboleth appear in their name, without prior
15  * written permission of the University Corporation for Advanced Internet Development. THIS SOFTWARE IS PROVIDED BY THE
16  * COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE
18  * DISCLAIMED AND THE ENTIRE RISK OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. IN NO
19  * EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC.
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
23  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 package edu.internet2.middleware.shibboleth.idp.provider;
27
28 import java.io.IOException;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.Date;
32 import java.util.Vector;
33
34 import javax.servlet.ServletException;
35 import javax.servlet.http.Cookie;
36 import javax.servlet.http.HttpServletRequest;
37 import javax.servlet.http.HttpServletResponse;
38
39 import org.apache.log4j.Logger;
40 import org.opensaml.SAMLAssertion;
41 import org.opensaml.SAMLAttribute;
42 import org.opensaml.SAMLAttributeStatement;
43 import org.opensaml.SAMLAudienceRestrictionCondition;
44 import org.opensaml.SAMLAuthenticationStatement;
45 import org.opensaml.SAMLException;
46 import org.opensaml.SAMLNameIdentifier;
47 import org.opensaml.SAMLRequest;
48 import org.opensaml.SAMLResponse;
49 import org.opensaml.SAMLStatement;
50 import org.opensaml.SAMLSubject;
51 import org.w3c.dom.Document;
52
53 import sun.misc.BASE64Decoder;
54 import edu.internet2.middleware.shibboleth.common.RelyingParty;
55 import edu.internet2.middleware.shibboleth.idp.IdPProtocolHandler;
56 import edu.internet2.middleware.shibboleth.idp.IdPProtocolSupport;
57 import edu.internet2.middleware.shibboleth.idp.InvalidClientDataException;
58
59 /**
60  * @author Walter Hoehn
61  */
62 public class E_AuthSSOHandler implements IdPProtocolHandler {
63
64         private static Logger log = Logger.getLogger(E_AuthSSOHandler.class.getName());
65         private final String name = "EAuth";
66         private final String eAuthPortal = "http://eauth.firstgov.gov/service/select";
67         private final String eAuthFed = "urn:mace:shibboleth:eAuthFed";
68         private String csid;
69
70         //TODO validate that the target wants artifact, since it is required for this profile
71         //TODO validate that we aren't using signatures
72         //TODO validate that we are using the right nameIdentifier format
73         //TODO more robust attribute values before we ship
74
75         /*
76          * @see edu.internet2.middleware.shibboleth.idp.IdPProtocolHandler#getHandlerName()
77          */
78         public String getHandlerName() {
79
80                 return "E-Authentication SSO";
81         }
82
83         /*
84          * @see edu.internet2.middleware.shibboleth.idp.IdPProtocolHandler#processRequest(javax.servlet.http.HttpServletRequest,
85          *      javax.servlet.http.HttpServletResponse, org.opensaml.SAMLRequest,
86          *      edu.internet2.middleware.shibboleth.idp.IdPProtocolSupport)
87          */
88         public SAMLResponse processRequest(HttpServletRequest request, HttpServletResponse response,
89                         SAMLRequest samlRequest, IdPProtocolSupport support) throws SAMLException, IOException, ServletException {
90
91                 // Sanity check
92                 if (request != null) {
93                         log.error("Protocol Handler received a SAML Request, but is unable to handle it.");
94                         throw new SAMLException(SAMLException.RESPONDER, "General error processing request.");
95                 }
96
97                 //If no aaid is specified, redirect to the eAuth portal
98                 if (request.getParameter("aaid") == null || request.getParameter("aaid").equals("")) {
99                         log.debug("Received an E-Authentication request with no (aaid) parameter.  "
100                                         + "Redirecting to the E-Authentication portal.");
101                         response.sendRedirect(eAuthPortal + "?csid=" + csid);
102                         return null;
103                 }
104
105                 //FUTURE at some point this needs to be integrated with SAML2 session reset
106                 //If session reset was requested, delete the session and re-direct back
107                 //Note, this only works with servler form-auth
108                 String reAuth = request.getParameter("sessionreset");
109                 if (reAuth != null && reAuth.equals("1")) {
110                         log.debug("E-Authebtication session reset requested.");
111                         Cookie session = new Cookie("JSESSIONID", null);
112                         session.setMaxAge(0);
113                         response.addCookie(session);
114
115                         response.sendRedirect(request.getRequestURI()
116                                         + (request.getQueryString() != null ? "?"
117                                                         + request.getQueryString().replaceAll("(^sessionreset=1&?|&?sessionreset=1)", "") : ""));
118                         return null;
119                 }
120
121                 try {
122                         IdPProtocolSupport.validateEngineData(request);
123                 } catch (InvalidClientDataException e1) {
124                         // TODO Auto-generated catch block
125                 }
126
127                 //TODO figure this out
128                 RelyingParty relyingParty = null;
129                 SAMLNameIdentifier nameId = null;
130                 String authenticationMethod = null;
131                 Date authTime = null;
132
133                 Document doc = org.opensaml.XML.parserPool.newDocument();
134                 ArrayList audiences = new ArrayList();
135                 if (relyingParty.getProviderId() != null) {
136                         audiences.add(relyingParty.getProviderId());
137                 }
138                 if (relyingParty.getName() != null && !relyingParty.getName().equals(relyingParty.getProviderId())) {
139                         audiences.add(relyingParty.getName());
140                 }
141                 String issuer = relyingParty.getIdentityProvider().getProviderId();
142                 Vector conditions = new Vector(1);
143                 if (audiences != null && audiences.size() > 0) {
144                         conditions.add(new SAMLAudienceRestrictionCondition(audiences));
145                 }
146
147                 //TODO need to pull this out into the generic artifact handling
148                 String[] confirmationMethods = {SAMLSubject.CONF_ARTIFACT};
149                 SAMLSubject subject = new SAMLSubject(nameId, Arrays.asList(confirmationMethods), null, null);
150                 //TODO pull from authN system? or make configurable
151                 ArrayList attributes = new ArrayList();
152                 attributes.add(new SAMLAttribute("assuranceLevel", "http://eauthentication.gsa.gov/federated/attribute", null,
153                                 0, Arrays.asList(new String[]{"2"})));
154
155                 //TODO Hack Alert!!!
156                 // Pull attributes from AA
157                 String hackFullName = null;
158                 if (nameId.getName().startsWith("uid=tomcat")) {
159                         hackFullName = "Tomcat Test User";
160                 } else if (nameId.getName().startsWith("uid=nfaut")) {
161                         hackFullName = "Nathan Faut";
162                 } else if (nameId.getName().startsWith("uid=wassa")) {
163                         hackFullName = "Walter F. Hoehn, Jr.";
164                 } else if (nameId.getName().startsWith("uid=mtebo")) {
165                         hackFullName = "Matt Tebo";
166                 } else if (nameId.getName().startsWith("uid=dblanchard")) {
167                         hackFullName = "Deb Blanchard";
168                 } else if (nameId.getName().startsWith("uid=rweiser")) {
169                         hackFullName = "Russ Weiser";
170                 } else if (nameId.getName().startsWith("uid=scarmody")) {
171                         hackFullName = "Steven Carmody";
172                 }
173                 attributes.add(new SAMLAttribute("commonName", "http://eauthentication.gsa.gov/federated/attribute", null, 0,
174                                 Arrays.asList(new String[]{hackFullName})));
175                 attributes.add(new SAMLAttribute("csid", "http://eauthentication.gsa.gov/federated/attribute", null, 0, Arrays
176                                 .asList(new String[]{csid})));
177                 try {
178                         SAMLStatement[] statements = {
179                                         new SAMLAuthenticationStatement(subject, authenticationMethod, authTime, request.getRemoteAddr(),
180                                                         null, null), new SAMLAttributeStatement((SAMLSubject) subject.clone(), attributes)};
181                         SAMLAssertion[] assertions = {new SAMLAssertion(issuer, new Date(System.currentTimeMillis()), new Date(
182                                         System.currentTimeMillis() + 300000), conditions, null, Arrays.asList(statements))};
183                         if (log.isDebugEnabled()) {
184                                 log.debug("Dumping generated SAML Assertions:"
185                                                 + System.getProperty("line.separator")
186                                                 + new String(new BASE64Decoder().decodeBuffer(new String(assertions[0].toBase64(), "ASCII")),
187                                                                 "UTF8"));
188                         }
189                         return null;
190                 } catch (CloneNotSupportedException e) { //TODO handle return null; } }
191
192                 }
193
194                 return null;
195
196         }
197
198         /*
199          * EAuthProfileHandler(String csid) throws ShibbolethConfigurationException { if (csid == null) { throw new
200          * ShibbolethConfigurationException( "EAuth support is enabled, but no (csid) parameter has been configured."); }
201          * this.csid = csid; }
202          */
203
204         /*
205          * String getRemoteProviderId(HttpServletRequest req) { return req.getParameter("aaid"); }
206          */
207
208         /*
209          * String getSAMLTargetParameter(HttpServletRequest request, RelyingParty relyingParty, Provider provider) {
210          * ProviderRole[] roles = provider.getRoles(); if (roles.length == 0) { log.error("Inappropriate metadata for EAuth
211          * provider."); return null; } for (int i = 0; roles.length > i; i++) { if (roles[i] instanceof SPProviderRole) {
212          * return ((SPProviderRole) roles[i]).getTarget(); } } log.error("Inappropriate metadata for EAuth provider.");
213          * return null; }
214          */
215
216         /*
217          * internet2.middleware.shibboleth.hs.HSRelyingParty) String getAcceptanceURL(HttpServletRequest request,
218          * HSRelyingParty relyingParty, Provider provider) throws InvalidClientDataException { //The EAuth profile requires
219          * metadata, since the assertion consumer is not supplied as a request parameter if (provider == null) {
220          * log.error("Unkown requesting service provider (" + relyingParty.getProviderId() + ")."); throw new
221          * InvalidClientDataException("Unkown requesting service provider."); } //Pull the consumer URL from the metadata
222          * ProviderRole[] roles = provider.getRoles(); if (roles.length == 0) { log.info("Inappropriate metadata for
223          * provider: no roles specified."); throw new InvalidClientDataException("Invalid metadata for requesting service
224          * provider."); } for (int i = 0; roles.length > i; i++) { if (roles[i] instanceof SPProviderRole) { Endpoint[]
225          * endpoints = ((SPProviderRole) roles[i]).getAssertionConsumerServiceURLs(); if (endpoints.length > 0) { return
226          * endpoints[0].getLocation(); } } } log.info("Inappropriate metadata for provider: no roles specified."); throw new
227          * InvalidClientDataException("Invalid metadata for requesting service provider."); }
228          */
229
230 }