Small refactoring.
[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 import org.w3c.dom.Element;
53
54 import sun.misc.BASE64Decoder;
55 import edu.internet2.middleware.shibboleth.common.RelyingParty;
56 import edu.internet2.middleware.shibboleth.common.ShibbolethConfigurationException;
57 import edu.internet2.middleware.shibboleth.idp.IdPProtocolHandler;
58 import edu.internet2.middleware.shibboleth.idp.IdPProtocolSupport;
59 import edu.internet2.middleware.shibboleth.idp.InvalidClientDataException;
60
61 /**
62  * @author Walter Hoehn
63  */
64 public class E_AuthSSOHandler extends SSOHandler implements IdPProtocolHandler {
65
66         private static Logger log = Logger.getLogger(E_AuthSSOHandler.class.getName());
67         private final String name = "EAuth";
68         private final String eAuthPortal = "http://eauth.firstgov.gov/service/select";
69         private final String eAuthFed = "urn:mace:shibboleth:eAuthFed";
70         private String csid;
71
72         // TODO validate that the target wants artifact, since it is required for this profile
73         // TODO validate that we aren't using signatures
74         // TODO validate that we are using the right nameIdentifier format
75         // TODO more robust attribute values before we ship
76         /**
77          * Required DOM-based constructor.
78          */
79         public E_AuthSSOHandler(Element config) throws ShibbolethConfigurationException {
80
81                 super(config);
82         }
83
84         /*
85          * @see edu.internet2.middleware.shibboleth.idp.IdPProtocolHandler#getHandlerName()
86          */
87         public String getHandlerName() {
88
89                 return "E-Authentication SSO";
90         }
91
92         /*
93          * @see edu.internet2.middleware.shibboleth.idp.IdPProtocolHandler#processRequest(javax.servlet.http.HttpServletRequest,
94          *      javax.servlet.http.HttpServletResponse, org.opensaml.SAMLRequest,
95          *      edu.internet2.middleware.shibboleth.idp.IdPProtocolSupport)
96          */
97         public SAMLResponse processRequest(HttpServletRequest request, HttpServletResponse response,
98                         SAMLRequest samlRequest, IdPProtocolSupport support) throws SAMLException, IOException, ServletException {
99
100                 // Sanity check
101                 if (request != null) {
102                         log.error("Protocol Handler received a SAML Request, but is unable to handle it.");
103                         throw new SAMLException(SAMLException.RESPONDER, "General error processing request.");
104                 }
105
106                 // If no aaid is specified, redirect to the eAuth portal
107                 if (request.getParameter("aaid") == null || request.getParameter("aaid").equals("")) {
108                         log.debug("Received an E-Authentication request with no (aaid) parameter.  "
109                                         + "Redirecting to the E-Authentication portal.");
110                         response.sendRedirect(eAuthPortal + "?csid=" + csid);
111                         return null;
112                 }
113
114                 // FUTURE at some point this needs to be integrated with SAML2 session reset
115                 // If session reset was requested, delete the session and re-direct back
116                 // Note, this only works with servler form-auth
117                 String reAuth = request.getParameter("sessionreset");
118                 if (reAuth != null && reAuth.equals("1")) {
119                         log.debug("E-Authebtication session reset requested.");
120                         Cookie session = new Cookie("JSESSIONID", null);
121                         session.setMaxAge(0);
122                         response.addCookie(session);
123
124                         response.sendRedirect(request.getRequestURI()
125                                         + (request.getQueryString() != null ? "?"
126                                                         + request.getQueryString().replaceAll("(^sessionreset=1&?|&?sessionreset=1)", "") : ""));
127                         return null;
128                 }
129
130                 try {
131                         validateEngineData(request);
132                 } catch (InvalidClientDataException e1) {
133                         // TODO Auto-generated catch block
134                 }
135
136                 // TODO figure this out
137                 RelyingParty relyingParty = null;
138                 SAMLNameIdentifier nameId = null;
139                 String authenticationMethod = null;
140                 Date authTime = null;
141
142                 Document doc = org.opensaml.XML.parserPool.newDocument();
143                 ArrayList audiences = new ArrayList();
144                 if (relyingParty.getProviderId() != null) {
145                         audiences.add(relyingParty.getProviderId());
146                 }
147                 if (relyingParty.getName() != null && !relyingParty.getName().equals(relyingParty.getProviderId())) {
148                         audiences.add(relyingParty.getName());
149                 }
150                 String issuer = relyingParty.getIdentityProvider().getProviderId();
151                 Vector conditions = new Vector(1);
152                 if (audiences != null && audiences.size() > 0) {
153                         conditions.add(new SAMLAudienceRestrictionCondition(audiences));
154                 }
155
156                 // TODO need to pull this out into the generic artifact handling
157                 String[] confirmationMethods = {SAMLSubject.CONF_ARTIFACT};
158                 SAMLSubject subject = new SAMLSubject(nameId, Arrays.asList(confirmationMethods), null, null);
159                 // TODO pull from authN system? or make configurable
160                 ArrayList attributes = new ArrayList();
161                 attributes.add(new SAMLAttribute("assuranceLevel", "http://eauthentication.gsa.gov/federated/attribute", null,
162                                 0, Arrays.asList(new String[]{"2"})));
163
164                 // TODO Hack Alert!!!
165                 // Pull attributes from AA
166                 String hackFullName = null;
167                 if (nameId.getName().startsWith("uid=tomcat")) {
168                         hackFullName = "Tomcat Test User";
169                 } else if (nameId.getName().startsWith("uid=nfaut")) {
170                         hackFullName = "Nathan Faut";
171                 } else if (nameId.getName().startsWith("uid=wassa")) {
172                         hackFullName = "Walter F. Hoehn, Jr.";
173                 } else if (nameId.getName().startsWith("uid=mtebo")) {
174                         hackFullName = "Matt Tebo";
175                 } else if (nameId.getName().startsWith("uid=dblanchard")) {
176                         hackFullName = "Deb Blanchard";
177                 } else if (nameId.getName().startsWith("uid=rweiser")) {
178                         hackFullName = "Russ Weiser";
179                 } else if (nameId.getName().startsWith("uid=scarmody")) {
180                         hackFullName = "Steven Carmody";
181                 }
182                 attributes.add(new SAMLAttribute("commonName", "http://eauthentication.gsa.gov/federated/attribute", null, 0,
183                                 Arrays.asList(new String[]{hackFullName})));
184                 attributes.add(new SAMLAttribute("csid", "http://eauthentication.gsa.gov/federated/attribute", null, 0, Arrays
185                                 .asList(new String[]{csid})));
186                 try {
187                         SAMLStatement[] statements = {
188                                         new SAMLAuthenticationStatement(subject, authenticationMethod, authTime, request.getRemoteAddr(),
189                                                         null, null), new SAMLAttributeStatement((SAMLSubject) subject.clone(), attributes)};
190                         SAMLAssertion[] assertions = {new SAMLAssertion(issuer, new Date(System.currentTimeMillis()), new Date(
191                                         System.currentTimeMillis() + 300000), conditions, null, Arrays.asList(statements))};
192                         if (log.isDebugEnabled()) {
193                                 log.debug("Dumping generated SAML Assertions:"
194                                                 + System.getProperty("line.separator")
195                                                 + new String(new BASE64Decoder().decodeBuffer(new String(assertions[0].toBase64(), "ASCII")),
196                                                                 "UTF8"));
197                         }
198                         return null;
199                 } catch (CloneNotSupportedException e) { // TODO handle return null; } }
200
201                 }
202
203                 return null;
204
205         }
206
207         /*
208          * EAuthProfileHandler(String csid) throws ShibbolethConfigurationException { if (csid == null) { throw new
209          * ShibbolethConfigurationException( "EAuth support is enabled, but no (csid) parameter has been configured."); }
210          * this.csid = csid; }
211          */
212
213         /*
214          * String getRemoteProviderId(HttpServletRequest req) { return req.getParameter("aaid"); }
215          */
216
217         /*
218          * String getSAMLTargetParameter(HttpServletRequest request, RelyingParty relyingParty, Provider provider) {
219          * ProviderRole[] roles = provider.getRoles(); if (roles.length == 0) { log.error("Inappropriate metadata for EAuth
220          * provider."); return null; } for (int i = 0; roles.length > i; i++) { if (roles[i] instanceof SPProviderRole) {
221          * return ((SPProviderRole) roles[i]).getTarget(); } } log.error("Inappropriate metadata for EAuth provider.");
222          * return null; }
223          */
224
225         /*
226          * internet2.middleware.shibboleth.hs.HSRelyingParty) String getAcceptanceURL(HttpServletRequest request,
227          * HSRelyingParty relyingParty, Provider provider) throws InvalidClientDataException { //The EAuth profile requires
228          * metadata, since the assertion consumer is not supplied as a request parameter if (provider == null) {
229          * log.error("Unkown requesting service provider (" + relyingParty.getProviderId() + ")."); throw new
230          * InvalidClientDataException("Unkown requesting service provider."); } //Pull the consumer URL from the metadata
231          * ProviderRole[] roles = provider.getRoles(); if (roles.length == 0) { log.info("Inappropriate metadata for
232          * provider: no roles specified."); throw new InvalidClientDataException("Invalid metadata for requesting service
233          * provider."); } for (int i = 0; roles.length > i; i++) { if (roles[i] instanceof SPProviderRole) { Endpoint[]
234          * endpoints = ((SPProviderRole) roles[i]).getAssertionConsumerServiceURLs(); if (endpoints.length > 0) { return
235          * endpoints[0].getLocation(); } } } log.info("Inappropriate metadata for provider: no roles specified."); throw new
236          * InvalidClientDataException("Invalid metadata for requesting service provider."); }
237          */
238
239 }