/*
* 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.filters;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
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.HttpSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.platform.api.engine.IPentahoSession;
import org.pentaho.platform.api.engine.IUserDetailsRoleListService;
import org.pentaho.platform.engine.core.system.PentahoSessionHolder;
import org.pentaho.platform.engine.core.system.PentahoSystem;
import org.pentaho.platform.engine.security.SecurityHelper;
import org.springframework.security.Authentication;
import org.springframework.security.context.HttpSessionContextIntegrationFilter;
import org.springframework.security.context.SecurityContext;
/**
*
* This servlet is used to filter Servlet requests coming from another server
* for processing and sets authentication for the user passed in by the
* parameter <b>_TRUST_USER_</b>. It then passes the request down the servlet
* chain to be serviced. Only requests coming from a trusted host will be
* authenticated. Implement the filter and setup the trusted hosts by editing
* the <b>web.xml</b> file as follows.
* <p>
*
* <pre>
*
* <filter>
* <filter-name>ProxyTrustingFilter</filter-name>
* <filter-class>org.pentaho.platform.web.http.filters.ProxyTrustingFilter</filter-class>
* <init-param>
* <param-name>TrustedIpAddrs</param-name>
* <param-value>192.168.10.60,192.168.10.61</param-value>
* </init-param>
* </filter>
* </pre>
*
* In the above example, when a request coming from IP addresses 192.168.10.60
* and 192.168.10.61 has the parameter _TRUST_USER_=<i>name</i> set, tha user
* <i>name</i> will be authenticated.
*
* <p>
* NOTES:
* <p>
*
* It is easy to spoof the URL or IP address so this technique should only be
* used if the server running the filter is not accessible to users. For example
* if the BI Platform is hosted in a DMZ.
* <p>
*
* For this class to be useful, both Pentaho servers should be using the same
* database repository.
* <p>
* The sending server should be using the ProxyServlet enabled to generate the
* requests.
* <p>
*
* @see org.pentaho.platform.web.servlet.ProxyServlet
* @author Doug Moran
*
*/
public class ProxyTrustingFilter implements Filter {
FilterConfig filterConfig;
String[] trustedIpAddrs = null;
private static final Log logger = LogFactory.getLog(ProxyTrustingFilter.class);
public Log getLogger() {
return ProxyTrustingFilter.logger;
}
public void init(final FilterConfig filterConfiguration) throws ServletException {
this.filterConfig = filterConfiguration;
trustedIpAddrs = null;
String hostStr = filterConfig.getInitParameter("TrustedIpAddrs"); //$NON-NLS-1$
if (hostStr != null) {
StringTokenizer st = new StringTokenizer(hostStr, ","); //$NON-NLS-1$
List addrs = new ArrayList();
while (st.hasMoreTokens()) {
String tok = st.nextToken().trim();
if (tok.length() > 0) {
addrs.add(tok);
// getLogger().info(
// Messages.getString("ProxyTrustingFilter.DEBUG_0001_TRUSTING",
// tok ) ); //$NON-NLS-1$
}
}
if (addrs.size() > 0) { // Guarantee that its null or has at least 1
// element
trustedIpAddrs = (String[]) addrs.toArray(new String[0]);
}
}
}
boolean isTrusted(final String addr) {
if (trustedIpAddrs != null) {
for (String element : trustedIpAddrs) {
if (element.equals(addr)) {
return (true);
}
}
}
return (false);
}
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException,
ServletException {
// long startTime = System.currentTimeMillis();
if ((trustedIpAddrs != null) && (request instanceof HttpServletRequest)) {
HttpServletRequest req = (HttpServletRequest) request;
String remoteHost = req.getRemoteAddr();
if (isTrusted(remoteHost)) {
String name = request.getParameter("_TRUST_USER_"); //$NON-NLS-1$
if ((name != null) && (name.length() > 0)) {
PentahoSystem.systemEntryPoint();
try {
IPentahoSession userSession = null;
boolean reset = false;
IPentahoSession existingSession = (IPentahoSession) req.getSession().getAttribute(
IPentahoSession.PENTAHO_SESSION_KEY);
if ( existingSession != null ) { // We have an existing session...
// If the existing session isn't initialized, or if it's not our trust user, we need
// to abandon the existing session, and bind the new information into the HttpSession and
// to the Servlet Authentication objects.
if ( (existingSession.getName() == null) || (!(name.equals(existingSession.getName())) ) ) {
// req.getSession().removeAttribute(IPentahoSession.PENTAHO_SESSION_KEY); // Explicitly unbind it
// existingSession = null; // null it so that logic below will cause it to be bound
reset = true;
}
}
IUserDetailsRoleListService userDetailsRoleListService = PentahoSystem.get(IUserDetailsRoleListService.class);
if (reset || ((existingSession == null) && (userDetailsRoleListService != null) )) {
HttpSession httpSession = req.getSession();
userSession = userDetailsRoleListService.getEffectiveUserSession(name, null );
if (existingSession != null) {
copyAttributesBetweenSessions(userSession, existingSession);
userSession = existingSession;
}
Authentication auth = (Authentication) userSession.getAttribute(SecurityHelper.SESSION_PRINCIPAL);
httpSession.setAttribute(IPentahoSession.PENTAHO_SESSION_KEY, userSession);
/**
* definition of anonymous inner class
*/
SecurityContext authWrapper = new SecurityContext() {
/**
*
*/
private static final long serialVersionUID = 1L;
private Authentication authentication;
public Authentication getAuthentication() {
return authentication;
};
public void setAuthentication(Authentication authentication) {
this.authentication = authentication;
};
}; // end anonymous inner class
authWrapper.setAuthentication(auth);
httpSession.setAttribute(HttpSessionContextIntegrationFilter.SPRING_SECURITY_CONTEXT_KEY,
authWrapper);
PentahoSessionHolder.setSession(userSession);
}
} finally {
PentahoSystem.systemExitPoint();
}
}
}
}
chain.doFilter(request, response);
// long stopTime = System.currentTimeMillis();
// getLogger().debug( Messages.getString(
// Messages.getString("ProxyTrustingFilter.DEBUG_0004_REQUEST_TIME"),
// String.valueOf( stopTime - startTime ) ) ); //$NON-NLS-1$
}
private void copyAttributesBetweenSessions(IPentahoSession from, IPentahoSession to) {
// Clear
Iterator it = to.getAttributeNames();
while (it.hasNext()) {
to.removeAttribute((String)it.next());
}
it = from.getAttributeNames();
while (it.hasNext()) {
String attrName = (String)it.next();
to.setAttribute(attrName, from.getAttribute(attrName));
}
}
public void destroy() {
}
/**
* @param args
*/
public static void main(final String[] args) {
}
}