/FileExtraction/Java_unseen/dlitz_resin/modules/resin/src/com/caucho/security/FormLogin.java
/*
 * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source 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; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Resin Open Source 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, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *
 *   Free Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.security;

import com.caucho.config.ConfigException;
import com.caucho.config.Service;
import com.caucho.server.http.CauchoRequest;
import com.caucho.server.http.CauchoResponse;
import com.caucho.server.session.SessionManager;
import com.caucho.server.webapp.WebApp;
import com.caucho.util.L10N;

import javax.annotation.PostConstruct;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.security.Principal;
import java.util.logging.Level;

/**
 * Used to authenticate users in a servlet request.  Applications will
 * implement the Authenticator interface with a bean for authentication.
 *
 * @since Resin 2.0.2
 */

@Service
public class FormLogin extends AbstractLogin
{
  private static final L10N L = new L10N(FormLogin.class);

  public static final String LOGIN_CHECK
    = "com.caucho.security.form.login";

  public static final String LOGIN_SAVED_PATH
    = "com.caucho.servlet.login.path";
  public static final String LOGIN_SAVED_QUERY
    = "com.caucho.servlet.login.query";

  protected String _loginPage;
  protected String _errorPage;
  protected boolean _internalForward;
  protected boolean _formURIPriority;

  private WebApp _webApp = WebApp.getCurrent();

  /**
   * Sets the login page.
   */
  public void setFormLoginPage(String formLoginPage)
    throws ConfigException
  {
    int colon = formLoginPage.indexOf(':');
    int slash = formLoginPage.indexOf('/');

    if (colon > 0 && colon < slash) {
    }
    else if (slash != 0)
      throw new ConfigException(L.l("form-login-page '{0}' must start with '/'.  The form-login-page is relative to the web-app root.", formLoginPage));

    _loginPage = formLoginPage;
  }

  public void setLoginPage(String loginPage)
  {
    setFormLoginPage(loginPage);
  }

  /**
   * Gets the login page.
   */
  public String getFormLoginPage()
  {
    return _loginPage;
  }

  /**
   * Sets the error page.
   */
  public void setFormErrorPage(String formErrorPage)
    throws ConfigException
  {
    if (! formErrorPage.startsWith("/"))
      throw new ConfigException(L.l("form-error-page '{0}' must start with '/'.  The form-error-page is relative to the web-app root.", formErrorPage));

    _errorPage = formErrorPage;
  }

  public void setErrorPage(String errorPage)
  {
    setFormErrorPage(errorPage);
  }

  /**
   * Gets the error page.
   */
  public String getFormErrorPage()
  {
    return _errorPage;
  }

  /**
   * Returns true if a successful login allows an internal forward
   * instead of a redirect.
   */
  public boolean getInternalForward()
  {
    return _internalForward;
  }

  /**
   * Set true if a successful login allows an internal forward
   * instead of a redirect.
   */
  public void setInternalForward(boolean internalForward)
  {
    _internalForward = internalForward;
  }

  /**
   * Returns true if the form's j_uri has priority over the saved
   * URL.
   */
  public boolean getFormURIPriority()
  {
    return _formURIPriority;
  }

  /**
   * True if the form's j_uri has priority over the saved URL.
   */
  public void setFormURIPriority(boolean formPriority)
  {
    _formURIPriority = formPriority;
  }

  /**
   * Initialize
   */
  @PostConstruct
  @Override
  public void init()
    throws ServletException
  {
    super.init();

    if (_errorPage == null)
      _errorPage = _loginPage;

    if (_loginPage == null)
      _loginPage = _errorPage;

    if (_loginPage == null)
      throw new ConfigException(L.l("FormLogin needs an form-login-page"));
  }

  /**
   * Returns the authentication type.
   */
  @Override
  public String getAuthType()
  {
    return "Form";
  }

  /**
   * Returns true if the request has a matching login.
   */
  @Override
  public boolean isLoginUsedForRequest(HttpServletRequest request)
  {
    return request.getServletPath().indexOf("j_security_check") >= 0;
  }

  /**
   * Logs a user in with a user name and a password.
   *
   * @param request servlet request
   *
   * @return the logged in principal on success, null on failure.
   */
  @Override
  public Principal getUserPrincipalImpl(HttpServletRequest request)
  {
    Principal user;

    Authenticator auth = getAuthenticator();

    if (auth instanceof CookieAuthenticator) {
      CookieAuthenticator cookieAuth = (CookieAuthenticator) auth;

      Cookie resinAuth = ((CauchoRequest) request).getCookie("resinauthid");

      if (resinAuth != null) {
        user = cookieAuth.authenticateByCookie(resinAuth.getValue());

        if (user != null)
          return user;
      }
    }

    String userName = request.getParameter("j_username");
    String passwordString = request.getParameter("j_password");

    if (userName == null || passwordString == null)
      return null;

    char []password = passwordString.toCharArray();

    BasicPrincipal basicUser = new BasicPrincipal(userName);

    Credentials credentials = new PasswordCredentials(password);

    user = auth.authenticate(basicUser, credentials, request);

    return user;
  }

