// ========================================================================
// $Id: FormAuthenticator.java,v 1.32 2005/08/13 00:01:27 gregwilkins Exp $
// Copyright 199-2004 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========================================================================
package org.browsermob.proxy.jetty.jetty.servlet;
import org.apache.commons.logging.Log;
import org.browsermob.proxy.jetty.http.*;
import org.browsermob.proxy.jetty.log.LogFactory;
import org.browsermob.proxy.jetty.util.Credential;
import org.browsermob.proxy.jetty.util.Password;
import org.browsermob.proxy.jetty.util.URI;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.Serializable;
import java.security.Principal;
/* ------------------------------------------------------------ */
/** FORM Authentication Authenticator.
* The HTTP Session is used to store the authentication status of the
* user, which can be distributed.
* If the realm implements SSORealm, SSO is supported.
*
* @version $Id: FormAuthenticator.java,v 1.32 2005/08/13 00:01:27 gregwilkins Exp $
* @author Greg Wilkins (gregw)
* @author dan@greening.name
*/
public class FormAuthenticator implements Authenticator
{
static Log log = LogFactory.getLog(FormAuthenticator.class);
/* ------------------------------------------------------------ */
public final static String __J_URI="org.browsermob.proxy.jetty.jetty.URI";
public final static String __J_AUTHENTICATED="org.browsermob.proxy.jetty.jetty.Auth";
public final static String __J_SECURITY_CHECK="j_security_check";
public final static String __J_USERNAME="j_username";
public final static String __J_PASSWORD="j_password";
private String _formErrorPage;
private String _formErrorPath;
private String _formLoginPage;
private String _formLoginPath;
/* ------------------------------------------------------------ */
public String getAuthMethod()
{
return HttpServletRequest.FORM_AUTH;
}
/* ------------------------------------------------------------ */
public void setLoginPage(String path)
{
if (!path.startsWith("/"))
{
log.warn("form-login-page must start with /");
path="/"+path;
}
_formLoginPage=path;
_formLoginPath=path;
if (_formLoginPath.indexOf('?')>0)
_formLoginPath=_formLoginPath.substring(0,_formLoginPath.indexOf('?'));
}
/* ------------------------------------------------------------ */
public String getLoginPage()
{
return _formLoginPage;
}
/* ------------------------------------------------------------ */
public void setErrorPage(String path)
{
if (path==null || path.trim().length()==0)
{
_formErrorPath=null;
_formErrorPage=null;
}
else
{
if (!path.startsWith("/"))
{
log.warn("form-error-page must start with /");
path="/"+path;
}
_formErrorPage=path;
_formErrorPath=path;
if (_formErrorPath!=null && _formErrorPath.indexOf('?')>0)
_formErrorPath=_formErrorPath.substring(0,_formErrorPath.indexOf('?'));
}
}
/* ------------------------------------------------------------ */
public String getErrorPage()
{
return _formErrorPage;
}
/* ------------------------------------------------------------ */
/** Perform form authentication.
* Called from SecurityHandler.
* @return UserPrincipal if authenticated else null.
*/
public Principal authenticate(UserRealm realm,
String pathInContext,
HttpRequest httpRequest,
HttpResponse httpResponse)
throws IOException
{
HttpServletRequest request =(ServletHttpRequest)httpRequest.getWrapper();
HttpServletResponse response = httpResponse==null?null:(HttpServletResponse) httpResponse.getWrapper();
// Handle paths
String uri = pathInContext;
// Setup session
HttpSession session=request.getSession(response!=null);
if (session==null)
return null;
// Handle a request for authentication.
if ( uri.substring(uri.lastIndexOf("/")+1).startsWith(__J_SECURITY_CHECK) )
{
// Check the session object for login info.
FormCredential form_cred=new FormCredential();
form_cred.authenticate(realm,
request.getParameter(__J_USERNAME),
request.getParameter(__J_PASSWORD),
httpRequest);
String nuri=(String)session.getAttribute(__J_URI);
if (nuri==null || nuri.length()==0)
{
nuri=request.getContextPath();
if (nuri.length()==0)
nuri="/";
}
if (form_cred._userPrincipal!=null)
{
// Authenticated OK
if(log.isDebugEnabled())log.debug("Form authentication OK for "+form_cred._jUserName);
session.removeAttribute(__J_URI); // Remove popped return URI.
httpRequest.setAuthType(SecurityConstraint.__FORM_AUTH);
httpRequest.setAuthUser(form_cred._jUserName);
httpRequest.setUserPrincipal(form_cred._userPrincipal);
session.setAttribute(__J_AUTHENTICATED,form_cred);
// Sign-on to SSO mechanism
if (realm instanceof SSORealm)
{
((SSORealm)realm).setSingleSignOn(httpRequest,
httpResponse,
form_cred._userPrincipal,
new Password(form_cred._jPassword));
}
// Redirect to original request
if (response!=null)
{
response.setContentLength(0);
response.sendRedirect(response.encodeRedirectURL(nuri));
}
}
else if (response!=null)
{
if(log.isDebugEnabled())log.debug("Form authentication FAILED for "+form_cred._jUserName);
if (_formErrorPage!=null)
{
response.setContentLength(0);
response.sendRedirect(response.encodeRedirectURL
(URI.addPaths(request.getContextPath(),
_formErrorPage)));
}
else
{
response.sendError(HttpResponse.__403_Forbidden);
}
}
// Security check is always false, only true after final redirection.
return null;
}
// Check if the session is already authenticated.
FormCredential form_cred = (FormCredential) session.getAttribute(__J_AUTHENTICATED);
if (form_cred != null)
{
// We have a form credential. Has it been distributed?
if (form_cred._userPrincipal==null)
{
// This form_cred appears to have been distributed. Need to reauth
form_cred.authenticate(realm, httpRequest);
// Sign-on to SSO mechanism
if (form_cred._userPrincipal!=null && realm instanceof SSORealm)
{
((SSORealm)realm).setSingleSignOn(httpRequest,
httpResponse,
form_cred._userPrincipal,
new Password(form_cred._jPassword));
}
}
else if (!realm.reauthenticate(form_cred._userPrincipal))
// Else check that it is still authenticated.
form_cred._userPrincipal=null;
// If this credential is still authenticated
if (form_cred._userPrincipal!=null)
{
if(log.isDebugEnabled())log.debug("FORM Authenticated for "+form_cred._userPrincipal.getName());
httpRequest.setAuthType(SecurityConstraint.__FORM_AUTH);
httpRequest.setAuthUser(form_cred._userPrincipal.getName());
httpRequest.setUserPrincipal(form_cred._userPrincipal);
return form_cred._userPrincipal;
}
else
session.setAttribute(__J_AUTHENTICATED,null);
}
else if (realm instanceof SSORealm)
{
// Try a single sign on.
Credential cred = ((SSORealm)realm).getSingleSignOn(httpRequest,httpResponse);
if (httpRequest.hasUserPrincipal())
{
form_cred=new FormCredential();
form_cred._userPrincipal=request.getUserPrincipal();
form_cred._jUserName=form_cred._userPrincipal.getName();
if (cred!=null)
form_cred._jPassword=cred.toString();
if(log.isDebugEnabled())log.debug("SSO for "+form_cred._userPrincipal);
httpRequest.setAuthType(SecurityConstraint.__FORM_AUTH);
session.setAttribute(__J_AUTHENTICATED,form_cred);
return form_cred._userPrincipal;
}
}
// Don't authenticate authform or errorpage
if (isLoginOrErrorPage(pathInContext))
return SecurityConstraint.__NOBODY;
// redirect to login page
if (response!=null)
{
if (httpRequest.getQuery()!=null)
uri+="?"+httpRequest.getQuery();
session.setAttribute(__J_URI,
request.getScheme() +
"://" + request.getServerName() +
":" + request.getServerPort() +
URI.addPaths(request.getContextPath(),uri));
response.setContentLength(0);
response.sendRedirect(response.encodeRedirectURL(URI.addPaths(request.getContextPath(),
_formLoginPage)));
}
return null;
}
public boolean isLoginOrErrorPage(String pathInContext)
{
return pathInContext!=null &&
(pathInContext.equals(_formErrorPath) || pathInContext.equals(_formLoginPath));
}
/* ------------------------------------------------------------ */
/** FORM Authentication credential holder.
*/
private static class FormCredential implements Serializable, HttpSessionBindingListener
{
String _jUserName;
String _jPassword;
transient Principal _userPrincipal;
transient UserRealm _realm;
void authenticate(UserRealm realm,String user,String password,HttpRequest request)
{
_jUserName=user;
_jPassword=password;
_userPrincipal = realm.authenticate(user, password, request);
if (_userPrincipal!=null)
_realm=realm;
}
void authenticate(UserRealm realm,HttpRequest request)
{
_userPrincipal = realm.authenticate(_jUserName, _jPassword, request);
if (_userPrincipal!=null)
_realm=realm;
}
public void valueBound(HttpSessionBindingEvent event) {}
public void valueUnbound(HttpSessionBindingEvent event)
{
if(log.isDebugEnabled())log.debug("Logout "+_jUserName);
if(_realm instanceof SSORealm)
((SSORealm)_realm).clearSingleSignOn(_jUserName);
if(_realm!=null && _userPrincipal!=null)
_realm.logout(_userPrincipal);
}
public int hashCode()
{
return _jUserName.hashCode()+_jPassword.hashCode();
}
public boolean equals(Object o)
{
if (!(o instanceof FormCredential))
return false;
FormCredential fc = (FormCredential)o;
return
_jUserName.equals(fc._jUserName) &&
_jPassword.equals(fc._jPassword);
}
public String toString()
{
return "Cred["+_jUserName+"]";
}
}
}