Package org.projectforge.web

Source Code of org.projectforge.web.UserFilter

/////////////////////////////////////////////////////////////////////////////
//
// Project ProjectForge Community Edition
//         www.projectforge.org
//
// Copyright (C) 2001-2014 Kai Reinhard (k.reinhard@micromata.de)
//
// ProjectForge is dual-licensed.
//
// This community edition is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
// by the Free Software Foundation; version 3 of the License.
//
// This community edition is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
// Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, see http://www.gnu.org/licenses/.
//
/////////////////////////////////////////////////////////////////////////////

package org.projectforge.web;

import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Locale;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.MDC;
import org.projectforge.common.NumberHelper;
import org.projectforge.common.StringHelper;
import org.projectforge.registry.Registry;
import org.projectforge.user.Login;
import org.projectforge.user.PFUserContext;
import org.projectforge.user.PFUserDO;
import org.projectforge.user.UserDao;
import org.projectforge.web.core.LogoServlet;
import org.projectforge.web.meb.SMSReceiverServlet;
import org.projectforge.web.wicket.WicketUtils;

/**
* Ensures that an user is logged in and put the user id, locale and ip to the logging mdc.<br/>
* Ignores login for: /ProjectForge/wa/resources/* with the suffixes: *.js, *.css, *.gif, *.png. <br/>
* Don't forget to call setServletContext on applications start-up!
*/
public class UserFilter implements Filter
{
  /**
   * Set after stay-logged-in functionality (used by MenuMobilePage).
   */
  public static final String USER_ATTR_STAY_LOGGED_IN = "stayLoggedIn";

  private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(UserFilter.class);

  private final static String SESSION_KEY_USER = "UserFilter.user";

  private static final String COOKIE_NAME_FOR_STAY_LOGGED_IN = "stayLoggedIn";

  private static final int COOKIE_MAX_AGE = 30 * 24 * 3600; // 30 days.

  private static String IGNORE_PREFIX_WICKET;

  private static String IGNORE_PREFIX_DOC;

  private static String IGNORE_PREFIX_SITE_DOC;

  private static String IGNORE_PREFIX_LOGO;

  private static String IGNORE_PREFIX_SMS_REVEIVE_SERVLET;

  private static String WICKET_PAGES_PREFIX;

  public static String CONTEXT_PATH;

  private static UserDao userDao;

  private static boolean updateRequiredFirst = false;

  public static void initialize(final UserDao userDao, final String contextPath)
  {
    UserFilter.userDao = userDao;
    CONTEXT_PATH = contextPath;
    WICKET_PAGES_PREFIX = CONTEXT_PATH + "/" + WicketUtils.WICKET_APPLICATION_PATH;
    IGNORE_PREFIX_WICKET = WICKET_PAGES_PREFIX + "resources";
    IGNORE_PREFIX_DOC = contextPath + "/secure/doc";
    IGNORE_PREFIX_SITE_DOC = contextPath + "/secure/site";
    IGNORE_PREFIX_LOGO = contextPath + "/" + LogoServlet.BASE_URL;
    IGNORE_PREFIX_SMS_REVEIVE_SERVLET = contextPath + "/" + SMSReceiverServlet.URL;
  }

  public static void setUpdateRequiredFirst(final boolean value)
  {
    updateRequiredFirst = value;
  }

  public static boolean isUpdateRequiredFirst()
  {
    return updateRequiredFirst;
  }

  public static Cookie getStayLoggedInCookie(final HttpServletRequest request)
  {
    return getCookie(request, COOKIE_NAME_FOR_STAY_LOGGED_IN);
  }

  public static Cookie getCookie(final HttpServletRequest request, final String name)
  {
    final Cookie[] cookies = request.getCookies();
    if (cookies != null) {
      for (final Cookie cookie : cookies) {
        if (name.equals(cookie.getName()) == true) {
          return cookie;
        }
      }
    }
    return null;
  }

  /**
   * Adds or refresh the given cookie.
   * @param request
   * @param response
   * @param stayLoggedInCookie
   */
  public static void addStayLoggedInCookie(final HttpServletRequest request, final HttpServletResponse response,
      final Cookie stayLoggedInCookie)
  {
    stayLoggedInCookie.setMaxAge(COOKIE_MAX_AGE);
    stayLoggedInCookie.setPath("/");
    if (request.isSecure() == true) {
      log.debug("Set secure cookie");
      stayLoggedInCookie.setSecure(true);
    } else {
      log.debug("Set unsecure cookie");
    }
    response.addCookie(stayLoggedInCookie); // Refresh cookie.
  }

