/*
* 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);
}
}