1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package no.feide.moria.servlet;
19
20 import java.io.IOException;
21 import java.util.HashMap;
22 import java.util.Iterator;
23 import java.util.Map;
24 import java.util.Properties;
25 import java.util.ResourceBundle;
26 import java.util.TreeMap;
27
28 import javax.servlet.RequestDispatcher;
29 import javax.servlet.ServletException;
30 import javax.servlet.http.Cookie;
31 import javax.servlet.http.HttpServlet;
32 import javax.servlet.http.HttpServletRequest;
33 import javax.servlet.http.HttpServletResponse;
34
35 import no.feide.moria.authorization.UnknownServicePrincipalException;
36 import no.feide.moria.controller.AuthenticationException;
37 import no.feide.moria.controller.AuthorizationException;
38 import no.feide.moria.controller.DirectoryUnavailableException;
39 import no.feide.moria.controller.IllegalInputException;
40 import no.feide.moria.controller.InoperableStateException;
41 import no.feide.moria.controller.MoriaController;
42 import no.feide.moria.controller.MoriaControllerException;
43 import no.feide.moria.controller.UnknownTicketException;
44 import no.feide.moria.log.MessageLogger;
45
46 /***
47 * Use this servlet to bootstrap the system. Set
48 * <load-on-startup>1</load-on-startup> in web.xml.
49 * @author Lars Preben S. Arnesen <lars.preben.arnesen@conduct.no>
50 * @version $Revision: 1.51 $
51 */
52 public final class LoginServlet
53 extends MoriaServlet {
54
55 /*** Used for logging. */
56 private final MessageLogger log = new MessageLogger(LoginServlet.class);
57
58 /***
59 * List of parameters required by <code>LoginServlet</code>. <br>
60 * <br>
61 * Current required parameters are:
62 * <ul>
63 * <li><code>RequestUtil.PROP_COOKIE_SSO</code>
64 * <li><code>RequestUtil.PROP_COOKIE_SSO_TTL</code>
65 * <li><code>RequestUtil.PROP_COOKIE_DENYSSO</code>
66 * <li><code>RequestUtil.PROP_COOKIE_DENYSSO_TTL</code>
67 * <li><code>RequestUtil.PROP_COOKIE_LANG</code>
68 * <li><code>RequestUtil.PROP_COOKIE_LANG_TTL</code>
69 * <li><code>RequestUtil.PROP_COOKIE_ORG</code>
70 * <li><code>RequestUtil.PROP_COOKIE_ORG_TTL</code>
71 * <li><code>RequestUtil.PROP_LOGIN_TICKET_PARAM</code>
72 * <li><code>RequestUtil.PROP_LOGIN_DEFAULT_LANGUAGE</code>
73 * <li><code>RequestUtil.PROP_LOGIN_URL_PREFIX</code>
74 * <li><code>RequestUtil.FAQ_LINK</code>
75 * <li><code>RequestUtil.PROP_FAQ_STATUS</code>
76 * <li><code>RequestUtil.PROP_FAQ_OWNER</code>
77 * </ul>
78 * @see RequestUtil#PROP_COOKIE_SSO
79 * @see RequestUtil#PROP_COOKIE_SSO_TTL
80 * @see RequestUtil#PROP_COOKIE_DENYSSO
81 * @see RequestUtil#PROP_COOKIE_DENYSSO_TTL
82 * @see RequestUtil#PROP_COOKIE_LANG
83 * @see RequestUtil#PROP_COOKIE_LANG_TTL
84 * @see RequestUtil#PROP_COOKIE_ORG
85 * @see RequestUtil#PROP_COOKIE_ORG_TTL
86 * @see RequestUtil#PROP_LOGIN_TICKET_PARAM
87 * @see RequestUtil#PROP_LOGIN_DEFAULT_LANGUAGE
88 * @see RequestUtil#PROP_LOGIN_URL_PREFIX
89 * @see RequestUtil#PROP_FAQ_STATUS
90 */
91 private static final String[] REQUIRED_PARAMETERS = {
92 RequestUtil.PROP_COOKIE_DENYSSO,
93 RequestUtil.PROP_LOGIN_TICKET_PARAM,
94 RequestUtil.PROP_COOKIE_SSO,
95 RequestUtil.PROP_COOKIE_LANG,
96 RequestUtil.PROP_LOGIN_DEFAULT_LANGUAGE,
97 RequestUtil.PROP_COOKIE_DENYSSO_TTL,
98 RequestUtil.PROP_COOKIE_ORG,
99 RequestUtil.PROP_COOKIE_ORG_TTL,
100 RequestUtil.PROP_COOKIE_SSO_TTL,
101 RequestUtil.PROP_COOKIE_LANG_TTL,
102 RequestUtil.PROP_LOGIN_URL_PREFIX,
103 RequestUtil.FAQ_LINK,
104 RequestUtil.PROP_FAQ_STATUS};
105
106 /***
107 *
108 * @return The required parameters for this servlet.
109 */
110 public static String[] getRequiredParameters() {
111 return REQUIRED_PARAMETERS;
112 }
113
114 /***
115 * Handles the GET request. The GET request should contain a login ticket as
116 * parameter. An SSO ticket can also be presented by the user's web browser
117 * (in form of a cookie). The method will try to perform an SSO
118 * authentication if the conditions for this is met, else it will present
119 * the login page to the user.
120 * @param request
121 * The HTTP request object.
122 * @param response
123 * The HTTP response object.
124 * @throws IOException
125 * Required by interface.
126 * @throws ServletException
127 * Required by interface.
128 */
129
130
131
132 public void doGet(final HttpServletRequest request,
133 final HttpServletResponse response)
134 throws IOException, ServletException {
135
136
137 final Properties config = getConfig();
138
139
140 final String denySSOChoice = RequestUtil.getCookieValue(config.getProperty(RequestUtil.PROP_COOKIE_DENYSSO), request.getCookies());
141 final boolean denySSO;
142 if (denySSOChoice == null || denySSOChoice.equals("false") || denySSOChoice.equals("")) {
143
144
145 request.setAttribute(RequestUtil.ATTR_SELECTED_DENYSSO, new Boolean(false));
146 denySSO = false;
147
148 } else {
149
150
151 request.setAttribute(RequestUtil.ATTR_SELECTED_DENYSSO, new Boolean(true));
152 denySSO = true;
153
154 }
155
156
157 if (!denySSO) {
158 final String serviceTicket;
159 try {
160
161
162 final String ssoTicketId = RequestUtil.getCookieValue(config.getProperty(RequestUtil.PROP_COOKIE_SSO), request.getCookies());
163
164 if (ssoTicketId != null) {
165 serviceTicket = MoriaController.attemptSingleSignOn(request.getParameter(config.getProperty(RequestUtil.PROP_LOGIN_TICKET_PARAM)), ssoTicketId);
166
167
168 response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
169 response.setHeader("Location", MoriaController.getRedirectURL(serviceTicket));
170
171
172 return;
173 }
174 } catch (UnknownTicketException e) {
175
176 } catch (MoriaControllerException e) {
177
178
179 } catch (UnknownServicePrincipalException e) {
180
181 }
182 }
183
184
185 showLoginPage(request, response, null);
186 }
187
188
189 /***
190 * Handles the POST request. The POST request indicates that the user is
191 * trying to authenticate. If the authentication is successful, the user is
192 * redirected back to the originating web service, else the user is
193 * presented with an error message.
194 * @param request
195 * The HTTP request.
196 * @param response
197 * The HTTP response.
198 * @throws IOException
199 * Required by interface.
200 * @throws ServletException
201 * Required by interface.
202 */
203 public void doPost(final HttpServletRequest request,
204 final HttpServletResponse response)
205 throws IOException, ServletException {
206
207
208 final Properties config = getConfig();
209
210
211 final String loginTicketId = request.getParameter(config.getProperty(RequestUtil.PROP_LOGIN_TICKET_PARAM));
212 final String ssoTicketId = request.getParameter(config.getProperty(RequestUtil.PROP_COOKIE_SSO));
213
214
215 final String username = request.getParameter(RequestUtil.PARAM_USERNAME);
216 final String password = request.getParameter(RequestUtil.PARAM_PASSWORD);
217 String org = request.getParameter(RequestUtil.PARAM_ORG);
218
219
220 String denySSOStr = request.getParameter(RequestUtil.PARAM_DENYSSO);
221 final boolean denySSO = (denySSOStr != null && denySSOStr.equals("true"));
222 if (denySSO)
223 denySSOStr = "true";
224 else
225 denySSOStr = "false";
226
227
228 final Cookie denySSOCookie = RequestUtil.createCookie((String) config.get(RequestUtil.PROP_COOKIE_DENYSSO), denySSOStr, new Integer((String) config.get(RequestUtil.PROP_COOKIE_DENYSSO_TTL)).intValue());
229 response.addCookie(denySSOCookie);
230 request.setAttribute(RequestUtil.ATTR_SELECTED_DENYSSO, new Boolean(denySSO));
231
232
233 if (username.indexOf("@") != -1)
234 org = username.substring(username.indexOf("@") + 1, username.length());
235 if (org == null || org.equals("") || org.equals("null")) {
236 showLoginPage(request, response, RequestUtil.ERROR_NO_ORG);
237 return;
238 } else if (!RequestUtil.parseConfig(getConfig(), RequestUtil.PROP_ORG, (String) config.get(RequestUtil.PROP_LOGIN_DEFAULT_LANGUAGE)).containsValue(org)) {
239 showLoginPage(request, response, RequestUtil.ERROR_INVALID_ORG);
240 return;
241 }
242
243
244 final Cookie orgCookie = RequestUtil.createCookie((String) config.get(RequestUtil.PROP_COOKIE_ORG), request.getParameter(RequestUtil.PARAM_ORG), new Integer((String) config.get(RequestUtil.PROP_COOKIE_ORG_TTL)).intValue());
245 response.addCookie(orgCookie);
246
247
248 final Map tickets;
249 final String redirectURL;
250 try {
251 tickets = MoriaController.attemptLogin(loginTicketId, ssoTicketId, username + "@" + org, password, denySSO);
252 redirectURL = MoriaController.getRedirectURL((String) tickets.get(MoriaController.SERVICE_TICKET));
253 } catch (AuthenticationException e) {
254 showLoginPage(request, response, RequestUtil.ERROR_AUTHENTICATION_FAILED);
255 return;
256 } catch (UnknownTicketException e) {
257 showLoginPage(request, response, RequestUtil.ERROR_UNKNOWN_TICKET);
258 return;
259 } catch (DirectoryUnavailableException e) {
260 showLoginPage(request, response, RequestUtil.ERROR_DIRECTORY_DOWN);
261 return;
262 } catch (InoperableStateException e) {
263 showLoginPage(request, response, RequestUtil.ERROR_MORIA_DOWN);
264 return;
265 } catch (IllegalInputException e) {
266 showLoginPage(request, response, RequestUtil.ERROR_NO_CREDENTIALS);
267 return;
268 } catch (AuthorizationException e) {
269 showLoginPage(request, response, RequestUtil.ERROR_AUTHORIZATION_FAILED);
270 return;
271 }
272
273
274
275
276 if (!denySSO) {
277 final Cookie ssoTicketCookie = RequestUtil.createCookie((String) config.get(RequestUtil.PROP_COOKIE_SSO), (String) tickets.get(MoriaController.SSO_TICKET), new Integer((String) config.get(RequestUtil.PROP_COOKIE_SSO_TTL)).intValue());
278 response.addCookie(ssoTicketCookie);
279 }
280
281
282 response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
283 response.setHeader("Location", redirectURL);
284 }
285
286
287 /***
288 * Displays the login page. The method fills the request object with values
289 * and then passes the request to the JSP.
290 * @param request
291 * The HTTP request.
292 * @param response
293 * The HTTP response.
294 * @param errorType
295 * The type of the error set by the caller.
296 * @throws IOException
297 * When the JSP throws this exception.
298 * @throws ServletException
299 * When the JSP throws this exception.
300 */
301
302 private void showLoginPage(final HttpServletRequest request,
303 final HttpServletResponse response,
304 String errorType)
305 throws IOException, ServletException {
306
307
308
309
310
311
312 final Properties config = getConfig();
313
314
315 final String loginTicket = request.getParameter(config.getProperty(RequestUtil.PROP_LOGIN_TICKET_PARAM));
316
317
318 request.setAttribute(RequestUtil.ATTR_BASE_URL, config.getProperty(RequestUtil.PROP_LOGIN_URL_PREFIX) + "?" + config.getProperty(RequestUtil.PROP_LOGIN_TICKET_PARAM) + "=" + loginTicket);
319
320
321 request.setAttribute("faqlink", config.get(RequestUtil.FAQ_LINK));
322
323
324 if (request.getParameter(RequestUtil.PARAM_LANG) != null)
325 response.addCookie(RequestUtil.createCookie((String) config.get(RequestUtil.PROP_COOKIE_LANG), request.getParameter(RequestUtil.PARAM_LANG), new Integer((String) config.get(RequestUtil.PROP_COOKIE_LANG_TTL)).intValue()));
326
327
328
329
330
331
332 HashMap serviceProperties = null;
333 try {
334
335 serviceProperties = MoriaController.getServiceProperties(loginTicket);
336 request.setAttribute(RequestUtil.ATTR_SEC_LEVEL, "" + MoriaController.getSecLevel(loginTicket));
337
338
339
340 } catch (UnknownTicketException e) {
341 errorType = RequestUtil.ERROR_UNKNOWN_TICKET;
342 } catch (IllegalInputException e) {
343 errorType = RequestUtil.ERROR_UNKNOWN_TICKET;
344 } catch (InoperableStateException e) {
345 errorType = RequestUtil.ERROR_MORIA_DOWN;
346 }
347
348
349 if (serviceProperties != null) {
350 request.setAttribute(RequestUtil.ATTR_CLIENT_NAME, serviceProperties.get(RequestUtil.CONFIG_DISPLAY_NAME));
351 request.setAttribute(RequestUtil.ATTR_CLIENT_URL, serviceProperties.get(RequestUtil.CONFIG_URL));
352 }
353
354
355
356
357
358
359 final Cookie[] userCookies = request.getCookies();
360
361
362 String cookieLanguage = null;
363 if (userCookies != null)
364 cookieLanguage = RequestUtil.getCookieValue((String) config.get(RequestUtil.PROP_COOKIE_LANG), userCookies);
365
366
367 String serviceLanguage = null;
368 if (serviceProperties != null)
369 serviceLanguage = (String) serviceProperties.get(RequestUtil.CONFIG_LANG);
370
371
372 final ResourceBundle bundle = RequestUtil.getBundle(RequestUtil.BUNDLE_LOGIN, request.getParameter(RequestUtil.PARAM_LANG), cookieLanguage, serviceLanguage, request.getHeader("Accept-Language"), (String) config.get(RequestUtil.PROP_LOGIN_DEFAULT_LANGUAGE));
373 request.setAttribute(RequestUtil.ATTR_BUNDLE, bundle);
374 request.setAttribute(RequestUtil.ATTR_LANGUAGES, RequestUtil.parseConfig(getConfig(), RequestUtil.PROP_LANGUAGE, RequestUtil.PROP_COMMON));
375 request.setAttribute(RequestUtil.ATTR_SELECTED_LANG, bundle.getLocale());
376
377
378
379
380
381
382 String servicePrincipal = null;
383 if (serviceProperties != null)
384 servicePrincipal = (String) serviceProperties.get(RequestUtil.CONFIG_SERVICE_PRINCIPAL);
385
386
387 TreeMap allowedOrganizations = null;
388 if (servicePrincipal != null) {
389
390
391 allowedOrganizations = RequestUtil.parseConfig(getConfig(), RequestUtil.PROP_ORG, bundle.getLocale().getLanguage());
392 Iterator i = allowedOrganizations.values().iterator();
393 while (i.hasNext()) {
394 try {
395 if (!MoriaController.isOrganizationAllowedForService(servicePrincipal, (String) i.next()))
396 i.remove();
397 } catch (UnknownServicePrincipalException e) {
398
399
400 log.logWarn("Unexpected service principal: " + servicePrincipal);
401 }
402 }
403 }
404 request.setAttribute(RequestUtil.ATTR_ORGANIZATIONS, allowedOrganizations);
405
406
407 try {
408 String [] attrs = MoriaController.getRequestedAttributes(loginTicket, servicePrincipal);
409 request.setAttribute(RequestUtil.ATTR_REQUESTED_ATTRIBUTES, attrs);
410 } catch (Exception e) {
411 log.logWarn("Unable to get the requested attributes: " + servicePrincipal);
412 }
413 String showAttrs = request.getParameter(RequestUtil.PARAM_SHOW_ATTRS);
414 String show = showAttrs != null ? showAttrs : "false";
415 request.setAttribute(RequestUtil.PARAM_SHOW_ATTRS, show);
416
417
418 String defaultOrganization = null;
419 if (serviceProperties != null)
420 defaultOrganization = (String) serviceProperties.get(RequestUtil.CONFIG_HOME);
421
422
423 String value = request.getParameter(RequestUtil.PARAM_ORG);
424 if (value != null)
425 defaultOrganization = value;
426
427
428 value = RequestUtil.getCookieValue((String) config.get(RequestUtil.PROP_COOKIE_ORG), userCookies);
429 if (value != null)
430 defaultOrganization = value;
431
432
433 request.setAttribute(RequestUtil.ATTR_SELECTED_ORG, defaultOrganization);
434
435
436
437
438
439
440 request.setAttribute(RequestUtil.ATTR_ERROR_TYPE, errorType);
441
442
443 final RequestDispatcher rd = getServletContext().getNamedDispatcher("Login.JSP");
444 rd.forward(request, response);
445 }
446
447
448 /***
449 * Gets the current configuration from the context. The configuration is
450 * expected to be set by the controller before requests are sent to this
451 * servlet.
452 * @return The current configuration, as read from the servlet context.
453 * @throws IllegalStateException
454 * If the configuration has not been set, or if any required
455 * configuration parameters are missing.
456 * @see #REQUIRED_PARAMETERS
457 */
458 private Properties getConfig() {
459 return getServletConfig(getRequiredParameters(), log);
460 }
461 }