View Javadoc
1   /*
2    * This software was designed and created by Jason Carroll.
3    * Copyright (c) 2002, 2003, 2004 Jason Carroll.
4    * The author can be reached at jcarroll@cowsultants.com
5    * ITracker website: http://www.cowsultants.com
6    * ITracker forums: http://www.cowsultants.com/phpBB/index.php
7    *
8    * This program is free software; you can redistribute it and/or modify
9    * it only under the terms of the GNU General Public License as published by
10   * the Free Software Foundation; either version 2 of the License, or
11   * (at your option) any later version.
12   *
13   * This program is distributed in the hope that it will be useful,
14   * but WITHOUT ANY WARRANTY; without even the implied warranty of
15   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   * GNU General Public License for more details.
17   */
18  
19  package org.itracker.web.util;
20  
21  import org.itracker.core.AuthenticationConstants;
22  import org.itracker.core.resources.ITrackerResources;
23  import org.itracker.model.*;
24  import org.itracker.model.util.UserUtilities;
25  import org.itracker.services.UserService;
26  import org.itracker.services.authentication.ITrackerUserDetails;
27  import org.slf4j.Logger;
28  import org.slf4j.LoggerFactory;
29  import org.springframework.security.core.GrantedAuthority;
30  import org.springframework.security.core.context.SecurityContextHolder;
31  import org.springframework.security.core.userdetails.UserDetails;
32  
33  import javax.servlet.ServletException;
34  import javax.servlet.http.Cookie;
35  import javax.servlet.http.HttpServletRequest;
36  import javax.servlet.http.HttpServletResponse;
37  import javax.servlet.http.HttpSession;
38  import java.io.IOException;
39  import java.security.Principal;
40  import java.util.*;
41  
42  public class LoginUtilities {
43  
44      private static final Logger logger = LoggerFactory.getLogger(LoginUtilities.class);
45      private static final int DEFAULT_SESSION_TIMEOUT = 30;
46  
47      public static boolean checkAutoLogin(HttpServletRequest request,
48                                           boolean allowSaveLogin) {
49          boolean foundLogin = false;
50  
51          if (request != null) {
52              int authType = getRequestAuthType(request);
53  
54              // Check for auto login in request
55              if (authType == AuthenticationConstants.AUTH_TYPE_REQUEST) {
56                  String redirectURL = request.getRequestURI().substring(
57                          request.getContextPath().length())
58                          + (request.getQueryString() != null ? "?"
59                          + request.getQueryString() : "");
60                  request.setAttribute(Constants.AUTH_TYPE_KEY,
61                          AuthenticationConstants.AUTH_TYPE_REQUEST);
62                  request.setAttribute(Constants.AUTH_REDIRECT_KEY,
63                          redirectURL);
64                  request.setAttribute("processLogin", "true");
65                  foundLogin = true;
66  
67              }
68  
69              // Add in check for client certs
70  
71              // Check for auto login with cookies, this will only happen if users
72              // are allowed to save
73              // their logins to cookies
74              if (allowSaveLogin && !foundLogin) {
75                  Cookie[] cookies = request.getCookies();
76                  if (cookies != null) {
77                      for (Cookie cookie : cookies) {
78                          if (Constants.COOKIE_NAME.equals(cookie.getName())) {
79                              int seperator = cookie.getValue().indexOf('~');
80                              final String login;
81                              if (seperator > 0) {
82                                  login = cookie.getValue()
83                                          .substring(0,
84                                                  seperator);
85                                  if (logger.isDebugEnabled()) {
86                                      logger
87                                              .debug("Attempting autologin for user "
88                                                      + login
89                                                      + ".");
90                                  }
91  
92                                  String redirectURL = request.getRequestURI()
93                                          .substring(
94                                                  request.getContextPath()
95                                                          .length())
96                                          + (request.getQueryString() != null ? "?"
97                                          + request.getQueryString()
98                                          : "");
99                                  request.setAttribute(Constants.AUTH_LOGIN_KEY,
100                                         cookie.getValue().substring(0,
101                                                 seperator));
102                                 request.setAttribute(Constants.AUTH_TYPE_KEY,
103                                         AuthenticationConstants.AUTH_TYPE_PASSWORD_ENC);
104 
105                                 request.setAttribute(Constants.AUTH_VALUE_KEY,
106                                         cookie.getValue().substring(
107                                                 seperator + 1));
108                                 request.setAttribute(
109                                         Constants.AUTH_REDIRECT_KEY,
110                                         redirectURL);
111                                 request.setAttribute("processLogin", "true");
112                                 foundLogin = true;
113                             }
114                         }
115                     }
116                 }
117             }
118 
119         }
120 
121         return foundLogin;
122     }
123 
124     public static int getRequestAuthType(HttpServletRequest request) {
125         int authType = AuthenticationConstants.AUTH_TYPE_UNKNOWN;
126 
127         try {
128             if (request.getAttribute(Constants.AUTH_TYPE_KEY) != null) {
129                 authType = (Integer) request
130                         .getAttribute(Constants.AUTH_TYPE_KEY);
131             }
132             if (request.getParameter(Constants.AUTH_TYPE_KEY) != null) {
133                 authType = Integer.valueOf(request
134                         .getParameter(Constants.AUTH_TYPE_KEY));
135             }
136         } catch (Exception e) {
137             logger
138                     .debug("Error retrieving auth type while checking auto login.  "
139                             + e.getMessage());
140         }
141 
142         return authType;
143     }
144 
145     /**
146      * Get a locale from request
147      * <p/>
148      * <p>
149      * TODO the order of retrieving locale from request should be:
150      * <ol>
151      * <li>request-attribute Constants.LOCALE_KEY</li>
152      * <li>request-param 'loc'</li>
153      * <li>session attribute <code>Constants.LOCALE_KEY</code></li>
154      * <li>cookie 'loc'</li>
155      * <li>request.getLocale()/request.getLocales()</li>
156      * <li>ITrackerResources.DEFAULT_LOCALE</li>
157      * </ol>
158      * </p>
159      */
160     @SuppressWarnings("unchecked")
161     public static Locale getCurrentLocale(HttpServletRequest request) {
162         Locale requestLocale = null;
163         HttpSession session = request.getSession(true);
164         try {
165 
166             requestLocale = (Locale) request.getAttribute(Constants.LOCALE_KEY);
167 
168             if (logger.isDebugEnabled()) {
169                 logger.debug("getCurrentLocale: request-attribute was {}",
170                         requestLocale);
171             }
172 
173             if (null == requestLocale) {
174                 // get locale from request param
175                 String loc = request
176                         .getParameter("loc");
177                 if (null != loc && loc.trim().length() > 1) {
178                     requestLocale = ITrackerResources.getLocale(loc);
179                 }
180 
181                 logger.debug("getCurrentLocale: request-parameter was {}",
182                         loc);
183 
184             }
185 
186             if (null == requestLocale) {
187                 // get it from the session
188                 requestLocale = (Locale) session
189                         .getAttribute(Constants.LOCALE_KEY);
190 //				if (logger.isDebugEnabled()) {
191 //					logger.debug("getCurrentLocale: session-attribute was "
192 //							+ requestLocale);
193 //				}
194             }
195 
196             if (null == requestLocale) {
197                 ResourceBundle bundle = ITrackerResources.getBundle(request
198                         .getLocale());
199                 if (logger.isDebugEnabled()) {
200                     logger
201                             .debug("getCurrentLocale: trying request header locale "
202                                     + request.getLocale());
203                 }
204                 if (bundle.getLocale().getLanguage().equals(
205                         request.getLocale().getLanguage())) {
206                     requestLocale = request.getLocale();
207                     if (logger.isDebugEnabled()) {
208                         logger.debug("getCurrentLocale: request-locale was "
209                                 + requestLocale);
210                     }
211                 }
212             }
213 
214             // is there no way to detect supported locales of current
215             // installation?
216 
217             if (null == requestLocale) {
218                 Enumeration<Locale> locales = (Enumeration<Locale>) request.getLocales();
219                 ResourceBundle bundle;
220                 Locale locale;
221                 while (locales.hasMoreElements()) {
222                     locale = locales.nextElement();
223                     bundle = ITrackerResources.getBundle(locale);
224 
225                     logger.debug("getCurrentLocale: request-locales processing {}, bundle: {}",
226                             locale, bundle);
227 
228                     if (bundle.getLocale().getLanguage().equals(
229                             locale.getLanguage())) {
230                         requestLocale = locale;
231 
232                         logger.debug("getCurrentLocale: request-locales locale was {}",
233                                 requestLocale);
234 
235                     }
236                 }
237             }
238 
239         } finally {
240             if (null == requestLocale) {
241                 // fall back to default locale
242                 requestLocale = ITrackerResources.getLocale();
243 
244                 logger.debug("getCurrentLocale: fallback default locale was {}",
245                         requestLocale);
246 
247             }
248             session.setAttribute(Constants.LOCALE_KEY, requestLocale);
249             request.setAttribute(Constants.LOCALE_KEY, requestLocale);
250             request.setAttribute("currLocale", requestLocale);
251 
252             logger.debug("getCurrentLocale: request and session was setup with {}",
253                     requestLocale);
254 
255         }
256 
257         return requestLocale;
258     }
259 
260     /**
261      * get current user from request-attribute currUser, if not set from request-session
262      *
263      * @return current user or null if unauthenticated
264      * @throws NullPointerException if the request was null
265      */
266     @Deprecated
267     public static User getCurrentUser(HttpServletRequest request) {
268 
269         final String remoteUser = request.getRemoteUser();
270         if (null == remoteUser) {
271             return null;
272         }
273         User currUser = (User) request.getAttribute("currUser");
274         if (null != currUser && currUser.getLogin().equals(remoteUser)) {
275             if (logger.isDebugEnabled()) {
276                 logger.debug("found user in request: " + remoteUser);
277             }
278         }
279         if (null == currUser) {
280             currUser = (User) request.getSession().getAttribute("currUser");
281             if (null != currUser && currUser.getLogin().equals(remoteUser)) {
282                 if (logger.isDebugEnabled()) {
283                     logger.debug("found user in session: " + remoteUser);
284                 }
285             }
286         }
287         if (null == currUser) {
288             currUser = ServletContextUtils.getItrackerServices().getUserService().getUserByLogin(remoteUser);
289             if (null != currUser && currUser.getLogin().equals(remoteUser)) {
290                 if (logger.isDebugEnabled()) {
291                     logger.debug("found user by login: " + remoteUser);
292                 }
293             }
294         }
295 
296         return currUser;
297     }
298 
299     /**
300      * Utility for accessing the current logged in user's principal
301      * @return current user principal
302      */
303     public static ITrackerUserDetails getPrincipal() {
304         Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
305         if (principal instanceof ITrackerUserDetails) {
306             return (ITrackerUserDetails)principal;
307         }
308 
309         return null;
310     }
311 
312     public static Boolean allowSaveLogin(HttpServletRequest request) {
313         return (boolean) request.getAttribute("allowSaveLogin");
314     }
315 
316     public static User setupSession(String login, HttpServletRequest request,
317                                     HttpServletResponse response) {
318         if (null == login) {
319             logger.warn("setupSession: null login", (logger.isDebugEnabled() ? new RuntimeException() : null));
320             throw new IllegalArgumentException("null login");
321         }
322         UserService userService = ServletContextUtils.getItrackerServices().getUserService();
323         User user = userService.getUserByLogin(login);
324         if (user != null) {
325             String encPassword = null;
326             Cookie[] cookies = request.getCookies();
327             if (cookies != null) {
328                 for (Cookie cookie : cookies) {
329                     if (Constants.COOKIE_NAME.equals(cookie.getName())) {
330                         int seperator = cookie.getValue().indexOf('~');
331                         if (seperator > 0) {
332                             encPassword = cookie.getValue().substring(
333                                     seperator + 1);
334                         }
335                     }
336                 }
337             }
338 
339             return setupSession(user, encPassword, request, response);
340         }
341         return null;
342     }
343 
344     public static User setupSession(User user, String encPassword,
345                                     HttpServletRequest request, HttpServletResponse response) {
346         if (user == null) {
347             logger.warn("setupSession: null user", (logger.isDebugEnabled() ? new RuntimeException() : null));
348             throw new IllegalArgumentException("null user");
349         }
350 
351         UserService userService = ServletContextUtils.getItrackerServices().getUserService();
352 
353         if (logger.isDebugEnabled()) {
354             logger.debug("Creating new session");
355         }
356         HttpSession session = request.getSession(true);
357 
358         if (logger.isDebugEnabled()) {
359             logger.debug("Setting session timeout to "
360                     + getConfiguredSessionTimeout() + " minutes");
361         }
362         session.setMaxInactiveInterval(getConfiguredSessionTimeout() * 60);
363 
364         if (logger.isDebugEnabled()) {
365             logger.debug("Setting session tracker");
366         }
367         session.setAttribute(Constants.SESSION_TRACKER_KEY, new SessionTracker(
368                 user.getLogin(), session.getId()));
369 
370         if (logger.isDebugEnabled()) {
371             logger.debug("Setting user information");
372         }
373         session.setAttribute(Constants.USER_KEY, user);
374 
375         if (logger.isDebugEnabled()) {
376             logger.debug("Setting preferences for user " + user.getLogin());
377         }
378         UserPreferences userPrefs = user.getPreferences();
379         // TODO : this is a hack, remove when possible
380         if (userPrefs == null) {
381             logger.warn("setupSession: got user with no preferences!: " + user + " (prefs: " + user.getPreferences() + ")");
382             userPrefs = new UserPreferences();
383         }
384         session.setAttribute(Constants.PREFERENCES_KEY, userPrefs);
385 
386         if (logger.isDebugEnabled()) {
387             logger.debug("Setting user " + user + " locale to " + ITrackerResources
388                     .getLocale(userPrefs.getUserLocale()));
389         }
390         session.setAttribute(Constants.LOCALE_KEY, ITrackerResources
391                 .getLocale(userPrefs.getUserLocale()));
392 
393         // TODO: cookie could be removed
394         Cookie cookie = new Cookie(Constants.COOKIE_NAME, "");
395         cookie.setPath(request.getContextPath());
396 
397         cookie.setValue("");
398         cookie.setMaxAge(0);
399 
400         response.addCookie(cookie);
401 
402         if (logger.isDebugEnabled()) {
403             logger.debug("Setting permissions for user " + user.getLogin());
404         }
405         Map<Integer, Set<PermissionType>> usersMapOfProjectIdsAndSetOfPermissionTypes = userService
406                 .getUsersMapOfProjectIdsAndSetOfPermissionTypes(user,
407                         AuthenticationConstants.REQ_SOURCE_WEB);
408         session.setAttribute(Constants.PERMISSIONS_KEY,
409                 usersMapOfProjectIdsAndSetOfPermissionTypes);
410 
411         // Reset some session forms
412         session.setAttribute(Constants.SEARCH_QUERY_KEY, null);
413 
414         SessionManager.clearSessionNeedsReset(user.getLogin());
415         if (logger.isDebugEnabled()) {
416             logger.debug("User session data updated.");
417         }
418         return user;
419     }
420 
421     public static int getConfiguredSessionTimeout() {
422         return (ServletContextUtils.getItrackerServices().getConfigurationService()
423                 .getIntegerProperty("web_session_timeout", DEFAULT_SESSION_TIMEOUT));
424     }
425 
426     @Deprecated
427     public static boolean hasPermission(int permissionNeeded,
428                                         HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
429         return hasPermission(PermissionType.valueOf(permissionNeeded), request, response);
430     }
431     public static boolean hasPermission(PermissionType permissionNeeded,
432                                               HttpServletRequest request, HttpServletResponse response)
433             throws IOException, ServletException {
434 
435         return hasPermission(new PermissionType[]{permissionNeeded}, request, response);
436 
437     }
438 
439 
440     @Deprecated
441     public static boolean hasPermission(PermissionType[] permissionsNeeded,
442                                         HttpServletRequest request, HttpServletResponse response)
443             throws IOException, ServletException {
444         try {
445             UserDetails user = LoginUtilities.getPrincipal();
446 
447             HttpSession session = request.getSession(false);
448             Map<Integer, Set<PermissionType>> permissions = (session == null) ? null
449                     : RequestHelper.getUserPermissions(session);
450             return UserUtilities.hasPermission(permissions, permissionsNeeded);
451         } catch (RuntimeException re) {
452             logger.debug("hasPermission: failed to check permission", re);
453             return false;
454         }
455     }
456 
457     /**
458      * Returns true if the user has any of required permissions for the project.
459      */
460     public static boolean hasAnyPermission(Project project, PermissionType[] permissionsNeeded) {
461 
462 
463         if (null == permissionsNeeded || permissionsNeeded.length == 0) {
464             permissionsNeeded = PermissionType.values();
465         }
466         for (PermissionType permissionType: permissionsNeeded) {
467             if (hasPermission(project, permissionType)) {
468                 return true;
469             }
470         }
471         return false;
472     }
473 
474     /**
475      * Returns true if the user has all of required permissions for the project.
476      */
477     public static boolean hasPermission(Project project, PermissionType[] permissionsNeeded) {
478 
479         for (PermissionType permissionType: permissionsNeeded) {
480             if (!hasPermission(project, permissionType)) {
481                 return false;
482             }
483         }
484         return true;
485     }
486 
487 
488     /**
489      * Returns true if the user has all the required permissions.
490      *
491      * @param permissionNeeded the permission to check for
492      */
493     public static boolean hasPermission(PermissionType permissionNeeded) {
494         return hasPermission(null, permissionNeeded);
495     }
496     /**
497      * Returns true if the user has the required permission for the given project.
498      *
499      * @param project project to which permission is checked for
500      * @param permissionNeeded the permission to check for
501      */
502     public static boolean hasPermission(Project project, PermissionType permissionNeeded) {
503         UserDetails user = getPrincipal();
504         if (null == user) {
505             return false;
506         }
507         if (permissionNeeded != PermissionType.USER_ADMIN
508                 && hasPermission(PermissionType.USER_ADMIN)) {
509             return true;
510         } else if (null != project && permissionNeeded != PermissionType.PRODUCT_ADMIN
511             && hasPermission(project, PermissionType.PRODUCT_ADMIN)) {
512             return true;
513         }
514         Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
515         String permissionName = permissionNeeded.name(project);
516         for (GrantedAuthority authority: authorities) {
517             if (authority.getAuthority().equals(permissionName)) {
518                 return true;
519             }
520         }
521         return false;
522     }
523 
524 
525     /**
526      * Returns true if the user has permission to view the requested issue.
527      *
528      * @param issue       an IssueModel of the issue to check view permission for
529      */
530     public static boolean canViewIssue (Issue issue) {
531         return  canViewIssue(issue, getPrincipal());
532     }
533 
534     /**
535      * Returns true if the user has permission to view the requested issue.
536      *
537      * @param issue       an IssueModel of the issue to check view permission for
538      * @param user        the user principal of the user to check permission for
539      */
540     public static boolean canViewIssue (Issue issue, UserDetails user) {
541         if (hasAnyPermission(issue.getProject(), new PermissionType[] {
542                   PermissionType.PRODUCT_ADMIN, PermissionType.ISSUE_VIEW_ALL})) {
543             return true;
544         }
545 
546         // TODO option on project that all users can view own issues
547         boolean canViewUsers = true;// hasPermission(issue.getProject(), PermissionType.ISSUE_VIEW_USERS);
548 
549         // I think owner & creator should always be able to view the issue
550         // otherwise it makes no sense of creating the issue itself.
551         // So put these checks before checking permissions for the whole project.
552         if (canViewUsers && issue.getCreator().getLogin().equals(user.getUsername())) {
553             if (logger.isInfoEnabled()) {
554                 logger.info("canViewIssue: issue: " + issue + ", user: " + user.getUsername()
555                         + ", permission: is creator");
556             }
557             return true;
558         }
559 
560         if (canViewUsers && issue.getOwner() != null) {
561             if (issue.getOwner().getLogin().equals(user.getUsername())) {
562 
563                 if (logger.isInfoEnabled()) {
564                     logger.info("canViewIssue: issue: " + issue + ", user: "
565                             + user.getUsername() + ", permission: is owner");
566                 }
567                 return true;
568             }
569         }
570         return false;
571     }
572 
573     public static boolean canEditIssue(Issue issue) {
574         return canEditIssue(issue, getPrincipal());
575     }
576 
577     public static boolean canEditIssue(Issue issue, UserDetails user) {
578            if (issue == null ) {
579                return false;
580            }
581 
582             if (hasAnyPermission(issue.getProject(), new PermissionType[] {
583                       PermissionType.ISSUE_EDIT_ALL})) {
584                 return true;
585             }
586 
587            if (!hasPermission(issue.getProject(), PermissionType.ISSUE_EDIT_USERS)) {
588                if (logger.isDebugEnabled()) {
589                    logger.debug("canEditIssue: user " + user.getUsername()
590                            + " has not permission  to edit issue " + issue.getId()
591                            + ":" + PermissionType.ISSUE_EDIT_USERS);
592                }
593                return false;
594            }
595 
596            if (issue.getCreator().getLogin().equals(user.getUsername())) {
597                if (logger.isDebugEnabled()) {
598                    logger.debug("canEditIssue: user " + user.getUsername()
599                            + " is creator of issue " + issue.getId());
600                }
601                return true;
602            }
603            if (issue.getOwner() != null) {
604                if (issue.getOwner().getLogin().equals(user.getUsername())) {
605                    if (logger.isDebugEnabled()) {
606                        logger.debug("canEditIssue: user " + user.getUsername()
607                                + " is owner of issue " + issue.getId());
608                    }
609                    return true;
610                }
611            }
612 
613            if (logger.isDebugEnabled()) {
614                logger.debug("canEditIssue: user " + user.getUsername()
615                        + " could not match permission, denied");
616            }
617            return false;
618        }
619 }