/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program 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 Lesser General Public License for more details.
*
* Copyright 2007 - 2009 Pentaho Corporation. All rights reserved.
*
*/
package org.pentaho.platform.web.http.security;
import java.io.IOException;
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.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.platform.web.http.messages.Messages;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.util.Assert;
/**
* Detects when an HTTP session which contains a logged-in user (as indicated by
* <code>request.getRemoteUser()</code>) is attempting to authenticate again
* without logging out. Upon detecting this condition, the session is
* invalidated, the security context is cleared, and the user is redirected to
* <code>sessionReuseDetectedUrl</code>. This prevents reuse of an HTTP
* session which contains potentially sensitive, user-specific data.
*
* <p>
* To use: Insert after <code>httpSessionContextIntegrationFilter</code> but
* before <code>authenticationProcessingFilter</code>.
* </p>
*
* <p>
* Note: Some code copied from <code>AbstractProcessingFilter</code>.
* </p>
*
* @author mlowery
*/
public class HttpSessionReuseDetectionFilter implements Filter, InitializingBean {
// ~ Static fields/initializers ============================================
private static final Log logger = LogFactory.getLog(HttpSessionReuseDetectionFilter.class);
// ~ Instance fields =======================================================
/**
* This should be the same URL that is set for
* <code>org.springframework.security.ui.webapp.AuthenticationProcessingFilter</code>.
*/
private String filterProcessesUrl;
private String sessionReuseDetectedUrl;
// ~ Constructors ==========================================================
public HttpSessionReuseDetectionFilter() {
super();
}
// ~ Methods ===============================================================
public void init(final FilterConfig filterConfig) throws ServletException {
}
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
throws IOException, ServletException {
if (!(request instanceof HttpServletRequest)) {
throw new ServletException();
}
if (!(response instanceof HttpServletResponse)) {
throw new ServletException();
}
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
if (requiresAuthentication(httpRequest, httpResponse)) {
if (HttpSessionReuseDetectionFilter.logger.isDebugEnabled()) {
HttpSessionReuseDetectionFilter.logger.debug(Messages.getInstance().getString("HttpSessionReuseDetectionFilter.DEBUG_PROCESS_AUTHN")); //$NON-NLS-1$
}
// TODO: this should use LogoutHandlers in latest Spring Security
if (null != httpRequest) {
String remoteUser = httpRequest.getRemoteUser();
if ((null != remoteUser) && (remoteUser.length() > 0)) {
if (HttpSessionReuseDetectionFilter.logger.isDebugEnabled()) {
HttpSessionReuseDetectionFilter.logger
.debug(Messages.getInstance().getString("HttpSessionReuseDetectionFilter.DEBUG_USER_ALREADY_LOGGED_IN", remoteUser)); //$NON-NLS-1$
}
HttpSession session = httpRequest.getSession(false);
if (null != session) {
if (HttpSessionReuseDetectionFilter.logger.isDebugEnabled()) {
HttpSessionReuseDetectionFilter.logger.debug(Messages.getInstance().getString("HttpSessionReuseDetectionFilter.DEBUG_INVALIDATING_SESSION")); //$NON-NLS-1$
}
session.invalidate();
}
SecurityContextHolder.clearContext();
if (HttpSessionReuseDetectionFilter.logger.isDebugEnabled()) {
HttpSessionReuseDetectionFilter.logger.debug(Messages.getInstance().getString(
"HttpSessionReuseDetectionFilter.DEBUG_REDIRECTING", sessionReuseDetectedUrl)); //$NON-NLS-1$
}
httpResponse.sendRedirect(httpResponse.encodeRedirectURL(httpRequest.getContextPath()
+ sessionReuseDetectedUrl));
return;
}
}
}
chain.doFilter(request, response);
}
public void destroy() {
}
public void afterPropertiesSet() throws Exception {
Assert.hasLength(filterProcessesUrl, Messages.getInstance()
.getString("HttpSessionReuseDetectionFilter.ERROR_0001_FILTERPROCESSESURL_NOT_SPECIFIED")); //$NON-NLS-1$
Assert.hasLength(sessionReuseDetectedUrl, Messages.getInstance()
.getString("HttpSessionReuseDetectionFilter.ERROR_0002_SESSIONREUSEDETECTEDURL_NOT_SPECIFIED")); //$NON-NLS-1$
}
/**
* <p>
* Indicates whether this filter should attempt to process a login request
* for the current invocation.
* </p>
*
* <p>
* It strips any parameters from the "path" section of the request URL (such
* as the jsessionid parameter in
* <em>http://host/myapp/index.html;jsessionid=blah</em>) before matching
* against the <code>filterProcessesUrl</code> property.
* </p>
*
* <p>
* Subclasses may override for special requirements, such as Tapestry
* integration.
* </p>
*
* @param request
* as received from the filter chain
* @param response
* as received from the filter chain
*
* @return <code>true</code> if the filter should attempt authentication,
* <code>false</code> otherwise
*/
protected boolean requiresAuthentication(final HttpServletRequest request, final HttpServletResponse response) {
String uri = request.getRequestURI();
int pathParamIndex = uri.indexOf(';');
if (pathParamIndex > 0) {
// strip everything after the first semi-colon
uri = uri.substring(0, pathParamIndex);
}
return uri.endsWith(request.getContextPath() + filterProcessesUrl);
}
public String getFilterProcessesUrl() {
return filterProcessesUrl;
}
public void setFilterProcessesUrl(final String filterProcessesUrl) {
this.filterProcessesUrl = filterProcessesUrl;
}
public String getSessionReuseDetectedUrl() {
return sessionReuseDetectedUrl;
}
public void setSessionReuseDetectedUrl(final String sessionReuseDetectedUrl) {
this.sessionReuseDetectedUrl = sessionReuseDetectedUrl;
}
}