1 package com.atlassian.seraph.auth;
2
3 import static com.atlassian.seraph.auth.LoginReason.AUTHENTICATED_FAILED;
4 import static com.atlassian.seraph.auth.LoginReason.AUTHENTICATION_DENIED;
5 import static com.atlassian.seraph.auth.LoginReason.AUTHORISATION_FAILED;
6 import static com.atlassian.seraph.auth.LoginReason.OK;
7 import com.atlassian.seraph.config.SecurityConfig;
8 import com.atlassian.seraph.config.SecurityConfigFactory;
9 import com.atlassian.seraph.cookie.CookieFactory;
10 import com.atlassian.seraph.cookie.CookieHandler;
11 import com.atlassian.seraph.elevatedsecurity.ElevatedSecurityGuard;
12 import com.atlassian.seraph.interceptor.LogoutInterceptor;
13 import com.atlassian.seraph.util.RedirectUtils;
14 import com.opensymphony.user.EntityNotFoundException;
15 import com.opensymphony.user.User;
16 import com.opensymphony.user.UserManager;
17 import com.opensymphony.user.provider.ejb.util.Base64;
18 import org.apache.log4j.Logger;
19
20 import java.io.IOException;
21 import java.security.Principal;
22 import java.util.List;
23 import java.util.Map;
24 import javax.servlet.http.Cookie;
25 import javax.servlet.http.HttpServletRequest;
26 import javax.servlet.http.HttpServletResponse;
27 import javax.servlet.http.HttpSession;
28
29
30
31
32
33
34 public class DefaultAuthenticator extends AbstractAuthenticator
35 {
36
37
38
39 public static final String LOGGED_IN_KEY = "seraph_defaultauthenticator_user";
40
41
42
43
44
45 public static final String LOGGED_OUT_KEY = "seraph_defaultauthenticator_logged_out_user";
46
47 private static final Logger log = Logger.getLogger(DefaultAuthenticator.class);
48
49
50
51 private String loginCookieKey;
52 private String authType;
53 private int autoLoginCookieAge;
54 private String loginCookiePath;
55
56 @Override
57 public void init(final Map<String, String> params, final SecurityConfig config)
58 {
59 if (log.isDebugEnabled())
60 {
61 log.debug(this.getClass().getName() + " $Revision: 39327 $ initializing");
62 }
63 super.init(params, config);
64 loginCookieKey = config.getLoginCookieKey();
65 authType = config.getAuthType();
66 autoLoginCookieAge = config.getAutoLoginCookieAge();
67 loginCookiePath = config.getLoginCookiePath();
68 }
69
70
71
72
73 @Deprecated
74 @Override
75 public boolean isUserInRole(final HttpServletRequest request, final String role)
76 {
77 return getRoleMapper().hasRole(getUser(request), request, role);
78 }
79
80
81
82
83
84
85
86
87 @Override
88 public boolean login(final HttpServletRequest request, final HttpServletResponse response, final String username, final String password, final boolean cookie)
89 throws AuthenticatorException
90 {
91 final String METHOD = "login : ";
92 final boolean dbg = log.isDebugEnabled();
93 final Principal user = getUser(username);
94 final CookieHandler cookieHandler = CookieFactory.getCookieHandler();
95
96
97 if (user == null)
98 {
99 log.info(METHOD + "'" + username + "' does not exist and cannot be authenticated.");
100 }
101 else
102 {
103 final boolean authenticated = authenticate(user, password);
104 if (dbg)
105 {
106 log.debug(METHOD + "'" + username + "' has " + (authenticated ? "been" : "not been") + " authenticated");
107 }
108 if (authenticated)
109 {
110 final HttpSession httpSession = request.getSession();
111
112 httpSession.removeAttribute(LOGGED_IN_KEY);
113 httpSession.setAttribute(LOGGED_OUT_KEY, null);
114
115 final boolean canLogin = getRoleMapper().canLogin(user, request);
116 if (dbg)
117 {
118 log.debug(METHOD + "'" + username + "' " + (canLogin ? "can" : "CANT") + " login according to the RoleMapper");
119 }
120 if (canLogin)
121 {
122 httpSession.setAttribute(LOGGED_IN_KEY, user);
123 if (cookie && (response != null))
124 {
125 cookieHandler.setCookie(request, response, getLoginCookieKey(), encodeCookie(username, password), autoLoginCookieAge, getCookiePath(request));
126 }
127 return true;
128 }
129 AUTHORISATION_FAILED.stampRequestResponse(request, response);
130 }
131 else
132 {
133 log.info(METHOD + "'" + username + "' could not be authenticated with the given password");
134 }
135 }
136
137 if ((response != null) && (cookieHandler.getCookie(request, getLoginCookieKey()) != null))
138 {
139 log.warn(METHOD + "'" + username + "' tried to login but they do not have USE permission or weren't found. Deleting cookie.");
140
141 try
142 {
143 cookieHandler.invalidateCookie(request, response, getLoginCookieKey(), getCookiePath(request));
144 }
145 catch (final Exception e)
146 {
147 log.error(METHOD + "Could not invalidate cookie: " + e, e);
148 }
149 }
150
151 return false;
152 }
153
154
155
156
157
158
159
160 protected RoleMapper getRoleMapper()
161 {
162 return SecurityConfigFactory.getInstance().getRoleMapper();
163 }
164
165
166
167
168
169
170
171
172
173
174 protected Principal getUser(final String username)
175 {
176 final String METHOD = "getUser : ";
177 if (log.isDebugEnabled())
178 {
179 log.debug(METHOD + "Looking in UserManager for '" + username + "'");
180 }
181 try
182 {
183 return UserManager.getInstance().getUser(username);
184 }
185 catch (final EntityNotFoundException e)
186 {
187 log.warn(METHOD + "Could not find user '" + username + "' in UserManager : " + e);
188 }
189 return null;
190 }
191
192
193
194
195
196
197
198
199
200 protected boolean authenticate(final Principal user, final String password)
201 {
202 return ((User) user).authenticate(password);
203 }
204
205 @Override
206 public boolean logout(final HttpServletRequest request, final HttpServletResponse response)
207 throws AuthenticatorException
208 {
209 final String METHOD = "logout : ";
210 final boolean dbg = log.isDebugEnabled();
211 if (dbg)
212 {
213 log.debug(METHOD + "Calling interceptors and clearing cookies");
214 }
215 final List<LogoutInterceptor> interceptors = getLogoutInterceptors();
216 final CookieHandler cookieHandler = CookieFactory.getCookieHandler();
217
218 for (final LogoutInterceptor interceptor : interceptors)
219 {
220 interceptor.beforeLogout(request, response);
221 }
222
223 request.getSession().setAttribute(LOGGED_IN_KEY, null);
224 request.getSession().setAttribute(LOGGED_OUT_KEY, Boolean.TRUE);
225 LoginReason.OUT.stampRequestResponse(request,response);
226
227
228
229 if ((response != null) && (cookieHandler.getCookie(request, getLoginCookieKey()) != null))
230 {
231 try
232 {
233 cookieHandler.invalidateCookie(request, response, getLoginCookieKey(), getCookiePath(request));
234 }
235 catch (final Exception e)
236 {
237 log.error(METHOD + "Could not invalidate cookie: " + e, e);
238 }
239 }
240
241 for (final Object element : interceptors)
242 {
243 final LogoutInterceptor interceptor = (LogoutInterceptor) element;
244 interceptor.afterLogout(request, response);
245 }
246
247 return true;
248 }
249
250
251
252
253
254
255
256
257
258
259 @Override
260 public Principal getUser(final HttpServletRequest request, final HttpServletResponse response)
261 {
262 final String METHOD = "getUser : ";
263 final boolean dbg = log.isDebugEnabled();
264
265 if (request.getSession(false) != null)
266 {
267 final Principal sessionUser = getUserFromSession(request);
268 if (sessionUser != null)
269 {
270 OK.stampRequestResponse(request, response);
271 return sessionUser;
272 }
273 }
274 else
275 {
276 final Principal cookieUser = getUserFromCookie(request, response);
277 if (cookieUser != null)
278 {
279 return cookieUser;
280 }
281 }
282
283 if (RedirectUtils.isBasicAuthentication(request, authType))
284 {
285 final Principal basicAuthUser = getUserFromBasicAuthentication(request, response);
286 if (basicAuthUser != null)
287 {
288 return basicAuthUser;
289 }
290 }
291
292 if (dbg)
293 {
294 log.debug(METHOD + "User not found in either Session, Cookie or Basic Auth.");
295 }
296
297 return null;
298 }
299
300
301
302
303
304
305
306
307
308
309 protected Principal getUserFromCookie(final HttpServletRequest httpServletRequest, final HttpServletResponse httpServletResponse)
310 {
311 final String METHOD = "getUserFromCookie : ";
312 final boolean dbg = log.isDebugEnabled();
313
314 final String cookieName = getLoginCookieKey();
315 final Cookie cookie = CookieFactory.getCookieHandler().getCookie(httpServletRequest, cookieName);
316 if (cookie == null)
317 {
318 if (dbg)
319 {
320 log.debug(METHOD + "No cookie called '" + cookieName + "' found. No principal returned.");
321 }
322 return null;
323 }
324
325 final String cookieValue = cookie.getValue();
326 if (dbg)
327 {
328 log.debug(METHOD + "Found cookie : '" + cookieName + "' with value : '" + cookieValue + "'");
329 }
330 final String[] values = decodeCookie(cookieValue);
331 if (values == null)
332 {
333 if (dbg)
334 {
335 log.debug(METHOD + "Unable to decode " + cookieName + " cookie with value : '" + cookieValue + "'");
336 }
337 return null;
338 }
339
340 final String userName = values[0];
341 final String password = values[1];
342 if (dbg)
343 {
344 log.debug(METHOD + "Got username : '" + userName + "' and password from cookie, attempting to authenticate user");
345 }
346
347 final ElevatedSecurityGuard securityGuard = getElevatedSecurityGuard();
348 if (!securityGuard.performElevatedSecurityCheck(httpServletRequest, userName))
349 {
350 if (dbg)
351 {
352 log.debug(METHOD + "'" + userName + "' failed elevated security check");
353 }
354 AUTHENTICATION_DENIED.stampRequestResponse(httpServletRequest, httpServletResponse);
355 securityGuard.onFailedLoginAttempt(httpServletRequest, userName);
356 return null;
357 }
358 else
359 {
360 try
361 {
362 final boolean loggedin = login(httpServletRequest, httpServletResponse, userName, password, false);
363 if (loggedin)
364 {
365 OK.stampRequestResponse(httpServletRequest, httpServletResponse);
366 securityGuard.onSuccessfulLoginAttempt(httpServletRequest, userName);
367 }
368 else
369 {
370 AUTHENTICATED_FAILED.stampRequestResponse(httpServletRequest, httpServletResponse);
371 securityGuard.onFailedLoginAttempt(httpServletRequest, userName);
372 return null;
373 }
374 }
375 catch (final Exception e)
376 {
377 log.warn(METHOD + "Cookie login for user : '" + userName + "' failed with exception: " + e, e);
378 return null;
379 }
380 }
381
382 if (dbg)
383 {
384 log.debug(METHOD + "Authenticated '" + userName + "' via a cookie. Now getting them again from the session");
385 }
386 return getUserFromSession(httpServletRequest);
387 }
388
389
390
391
392
393
394
395
396
397 protected Principal getUserFromSession(final HttpServletRequest request)
398 {
399 final String METHOD = "getUserFromSession : ";
400 final boolean dbg = log.isDebugEnabled();
401 try
402 {
403 if (request.getSession().getAttribute(LOGGED_OUT_KEY) != null)
404 {
405 if (dbg)
406 {
407 log.debug(METHOD + "Session found; user has already logged out. eg has LOGGED_OUT_KEY in session");
408 }
409 return null;
410 }
411 final Principal principal = (Principal) request.getSession().getAttribute(LOGGED_IN_KEY);
412 if (dbg)
413 {
414 if (principal == null)
415 {
416 log.debug(METHOD + "Session found; BUT it has no Principal in it");
417 }
418 else
419 {
420 log.debug(METHOD + "Session found; '" + principal.getName() + "' is present");
421 }
422 }
423 return principal;
424 }
425 catch (final Exception e)
426 {
427 log.warn(METHOD + "Exception when retrieving user from session: " + e, e);
428 return null;
429 }
430 }
431
432
433
434
435
436
437
438
439
440
441
442 protected Principal getUserFromBasicAuthentication(final HttpServletRequest httpServletRequest, final HttpServletResponse httpServletResponse)
443 {
444 final String METHOD = "getUserFromSession : ";
445 final boolean dbg = log.isDebugEnabled();
446
447 final String header = httpServletRequest.getHeader("Authorization");
448 LoginReason reason = OK;
449
450 if ((header != null) && header.startsWith("Basic "))
451 {
452 if (dbg)
453 {
454 log.debug(METHOD + "Looking in Basic Auth headers");
455 }
456 final String base64Token = header.substring(6);
457 final String token = new String(Base64.decode(base64Token.getBytes()));
458
459 String userName = "";
460 String password = "";
461
462 final int delim = token.indexOf(":");
463
464 if (delim != -1)
465 {
466 userName = token.substring(0, delim);
467 password = token.substring(delim + 1);
468 }
469
470 final ElevatedSecurityGuard securityGuard = getElevatedSecurityGuard();
471 if (!securityGuard.performElevatedSecurityCheck(httpServletRequest, userName))
472 {
473 if (dbg)
474 {
475 log.debug(METHOD + "'" + userName + "' failed elevated security check");
476 }
477 reason = AUTHENTICATION_DENIED.stampRequestResponse(httpServletRequest, httpServletResponse);
478 securityGuard.onFailedLoginAttempt(httpServletRequest, userName);
479 }
480 else
481 {
482 if (dbg)
483 {
484 log.debug(METHOD + "'" + userName + "' does not require elevated security check. Attempting authentication...");
485 }
486
487 try
488 {
489 final boolean loggedin = login(httpServletRequest, httpServletResponse, userName, password, false);
490 if (loggedin)
491 {
492 reason = OK.stampRequestResponse(httpServletRequest, httpServletResponse);
493 securityGuard.onSuccessfulLoginAttempt(httpServletRequest, userName);
494 if (dbg)
495 {
496 log.debug(METHOD + "Authenticated '" + userName + "' via Basic Auth");
497 }
498 return getUser(userName);
499 }
500 else
501 {
502 reason = AUTHENTICATED_FAILED.stampRequestResponse(httpServletRequest, httpServletResponse);
503 securityGuard.onFailedLoginAttempt(httpServletRequest, userName);
504 }
505 }
506 catch (final AuthenticatorException e)
507 {
508 log.warn(METHOD + "Exception trying to login '" + userName + "' via Basic Auth:" + e, e);
509 }
510 }
511 try
512 {
513 httpServletResponse.sendError(401, "Basic Authentication Failure - Reason : " + reason.toString());
514 }
515 catch (final IOException e)
516 {
517 log.warn(METHOD + "Exception trying to send Basic Auth failed error: " + e, e);
518 }
519 return null;
520 }
521
522 httpServletResponse.setStatus(401);
523 httpServletResponse.setHeader("WWW-Authenticate", "BASIC realm=\"protected-area\"");
524 return null;
525 }
526
527
528
529
530
531
532
533
534
535 protected String getCookiePath(final HttpServletRequest request)
536 {
537 if (getLoginCookiePath() != null)
538 {
539 return getLoginCookiePath();
540 }
541
542 final String path = request.getContextPath();
543 if ((path == null) || path.equals(""))
544 {
545 return "/";
546 }
547
548
549 if (!path.startsWith("/"))
550 {
551 return "/" + path;
552 }
553
554 return path;
555 }
556
557 protected String getLoginCookieKey()
558 {
559 return loginCookieKey;
560 }
561
562 public String getAuthType()
563 {
564 return authType;
565 }
566
567 protected List<LogoutInterceptor> getLogoutInterceptors()
568 {
569 return getConfig().getInterceptors(LogoutInterceptor.class);
570 }
571
572 protected String encodeCookie(final String username, final String password)
573 {
574 return CookieFactory.getCookieEncoder().encodePasswordCookie(username, password, getConfig().getCookieEncoding());
575 }
576
577 protected String[] decodeCookie(final String value)
578 {
579 return CookieFactory.getCookieEncoder().decodePasswordCookie(value, getConfig().getCookieEncoding());
580 }
581
582 protected String getLoginCookiePath()
583 {
584 return loginCookiePath;
585 }
586
587 protected ElevatedSecurityGuard getElevatedSecurityGuard()
588 {
589 return getConfig().getElevatedSecurityGuard();
590 }
591
592 }