  /**
   * Returns true if a new login overrides the saved user
   */
  @Override
  protected boolean isSavedUserValid(HttpServletRequest request,
                                     Principal savedUser)
  {
    String userName = request.getParameter("j_username");

    // server/135j
    return userName == null;// || userName.equals(savedUser.getName());
  }

  /**
   * Updates after a successful login
   *
   * @param request servlet request
   * @param response servlet response, in case any cookie need sending.
   *
   * @return the logged in principal on success, null on failure.
   */
  @Override
  public void loginSuccessResponse(Principal user,
                                   HttpServletRequest request,
                                   HttpServletResponse response)
    throws ServletException, IOException
  {
    if (request.getAttribute(LOGIN_CHECK) != null)
      return;
    request.setAttribute(LOGIN_CHECK, "login");

    WebApp webApp = _webApp;

    String jUseCookieAuth = (String) request.getParameter("j_use_cookie_auth");

    Authenticator auth = getAuthenticator();

    if (auth instanceof CookieAuthenticator
        && ((CookieAuthenticator) auth).isCookieSupported(jUseCookieAuth)) {
      CookieAuthenticator cookieAuth = (CookieAuthenticator) auth;

      generateCookie(user, cookieAuth, webApp, request, response);
    }

    String path = request.getServletPath();

    if (path == null)
      path = request.getPathInfo();
    else if (request.getPathInfo() != null)
      path = path + request.getPathInfo();

    if (path.equals("")) {
      // Forward?
      path = request.getContextPath() + "/";
      response.sendRedirect(response.encodeRedirectURL(path));
      return;
    }

    if (path.endsWith("/j_security_check")) {
      RequestDispatcher disp;
      disp = webApp.getNamedDispatcher("j_security_check");

      if (disp == null)
        throw new ServletException(L.l("j_security_check servlet must be defined to use form-based login."));

      disp.forward(request, response);
      return;
    }
  }

  /**
   * Logs a user in with a user name and a password.
   *
   * @param request servlet request
   * @param response servlet response, in case any cookie need sending.
   * @param application servlet application
   *
   * @return the logged in principal on success, null on failure.
   */
  @Override
  public void loginChallenge(HttpServletRequest request,
                             HttpServletResponse response)
    throws ServletException, IOException
  {
    String path = request.getServletPath();

    if (path == null)
      path = request.getPathInfo();
    else if (request.getPathInfo() != null)
      path = path + request.getPathInfo();
    
    if (path.equals("")) {
      // Forward?
      path = request.getContextPath() + "/";
      response.sendRedirect(response.encodeRedirectURL(path));
      return;
    }

    WebApp webApp = _webApp;

    String uri = request.getRequestURI();

    if (path.endsWith("/j_security_check")) {
      // server/12d8, server/12bs

      if (response instanceof CauchoResponse) {
        ((CauchoResponse) response).setNoCache(true);
      }
      else {
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
      }

      RequestDispatcher disp = webApp.getRequestDispatcher(_errorPage);
      disp.forward(request, response);
      return;
    }
    else if (uri.equals(_loginPage) || uri.equals(_errorPage)) {
      request.getRequestDispatcher(path).forward(request, response);
      return;
    }

    HttpSession session = request.getSession();

    session.setAttribute(LOGIN_SAVED_PATH, path);
    session.setAttribute(LOGIN_SAVED_QUERY, request.getQueryString());

    if (response instanceof CauchoResponse) {
      ((CauchoResponse) response).killCache();
      ((CauchoResponse) response).setNoCache(true);
    }
    else {
      response.setHeader("Cache-Control", "no-cache");
    }

    // In case where the authenticator is something like https:/
    if (! _loginPage.startsWith("/")) {
      response.sendRedirect(response.encodeRedirectURL(_loginPage));
      return;
    }

    // Forwards to the loginPage, never redirects according to the spec.
    request.setAttribute(LOGIN_CHECK, "login");
    //RequestDispatcher disp = app.getLoginDispatcher(loginPage);
    RequestDispatcher disp = webApp.getRequestDispatcher(_loginPage);
    disp.forward(request, response);

    if (log.isLoggable(Level.FINE))
      log.fine(this + " request '" + uri + "' has no authenticated user");
  }

  private void generateCookie(Principal user,
                              CookieAuthenticator auth,
                              WebApp webApp,
                              HttpServletRequest request,
                              HttpServletResponse response)
  {
    if (webApp == null)
      return;

    SessionManager manager = webApp.getSessionManager();
    String value = manager.createCookieValue();

    Cookie cookie = new Cookie("resinauthid", value);
    cookie.setVersion(1);

    long cookieMaxAge = 365L * 24L * 3600L * 1000L;

    cookie.setMaxAge((int) (cookieMaxAge / 1000L));
    cookie.setPath("/");
    cookie.setDomain(webApp.generateCookieDomain(request));

    auth.associateCookie(user, value);

    response.addCookie(cookie);
  }
}