  public static void login(final HttpServletRequest request, final PFUserDO user)
  {
    final HttpSession session = request.getSession();
    final PFUserDO storedUser = new PFUserDO();
    copyUser(user, storedUser);
    session.setAttribute(SESSION_KEY_USER, storedUser);
  }

  public static void updateUser(final HttpServletRequest request, final PFUserDO user)
  {
    final PFUserDO origUser = getUser(request);
    if (origUser.getId().equals(user.getId()) == false) {
      log.error("**** Intruser? User id of the session user is different to the id of the given user!");
      return;
    }
    copyUser(user, origUser);
  }

  private static void copyUser(final PFUserDO srcUser, final PFUserDO destUser)
  {
    destUser.copyValuesFrom(srcUser, "password", "stayLoggedInKey");
  }

  public static PFUserDO getUser(final HttpServletRequest request)
  {
    final HttpSession session = request.getSession();
    if (session == null) {
      return null;
    }
    return (PFUserDO) session.getAttribute(SESSION_KEY_USER);
  }

  public void destroy()
  {
    // do nothing
  }

  public void init(final FilterConfig filterConfig) throws ServletException
  {
    // do nothing
  }

  public void doFilter(final ServletRequest req, final ServletResponse resp, final FilterChain chain) throws IOException, ServletException
  {
    HttpServletRequest request = (HttpServletRequest) req;
    if (log.isDebugEnabled() == true) {
      log.debug("doFilter " + request.getRequestURI() + ": " + request.getSession().getId());
      final Cookie[] cookies = request.getCookies();
      if (cookies != null) {
        for (final Cookie cookie : cookies) {
          log.debug("Cookie "
              + cookie.getName()
              + ", path="
              + cookie.getPath()
              + ", value="
              + cookie.getValue()
              + ", secure="
              + cookie.getVersion()
              + ", maxAge="
              + cookie.getMaxAge()
              + ", domain="
              + cookie.getDomain());
        }
      }
    }
    final HttpServletResponse response = (HttpServletResponse) resp;
    PFUserDO user = null;
    try {
      MDC.put("ip", request.getRemoteAddr());
      MDC.put("session", request.getSession().getId());
      if (ignoreFilterFor(request) == true) {
        // Ignore the filter for this request:
        if (log.isDebugEnabled() == true) {
          log.debug("Ignore: " + request.getRequestURI());
        }
        chain.doFilter(request, response);
      } else {
        // final boolean sessionTimeout = request.isRequestedSessionIdValid() == false;
        user = (PFUserDO) request.getSession().getAttribute(SESSION_KEY_USER);
        if (user != null) {
          if (updateRequiredFirst == false) {
            // Get the fresh user from the user cache (not in maintenance mode because user group cache is perhaps not initialized correctly
            // if updates of e. g. the user table are necessary.
            user = Registry.instance().getUserGroupCache().getUser(user.getId());
          }
          if (log.isDebugEnabled() == true) {
            log.debug("User found in session: " + request.getRequestURI());
          }
        } else if (updateRequiredFirst == false) {
          // Ignore stay-logged-in if redirect to update page is required.
          user = checkStayLoggedIn(request, response);
          if (user != null) {
            if (log.isDebugEnabled() == true) {
              log.debug("User's stay logged-in cookie found: " + request.getRequestURI());
            }
            user.setAttribute(USER_ATTR_STAY_LOGGED_IN, true); // Used by MenuMobilePage.
            UserFilter.login(request, user);
          }
        }
        if (user != null) {
          MDC.put("user", user.getUsername());
          PFUserContext.setUser(user);
          request = decorateWithLocale(request, user);
          chain.doFilter(request, response);
        } else {
          if (((HttpServletRequest) req).getRequestURI().startsWith(WICKET_PAGES_PREFIX) == true) {
            // Access-checking is done by Wicket, not by this filter:
            request = decorateWithLocale(request, user);
            chain.doFilter(request, response);
          } else {
            response.getWriter().append("No access.");
          }
        }
      }
    } finally {
      PFUserContext.setUser(null);
      MDC.remove("ip");
      MDC.remove("session");
      if (user != null) {
        MDC.remove("user");
      }
      if (log.isDebugEnabled() == true) {
        log.debug("doFilter finished for " + request.getRequestURI() + ": " + request.getSession().getId());
      }
    }
  }

