/*!
* 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 (c) 2002-2016 Pentaho Corporation.. All rights reserved.
*/
package org.pentaho.platform.web.http.filters;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.platform.api.engine.IPentahoSession;
import org.pentaho.platform.api.mt.ITenant;
import org.pentaho.platform.api.mt.ITenantedPrincipleNameResolver;
import org.pentaho.platform.engine.core.system.PentahoSessionHolder;
import org.pentaho.platform.engine.core.system.PentahoSystem;
import org.pentaho.platform.engine.core.system.UserSession;
import org.pentaho.platform.engine.security.SecurityHelper;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
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 java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
/**
*
* 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 will conditionally look for the parameter in the HTTP
* Header too. 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>
*
* The parameter that this filter looks for can be configured in the init parameters, as well as whether to check the
* request headers. The following shows the defaults used if these settings aren't provided.
*
* <pre>
* <init-param>
* <param-name>CheckHeader</param-name>
* <param-value>true</param-value>
* </init-param>
* <init-param>
* <param-name>RequestParameterName</param-name>
* <param-value>_TRUST_USER_</param-value>
* </init-param>
* <init-param>
* <param-name>HeaderName</param-name>
* <param-value>_TRUST_USER_</param-value>
* </init-param>
* </pre>
*
*
* @see org.pentaho.platform.web.servlet.ProxyServlet
* @author Doug Moran
*
*/
public class ProxyTrustingFilter implements Filter {
FilterConfig filterConfig;
private static final String DefaultParameterName = "_TRUST_USER_"; //$NON-NLS-1$
String[] trustedIpAddrs = null;
private boolean checkHeader = true;
private String requestParameterName;
private String headerName;
private Map<String, Pattern> ipPatterns = new HashMap<String, Pattern>();
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<String> addrs = new ArrayList<String>();
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] );
}
}
String checkHeaderString = filterConfig.getInitParameter( "CheckHeader" ); //$NON-NLS-1$
if ( !isEmpty( checkHeaderString ) ) {
this.checkHeader = checkHeaderString.equalsIgnoreCase( "true" ); //$NON-NLS-1$
}
String requestParameterSetting = filterConfig.getInitParameter( "RequestParameterName" ); //$NON-NLS-1$
if ( !isEmpty( requestParameterSetting ) ) {
this.requestParameterName = requestParameterSetting;
} else {
this.requestParameterName = ProxyTrustingFilter.DefaultParameterName;
}
String headerNameSetting = filterConfig.getInitParameter( "HeaderName" ); //$NON-NLS-1$
if ( !isEmpty( headerNameSetting ) ) {
this.headerName = headerNameSetting;
} else {
this.headerName = ProxyTrustingFilter.DefaultParameterName;
}
}
boolean isTrusted( final String addr ) {
if ( trustedIpAddrs != null ) {
for ( String element : trustedIpAddrs ) {
if ( element.equals( addr ) ) {
return ( true );
}
// Reuse an pattern for this element
Pattern pat = ipPatterns.get( element );
if ( pat == null ) {
// first one created, put it in the map for re-use
try {
pat = Pattern.compile( element );
ipPatterns.put( element, pat );
} catch ( PatternSyntaxException ignored ) {
continue;
}
}
Matcher matcher = pat.matcher( addr );
if ( matcher.find() ) {
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 ) ) {
final HttpServletRequest req = (HttpServletRequest) request;
String remoteHost = req.getRemoteAddr();
if ( isTrusted( remoteHost ) ) {
String name = getTrustUser( req );
if ( !isEmpty( name ) ) {
try {
becomeUser( name );
HttpSession httpSession = req.getSession();
httpSession.setAttribute( PentahoSystem.PENTAHO_SESSION_KEY, PentahoSessionHolder.getSession() );
/**
* 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( SecurityContextHolder.getContext().getAuthentication() );
httpSession.setAttribute( HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, authWrapper );
} catch ( Exception e ) {
throw new ServletException( e );
}
}
}
}
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$
}
public void destroy() {
}
/**
* @param args
*/
public static void main( final String[] args ) {
}
/**
*
* @return the name of the request header that will contain the trusted user name
*/
protected String getHeaderName() {
return this.headerName;
}
/**
* @return the name of the request parameter that will contain the trusted user name
*/
protected String getParameterName() {
return this.requestParameterName;
}
/**
* @return true if the filter should consult the http header for the trusted user
*/
protected boolean checkHeader() {
return checkHeader;
}
/**
* Gets the trusted user from the request, and optionally from the HTTP Header
*
* @param request
* The HttpServletRequest to examine for the trusted information
* @return The name of the trusted user
*/
protected String getTrustUser( HttpServletRequest request ) {
String name = request.getParameter( getParameterName() );
if ( checkHeader() && isEmpty( name ) ) {
name = request.getHeader( normalizeHeaderName( getHeaderName() ) );
}
return name;
}
public boolean isEmpty( String str ) {
return ( ( str == null ) || ( str.length() == 0 ) );
}
protected String normalizeHeaderName( final String in ) {
String lower = in.toLowerCase();
return Character.toUpperCase( lower.charAt( 0 ) ) + lower.substring( 1 );
}
// cloned from SecurityHelper and adapted to add a default location to the session
protected void becomeUser( final String principalName ) {
UserSession session = null;
Locale locale = Locale.getDefault();
ITenantedPrincipleNameResolver tenantedUserNameUtils =
PentahoSystem.get( ITenantedPrincipleNameResolver.class, "tenantedUserNameUtils", null );
if ( tenantedUserNameUtils != null ) {
session = new UserSession( principalName, locale, false, null );
ITenant tenant = tenantedUserNameUtils.getTenant( principalName );
session.setAttribute( IPentahoSession.TENANT_ID_KEY, tenant.getId() );
session.setAuthenticated( tenant.getId(), principalName );
} else {
session = new UserSession( principalName, locale, false, null );
session.setAuthenticated( principalName );
}
PentahoSessionHolder.setSession( session );
Authentication auth = SecurityHelper.getInstance().createAuthentication( principalName );
SecurityContextHolder.getContext().setAuthentication( auth );
PentahoSystem.sessionStartup( PentahoSessionHolder.getSession(), null );
}
}