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