4655ed77d0b14aa7a5dbd29505789c1175d88d30
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / wayf / WayfService.java
1 package edu.internet2.middleware.shibboleth.wayf;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.net.URLEncoder;
6
7 import javax.servlet.RequestDispatcher;
8 import javax.servlet.ServletException;
9 import javax.servlet.UnavailableException;
10 import javax.servlet.http.HttpServlet;
11 import javax.servlet.http.HttpServletRequest;
12 import javax.servlet.http.HttpServletResponse;
13
14 import org.apache.log4j.Logger;
15 import org.xml.sax.SAXException;
16
17 /**
18  * A servlet implementation of the Shibboleth WAYF service.  Allows a browser user to 
19  * select from among a group of origin sites.  User selection is optionally cached 
20  * and the user is forwarded to the HandleService appropriate to his selection.
21  *
22  * @author Walter Hoehn wassa@columbia.edu
23  */
24 public class WayfService extends HttpServlet {
25
26         private String wayfConfigFileLocation;
27         private String siteConfigFileLocation;
28         private WayfConfig config;
29         private WayfOrigins originConfig;
30         private static Logger log = Logger.getLogger(WayfService.class.getName());
31
32         /**
33          * @see GenericServlet#init()
34          */
35         public void init() throws ServletException {
36
37                 super.init();
38                 log.info("Initializing WAYF.");
39                 loadInitParams();
40                 log.info("Loading configuration from file.");
41                 configure();
42                 initViewConfig();
43                 log.info("WAYF initialization completed.");
44         }
45
46         /**
47          * Populates WayfConfig and WayfOrigins objects from file contents.
48          */
49         private void configure() throws UnavailableException {
50
51                 InputStream is = getServletContext().getResourceAsStream(wayfConfigFileLocation);
52                 WayfConfigDigester digester = new WayfConfigDigester(getServletContext());
53                 InputStream siteIs = getServletContext().getResourceAsStream(siteConfigFileLocation);
54                 OriginSitesDigester siteDigester = new OriginSitesDigester(getServletContext());
55
56                 try {
57                         digester.setValidating(true);
58                         config = (WayfConfig) digester.parse(is);
59
60                 } catch (SAXException se) {
61                         log.fatal("Error parsing WAYF configuration file.", se);
62                         throw new UnavailableException("Error parsing WAYF configuration file.");
63                 } catch (IOException ioe) {
64                         log.fatal("Error reading WAYF configuration file.", ioe);
65                         throw new UnavailableException("Error reading WAYF configuration file.");
66                 }
67
68                 try {
69                         siteDigester.setValidating(true);
70                         originConfig = (WayfOrigins) siteDigester.parse(siteIs);
71
72                 } catch (SAXException se) {
73                         log.fatal("Error parsing site file.", se);
74                         throw new UnavailableException("Error parsing site file.");
75                 } catch (IOException ioe) {
76                         log.fatal("Error reading site file.", ioe);
77                         throw new UnavailableException("Error reading site file.");
78                 }
79         }
80         
81         /**
82          * Setup application-wide beans for view
83          */
84         private void initViewConfig() {
85                 getServletContext().setAttribute("originsets", originConfig.getOriginSets());
86                 getServletContext().setAttribute("supportContact", config.getSupportContact());
87                 getServletContext().setAttribute("helpText", config.getHelpText());
88                 getServletContext().setAttribute("searchResultEmptyText", config.getSearchResultEmptyText());
89                 getServletContext().setAttribute("logoLocation", config.getLogoLocation());
90         }
91
92         /**
93          * Reads parameters from web.xml <init-param /> construct.
94          */
95         private void loadInitParams() {
96
97                 wayfConfigFileLocation = getServletConfig().getInitParameter("WAYFConfigFileLocation");
98                 if (wayfConfigFileLocation == null) {
99                         log.warn("No WAYFConfigFileLocation parameter found... using default location.");
100                         wayfConfigFileLocation = "/WEB-INF/conf/wayfconfig.xml";
101                 }
102                 siteConfigFileLocation = getServletConfig().getInitParameter("SiteConfigFileLocation");
103                 if (siteConfigFileLocation == null) {
104                         log.warn("No SiteonfigFileLocation parameter found... using default location.");
105                         siteConfigFileLocation = "/WEB-INF/conf/sites.xml";
106                 }
107
108         }
109
110         /**
111          * @see HttpServlet#doGet(HttpServletRequest, HttpServletResponse)
112          */
113         public void doGet(HttpServletRequest req, HttpServletResponse res) {
114
115                 log.info("Handling WAYF request.");
116                 //Tell the browser not to cache the WAYF page
117                 res.setHeader("Cache-Control", "no-cache");
118                 res.setHeader("Pragma", "no-cache");
119                 res.setDateHeader("Expires", 0);
120
121                 //Decide how to route the request based on query string
122                 String requestType = req.getParameter("action");
123                 if (requestType == null) {
124                         requestType = "lookup";
125                 }
126                 try {
127                         if (requestType.equals("deleteFromCache")) {
128                                 log.debug("Deleting saved HS from cache");
129                                 WayfCacheFactory.getInstance(config.getCacheType()).deleteHsFromCache(req, res);
130                                 handleLookup(req, res);
131                         } else if (WayfCacheFactory.getInstance(config.getCacheType()).hasCachedHS(req)) {
132                                 forwardToHS(
133                                         req,
134                                         res,
135                                         WayfCacheFactory.getInstance(config.getCacheType()).getCachedHS(req));
136                         } else if (requestType.equals("search")) {
137                                 handleSearch(req, res);
138                         } else if (requestType.equals("selection")) {
139                                 handleSelection(req, res);
140                         } else {
141                                 handleLookup(req, res);
142                         }
143                 } catch (WayfException we) {
144                         handleError(req, res, we);
145                 }
146         }
147
148         /**
149          * Displays a WAYF selection page.
150          */
151         private void handleLookup(HttpServletRequest req, HttpServletResponse res) throws WayfException {
152
153                 if ((getSHIRE(req) == null) || (getTarget(req) == null)) {
154                         throw new WayfException("Invalid or missing data from SHIRE");
155                 }
156                 req.setAttribute("shire", getSHIRE(req));
157                 req.setAttribute("target", getTarget(req));
158                 req.setAttribute("encodedShire", URLEncoder.encode(getSHIRE(req)));
159                 req.setAttribute("encodedTarget", URLEncoder.encode(getTarget(req)));
160
161                 log.debug("Displaying WAYF selection page.");
162                 RequestDispatcher rd = req.getRequestDispatcher("/wayf.jsp");
163                 try {
164                         rd.forward(req, res);
165                 } catch (IOException ioe) {
166                         throw new WayfException("Problem displaying WAYF UI." + ioe.toString());
167                 } catch (ServletException se) {
168                         throw new WayfException("Problem displaying WAYF UI." + se.toString());
169                 }
170         }
171         
172         /**
173          * Looks for origin sites that match search terms supplied by the user
174          */
175         private void handleSearch(HttpServletRequest req, HttpServletResponse res) throws WayfException {
176
177                 if (req.getParameter("string") != null) {
178                         Origin[] origins = originConfig.seachForMatchingOrigins(req.getParameter("string"), config);
179                         if (origins.length != 0) {
180                                 req.setAttribute("searchresults", origins);
181                         } else {
182                                 req.setAttribute("searchResultsEmpty", "true");
183                         }
184                 }
185                 handleLookup(req, res);
186         }
187
188         /**
189          * Registers a user's HS selection and forwards appropriately
190          */
191         private void handleSelection(HttpServletRequest req, HttpServletResponse res) throws WayfException {
192
193                 String handleService = originConfig.lookupHSbyName(req.getParameter("origin"));
194                 if (handleService == null) {
195                         handleLookup(req, res);
196                 } else {
197                         WayfCacheFactory.getInstance(config.getCacheType()).addHsToCache(handleService, req, res);
198                         forwardToHS(req, res, handleService);
199                 }
200
201         }
202
203         /**
204          * Uses an HTTP Status 307 redirect to forward the user the HS.
205          * @param handleService The URL of the Shiboleth HS.
206          */
207         private void forwardToHS(HttpServletRequest req, HttpServletResponse res, String handleService)
208                 throws WayfException {
209
210                 String shire = getSHIRE(req);
211                 String target = getTarget(req);
212                 log.info("Redirecting to selected Handle Service");
213                 try {
214                         res.sendRedirect(
215                                 handleService
216                                         + "?target="
217                                         + URLEncoder.encode(target)
218                                         + "&shire="
219                                         + URLEncoder.encode(shire));
220                 } catch (IOException ioe) {
221                         throw new WayfException("Error forwarding to HS: " + ioe.toString());
222                 }
223
224         }
225
226         /**
227          * Handles all "recoverable" errors in WAYF processing by logging the error
228          * and forwarding the user to an appropriate error page.
229          * @param we The WayfException respective to the error being handled
230          */
231         private void handleError(HttpServletRequest req, HttpServletResponse res, WayfException we) {
232
233                 log.error("WAYF Failure: " + we.toString());
234                 log.debug("Displaying WAYF error page.");
235                 req.setAttribute("errorText", we.toString());
236                 req.setAttribute("requestURL", req.getRequestURI().toString());
237                 RequestDispatcher rd = req.getRequestDispatcher("/wayferror.jsp");
238
239                 try {
240                         rd.forward(req, res);
241                 } catch (IOException ioe) {
242                         log.error("Problem trying to display WAYF error page: " + ioe.toString());
243                 } catch (ServletException se) {
244                         log.error("Problem trying to display WAYF error page: " + se.toString());
245                 }
246         }
247         /**
248          * Retrieves the SHIRE from the request.
249          * @throws WayfException If the request does not contain a shire parameter.
250          */
251         private String getSHIRE(HttpServletRequest req) throws WayfException {
252
253                 String shire = (String) req.getAttribute("shire");
254                 if (req.getParameter("shire") != null) {
255                         shire = req.getParameter("shire");
256                 }
257                 if (shire == null) {
258                         throw new WayfException("Invalid data from SHIRE: No acceptance URL received.");
259                 }
260                 return shire;
261         }
262         /**
263          * Retrieves the user's target URL from the request.
264          * @throws WayfException If the request does not contain a target parameter
265          */
266         private String getTarget(HttpServletRequest req) throws WayfException {
267
268                 String target = (String) req.getAttribute("target");
269                 if (req.getParameter("target") != null) {
270                         target = req.getParameter("target");
271                 }
272                 if (target == null) {
273                         throw new WayfException("Invalid data from SHIRE: No target URL received.");
274                 }
275                 return target;
276         }
277
278 }