blob: 8eea2576417b2bc9471f3f11131aef29d2a9adc2 [file] [log] [blame]
/*******************************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.ofbiz.webapp.control;
import java.io.IOException;
import java.util.Set;
import java.util.HashSet;
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 org.apache.ofbiz.base.util.Debug;
/*
* A Filter used to specify a whitelist of allowed paths to the OFBiz application.
* Requests that do not match any of the paths listed in allowedPaths are redirected to redirectPath, or an error code
* is returned (the error code can be set in errorCode, the default value is 403).
* If forceRedirectAll is set to Y then allowedPaths is ignored and all requests are redirected to redirectPath; note
* that forceRedirectAll is ignored if redirectPath is not set.
*
* Init parameters:
* - forceRedirectAll: when set to Y, and redirectPath is set, then redirects all traffic to redirectPath
* - allowedPaths: a colon separated list of URL or URI that are allowed;
* non matching request paths are redirected, or an error code is returned,
* according to the setup of redirectPath and errorCode
* - redirectPath: if the path requested is not in the allowedPaths, or forceRedirectAll is set to Y,
* specifies the the path to which the request is redirected to;
* - errorCode: the error code set in the response if the path requested is not in the allowedPaths
* and redirectPath is not set; defaults to 403
*
* Interaction with the context:
* - for its internal logic (to avoid an infinite loop of redirections when forceRedirectAll is set) the filter sets
* a session parameter (_FORCE_REDIRECT_=true) before the first redirection; the parameter is removed during the
* second pass before the request is forwarded to the next filter in the chain
* - the filter skips the check against the whitelist of allowed paths if a request attribute
* with name _FORWARDED_FROM_SERVLET_ is present; this attribute is typically set by the ControlServlet to indicate
* that the request path is safe and should not be checked again
*/
public class ControlFilter implements Filter {
public static final String FORWARDED_FROM_SERVLET = "_FORWARDED_FROM_SERVLET_";
private static final String module = ControlFilter.class.getName();
private boolean redirectAll;
private boolean redirectPathIsUrl;
private String redirectPath;
protected int errorCode;
private Set<String> allowedPaths = new HashSet<>();
@Override
public void init(FilterConfig filterConfig) throws ServletException {
redirectPath = filterConfig.getInitParameter("redirectPath");
redirectPathIsUrl = (redirectPath != null && redirectPath.toLowerCase().startsWith("http"));
String redirectAllString = filterConfig.getInitParameter("forceRedirectAll");
redirectAll = (redirectPath != null && redirectAllString != null && "Y".equalsIgnoreCase(redirectAllString));
String errorCodeString = filterConfig.getInitParameter("errorCode");
errorCode = 403;
if (errorCodeString != null) {
try {
errorCode = Integer.parseInt(errorCodeString);
} catch (NumberFormatException nfe) {
Debug.logWarning(nfe, "Error code specified would not parse to Integer: " + errorCodeString, module);
Debug.logWarning(nfe, "The default error code will be used: " + errorCode, module);
}
}
String allowedPathsString = filterConfig.getInitParameter("allowedPaths");
if (allowedPathsString != null) {
String[] result = allowedPathsString.split(":");
for (int x = 0; x < result.length; x++) {
allowedPaths.add(result[x]);
}
// if an URI is specified in the redirectPath parameter, it is added to the allowed list
if (redirectPath != null && !redirectPathIsUrl) {
allowedPaths.add(redirectPath);
}
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// check if we are told to redirect everything
if (redirectAll) {
// little trick here so we don't loop on ourselves
if (httpRequest.getSession().getAttribute("_FORCE_REDIRECT_") == null) {
httpRequest.getSession().setAttribute("_FORCE_REDIRECT_", "true");
Debug.logWarning("Redirecting user to: " + redirectPath, module);
if (redirectPathIsUrl) {
httpResponse.sendRedirect(redirectPath);
} else {
httpResponse.sendRedirect(httpRequest.getContextPath() + redirectPath);
}
return;
} else {
httpRequest.getSession().removeAttribute("_FORCE_REDIRECT_");
chain.doFilter(httpRequest, httpResponse);
return;
}
}
if (httpRequest.getAttribute(FORWARDED_FROM_SERVLET) == null && !allowedPaths.isEmpty()) {
// check to make sure the requested url is allowed
// get the request URI without the webapp mount point
String requestUri = httpRequest.getRequestURI().substring(httpRequest.getContextPath().length());
int offset = requestUri.indexOf("/", 1);
if (offset == -1) {
offset = requestUri.length();
}
while (!allowedPaths.contains(requestUri.substring(0, offset))) {
offset = requestUri.indexOf("/", offset + 1);
if (offset == -1) {
if (allowedPaths.contains(requestUri)) {
break;
}
// path not allowed
if (redirectPath == null) {
httpResponse.sendError(errorCode, httpRequest.getRequestURI());
} else {
if (redirectPathIsUrl) {
httpResponse.sendRedirect(redirectPath);
} else {
httpResponse.sendRedirect(httpRequest.getContextPath() + redirectPath);
}
}
if (Debug.infoOn()) {
Debug.logInfo("[Filtered request]: " + httpRequest.getRequestURI() + " --> " + (redirectPath == null? errorCode: redirectPath), module);
}
return;
}
}
chain.doFilter(request, httpResponse);
}
}
@Override
public void destroy() {
}
}