Fixed problem where drop down list was submitting displayName attribute instead of...
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / wayf / WayfService.java
1 /* 
2  * The Shibboleth License, Version 1. 
3  * Copyright (c) 2002 
4  * University Corporation for Advanced Internet Development, Inc. 
5  * All rights reserved
6  * 
7  * 
8  * Redistribution and use in source and binary forms, with or without 
9  * modification, are permitted provided that the following conditions are met:
10  * 
11  * Redistributions of source code must retain the above copyright notice, this 
12  * list of conditions and the following disclaimer.
13  * 
14  * Redistributions in binary form must reproduce the above copyright notice, 
15  * this list of conditions and the following disclaimer in the documentation 
16  * and/or other materials provided with the distribution, if any, must include 
17  * the following acknowledgment: "This product includes software developed by 
18  * the University Corporation for Advanced Internet Development 
19  * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
20  * may appear in the software itself, if and wherever such third-party 
21  * acknowledgments normally appear.
22  * 
23  * Neither the name of Shibboleth nor the names of its contributors, nor 
24  * Internet2, nor the University Corporation for Advanced Internet Development, 
25  * Inc., nor UCAID may be used to endorse or promote products derived from this 
26  * software without specific prior written permission. For written permission, 
27  * please contact shibboleth@shibboleth.org
28  * 
29  * Products derived from this software may not be called Shibboleth, Internet2, 
30  * UCAID, or the University Corporation for Advanced Internet Development, nor 
31  * may Shibboleth appear in their name, without prior written permission of the 
32  * University Corporation for Advanced Internet Development.
33  * 
34  * 
35  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
36  * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
37  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
38  * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
39  * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
40  * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
41  * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
42  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
43  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
44  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
46  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
47  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48  */
49
50 package edu.internet2.middleware.shibboleth.wayf;
51
52 import java.io.IOException;
53 import java.io.InputStream;
54 import java.net.URLEncoder;
55
56 import javax.servlet.RequestDispatcher;
57 import javax.servlet.ServletException;
58 import javax.servlet.UnavailableException;
59 import javax.servlet.http.HttpServlet;
60 import javax.servlet.http.HttpServletRequest;
61 import javax.servlet.http.HttpServletResponse;
62
63 import org.apache.log4j.Logger;
64 import org.xml.sax.SAXException;
65
66 /**
67  * A servlet implementation of the Shibboleth WAYF service.  Allows a browser user to 
68  * select from among a group of origin sites.  User selection is optionally cached 
69  * and the user is forwarded to the HandleService appropriate to his selection.
70  *
71  * @author Walter Hoehn wassa&#064;columbia.edu
72  */
73 public class WayfService extends HttpServlet {
74
75         private String wayfConfigFileLocation;
76         private String siteConfigFileLocation;
77         private WayfConfig config;
78         private WayfOrigins originConfig;
79         private WayfCacheOptions wOptions = new WayfCacheOptions();
80         private static Logger log = Logger.getLogger(WayfService.class.getName());
81
82         /**
83          * @see GenericServlet#init()
84          */
85         public void init() throws ServletException {
86
87                 super.init();
88                 log.info("Initializing WAYF.");
89                 loadInitParams();
90                 log.info("Loading configuration from file.");
91                 configure();
92                 
93                 //Setup Cacheing options
94                 wOptions.setDomain(config.getCacheDomain());
95                 wOptions.setExpiration(config.getCacheExpiration());
96                 
97                 initViewConfig();
98                 log.info("WAYF initialization completed.");
99         }
100
101         /**
102          * Populates WayfConfig and WayfOrigins objects from file contents.
103          */
104         private void configure() throws UnavailableException {
105
106                 InputStream is = getServletContext().getResourceAsStream(wayfConfigFileLocation);
107                 WayfConfigDigester digester = new WayfConfigDigester(getServletContext());
108                 InputStream siteIs = getServletContext().getResourceAsStream(siteConfigFileLocation);
109                 OriginSitesDigester siteDigester = new OriginSitesDigester(getServletContext());
110
111                 try {
112                         digester.setValidating(true);
113                         config = (WayfConfig) digester.parse(is);
114
115                 } catch (SAXException se) {
116                         log.fatal("Error parsing WAYF configuration file.", se);
117                         throw new UnavailableException("Error parsing WAYF configuration file.");
118                 } catch (IOException ioe) {
119                         log.fatal("Error reading WAYF configuration file.", ioe);
120                         throw new UnavailableException("Error reading WAYF configuration file.");
121                 }
122
123                 try {
124                         siteDigester.setValidating(true);
125                         originConfig = (WayfOrigins) siteDigester.parse(siteIs);
126
127                 } catch (SAXException se) {
128                         log.fatal("Error parsing site file.", se);
129                         throw new UnavailableException("Error parsing site file.");
130                 } catch (IOException ioe) {
131                         log.fatal("Error reading site file.", ioe);
132                         throw new UnavailableException("Error reading site file.");
133                 }
134         }
135         
136         /**
137          * Setup application-wide beans for view
138          */
139         private void initViewConfig() {
140                 getServletContext().setAttribute("originsets", originConfig.getOriginSets());
141                 getServletContext().setAttribute("supportContact", config.getSupportContact());
142                 getServletContext().setAttribute("helpText", config.getHelpText());
143                 getServletContext().setAttribute("searchResultEmptyText", config.getSearchResultEmptyText());
144                 getServletContext().setAttribute("logoLocation", config.getLogoLocation());
145         }
146
147         /**
148          * Reads parameters from web.xml <init-param /> construct.
149          */
150         private void loadInitParams() {
151
152                 wayfConfigFileLocation = getServletConfig().getInitParameter("WAYFConfigFileLocation");
153                 if (wayfConfigFileLocation == null) {
154                         log.warn("No WAYFConfigFileLocation parameter found... using default location.");
155                         wayfConfigFileLocation = "/WEB-INF/conf/wayfconfig.xml";
156                 }
157                 siteConfigFileLocation = getServletConfig().getInitParameter("SiteConfigFileLocation");
158                 if (siteConfigFileLocation == null) {
159                         log.warn("No SiteonfigFileLocation parameter found... using default location.");
160                         siteConfigFileLocation = "/WEB-INF/conf/sites.xml";
161                 }
162
163         }
164
165         /**
166          * @see HttpServlet#doGet(HttpServletRequest, HttpServletResponse)
167          */
168         public void doGet(HttpServletRequest req, HttpServletResponse res) {
169
170                 log.info("Handling WAYF request.");
171                 //Tell the browser not to cache the WAYF page
172                 res.setHeader("Cache-Control", "no-cache");
173                 res.setHeader("Pragma", "no-cache");
174                 res.setDateHeader("Expires", 0);
175
176                 //Decide how to route the request based on query string
177                 String requestType = req.getParameter("action");
178                 if (requestType == null) {
179                         requestType = "lookup";
180                 }
181                 try {
182                         if (requestType.equals("deleteFromCache")) {
183                                 log.debug("Deleting saved HS from cache");
184                                 WayfCacheFactory.getInstance(config.getCacheType(), wOptions).deleteHsFromCache(req, res);
185                                 handleLookup(req, res);
186                         } else if (WayfCacheFactory.getInstance(config.getCacheType()).hasCachedHS(req)) {
187                                 forwardToHS(
188                                         req,
189                                         res,
190                                         WayfCacheFactory.getInstance(config.getCacheType()).getCachedHS(req));
191                         } else if (requestType.equals("search")) {
192                                 handleSearch(req, res);
193                         } else if (requestType.equals("selection")) {
194                                 handleSelection(req, res);
195                         } else {
196                                 handleLookup(req, res);
197                         }
198                 } catch (WayfException we) {
199                         handleError(req, res, we);
200                 }
201         }
202
203         /**
204          * Displays a WAYF selection page.
205          */
206         private void handleLookup(HttpServletRequest req, HttpServletResponse res) throws WayfException {
207
208                 if ((getSHIRE(req) == null) || (getTarget(req) == null)) {
209                         throw new WayfException("Invalid or missing data from SHIRE");
210                 }
211                 req.setAttribute("shire", getSHIRE(req));
212                 req.setAttribute("target", getTarget(req));
213                 req.setAttribute("encodedShire", URLEncoder.encode(getSHIRE(req)));
214                 req.setAttribute("encodedTarget", URLEncoder.encode(getTarget(req)));
215                 req.setAttribute("requestURL", req.getRequestURI().toString());
216
217                 log.debug("Displaying WAYF selection page.");
218                 RequestDispatcher rd = req.getRequestDispatcher("/wayf.jsp");
219                 try {
220                         rd.forward(req, res);
221                 } catch (IOException ioe) {
222                         throw new WayfException("Problem displaying WAYF UI." + ioe.toString());
223                 } catch (ServletException se) {
224                         throw new WayfException("Problem displaying WAYF UI." + se.toString());
225                 }
226         }
227         
228         /**
229          * Looks for origin sites that match search terms supplied by the user
230          */
231         private void handleSearch(HttpServletRequest req, HttpServletResponse res) throws WayfException {
232
233                 if (req.getParameter("string") != null) {
234                         Origin[] origins = originConfig.seachForMatchingOrigins(req.getParameter("string"), config);
235                         if (origins.length != 0) {
236                                 req.setAttribute("searchresults", origins);
237                         } else {
238                                 req.setAttribute("searchResultsEmpty", "true");
239                         }
240                 }
241                 handleLookup(req, res);
242         }
243
244         /**
245          * Registers a user's HS selection and forwards appropriately
246          */
247         private void handleSelection(HttpServletRequest req, HttpServletResponse res) throws WayfException {
248
249                 log.debug("Processing handle selection: " + req.getParameter("origin"));
250                 String handleService = originConfig.lookupHSbyName(req.getParameter("origin"));
251                 if (handleService == null) {
252                         handleLookup(req, res);
253                 } else {
254                         WayfCacheFactory.getInstance(config.getCacheType(), wOptions).addHsToCache(handleService, req, res);
255                         forwardToHS(req, res, handleService);
256                 }
257
258         }
259
260         /**
261          * Uses an HTTP Status 307 redirect to forward the user the HS.
262          * @param handleService The URL of the Shiboleth HS.
263          */
264         private void forwardToHS(HttpServletRequest req, HttpServletResponse res, String handleService)
265                 throws WayfException {
266
267                 String shire = getSHIRE(req);
268                 String target = getTarget(req);
269                 log.info("Redirecting to selected Handle Service");
270                 try {
271                         res.sendRedirect(
272                                 handleService
273                                         + "?target="
274                                         + URLEncoder.encode(target)
275                                         + "&shire="
276                                         + URLEncoder.encode(shire));
277                 } catch (IOException ioe) {
278                         throw new WayfException("Error forwarding to HS: " + ioe.toString());
279                 }
280
281         }
282
283         /**
284          * Handles all "recoverable" errors in WAYF processing by logging the error
285          * and forwarding the user to an appropriate error page.
286          * @param we The WayfException respective to the error being handled
287          */
288         private void handleError(HttpServletRequest req, HttpServletResponse res, WayfException we) {
289
290                 log.error("WAYF Failure: " + we.toString());
291                 log.debug("Displaying WAYF error page.");
292                 req.setAttribute("errorText", we.toString());
293                 req.setAttribute("requestURL", req.getRequestURI().toString());
294                 RequestDispatcher rd = req.getRequestDispatcher("/wayferror.jsp");
295
296                 try {
297                         rd.forward(req, res);
298                 } catch (IOException ioe) {
299                         log.error("Problem trying to display WAYF error page: " + ioe.toString());
300                 } catch (ServletException se) {
301                         log.error("Problem trying to display WAYF error page: " + se.toString());
302                 }
303         }
304         /**
305          * Retrieves the SHIRE from the request.
306          * @throws WayfException If the request does not contain a shire parameter.
307          */
308         private String getSHIRE(HttpServletRequest req) throws WayfException {
309
310                 String shire = (String) req.getAttribute("shire");
311                 if (req.getParameter("shire") != null) {
312                         shire = req.getParameter("shire");
313                 }
314                 if (shire == null) {
315                         throw new WayfException("Invalid data from SHIRE: No acceptance URL received.");
316                 }
317                 return shire;
318         }
319         /**
320          * Retrieves the user's target URL from the request.
321          * @throws WayfException If the request does not contain a target parameter
322          */
323         private String getTarget(HttpServletRequest req) throws WayfException {
324
325                 String target = (String) req.getAttribute("target");
326                 if (req.getParameter("target") != null) {
327                         target = req.getParameter("target");
328                 }
329                 if (target == null) {
330                         throw new WayfException("Invalid data from SHIRE: No target URL received.");
331                 }
332                 return target;
333         }
334
335 }