/*******************************************************************************
* Copyright 2009, 2010 Innovation Gate GmbH. All Rights Reserved.
*
* This file is part of the OpenWGA server platform.
*
* OpenWGA 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 3 of the License, or
* (at your option) any later version.
*
* In addition, a special exception is granted by the copyright holders
* of OpenWGA called "OpenWGA plugin exception". You should have received
* a copy of this exception along with OpenWGA in file COPYING.
* If not, see <http://www.openwga.com/gpl-plugin-exception>.
*
* OpenWGA 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenWGA in file COPYING.
* If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package de.innovationgate.wgpublisher;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import de.innovationgate.wga.config.FilterMapping;
import de.innovationgate.wga.modules.ModuleDefinition;
import de.innovationgate.wga.modules.types.WGAServicesSoapProtocolModuleType;
import de.innovationgate.wgpublisher.services.WGAServicesSOAPDisabledServlet;
import de.innovationgate.wgpublisher.services.WGAServicesSOAPFilter;
import de.innovationgate.wgpublisher.url.WGAURLBuilder;
public class WGAFilterChain implements FilterChain {
private LinkedList _filters = new LinkedList();
// hashmap containing a urlpattern list for each filter
private HashMap _filterToURLPatterns = new HashMap();
// the following patterns are blacklisted for filter match
// if they are not explicit whitelisted by _whitelistURLPatterns
private static LinkedList _blacklistURLPatterns = new LinkedList();
static {
_blacklistURLPatterns.add("/static*");
_blacklistURLPatterns.add("/wgadmin*");
_blacklistURLPatterns.add("/admin*");
_blacklistURLPatterns.add("/login*");
_blacklistURLPatterns.add("/logout*");
_blacklistURLPatterns.add("/domainkey*");
_blacklistURLPatterns.add("*.jsp");
_blacklistURLPatterns.add("/plugin-management*");
_blacklistURLPatterns.add("/start*");
_blacklistURLPatterns.add("/joblog*");
_blacklistURLPatterns.add("/favicon.ico");
}
// the following patterns are explicit whitelisted from the blacklist above
private static LinkedList _whitelistURLPatterns = new LinkedList();
static {
_whitelistURLPatterns.add("/plugin-management/html/approval:*");
}
public WGAFilterChain(WGACore core, ServletContext servletContext) {
createFakeSessionFilter(core);
// WGAServices filter
createWGAServicesSOAPFilter(core);
createVirtualHostingFilter(core);
// Filters from registered filter mappings
Iterator filterConfigs = core.getFilterMappings().iterator();
// create filter instances and call init method
while (filterConfigs.hasNext()) {
WGAFilterConfig config = (WGAFilterConfig) filterConfigs.next();
if (config.isEnabled()) {
config.setServletContext(servletContext);
try {
Filter filter = (Filter) Class.forName(config.getFilterClassName(), true, WGACore.getLibraryLoader()).newInstance();
// call init method
filter.init(config);
_filters.add(filter);
_filterToURLPatterns.put(filter, config.getUrlpatterns());
core.getLog().info("Filter '" + config.getFilterName() + "' added to filter chain.");
} catch (Throwable e) {
core.getLog().error("Unable to initialize filter '" + config.getFilterName() + "' - '" + config.getFilterClassName() + "'.", e);
}
}
}
}
private void createWGAServicesSOAPFilter(WGACore core) {
try {
// Determine the protocol implementation to use
String implClass = core.getWgaConfiguration().getWebServiceProtocolImplementation();
ModuleDefinition protocolDefinition = null;
if (implClass != null) {
protocolDefinition = core.getModuleRegistry().getModuleDefinition(WGAServicesSoapProtocolModuleType.class, implClass);
if (protocolDefinition == null) {
core.getLog().error("Unable to use WGAServices protocol implementation " + implClass + " because it is not registered");
}
}
// If not configured or configured not found, try to take the "first best"
if (protocolDefinition == null) {
Iterator<ModuleDefinition> impls = core.getModuleRegistry().getModulesForType(WGAServicesSoapProtocolModuleType.class).values().iterator();
if (impls.hasNext()) {
protocolDefinition = impls.next();
}
}
// Create and init the filter (which we do anyway, even if there are no protocol implementation)
FilterMapping filterMapping = new FilterMapping();
filterMapping.setName("WGAServices SOAP Request Filter");
filterMapping.setEnabled(true);
filterMapping.setImplClassName(WGAServicesSOAPFilter.class.getName());
List<String> patterns = new ArrayList<String>();
patterns.add("/services/*");
patterns.add("//services/*");
filterMapping.setUrlPatterns(patterns);
Map<String, String> parameters = new HashMap<String, String>();
// If we have a definition we pass on the servlet class to the filter, otherwise we used the "SOAP disabled" servlet
if (protocolDefinition != null) {
parameters.put(WGAServicesSOAPFilter.PARAM_PROTOCOL_SERVLET, protocolDefinition.getImplementationClass().getName());
}
else {
parameters.put(WGAServicesSOAPFilter.PARAM_PROTOCOL_SERVLET, WGAServicesSOAPDisabledServlet.class.getName());
}
filterMapping.setInitParameters(parameters);
WGAServicesSOAPFilter filter = new WGAServicesSOAPFilter();
WGAFilterConfig filterConfig = WGAFilterConfig.createFromMapping(filterMapping);
filterConfig.setServletContext(core.getServletContext());
filter.init(filterConfig);
_filters.add(filter);
_filterToURLPatterns.put(filter, filterMapping.getUrlPatterns());
if (protocolDefinition != null) {
core.getLog().info("WGAServices enabled using protocol implementation " + protocolDefinition.getTitle(Locale.getDefault()));
}
else {
core.getLog().info("No WGAServices SOAP protocol implementation available. SOAP Web Service is disabled.");
}
}
catch (Exception e) {
core.getLog().error("Exception initializing WGAServices SOAP protocol", e);
}
}
private void createVirtualHostingFilter(WGACore core) {
try {
FilterMapping filterMapping = new FilterMapping();
filterMapping.setName("WGA Virtual Hosting Filter");
filterMapping.setEnabled(true);
filterMapping.setImplClassName(WGAVirtualHostingFilter.class.getName());
List<String> patterns = new ArrayList<String>();
patterns.add("*");
filterMapping.setUrlPatterns(patterns);
Map<String, String> parameters = new HashMap<String, String>();
filterMapping.setInitParameters(parameters);
WGAVirtualHostingFilter filter = new WGAVirtualHostingFilter();
WGAFilterConfig filterConfig = WGAFilterConfig.createFromMapping(filterMapping);
filterConfig.setServletContext(core.getServletContext());
filter.init(filterConfig);
_filters.add(filter);
_filterToURLPatterns.put(filter, filterMapping.getUrlPatterns());
core.getLog().info("WGA Virtual Hosting filter enabled.");
}
catch (Exception e) {
core.getLog().error("Exception initializing WGA Virtual Hosting Filter", e);
}
}
private void createFakeSessionFilter(WGACore core) {
try {
FilterMapping filterMapping = new FilterMapping();
filterMapping.setName("WGA Fake Session Filter");
filterMapping.setEnabled(true);
filterMapping.setImplClassName(WGAFakeSessionFilter.class.getName());
List<String> patterns = new ArrayList<String>();
patterns.add("*");
filterMapping.setUrlPatterns(patterns);
Map<String, String> parameters = new HashMap<String, String>();
filterMapping.setInitParameters(parameters);
WGAFakeSessionFilter filter = new WGAFakeSessionFilter();
WGAFilterConfig filterConfig = WGAFilterConfig.createFromMapping(filterMapping);
filterConfig.setServletContext(core.getServletContext());
filter.init(filterConfig);
_filters.add(filter);
_filterToURLPatterns.put(filter, filterMapping.getUrlPatterns());
core.getLog().info("WGA Fake Session Filter enabled.");
}
catch (Exception e) {
core.getLog().error("Exception initializing WGA Fake Session Filter", e);
}
}
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
Integer filterIndex = (Integer)request.getAttribute("de.innovationgate.wga.filter.index");
if (filterIndex == null) {
filterIndex = new Integer(0);
request.setAttribute("de.innovationgate.wga.filter.index", filterIndex);
}
if (_filters.size() > filterIndex.intValue()) {
// retrieve filter
Filter filter = (Filter) _filters.get(filterIndex.intValue());
filterIndex = new Integer(filterIndex.intValue() + 1);
request.setAttribute("de.innovationgate.wga.filter.index", filterIndex);
// check URLPattern
if (callFilterWithinThisRequest(request, filter)) {
// call filter
filter.doFilter(request, response, this);
} else {
// go to next filter
doFilter(request, response);
}
} else {
// last filter reached - dispatch to parent chain
((FilterChain)request.getAttribute("de.innovationgate.wga.filter.parentChain")).doFilter(request, response);
}
}
private boolean callFilterWithinThisRequest(ServletRequest request, Filter filter) {
HttpServletRequest hreq = (HttpServletRequest) request;
String url = hreq.getRequestURI().substring(hreq.getContextPath().length());
// retrieve urlpatterns
List urlpatterns = (List)_filterToURLPatterns.get(filter);
if (urlpatterns == null || urlpatterns.isEmpty()) {
return false;
} else {
List<String> blackList = new ArrayList<String>();
blackList.addAll(_blacklistURLPatterns);
List<String> whiteList = new ArrayList<String>();
whiteList.addAll(_whitelistURLPatterns);
if (filter instanceof WGAFilterURLPatternProvider) {
WGAFilterURLPatternProvider provider = (WGAFilterURLPatternProvider)filter;
if (provider.getBlackListURLPatterns() != null) {
blackList.addAll(provider.getBlackListURLPatterns());
}
if (provider.getWhiteListURLPatterns() != null) {
whiteList.addAll(provider.getWhiteListURLPatterns());
}
}
// first check black and whitelist
// if not blacklisted check configured patterns
if (matches(url, blackList) && !(matches(url, whiteList))) {
return false;
} else {
return matches(url, urlpatterns);
}
}
}
private boolean matches(String url, List patterns) {
// first check if url match exactly
if (patterns.contains(url)) {
return true;
} else {
Iterator patternsIt = patterns.iterator();
while (patternsIt.hasNext()) {
String pattern = (String) patternsIt.next();
if (pattern.startsWith("*")) {
pattern = pattern.substring(pattern.indexOf("*")+1);
if (url.endsWith(pattern)) {
return true;
}
} else if (pattern.endsWith("*")) {
pattern = pattern.substring(0, pattern.indexOf("*"));
if (url.startsWith(pattern)) {
return true;
}
}
}
}
return false;
}
public void init(ServletRequest request, FilterChain parentChain) {
request.setAttribute("de.innovationgate.wga.filter.parentChain", parentChain);
}
}