  /**
   * User is not logged. Checks a stay-logged-in-cookie.
   * @return user if valid cookie found, otherwise null.
   */
  private PFUserDO checkStayLoggedIn(final HttpServletRequest request, final HttpServletResponse response)
  {
    final Cookie sessionIdCookie = getCookie(request, "JSESSIONID");
    if (sessionIdCookie != null && sessionIdCookie.getSecure() == false && request.isSecure() == true) {
      // Hack for developers: Safari (may-be also other browsers) don't update unsecure cookies for secure connections. This seems to be
      // occurring
      // if you use ProjectForge on localhost with http and https (e. g. for testing). You have to delete this cookie normally in your
      // browser.
      final Cookie cookie = new Cookie("JSESSIONID", "to be deleted");
      cookie.setMaxAge(0);
      cookie.setPath(sessionIdCookie.getPath()); // Doesn't work for Safari: getPath() returns always null!
      response.addCookie(cookie);
    }
    final Cookie stayLoggedInCookie = getStayLoggedInCookie(request);
    if (stayLoggedInCookie != null) {
      final String value = stayLoggedInCookie.getValue();
      if (StringUtils.isBlank(value) == true) {
        return null;
      }
      final String[] values = value.split(":");
      if (values == null || values.length != 3) {
        log.warn("Invalid cookie found: " + value);
        return null;
      }
      final Integer userId = NumberHelper.parseInteger(values[0]);
      final PFUserDO user = userDao.internalGetById(userId);
      if (user == null) {
        log.warn("Invalid cookie found (user not found): " + value);
        return null;
      }
      if (user.getUsername().equals(values[1]) == false) {
        log.warn("Invalid cookie found (user name wrong, maybe changed): " + value);
        return null;
      }
      if (values[2] == null || values[2].equals(user.getStayLoggedInKey()) == false) {
        log.warn("Invalid cookie found (stay-logged-in key, maybe renewed and/or user password changed): " + value);
        return null;
      }
      if (Login.getInstance().checkStayLoggedIn(user) == false) {
        log.warn("Stay-logged-in wasn't accepted by the login handler: " + user.getUserDisplayname());
        return null;
      }
      addStayLoggedInCookie(request, response, stayLoggedInCookie);
      log.info("User successfully logged in using stay-logged-in method: " + user.getUserDisplayname());
      return user;
    }
    return null;
  }

  /**
   * @param request
   * @param user
   * @return
   */
  protected HttpServletRequest decorateWithLocale(HttpServletRequest request, final PFUserDO user)
  {
    final Locale locale = PFUserContext.getLocale(request.getLocale());
    request = new HttpServletRequestWrapper(request) {
      @Override
      public Locale getLocale()
      {
        return locale;
      }

      @Override
      public Enumeration< ? > getLocales()
      {
        return Collections.enumeration(Collections.singleton(locale));
      }
    };
    return request;
  }

  /**
   * Will be called by doFilter.
   * @param req from do Filter.
   * @return true, if the filter should ignore this request, otherwise false.
   */
  protected boolean ignoreFilterFor(final ServletRequest req)
  {
    final HttpServletRequest hreq = (HttpServletRequest) req;
    final String uri = hreq.getRequestURI();
    // If you have an NPE you have probably forgotten to call setServletContext on applications start-up.
    // Paranoia setting. May-be there could be a vulnerability with request parameters:
    if (uri.contains("?") == false) {
      // if (uri.startsWith(IGNORE_PREFIX_WICKET) && StringHelper.endsWith(uri, ".js", ".css", ".gif", ".png") == true) {
      // No access checking for Wicket resources.
      // return true;
      // } else if (StringHelper.startsWith(uri, IGNORE_PREFIX_DOC, IGNORE_PREFIX_SITE_DOC) == true
      // && StringHelper.endsWith(uri, ".html", ".pdf", ".js", ".css", ".gif", ".png") == true) {
      // No access checking for documentation (including site doc).
      // return true;
      // } else
      if (StringHelper.startsWith(uri, IGNORE_PREFIX_LOGO, IGNORE_PREFIX_SMS_REVEIVE_SERVLET) == true) {
        // No access checking for logo and sms receiver servlet.
        // The sms receiver servlet has its own authentification (key).
        return true;
      }
    }
    return false;
  }
}
TOP

Related Classes of org.projectforge.web.UserFilter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.