This is an automated email from the ASF dual-hosted git repository. jleroux pushed a commit to branch release18.12 in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git
commit 9fa3dbe1eaaff766cf92c8f6f8e9913b0e8f5091 Author: Jacques Le Roux <[email protected]> AuthorDate: Sun Apr 5 10:04:15 2020 +0200 Revert "Merge branch 'JacquesLeRoux-POC-for-CSRF-Token-OFBIZ-11306' into trunk" This reverts commit 0add8bedbca231ffd839eb733f1041ce5487e9d6. --- .../humanres/template/category/CategoryTree.ftl | 16 +- .../category/ftl/CatalogAltUrlSeoTransform.java | 8 +- .../product/template/category/CategoryTree.ftl | 2 +- .../java/org/apache/ofbiz/common/CommonEvents.java | 3 +- .../common/webcommon/WEB-INF/common-controller.xml | 4 +- framework/security/config/security.properties | 22 +- .../apache/ofbiz/security/CsrfDefenseStrategy.java | 93 ----- .../java/org/apache/ofbiz/security/CsrfUtil.java | 358 ----------------- .../ofbiz/security/ICsrfDefenseStrategy.java | 55 --- .../ofbiz/security/NoCsrfDefenseStrategy.java | 50 --- .../org/apache/ofbiz/security/CsrfUtilTests.java | 264 ------------- .../webapp/config/freemarkerTransforms.properties | 2 - framework/webapp/dtd/site-conf.xsd | 14 - .../ofbiz/webapp/control/ConfigXMLReader.java | 3 - .../ofbiz/webapp/control/ControlEventListener.java | 3 - .../ofbiz/webapp/control/RequestHandler.java | 438 ++++++++++----------- .../ofbiz/webapp/ftl/CsrfTokenAjaxTransform.java | 75 ---- .../webapp/ftl/CsrfTokenPairNonAjaxTransform.java | 76 ---- .../webtools/groovyScripts/entity/CheckDb.groovy | 7 +- .../webtools/groovyScripts/entity/EntityRef.groovy | 6 - framework/webtools/template/entity/CheckDb.ftl | 28 +- .../webtools/template/entity/EntityRefList.ftl | 9 +- framework/webtools/template/entity/ViewGeneric.ftl | 1 - .../widget/renderer/macro/MacroFormRenderer.java | 14 +- themes/bluelight/template/Header.ftl | 6 +- .../common-theme/template/includes/ListLocales.ftl | 2 +- .../template/macro/CsvFormMacroLibrary.ftl | 2 +- .../template/macro/FoFormMacroLibrary.ftl | 2 +- .../template/macro/HtmlFormMacroLibrary.ftl | 8 +- .../template/macro/TextFormMacroLibrary.ftl | 2 +- .../template/macro/XlsFormMacroLibrary.ftl | 2 +- .../template/macro/XmlFormMacroLibrary.ftl | 2 +- .../webapp/common/js/util/OfbizUtil.js | 12 +- themes/flatgrey/template/Header.ftl | 6 +- themes/rainbowstone/template/includes/Header.ftl | 4 - .../rainbowstone/template/includes/TopAppBar.ftl | 2 +- themes/tomahawk/template/AppBarClose.ftl | 2 +- themes/tomahawk/template/Header.ftl | 4 - 38 files changed, 263 insertions(+), 1344 deletions(-) diff --git a/applications/humanres/template/category/CategoryTree.ftl b/applications/humanres/template/category/CategoryTree.ftl index f14bbfc..10a08ac 100644 --- a/applications/humanres/template/category/CategoryTree.ftl +++ b/applications/humanres/template/category/CategoryTree.ftl @@ -61,18 +61,18 @@ var rawdata = [ "plugins" : [ "themes", "json_data","ui" ,"cookies", "types", "crrm", "contextmenu"], "json_data" : { "data" : rawdata, - "ajax" : { "url" : "<@ofbizUrl>getHRChild</@ofbizUrl>", "type" : "POST", - "data" : function (n) { - return { + "ajax" : { "url" : "<@ofbizUrl>getHRChild</@ofbizUrl>", "type" : "POST", + "data" : function (n) { + return { "partyId" : n.attr ? n.attr("id").replace("node_","") : 1 , "additionParam" : "','category" , "hrefString" : "viewprofile?partyId=" , "onclickFunction" : "callDocument" - }; + }; }, - success : function(data) { - return data.hrTree; - } + success : function(data) { + return data.hrTree; + } } }, "types" : { @@ -92,7 +92,7 @@ var rawdata = [ } function callDocument(id,type) { - window.location = "viewprofile?partyId=" + id + "&<@csrfTokenPair>viewprofile</@csrfTokenPair>"; + window.location = "viewprofile?partyId=" + id; } function callEmplDocument(id,type) { diff --git a/applications/product/src/main/java/org/apache/ofbiz/product/category/ftl/CatalogAltUrlSeoTransform.java b/applications/product/src/main/java/org/apache/ofbiz/product/category/ftl/CatalogAltUrlSeoTransform.java index 8b56c55..c986f1e 100644 --- a/applications/product/src/main/java/org/apache/ofbiz/product/category/ftl/CatalogAltUrlSeoTransform.java +++ b/applications/product/src/main/java/org/apache/ofbiz/product/category/ftl/CatalogAltUrlSeoTransform.java @@ -25,14 +25,12 @@ import java.util.Map; import javax.servlet.http.HttpServletRequest; -import org.apache.ofbiz.security.CsrfUtil; import org.apache.ofbiz.base.util.Debug; import org.apache.ofbiz.base.util.UtilValidate; import org.apache.ofbiz.base.util.template.FreeMarkerWorker; import org.apache.ofbiz.entity.Delegator; import org.apache.ofbiz.entity.GenericEntityException; import org.apache.ofbiz.entity.GenericValue; -import org.apache.ofbiz.entity.util.EntityQuery; import org.apache.ofbiz.entity.util.EntityUtilProperties; import org.apache.ofbiz.product.category.CatalogUrlFilter; import org.apache.ofbiz.product.category.CategoryContentWrapper; @@ -50,6 +48,7 @@ import freemarker.template.SimpleNumber; import freemarker.template.SimpleScalar; import freemarker.template.TemplateModelException; import freemarker.template.TemplateTransformModel; +import org.apache.ofbiz.entity.util.EntityQuery; public class CatalogAltUrlSeoTransform implements TemplateTransformModel { public final static String module = CatalogUrlSeoTransform.class.getName(); @@ -127,11 +126,6 @@ public class CatalogAltUrlSeoTransform implements TemplateTransformModel { url = CatalogUrlFilter.makeCategoryUrl(request, previousCategoryId, productCategoryId, productId, viewSize, viewIndex, viewSort, searchString); } } - - // add / update csrf token to link when required - String tokenValue = CsrfUtil.generateTokenForNonAjax(request, "product"); - url = CsrfUtil.addOrUpdateTokenInUrl(url, tokenValue); - // make the link if (fullPath) { try { diff --git a/applications/product/template/category/CategoryTree.ftl b/applications/product/template/category/CategoryTree.ftl index dd4ca21..dce62c7 100644 --- a/applications/product/template/category/CategoryTree.ftl +++ b/applications/product/template/category/CategoryTree.ftl @@ -65,7 +65,7 @@ var rawdata = [ "plugins" : [ "themes", "json_data","ui" ,"cookies", "types"], "json_data" : { "data" : rawdata, - "ajax" : { "url" : "getChild", + "ajax" : { "url" : "<@ofbizUrl>getChild</@ofbizUrl>", "type" : "POST", "data" : function (n) { return { diff --git a/framework/common/src/main/java/org/apache/ofbiz/common/CommonEvents.java b/framework/common/src/main/java/org/apache/ofbiz/common/CommonEvents.java index 34fbb3b..d6b104c 100644 --- a/framework/common/src/main/java/org/apache/ofbiz/common/CommonEvents.java +++ b/framework/common/src/main/java/org/apache/ofbiz/common/CommonEvents.java @@ -77,8 +77,7 @@ public class CommonEvents { "thisRequestUri", "org.apache.tomcat.util.net.secure_protocol_version", "userLogin", - "impersonateLogin", - "requestMapMap" // requestMapMap is used by CSRFUtil + "impersonateLogin" }; /** Simple event to set the users per-session locale setting. The user's locale diff --git a/framework/common/webcommon/WEB-INF/common-controller.xml b/framework/common/webcommon/WEB-INF/common-controller.xml index b2cd339..80407c6 100644 --- a/framework/common/webcommon/WEB-INF/common-controller.xml +++ b/framework/common/webcommon/WEB-INF/common-controller.xml @@ -75,7 +75,7 @@ under the License. <response name="error" type="view" value="login"/> </request-map> <request-map uri="logout"> - <security https="true" auth="true" csrf-token="false"/> + <security https="true" auth="true"/> <event type="java" path="org.apache.ofbiz.webapp.control.LoginWorker" invoke="logout"/> <response name="success" type="request-redirect" value="main"/> <response name="error" type="view" value="main"/> @@ -317,7 +317,7 @@ under the License. <!-- Set TimeZone from user's browser --> <!-- XXX The auth setting is inconsistent with the one in the service for a good reason, see OFBIZ-10471 for an explanation --> - <request-map uri="SetTimeZoneFromBrowser" method="post"> + <request-map uri="SetTimeZoneFromBrowser"> <security https="false" auth="false"/> <event type="service" invoke="SetTimeZoneFromBrowser"/> <response name="success" type="request" value="json"/> diff --git a/framework/security/config/security.properties b/framework/security/config/security.properties index 2a639c5..b9e0b2e 100644 --- a/framework/security/config/security.properties +++ b/framework/security/config/security.properties @@ -1,4 +1,4 @@ -############################################################################## +############################################################################### # 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 @@ -149,27 +149,9 @@ security.jwt.token.expireTime=1800 # -- To make this work you also have to configure a secret key with security.token.key security.internal.sso.enabled=false -# -- The secret key for the JWT token signature. Read Passwords and JWT (JSON Web Tokens) usage documentation to choose the way you want to store this key +# -- The secret key for the JWT token signature. Read Passwords and JWT (JSON Web Tokens) usage documentation to choose the way you want to store this key security.token.key=security.token.key # -- By default the SameSite value in SameSiteFilter is strict. This allows to change it ot lax if needed SameSiteCookieAttribute= -# -- The cache size for the Tokens Maps that stores the CSRF tokens. -# -- RemoveEldestEntry is used when it's get above csrf.cache.size -# -- Default is 5000 -# -- TODO: separate tokenMap from partyTokenMap -csrf.cache.size= - -# -- Parameter name for CSRF token. Default is "csrf" if not specified -csrf.tokenName.nonAjax= - -# -- The csrf.entity.request.limit is used to show how to avoid cluttering the Tokens Maps cache with URIs starting with "entity/" -# -- It can be useful with large Database contents, ie with a large numbers of tuples, like "entity/edit/Agreement/10000, etc. -# -- The same principle can be extended to other cases similar to "entity/" URIs (harcoded or using similar properties). -# -- Default is 3 -csrf.entity.request.limit= - -# csrf defense strategy. Default is org.apache.ofbiz.security.CsrfDefenseStrategy if not specified. -# use org.apache.ofbiz.security.NoCsrfDefenseStrategy to disable CSRF check totally. -csrf.defense.strategy= \ No newline at end of file diff --git a/framework/security/src/main/java/org/apache/ofbiz/security/CsrfDefenseStrategy.java b/framework/security/src/main/java/org/apache/ofbiz/security/CsrfDefenseStrategy.java deleted file mode 100644 index 5b72990..0000000 --- a/framework/security/src/main/java/org/apache/ofbiz/security/CsrfDefenseStrategy.java +++ /dev/null @@ -1,93 +0,0 @@ -/******************************************************************************* - * 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.security; - -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; - -import javax.servlet.http.HttpServletRequest; - -import org.apache.ofbiz.base.util.Debug; -import org.apache.ofbiz.base.util.UtilProperties; -import org.apache.ofbiz.webapp.control.RequestHandlerExceptionAllowExternalRequests; - -public class CsrfDefenseStrategy implements ICsrfDefenseStrategy { - - public static final String module = CsrfDefenseStrategy.class.getName(); - private static SecureRandom secureRandom = null; - private static final String prng = "SHA1PRNG"; - private static final String CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - private static int csrfEntityErequestLimit = (int) Long.parseLong(UtilProperties.getPropertyValue("security", "csrf.entity.request.limit", "3")); - - static{ - try { - secureRandom = SecureRandom.getInstance(prng); - } catch (NoSuchAlgorithmException e) { - Debug.logError(e, module); - } - } - - @Override - public String generateToken() { - StringBuilder sb = new StringBuilder(); - for (int i = 1; i < 12 + 1; i++) { - int index = secureRandom.nextInt(CHARSET.length()); - char c = CHARSET.charAt(index); - sb.append(c); - } - return sb.toString(); - } - - @Override - public int maxSubFolderInRequestUrlForTokenMapLookup(String requestUri){ - if (requestUri.startsWith("entity/")){ - return csrfEntityErequestLimit; - } - return 0; - } - - @Override - public boolean modifySecurityCsrfToken(String requestUri, String requestMapMethod, String securityCsrfToken) { - // main request URI is exempted from CSRF token check - if (requestUri.equals("main")) { - return false; - } else { - return !"false".equals(securityCsrfToken); - } - } - - - @Override - public boolean keepTokenAfterUse(String requestUri, String requestMethod) { - // to allow back and forth browser buttons to work, - // token value is unchanged when request.getMethod is GET - if ("GET".equals(requestMethod)) { - return true; - } - return false; - } - - @Override - public void invalidTokenResponse(String requestUri, HttpServletRequest request) throws RequestHandlerExceptionAllowExternalRequests { - request.setAttribute("_ERROR_MESSAGE_", - "Invalid or missing CSRF token to path '" + request.getPathInfo() + "'. Click <a href='" - + request.getContextPath() + "'>here</a> to continue."); - throw new RequestHandlerExceptionAllowExternalRequests(); - } -} diff --git a/framework/security/src/main/java/org/apache/ofbiz/security/CsrfUtil.java b/framework/security/src/main/java/org/apache/ofbiz/security/CsrfUtil.java deleted file mode 100644 index 9d400b8..0000000 --- a/framework/security/src/main/java/org/apache/ofbiz/security/CsrfUtil.java +++ /dev/null @@ -1,358 +0,0 @@ -/******************************************************************************* - * 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.security; - -import java.net.MalformedURLException; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; -import javax.ws.rs.core.MultivaluedHashMap; - -import org.apache.commons.lang.StringUtils; -import org.apache.cxf.jaxrs.model.URITemplate; -import org.apache.ofbiz.base.component.ComponentConfig; -import org.apache.ofbiz.base.util.Debug; -import org.apache.ofbiz.base.util.UtilGenerics; -import org.apache.ofbiz.base.util.UtilProperties; -import org.apache.ofbiz.base.util.UtilValidate; -import org.apache.ofbiz.entity.GenericValue; -import org.apache.ofbiz.webapp.control.ConfigXMLReader; -import org.apache.ofbiz.webapp.control.RequestHandler; -import org.apache.ofbiz.webapp.control.RequestHandlerException; -import org.apache.ofbiz.webapp.control.RequestHandlerExceptionAllowExternalRequests; -import org.apache.ofbiz.webapp.control.WebAppConfigurationException; - -public class CsrfUtil { - - public static final String module = CsrfUtil.class.getName(); - public static String tokenNameNonAjax = UtilProperties.getPropertyValue("security", "csrf.tokenName.nonAjax", "csrf"); - public static ICsrfDefenseStrategy strategy; - private static int cacheSize = (int) Long.parseLong(UtilProperties.getPropertyValue("security", "csrf.cache.size", "5000")); - private static LinkedHashMap<String, Map<String, Map<String, String>>> csrfTokenCache = new LinkedHashMap<String, Map<String, Map<String, String>>>() { - private static final long serialVersionUID = 1L; - protected boolean removeEldestEntry(Map.Entry<String, Map<String, Map<String, String>>> eldest) { - return size() > cacheSize; // TODO use also csrf.cache.size here? - } - }; - - private CsrfUtil() { - } - - static { - try { - String className = UtilProperties.getPropertyValue("security", "csrf.defense.strategy", CsrfDefenseStrategy.class.getCanonicalName()); - Class<?> c = Class.forName(className); - strategy = (ICsrfDefenseStrategy)c.newInstance(); - } catch (Exception e){ - Debug.logError(e, module); - strategy = new CsrfDefenseStrategy(); - } - } - - public static Map<String, String> getTokenMap(HttpServletRequest request, String targetContextPath) { - - HttpSession session = request.getSession(); - GenericValue userLogin = (GenericValue) session.getAttribute("userLogin"); - String partyId = null; - if (userLogin != null && userLogin.get("partyId") != null) { - partyId = userLogin.getString("partyId"); - } - - Map<String, String> tokenMap = null; - if (UtilValidate.isNotEmpty(partyId)) { - Map<String, Map<String, String>> partyTokenMap = csrfTokenCache.get(partyId); - if (partyTokenMap == null) { - partyTokenMap = new HashMap<String, Map<String, String>>(); - csrfTokenCache.put(partyId, partyTokenMap); - } - - tokenMap = partyTokenMap.get(targetContextPath); - if (tokenMap == null) { - tokenMap = new LinkedHashMap<String, String>() { - private static final long serialVersionUID = 1L; - protected boolean removeEldestEntry(Map.Entry<String, String> eldest) { - return size() > cacheSize; - } - }; - partyTokenMap.put(targetContextPath, tokenMap); - } - } else { - tokenMap = UtilGenerics.cast(session.getAttribute("CSRF-Token")); - if (tokenMap == null) { - tokenMap = new LinkedHashMap<String, String>() { - private static final long serialVersionUID = 1L; - protected boolean removeEldestEntry(Map.Entry<String, String> eldest) { - return size() > cacheSize; - } - }; - session.setAttribute("CSRF-Token", tokenMap); - } - } - return tokenMap; - } - - private static String generateToken() { - return strategy.generateToken(); - } - - /** - * Reduce number of subfolder from request uri, if needed, before using it to generate CSRF token. - * @param requestUri - * @return - */ - static String getRequestUriWithSubFolderLimit(String requestUri){ - int limit = CsrfUtil.strategy.maxSubFolderInRequestUrlForTokenMapLookup(requestUri); - if (limit<1){ - return requestUri; - } - while(StringUtils.countMatches(requestUri, "/")+1>limit){ - requestUri = requestUri.substring(0, requestUri.lastIndexOf("/")); - } - return requestUri; - } - - static String getRequestUriFromPath(String pathOrRequestUri){ - String requestUri = pathOrRequestUri; - // remove any query string - if (requestUri.contains("?")) { - // e.g. "/viewprofile?partyId=Company" to "/viewprofile" - requestUri = requestUri.substring(0, requestUri.indexOf("?")); - } - String controlServletPart = "/control/"; // TODO remove with OFBIZ-11229 - if (requestUri.contains(controlServletPart)) { - // e.g. "/partymgr/control/viewprofile" to "viewprofile" - requestUri = requestUri.substring(requestUri.indexOf(controlServletPart) + controlServletPart.length()); - } - if (requestUri.startsWith("/")) { - // e.g. "/viewprofile" to "viewprofile" - requestUri = requestUri.substring(1); - } - if (requestUri.contains("#")){ - // e.g. "view/entityref_main#org.apache.ofbiz.accounting.budget" to "view/entityref_main" - requestUri = requestUri.substring(0, requestUri.indexOf("#")); - } - return requestUri; - } - - /** - * Generate CSRF token for non-ajax request if required and add it as key to token map in session When token map - * size limit is reached, the eldest entry will be deleted each time a new entry is added. - * Token only generated for up to 3 subfolders in the path so 'entity/find/Budget/0001' & 'entity/find/Budget/0002' - * should share the same CSRF token. - * - * @param request - * @param pathOrRequestUri - * @return csrf token - */ - public static String generateTokenForNonAjax(HttpServletRequest request, String pathOrRequestUri) { - if (UtilValidate.isEmpty(pathOrRequestUri) - || pathOrRequestUri.startsWith("javascript") - || pathOrRequestUri.startsWith("#") ) { - return ""; - } - - if (pathOrRequestUri.contains("/")) { - pathOrRequestUri = pathOrRequestUri.replaceAll("/", "/"); - } - - String requestUri = getRequestUriWithSubFolderLimit(getRequestUriFromPath(pathOrRequestUri)); - - Map<String, String> tokenMap = null; - - ConfigXMLReader.RequestMap requestMap = null; - // TODO when OFBIZ-11354 will be done this will need to be removed even if it should be OK as is - if (pathOrRequestUri.contains("/control/")) { - tokenMap = getTokenMap(request, "/" + RequestHandler.getRequestUri(pathOrRequestUri)); - requestMap = findRequestMap(pathOrRequestUri); - } else { - tokenMap = getTokenMap(request, request.getContextPath()); - Map<String, ConfigXMLReader.RequestMap> requestMapMap = UtilGenerics - .cast(request.getAttribute("requestMapMap")); - requestMap = findRequestMap(requestMapMap, pathOrRequestUri); - } - if (requestMap == null) { - Debug.logError("Cannot find the corresponding request map for path: " + pathOrRequestUri, module); - } - String tokenValue = ""; - if (requestMap != null && requestMap.securityCsrfToken) { - if (tokenMap.containsKey(requestUri)) { - tokenValue = tokenMap.get(requestUri); - } else { - tokenValue = generateToken(); - tokenMap.put(requestUri, tokenValue); - } - } - return tokenValue; - } - - static ConfigXMLReader.RequestMap findRequestMap(String _urlWithControlPath){ - - String requestUri = getRequestUriFromPath(_urlWithControlPath); - - List<ComponentConfig.WebappInfo> webappInfos = ComponentConfig.getAllWebappResourceInfos().stream() - .filter(line -> line.contextRoot.contains(RequestHandler.getRequestUri(_urlWithControlPath))) - .collect(Collectors.toList()); - - ConfigXMLReader.RequestMap requestMap = null; - if (UtilValidate.isNotEmpty(webappInfos)) { - try { - if (StringUtils.countMatches(requestUri, "/")==1){ - requestMap = ConfigXMLReader.getControllerConfig(webappInfos.get(0)).getRequestMapMap() - .get(requestUri.substring(0, requestUri.indexOf("/"))); - } else { - requestMap = ConfigXMLReader.getControllerConfig(webappInfos.get(0)).getRequestMapMap() - .get(requestUri); - } - } catch (WebAppConfigurationException | MalformedURLException e) { - Debug.logError(e, module); - } - } - return requestMap; - } - - static ConfigXMLReader.RequestMap findRequestMap(Map<String, ConfigXMLReader.RequestMap> requestMapMap, - String _urlWithoutControlPath) { - String path = _urlWithoutControlPath; - if (_urlWithoutControlPath.startsWith("/")) { - path = _urlWithoutControlPath.substring(1); - } - int charPos = path.indexOf("?"); - if (charPos != -1) { - path = path.substring(0, charPos); - } - MultivaluedHashMap<String, String> vars = new MultivaluedHashMap<>(); - for (Map.Entry<String, ConfigXMLReader.RequestMap> entry : requestMapMap.entrySet()) { - URITemplate uriTemplate = URITemplate.createExactTemplate(entry.getKey()); - // Check if current path the URI template exactly. - if (uriTemplate.match(path, vars) && vars.getFirst(URITemplate.FINAL_MATCH_GROUP).equals("/")) { - return entry.getValue(); - } - } - // the path could be request uri with orderride - if (path.contains("/")) { - return requestMapMap.get(path.substring(0, path.indexOf("/"))); - } - return null; - } - - /** - * generate csrf token for AJAX and add it as value to token cache - * - * @param request - * @return csrf token - */ - public static String generateTokenForAjax(HttpServletRequest request) { - HttpSession session = request.getSession(); - String tokenValue = (String) session.getAttribute("X-CSRF-Token"); - if (tokenValue == null) { - tokenValue = generateToken(); - session.setAttribute("X-CSRF-Token", tokenValue); - } - return tokenValue; - } - - /** - * get csrf token for AJAX - * - * @param session - * @return csrf token - */ - public static String getTokenForAjax(HttpSession session) { - return (String) session.getAttribute("X-CSRF-Token"); - } - - public static String addOrUpdateTokenInUrl(String link, String csrfToken) { - if (link.contains(CsrfUtil.tokenNameNonAjax)) { - return link.replaceFirst("\\b"+CsrfUtil.tokenNameNonAjax+"=.*?(&|$)", CsrfUtil.tokenNameNonAjax+"=" + csrfToken + "$1"); - } else if (!"".equals(csrfToken)) { - if (link.contains("?")) { - return link + "&"+CsrfUtil.tokenNameNonAjax+"=" + csrfToken; - } else { - return link + "?"+CsrfUtil.tokenNameNonAjax+"=" + csrfToken; - } - } - return link; - } - - public static String addOrUpdateTokenInQueryString(String link, String csrfToken) { - if (UtilValidate.isNotEmpty(link)) { - if (link.contains(CsrfUtil.tokenNameNonAjax)) { - return link.replaceFirst("\\b"+CsrfUtil.tokenNameNonAjax+"=.*?(&|$)", CsrfUtil.tokenNameNonAjax+"=" + csrfToken + "$1"); - } else { - if (UtilValidate.isNotEmpty(csrfToken)) { - return link + "&"+CsrfUtil.tokenNameNonAjax+"=" + csrfToken; - } else { - return link; - } - } - } else { - return CsrfUtil.tokenNameNonAjax+"=" + csrfToken; - } - } - - public static void checkToken(HttpServletRequest request, String _path) - throws RequestHandlerException, RequestHandlerExceptionAllowExternalRequests { - String path = _path; - if (_path.startsWith("/")) { - path = _path.substring(1); - } - if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With")) && !"GET".equals(request.getMethod())) { - String csrfToken = request.getHeader("X-CSRF-Token"); - HttpSession session = request.getSession(); - if ((UtilValidate.isEmpty(csrfToken) || !csrfToken.equals(CsrfUtil.getTokenForAjax(session))) - && !"/SetTimeZoneFromBrowser".equals(request.getPathInfo())) { // TODO maybe this can be improved... - throw new RequestHandlerException( - "Invalid or missing CSRF token for AJAX call to path '" + request.getPathInfo() + "'"); - } - } else { - Map<String, String> tokenMap = CsrfUtil.getTokenMap(request, request.getContextPath()); - String csrfToken = request.getParameter(CsrfUtil.tokenNameNonAjax); - String limitPath = getRequestUriWithSubFolderLimit(path); - if (UtilValidate.isNotEmpty(csrfToken) && tokenMap.containsKey(limitPath) - && csrfToken.equals(tokenMap.get(limitPath))) { - if (!CsrfUtil.strategy.keepTokenAfterUse(path,request.getMethod())) { - tokenMap.remove(limitPath); - } - } else { - CsrfUtil.strategy.invalidTokenResponse(path, request); - } - } - } - - public static void cleanupTokenMap(HttpSession session) { - GenericValue userLogin = (GenericValue) session.getAttribute("userLogin"); - String partyId = null; - if (userLogin != null && userLogin.get("partyId") != null) { - partyId = userLogin.getString("partyId"); - Map<String, Map<String, String>> partyTokenMap = csrfTokenCache.get(partyId); - if (partyTokenMap != null) { - String contextPath = session.getServletContext().getContextPath(); - partyTokenMap.remove(contextPath); - if (partyTokenMap.isEmpty()) { - csrfTokenCache.remove(partyId); - } - } - } - } -} diff --git a/framework/security/src/main/java/org/apache/ofbiz/security/ICsrfDefenseStrategy.java b/framework/security/src/main/java/org/apache/ofbiz/security/ICsrfDefenseStrategy.java deleted file mode 100644 index 322afb5..0000000 --- a/framework/security/src/main/java/org/apache/ofbiz/security/ICsrfDefenseStrategy.java +++ /dev/null @@ -1,55 +0,0 @@ -/******************************************************************************* - * 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.security; - -import javax.servlet.http.HttpServletRequest; - -import org.apache.ofbiz.webapp.control.RequestHandlerExceptionAllowExternalRequests; - -public interface ICsrfDefenseStrategy { - - String generateToken(); - - /** - * Limit the number of subfolders in request uri to reduce the number of CSRF tokens needed. - * @param requestUri - * @return - */ - int maxSubFolderInRequestUrlForTokenMapLookup(String requestUri); - - /** - * Override security csrf-token value in request map - * @param requestUri - * @param requestMapMethod get, post or all - * @param securityCsrfToken - * @return - */ - boolean modifySecurityCsrfToken(String requestUri, String requestMapMethod, String securityCsrfToken); - - /** - * Whether to reuse the token after it is consumed - * @param requestUri - * @param requestMethod GET, POST, or PUT - * @return - */ - boolean keepTokenAfterUse(String requestUri, String requestMethod); - - void invalidTokenResponse(String requestUri, HttpServletRequest request) throws RequestHandlerExceptionAllowExternalRequests; - -} \ No newline at end of file diff --git a/framework/security/src/main/java/org/apache/ofbiz/security/NoCsrfDefenseStrategy.java b/framework/security/src/main/java/org/apache/ofbiz/security/NoCsrfDefenseStrategy.java deleted file mode 100644 index 279310c..0000000 --- a/framework/security/src/main/java/org/apache/ofbiz/security/NoCsrfDefenseStrategy.java +++ /dev/null @@ -1,50 +0,0 @@ -/******************************************************************************* - * 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.security; - -import javax.servlet.http.HttpServletRequest; - -public class NoCsrfDefenseStrategy implements ICsrfDefenseStrategy { - - @Override - public String generateToken() { - return null; - } - - @Override - public int maxSubFolderInRequestUrlForTokenMapLookup(String requestUri){ - return 0; - } - - @Override - public boolean modifySecurityCsrfToken(String requestUri, String requestMapMethod, String securityCsrfToken) { - // all SecurityCsrfToken checks in request maps are read as false - return false; - } - - @Override - public boolean keepTokenAfterUse(String requestUri, String requestMethod) { - return false; - } - - @Override - public void invalidTokenResponse(String requestUri, HttpServletRequest request) { - - } -} \ No newline at end of file diff --git a/framework/security/src/test/java/org/apache/ofbiz/security/CsrfUtilTests.java b/framework/security/src/test/java/org/apache/ofbiz/security/CsrfUtilTests.java deleted file mode 100644 index 53d0096..0000000 --- a/framework/security/src/test/java/org/apache/ofbiz/security/CsrfUtilTests.java +++ /dev/null @@ -1,264 +0,0 @@ -/* - * 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.security; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; - -import org.apache.ofbiz.entity.GenericValue; -import org.apache.ofbiz.webapp.control.ConfigXMLReader; -import org.junit.Test; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - -public class CsrfUtilTests { - - @Test - public void testGetTokenMap(){ - HttpServletRequest request = mock(HttpServletRequest.class); - HttpSession session = mock(HttpSession.class); - when(request.getSession()).thenReturn(session); - - // prepare the token map to be retrieved from session - Map<String,String> tokenMap = new LinkedHashMap<String, String>(); - tokenMap.put("uri_1","abcd"); - when(session.getAttribute("CSRF-Token")).thenReturn(tokenMap); - - // without userLogin in session, test token map is retrieved from session - Map<String, String> resultMap = CsrfUtil.getTokenMap(request, ""); - assertEquals("abcd", resultMap.get("uri_1")); - - // add userLogin to session - GenericValue userLogin = mock(GenericValue.class); - when(userLogin.get("partyId")).thenReturn("10000"); - when(userLogin.getString("partyId")).thenReturn("10000"); - when(session.getAttribute("userLogin")).thenReturn(userLogin); - - // with userLogin in session, test token map is not retrieved from session - resultMap = CsrfUtil.getTokenMap(request, "/partymgr"); - assertNull(resultMap.get("uri_1")); - - } - - @Test - public void testGetRequestUriWithSubFolderLimit(){ - CsrfUtil.strategy = new CsrfDefenseStrategy(); - - // limit only when request uri starts with 'entity' - String limitRequestUri = CsrfUtil.getRequestUriWithSubFolderLimit("entity/find/Budget/0002"); - assertEquals("entity/find/Budget", limitRequestUri); - - limitRequestUri = CsrfUtil.getRequestUriWithSubFolderLimit("a/b/c/d"); - assertEquals("a/b/c/d", limitRequestUri); - } - - @Test - public void testGetRequestUriFromPath(){ - String requestUri = CsrfUtil.getRequestUriFromPath("/viewprofile?partyId=Company"); - assertEquals("viewprofile", requestUri); - - requestUri = CsrfUtil.getRequestUriFromPath("/partymgr/control/viewprofile"); - assertEquals("viewprofile", requestUri); - - requestUri = CsrfUtil.getRequestUriFromPath("view/entityref_main#org.apache.ofbiz.accounting.budget"); - assertEquals("view/entityref_main", requestUri); - } - - - @Test - public void testGenerateTokenForNonAjax() throws ParserConfigurationException { - HttpServletRequest request = mock(HttpServletRequest.class); - HttpSession session = mock(HttpSession.class); - when(request.getSession()).thenReturn(session); - - // add userLogin to session - GenericValue userLogin = mock(GenericValue.class); - when(userLogin.get("partyId")).thenReturn("10000"); - when(userLogin.getString("partyId")).thenReturn("10000"); - when(session.getAttribute("userLogin")).thenReturn(userLogin); - - String token = CsrfUtil.generateTokenForNonAjax(request, ""); - assertEquals("", token); - - token = CsrfUtil.generateTokenForNonAjax(request, "javascript:"); - assertEquals("", token); - - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - DocumentBuilder builder = dbf.newDocumentBuilder(); - Document doc = builder.newDocument(); - - Map<String, ConfigXMLReader.RequestMap> requestMapMap = new HashMap<>(); - { - Element requestMapElement = doc.createElement("request-map"); - requestMapElement.setAttribute("uri", "checkLogin"); - ConfigXMLReader.RequestMap requestMap = new ConfigXMLReader.RequestMap(requestMapElement); - requestMapMap.put(requestMap.uri, requestMap); - } - { - Element requestMapElement = doc.createElement("request-map"); - requestMapElement.setAttribute("uri", "entity/find/{entityName}/{pkValues: .*}"); - ConfigXMLReader.RequestMap requestMap = new ConfigXMLReader.RequestMap(requestMapElement); - requestMapMap.put(requestMap.uri, requestMap); - } - when(request.getAttribute("requestMapMap")).thenReturn(requestMapMap); - - token = CsrfUtil.generateTokenForNonAjax(request, "checkLogin"); - assertNotEquals("", token); - - CsrfUtil.strategy = new CsrfDefenseStrategy(); - - token = CsrfUtil.generateTokenForNonAjax(request, "entity/find/Budget/0001"); - assertNotEquals("", token); - - String token2 = CsrfUtil.generateTokenForNonAjax(request, "entity/find/Budget/0001"); - // test support for treating "/" as "/" - assertEquals(token2, token); - - token2 = CsrfUtil.generateTokenForNonAjax(request, "entity/find/Budget/0002"); - // token only generated for up to 3 subfolders in the path - assertEquals(token2, token); - } - - @Test - public void testFindRequestMapWithoutControlPath() throws ParserConfigurationException { - - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - DocumentBuilder builder = dbf.newDocumentBuilder(); - Document doc = builder.newDocument(); - - Map<String, ConfigXMLReader.RequestMap> requestMapMap = new HashMap<>(); - { - Element requestMapElement = doc.createElement("request-map"); - requestMapElement.setAttribute("uri", "checkLogin"); - ConfigXMLReader.RequestMap requestMap = new ConfigXMLReader.RequestMap(requestMapElement); - requestMapMap.put(requestMap.uri, requestMap); - } - // REST request like /entity/find/AccommodationClass - { - Element requestMapElement = doc.createElement("request-map"); - requestMapElement.setAttribute("uri", "entity/find/{entityName}"); - ConfigXMLReader.RequestMap requestMap = new ConfigXMLReader.RequestMap(requestMapElement); - requestMapMap.put(requestMap.uri, requestMap); - } - // View override like /view/ModelInduceFromDb - { - Element requestMapElement = doc.createElement("request-map"); - requestMapElement.setAttribute("uri", "view"); - ConfigXMLReader.RequestMap requestMap = new ConfigXMLReader.RequestMap(requestMapElement); - requestMapMap.put(requestMap.uri, requestMap); - } - { - Element requestMapElement = doc.createElement("request-map"); - requestMapElement.setAttribute("uri", "ModelInduceFromDb"); - ConfigXMLReader.RequestMap requestMap = new ConfigXMLReader.RequestMap(requestMapElement); - requestMapMap.put(requestMap.uri, requestMap); - } - - // test usual request - ConfigXMLReader.RequestMap requestMap = CsrfUtil.findRequestMap(requestMapMap, "/checkLogin"); - assertEquals(requestMap.uri, "checkLogin"); - - // test usual request - requestMap = CsrfUtil.findRequestMap(requestMapMap, "checkLogin"); - assertEquals(requestMap.uri, "checkLogin"); - - // test REST request - requestMap = CsrfUtil.findRequestMap(requestMapMap, "/entity/find/AccommodationClass"); - assertEquals(requestMap.uri, "entity/find/{entityName}"); - - // test view orderride - requestMap = CsrfUtil.findRequestMap(requestMapMap, "/view/ModelInduceFromDb"); - assertEquals(requestMap.uri, "view"); - - } - - @Test - public void testGenerateTokenForAjax() { - HttpServletRequest request = mock(HttpServletRequest.class); - HttpSession session = mock(HttpSession.class); - when(request.getSession()).thenReturn(session); - when(session.getAttribute("X-CSRF-Token")).thenReturn("abcd"); - - String token = CsrfUtil.generateTokenForAjax(request); - assertEquals("abcd", token); - } - - @Test - public void testGetTokenForAjax(){ - HttpSession session = mock(HttpSession.class); - when(session.getAttribute("X-CSRF-Token")).thenReturn("abcd"); - - String token = CsrfUtil.getTokenForAjax(session); - assertEquals("abcd", token); - } - - @Test - public void testAddOrUpdateTokenInUrl(){ - CsrfUtil.tokenNameNonAjax = "csrfToken"; - - // test link without csrfToken - String url = CsrfUtil.addOrUpdateTokenInUrl("https://localhost:8443/catalog/control/login", "abcd"); - assertEquals("https://localhost:8443/catalog/control/login?csrfToken=abcd", url); - - // test link with query string and without csrfToken - url = CsrfUtil.addOrUpdateTokenInUrl("https://localhost:8443/partymgr/control/EditCommunicationEvent?communicationEventId=10000", "abcd"); - assertEquals("https://localhost:8443/partymgr/control/EditCommunicationEvent?communicationEventId=10000&csrfToken=abcd", url); - - // test link with csrfToken - url = CsrfUtil.addOrUpdateTokenInUrl("https://localhost:8443/catalog/control/login?csrfToken=abcd", "efgh"); - assertEquals("https://localhost:8443/catalog/control/login?csrfToken=efgh", url); - - // test link with csrfToken amd empty csrfToken replacement - url = CsrfUtil.addOrUpdateTokenInUrl("https://localhost:8443/catalog/control/login?csrfToken=abcd", ""); - assertEquals("https://localhost:8443/catalog/control/login?csrfToken=", url); - } - - @Test - public void testAddOrUpdateTokenInQueryString(){ - CsrfUtil.tokenNameNonAjax = "csrfToken"; - - String queryString = CsrfUtil.addOrUpdateTokenInQueryString("", "abcd"); - assertEquals(queryString, "csrfToken=abcd"); - - queryString = CsrfUtil.addOrUpdateTokenInQueryString("csrfToken=abcd&a=b", "efgh"); - assertEquals(queryString, "csrfToken=efgh&a=b"); - - queryString = CsrfUtil.addOrUpdateTokenInQueryString("csrfToken=abcd&a=b", ""); - assertEquals(queryString, "csrfToken=&a=b"); - - queryString = CsrfUtil.addOrUpdateTokenInQueryString("a=b", "abcd"); - assertEquals(queryString, "a=b&csrfToken=abcd"); - - queryString = CsrfUtil.addOrUpdateTokenInQueryString("a=b", ""); - assertEquals(queryString, "a=b"); - } -} diff --git a/framework/webapp/config/freemarkerTransforms.properties b/framework/webapp/config/freemarkerTransforms.properties index f6a2f6b..535e48d 100644 --- a/framework/webapp/config/freemarkerTransforms.properties +++ b/framework/webapp/config/freemarkerTransforms.properties @@ -28,5 +28,3 @@ ofbizAmount=org.apache.ofbiz.webapp.ftl.OfbizAmountTransform setRequestAttribute=org.apache.ofbiz.webapp.ftl.SetRequestAttributeMethod renderWrappedText=org.apache.ofbiz.webapp.ftl.RenderWrappedTextTransform setContextField=org.apache.ofbiz.webapp.ftl.SetContextFieldTransform -csrfTokenAjax=org.apache.ofbiz.webapp.ftl.CsrfTokenAjaxTransform -csrfTokenPair=org.apache.ofbiz.webapp.ftl.CsrfTokenPairNonAjaxTransform diff --git a/framework/webapp/dtd/site-conf.xsd b/framework/webapp/dtd/site-conf.xsd index 01d0046..fc9a966 100644 --- a/framework/webapp/dtd/site-conf.xsd +++ b/framework/webapp/dtd/site-conf.xsd @@ -305,20 +305,6 @@ under the License. </xs:documentation> </xs:annotation> </xs:attribute> - <xs:attribute name="csrf-token" use="optional" default=""> - <xs:annotation> - <xs:documentation> - If true csrf token is expected. If false no csrf token check. Default to "". - </xs:documentation> - </xs:annotation> - <xs:simpleType> - <xs:restriction base="xs:token"> - <xs:enumeration value=""/> - <xs:enumeration value="true"/> - <xs:enumeration value="false"/> - </xs:restriction> - </xs:simpleType> - </xs:attribute> </xs:attributeGroup> <xs:element name="metric"> <xs:annotation> diff --git a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/ConfigXMLReader.java b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/ConfigXMLReader.java index e350b95..8181eb8 100644 --- a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/ConfigXMLReader.java +++ b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/ConfigXMLReader.java @@ -51,7 +51,6 @@ import org.apache.ofbiz.base.util.cache.UtilCache; import org.apache.ofbiz.base.util.collections.MapContext; import org.apache.ofbiz.base.util.collections.MultivaluedMapContext; import org.apache.ofbiz.base.util.collections.MultivaluedMapContextAdapter; -import org.apache.ofbiz.security.CsrfUtil; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -466,7 +465,6 @@ public class ConfigXMLReader { public Event event; public boolean securityHttps = true; public boolean securityAuth = false; - public boolean securityCsrfToken = true; public boolean securityCert = false; public boolean securityExternalView = true; public boolean securityDirectRequest = true; @@ -498,7 +496,6 @@ public class ConfigXMLReader { this.securityCert = "true".equals(securityElement.getAttribute("cert")); this.securityExternalView = !"false".equals(securityElement.getAttribute("external-view")); this.securityDirectRequest = !"false".equals(securityElement.getAttribute("direct-request")); - this.securityCsrfToken = CsrfUtil.strategy.modifySecurityCsrfToken(this.uri, this.method, securityElement.getAttribute("csrf-token")); } // Check for event Element eventElement = UtilXml.firstChildElement(requestMapElement, "event"); diff --git a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/ControlEventListener.java b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/ControlEventListener.java index 2bb0aab..353b56b 100644 --- a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/ControlEventListener.java +++ b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/ControlEventListener.java @@ -26,7 +26,6 @@ import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; -import org.apache.ofbiz.security.CsrfUtil; import org.apache.ofbiz.base.util.Debug; import org.apache.ofbiz.base.util.UtilDateTime; import org.apache.ofbiz.base.util.UtilGenerics; @@ -70,8 +69,6 @@ public class ControlEventListener implements HttpSessionListener { public void sessionDestroyed(HttpSessionEvent event) { HttpSession session = event.getSession(); - CsrfUtil.cleanupTokenMap(session); - // Finalize the Visit boolean beganTransaction = false; try { diff --git a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/RequestHandler.java b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/RequestHandler.java index b18fa8d..e1d1745 100644 --- a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/RequestHandler.java +++ b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/RequestHandler.java @@ -18,11 +18,12 @@ *******************************************************************************/ package org.apache.ofbiz.webapp.control; +import java.net.MalformedURLException; +import org.apache.ofbiz.base.location.FlexibleLocation; import static org.apache.ofbiz.base.util.UtilGenerics.checkMap; import java.io.IOException; import java.io.Serializable; -import java.net.MalformedURLException; import java.net.URL; import java.security.cert.X509Certificate; import java.util.Collection; @@ -32,20 +33,13 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Objects; import java.util.Optional; -import java.util.function.Predicate; -import java.util.stream.Stream; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -import javax.ws.rs.core.MultivaluedHashMap; -import org.apache.cxf.jaxrs.model.URITemplate; -import org.apache.ofbiz.base.location.FlexibleLocation; -import org.apache.ofbiz.security.CsrfUtil; import org.apache.ofbiz.base.util.Debug; import org.apache.ofbiz.base.util.SSLUtil; import org.apache.ofbiz.base.util.StringUtil; @@ -56,13 +50,13 @@ import org.apache.ofbiz.base.util.UtilMisc; import org.apache.ofbiz.base.util.UtilObject; import org.apache.ofbiz.base.util.UtilProperties; import org.apache.ofbiz.base.util.UtilValidate; +import org.apache.ofbiz.base.util.collections.MultivaluedMapContext; import org.apache.ofbiz.entity.Delegator; import org.apache.ofbiz.entity.GenericEntityException; import org.apache.ofbiz.entity.GenericValue; import org.apache.ofbiz.entity.util.EntityQuery; import org.apache.ofbiz.entity.util.EntityUtilProperties; import org.apache.ofbiz.webapp.OfbizUrlBuilder; -import org.apache.ofbiz.webapp.control.ConfigXMLReader.ControllerConfig; import org.apache.ofbiz.webapp.control.ConfigXMLReader.RequestMap; import org.apache.ofbiz.webapp.event.EventFactory; import org.apache.ofbiz.webapp.event.EventHandler; @@ -81,6 +75,8 @@ import org.apache.ofbiz.widget.model.ThemeFactory; public class RequestHandler { public static final String module = RequestHandler.class.getName(); + private final static String defaultStatusCodeString = + UtilProperties.getPropertyValue("requestHandler", "status-code", "302"); private final ViewFactory viewFactory; private final EventFactory eventFactory; private final URL controllerConfigURL; @@ -88,6 +84,66 @@ public class RequestHandler { private final boolean trackVisit; private ControllerConfig ccfg; + static class ControllerConfig { + private final MultivaluedMapContext<String, RequestMap> requestMapMap; + private final Map<String, ConfigXMLReader.ViewMap> viewMapMap; + private String statusCodeString; + private final String defaultRequest; + private final Map<String, ConfigXMLReader.Event> firstVisitEventList; + private final Map<String, ConfigXMLReader.Event> preprocessorEventList; + private final Map<String, ConfigXMLReader.Event> postprocessorEventList; + private final String protectView; + + ControllerConfig(ConfigXMLReader.ControllerConfig ccfg) throws WebAppConfigurationException { + preprocessorEventList = ccfg.getPreprocessorEventList(); + postprocessorEventList = ccfg.getPostprocessorEventList(); + requestMapMap = ccfg.getRequestMapMultiMap(); + viewMapMap = ccfg.getViewMapMap(); + defaultRequest = ccfg.getDefaultRequest(); + firstVisitEventList = ccfg.getFirstVisitEventList(); + protectView = ccfg.getProtectView(); + + String status = ccfg.getStatusCode(); + statusCodeString = UtilValidate.isEmpty(status) ? defaultStatusCodeString : status; + } + + public MultivaluedMapContext<String, RequestMap> getRequestMapMap() { + return requestMapMap; + } + + public Map<String, ConfigXMLReader.ViewMap> getViewMapMap() { + return viewMapMap; + } + + public String getStatusCodeString() { + return statusCodeString; + } + + public String getDefaultRequest() { + return defaultRequest; + } + + public void setStatusCodeString(String statusCodeString) { + this.statusCodeString = statusCodeString; + } + + public Map<String, ConfigXMLReader.Event> getFirstVisitEventList() { + return firstVisitEventList; + } + + public Map<String, ConfigXMLReader.Event> getPreprocessorEventList() { + return preprocessorEventList; + } + + public Map<String, ConfigXMLReader.Event> getPostprocessorEventList() { + return postprocessorEventList; + } + + public String getProtectView() { + return protectView; + } + } + public static RequestHandler getRequestHandler(ServletContext servletContext) { RequestHandler rh = (RequestHandler) servletContext.getAttribute("_REQUEST_HANDLER_"); if (rh == null) { @@ -124,7 +180,7 @@ public class RequestHandler { } /** - * Finds a collection of request maps in {@code ccfg} matching {@code req}. + * Find a collection of request maps in {@code ccfg} matching {@code req}. * Otherwise fall back to matching the {@code defaultReq} field in {@code ccfg}. * * @param ccfg The controller containing the current configuration @@ -132,25 +188,22 @@ public class RequestHandler { * @return a collection of request maps which might be empty */ static Collection<RequestMap> resolveURI(ControllerConfig ccfg, HttpServletRequest req) { - Map<String, List<RequestMap>> requestMapMap = ccfg.getRequestMapMultiMap(); - Collection<RequestMap> rmaps = resolveTemplateURI(requestMapMap, req); - if (rmaps.isEmpty()) { - Map<String, ConfigXMLReader.ViewMap> viewMapMap = ccfg.getViewMapMap(); - String defaultRequest = ccfg.getDefaultRequest(); - String path = req.getPathInfo(); - String requestUri = getRequestUri(path); - String overrideViewUri = getOverrideViewUri(path); - if (requestMapMap.containsKey(requestUri) - // Ensure that overridden view exists. - && (overrideViewUri == null || viewMapMap.containsKey(overrideViewUri) - || ("SOAPService".equals(requestUri) && "wsdl".equalsIgnoreCase(req.getQueryString())))){ - rmaps = requestMapMap.get(requestUri); - req.setAttribute("overriddenView", overrideViewUri); - } else if (defaultRequest != null) { - rmaps = requestMapMap.get(defaultRequest); - } else { - rmaps = null; - } + Map<String, List<RequestMap>> requestMapMap = ccfg.getRequestMapMap(); + Map<String, ConfigXMLReader.ViewMap> viewMapMap = ccfg.getViewMapMap(); + String defaultRequest = ccfg.getDefaultRequest(); + String path = req.getPathInfo(); + String requestUri = getRequestUri(path); + String viewUri = getOverrideViewUri(path); + Collection<RequestMap> rmaps; + if (requestMapMap.containsKey(requestUri) + // Ensure that overridden view exists. + && (viewUri == null || viewMapMap.containsKey(viewUri) + || ("SOAPService".equals(requestUri) && "wsdl".equalsIgnoreCase(req.getQueryString())))){ + rmaps = requestMapMap.get(requestUri); + } else if (defaultRequest != null) { + rmaps = requestMapMap.get(defaultRequest); + } else { + rmaps = null; } return rmaps != null ? rmaps : Collections.emptyList(); } @@ -179,33 +232,6 @@ public class RequestHandler { } } - /** - * Finds the request maps matching a segmented path. - * - * <p>A segmented path can match request maps where the {@code uri} attribute - * contains an URI template like in the {@code foo/bar/{baz}} example. - * - * @param rMapMap the map associating URIs to a list of request maps corresponding to different HTTP methods - * @param request the HTTP request to match - * @return a collection of request maps which might be empty but not {@code null} - */ - private static Collection<RequestMap> resolveTemplateURI(Map<String, List<RequestMap>> rMapMap, - HttpServletRequest request) { - // Retrieve the request path without the leading '/' character. - String path = request.getPathInfo().substring(1); - MultivaluedHashMap<String, String> vars = new MultivaluedHashMap<>(); - for (Map.Entry<String, List<RequestMap>> entry : rMapMap.entrySet()) { - URITemplate uriTemplate = URITemplate.createExactTemplate(entry.getKey()); - // Check if current path the URI template exactly. - if (uriTemplate.match(path, vars) && vars.getFirst("FINAL_MATCH_GROUP").equals("/")) { - // Set attributes from template variables to be used in context. - uriTemplate.getVariables().forEach(var -> request.setAttribute(var, vars.getFirst(var))); - return entry.getValue(); - } - } - return Collections.emptyList(); - } - public void doRequest(HttpServletRequest request, HttpServletResponse response, String chain, GenericValue userLogin, Delegator delegator) throws RequestHandlerException, RequestHandlerExceptionAllowExternalRequests { @@ -216,7 +242,7 @@ public class RequestHandler { // Parse controller config. try { - ccfg = ConfigXMLReader.getControllerConfig(controllerConfigURL); + ccfg = new ControllerConfig(getControllerConfig()); } catch (WebAppConfigurationException e) { Debug.logError(e, "Exception thrown while parsing controller.xml file: ", module); throw new RequestHandlerException(e); @@ -241,27 +267,18 @@ public class RequestHandler { String path = request.getPathInfo(); String requestUri = getRequestUri(path); + String overrideViewUri = getOverrideViewUri(path); Collection<RequestMap> rmaps = resolveURI(ccfg, request); if (rmaps.isEmpty()) { if (throwRequestHandlerExceptionOnMissingLocalRequest) { - if (path.contains("/checkLogin/")) { - // Nested requests related with checkLogin uselessly clutter the log. There is nothing to worry about, better remove this wrong error message. - return; - } else if (path.contains("/images/") || path.contains("d.png")) { - if (Debug.warningOn()) Debug.logWarning("You should check if this request is really a problem or a false alarm: " + request.getRequestURL(), module); - throw new RequestHandlerException(requestMissingErrorMessage); - } else { - throw new RequestHandlerException(requestMissingErrorMessage); - } + throw new RequestHandlerException(requestMissingErrorMessage); } else { - throw new RequestHandlerExceptionAllowExternalRequests(); + throw new RequestHandlerExceptionAllowExternalRequests(); } } - // The "overriddenView" attribute is set by resolveURI when necessary. - String overrideViewUri = (String) request.getAttribute("overriddenView"); - String method = UtilHttp.getRequestMethod(request); + String method = request.getMethod(); RequestMap requestMap = resolveMethod(method, rmaps).orElseThrow(() -> { String msg = UtilProperties.getMessage("WebappUiLabels", "RequestMethodNotMatchConfig", UtilMisc.toList(requestUri, method), UtilHttp.getLocale(request)); @@ -280,7 +297,7 @@ public class RequestHandler { // Check for chained request. if (chain != null) { String chainRequestUri = RequestHandler.getRequestUri(chain); - requestMap = ccfg.getRequestMapMap().get(chainRequestUri); + requestMap = ccfg.getRequestMapMap().getFirst(chainRequestUri); if (requestMap == null) { throw new RequestHandlerException("Unknown chained request [" + chainRequestUri + "]; this request does not exist"); } @@ -304,11 +321,11 @@ public class RequestHandler { // Check to make sure we are allowed to access this request directly. (Also checks if this request is defined.) // If the request cannot be called, or is not defined, check and see if there is a default-request we can process if (!requestMap.securityDirectRequest) { - if (ccfg.getDefaultRequest() == null || !ccfg.getRequestMapMap().get(ccfg.getDefaultRequest()).securityDirectRequest) { + if (ccfg.getDefaultRequest() == null || !ccfg.getRequestMapMap().getFirst(ccfg.getDefaultRequest()).securityDirectRequest) { // use the same message as if it was missing for security reasons, ie so can't tell if it is missing or direct request is not allowed throw new RequestHandlerException(requestMissingErrorMessage); } else { - requestMap = ccfg.getRequestMapMap().get(ccfg.getDefaultRequest()); + requestMap = ccfg.getRequestMapMap().getFirst(ccfg.getDefaultRequest()); } } // Check if we SHOULD be secure and are not. @@ -350,7 +367,7 @@ public class RequestHandler { String newUrl = RequestHandler.makeUrl(request, response, urlBuf.toString()); if (newUrl.toUpperCase().startsWith("HTTPS")) { // if we are supposed to be secure, redirect secure. - callRedirect(newUrl, response, request, ccfg.getStatusCode()); + callRedirect(newUrl, response, request, ccfg.getStatusCodeString()); return; } } @@ -358,7 +375,33 @@ public class RequestHandler { // Check for HTTPS client (x.509) security if (request.isSecure() && requestMap.securityCert) { - if (!checkCertificates(request, certs -> SSLUtil.isClientTrusted(certs, null))) { + X509Certificate[] clientCerts = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate"); // 2.2 spec + if (clientCerts == null) { + clientCerts = (X509Certificate[]) request.getAttribute("javax.net.ssl.peer_certificates"); // 2.1 spec + } + if (clientCerts == null) { + Debug.logWarning("Received no client certificates from browser", module); + } + + // check if the client has a valid certificate (in our db store) + boolean foundTrustedCert = false; + + if (clientCerts == null) { + throw new RequestHandlerException(requestMissingErrorMessage); + } else { + if (Debug.infoOn()) { + for (int i = 0; i < clientCerts.length; i++) { + Debug.logInfo(clientCerts[i].getSubjectX500Principal().getName(), module); + } + } + + // check if this is a trusted cert + if (SSLUtil.isClientTrusted(clientCerts, null)) { + foundTrustedCert = true; + } + } + + if (!foundTrustedCert) { Debug.logWarning(requestMissingErrorMessage, module); throw new RequestHandlerException(requestMissingErrorMessage); } @@ -429,20 +472,13 @@ public class RequestHandler { if (Debug.verboseOn()) Debug.logVerbose("[Processing Request]: " + requestMap.uri + showSessionId(request), module); request.setAttribute("thisRequestUri", requestMap.uri); // store the actual request URI - // Store current requestMap map to be referred later when generating csrf token - request.setAttribute("requestMapMap", getControllerConfig().getRequestMapMap()); - - // Perform CSRF token check when request not on chain - if (chain==null && originalRequestMap.securityCsrfToken) { - CsrfUtil.checkToken(request, path); - } // Perform security check. if (requestMap.securityAuth) { // Invoke the security handler // catch exceptions and throw RequestHandlerException if failed. if (Debug.verboseOn()) Debug.logVerbose("[RequestHandler]: AuthRequired. Running security check. " + showSessionId(request), module); - ConfigXMLReader.Event checkLoginEvent = ccfg.getRequestMapMap().get("checkLogin").event; + ConfigXMLReader.Event checkLoginEvent = ccfg.getRequestMapMap().getFirst("checkLogin").event; String checkLoginReturnString = null; try { @@ -455,9 +491,9 @@ public class RequestHandler { eventReturn = checkLoginReturnString; // if the request is an ajax request we don't want to return the default login check if (!"XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) { - requestMap = ccfg.getRequestMapMap().get("checkLogin"); + requestMap = ccfg.getRequestMapMap().getFirst("checkLogin"); } else { - requestMap = ccfg.getRequestMapMap().get("ajaxCheckLogin"); + requestMap = ccfg.getRequestMapMap().getFirst("ajaxCheckLogin"); } } } @@ -466,7 +502,9 @@ public class RequestHandler { // we know this is the case if the _PREVIOUS_PARAM_MAP_ attribute is there, but the _PREVIOUS_REQUEST_ attribute has already been removed if (request.getSession().getAttribute("_PREVIOUS_PARAM_MAP_FORM_") != null && request.getSession().getAttribute("_PREVIOUS_REQUEST_") == null) { Map<String, Object> previousParamMap = UtilGenerics.checkMap(request.getSession().getAttribute("_PREVIOUS_PARAM_MAP_FORM_"), String.class, Object.class); - previousParamMap.forEach(request::setAttribute); + for (Map.Entry<String, Object> previousParamEntry: previousParamMap.entrySet()) { + request.setAttribute(previousParamEntry.getKey(), previousParamEntry.getValue()); + } // to avoid this data being included again, now remove the _PREVIOUS_PARAM_MAP_ attribute request.getSession().removeAttribute("_PREVIOUS_PARAM_MAP_FORM_"); @@ -561,7 +599,6 @@ public class RequestHandler { for (Map.Entry<String, Object> entry: preRequestMap.entrySet()) { String key = entry.getKey(); if ("_ERROR_MESSAGE_LIST_".equals(key) || "_ERROR_MESSAGE_MAP_".equals(key) || "_ERROR_MESSAGE_".equals(key) || - "_WARNING_MESSAGE_LIST_".equals(key) || "_WARNING_MESSAGE_".equals(key) || "_EVENT_MESSAGE_LIST_".equals(key) || "_EVENT_MESSAGE_".equals(key)) { request.setAttribute(key, entry.getValue()); } @@ -587,13 +624,8 @@ public class RequestHandler { if (UtilValidate.isNotEmpty(queryString)) { redirectTarget += "?" + queryString; } - String link = makeLink(request, response, redirectTarget); - - // add / update csrf token to link when required - String tokenValue = CsrfUtil.generateTokenForNonAjax(request,redirectTarget); - link = CsrfUtil.addOrUpdateTokenInUrl(link, tokenValue); - - callRedirect(link, response, request, ccfg.getStatusCode()); + + callRedirect(makeLink(request, response, redirectTarget), response, request, ccfg.getStatusCodeString()); return; } } @@ -651,44 +683,31 @@ public class RequestHandler { } } - // The status code used to redirect the HTTP client. - String redirectSC = UtilValidate.isNotEmpty(nextRequestResponse.statusCode) - ? nextRequestResponse.statusCode - : ccfg.getStatusCode(); - + String responseStatusCode = nextRequestResponse.statusCode; + if(UtilValidate.isNotEmpty(responseStatusCode)) + ccfg.setStatusCodeString(responseStatusCode); + if ("url".equals(nextRequestResponse.type)) { if (Debug.verboseOn()) Debug.logVerbose("[RequestHandler.doRequest]: Response is a URL redirect." + showSessionId(request), module); - callRedirect(nextRequestResponse.value, response, request, redirectSC); + callRedirect(nextRequestResponse.value, response, request, ccfg.getStatusCodeString()); } else if ("url-redirect".equals(nextRequestResponse.type)) { // check for a cross-application redirect if (Debug.verboseOn()) Debug.logVerbose("[RequestHandler.doRequest]: Response is a URL redirect with redirect parameters." + showSessionId(request), module); callRedirect(nextRequestResponse.value + this.makeQueryString(request, nextRequestResponse), response, - request, redirectSC); + request, ccfg.getStatusCodeString()); } else if ("cross-redirect".equals(nextRequestResponse.type)) { // check for a cross-application redirect if (Debug.verboseOn()) Debug.logVerbose("[RequestHandler.doRequest]: Response is a Cross-Application redirect." + showSessionId(request), module); String url = nextRequestResponse.value.startsWith("/") ? nextRequestResponse.value : "/" + nextRequestResponse.value; - callRedirect(url + this.makeQueryString(request, nextRequestResponse), response, request, redirectSC); + callRedirect(url + this.makeQueryString(request, nextRequestResponse), response, request, ccfg.getStatusCodeString()); } else if ("request-redirect".equals(nextRequestResponse.type)) { if (Debug.verboseOn()) Debug.logVerbose("[RequestHandler.doRequest]: Response is a Request redirect." + showSessionId(request), module); - String link = makeLinkWithQueryString(request, response, "/" + nextRequestResponse.value, nextRequestResponse); - - // add / update csrf token to link when required - String tokenValue = CsrfUtil.generateTokenForNonAjax(request, nextRequestResponse.value); - link = CsrfUtil.addOrUpdateTokenInUrl(link, tokenValue); - - callRedirect(link, response, request, redirectSC); + callRedirect(makeLinkWithQueryString(request, response, "/" + nextRequestResponse.value, nextRequestResponse), response, request, ccfg.getStatusCodeString()); } else if ("request-redirect-noparam".equals(nextRequestResponse.type)) { if (Debug.verboseOn()) Debug.logVerbose("[RequestHandler.doRequest]: Response is a Request redirect with no parameters." + showSessionId(request), module); - String link = makeLink(request, response, nextRequestResponse.value); - - // add token to link when required - String tokenValue = CsrfUtil.generateTokenForNonAjax(request, nextRequestResponse.value); - link = CsrfUtil.addOrUpdateTokenInUrl(link, tokenValue); - - callRedirect(link, response, request, redirectSC); + callRedirect(makeLink(request, response, nextRequestResponse.value), response, request, ccfg.getStatusCodeString()); } else if ("view".equals(nextRequestResponse.type)) { if (Debug.verboseOn()) Debug.logVerbose("[RequestHandler.doRequest]: Response is a view." + showSessionId(request), module); @@ -705,13 +724,13 @@ public class RequestHandler { Map<String, Object> urlParams = null; if (session.getAttribute("_SAVED_VIEW_NAME_") != null) { viewName = (String) session.getAttribute("_SAVED_VIEW_NAME_"); - urlParams = UtilGenerics.cast(session.getAttribute("_SAVED_VIEW_PARAMS_")); + urlParams = UtilGenerics.<String, Object>checkMap(session.getAttribute("_SAVED_VIEW_PARAMS_")); } else if (session.getAttribute("_HOME_VIEW_NAME_") != null) { viewName = (String) session.getAttribute("_HOME_VIEW_NAME_"); - urlParams = UtilGenerics.cast(session.getAttribute("_HOME_VIEW_PARAMS_")); + urlParams = UtilGenerics.<String, Object>checkMap(session.getAttribute("_HOME_VIEW_PARAMS_")); } else if (session.getAttribute("_LAST_VIEW_NAME_") != null) { viewName = (String) session.getAttribute("_LAST_VIEW_NAME_"); - urlParams = UtilGenerics.cast(session.getAttribute("_LAST_VIEW_PARAMS_")); + urlParams = UtilGenerics.<String, Object>checkMap(session.getAttribute("_LAST_VIEW_PARAMS_")); } else if (UtilValidate.isNotEmpty(nextRequestResponse.value)) { viewName = nextRequestResponse.value; } @@ -756,7 +775,7 @@ public class RequestHandler { Map<String, Object> urlParams = null; if (session.getAttribute("_HOME_VIEW_NAME_") != null) { viewName = (String) session.getAttribute("_HOME_VIEW_NAME_"); - urlParams = UtilGenerics.cast(session.getAttribute("_HOME_VIEW_PARAMS_")); + urlParams = UtilGenerics.<String, Object>checkMap(session.getAttribute("_HOME_VIEW_PARAMS_")); } if (urlParams != null) { for (Map.Entry<String, Object> urlParamEntry: urlParams.entrySet()) { @@ -789,7 +808,7 @@ public class RequestHandler { try { String errorPageLocation = getControllerConfig().getErrorpage(); errorPage = FlexibleLocation.resolveLocation(errorPageLocation); - } catch (MalformedURLException e) { + } catch (WebAppConfigurationException | MalformedURLException e) { Debug.logError(e, "Exception thrown while parsing controller.xml file: ", module); } if (errorPage == null) { @@ -800,8 +819,14 @@ public class RequestHandler { /** Returns the default status-code for this request. */ public String getStatusCode(HttpServletRequest request) { - String statusCode = getControllerConfig().getStatusCode(); - return UtilValidate.isNotEmpty(statusCode) ? statusCode : null; + String statusCode = null; + try { + statusCode = getControllerConfig().getStatusCode(); + } catch (WebAppConfigurationException e) { + Debug.logError(e, "Exception thrown while parsing controller.xml file: ", module); + } + if (UtilValidate.isNotEmpty(statusCode)) return statusCode; + return null; } /** Returns the ViewFactory Object. */ @@ -846,11 +871,11 @@ public class RequestHandler { return nextPage; } - private static void callRedirect(String url, HttpServletResponse resp, HttpServletRequest req, String statusCodeString) throws RequestHandlerException { + private void callRedirect(String url, HttpServletResponse resp, HttpServletRequest req, String statusCodeString) throws RequestHandlerException { if (Debug.infoOn()) Debug.logInfo("Sending redirect to: [" + url + "]. " + showSessionId(req), module); // set the attributes in the session so we can access it. Enumeration<String> attributeNameEnum = UtilGenerics.cast(req.getAttributeNames()); - Map<String, Object> reqAttrMap = new HashMap<>(); + Map<String, Object> reqAttrMap = new HashMap<String, Object>(); Integer statusCode; try { statusCode = Integer.valueOf(statusCodeString); @@ -943,7 +968,13 @@ public class RequestHandler { req.getSession().removeAttribute("_SAVED_VIEW_PARAMS_"); } - ConfigXMLReader.ViewMap viewMap = (view == null) ? null : getControllerConfig().getViewMapMap().get(view); + ConfigXMLReader.ViewMap viewMap = null; + try { + viewMap = (view == null ? null : getControllerConfig().getViewMapMap().get(view)); + } catch (WebAppConfigurationException e) { + Debug.logError(e, "Exception thrown while parsing controller.xml file: ", module); + throw new RequestHandlerException(e); + } if (viewMap == null) { throw new RequestHandlerException("No definition found for view with name [" + view + "]"); } @@ -1068,7 +1099,7 @@ public class RequestHandler { } } - private static void addNameValuePairToQueryString(StringBuilder queryString, String name, String value) { + private void addNameValuePairToQueryString(StringBuilder queryString, String name, String value) { if (UtilValidate.isNotEmpty(value)) { if (queryString.length() > 1) { queryString.append("&"); @@ -1093,10 +1124,6 @@ public class RequestHandler { } public String makeLink(HttpServletRequest request, HttpServletResponse response, String url, boolean fullPath, boolean secure, boolean encode) { - return makeLink(request, response, url, fullPath, secure, encode, ""); - } - - public String makeLink(HttpServletRequest request, HttpServletResponse response, String url, boolean fullPath, boolean secure, boolean encode, String targetControlPath) { WebSiteProperties webSiteProps = null; try { webSiteProps = WebSiteProperties.from(request); @@ -1108,7 +1135,13 @@ public class RequestHandler { String requestUri = RequestHandler.getRequestUri(url); ConfigXMLReader.RequestMap requestMap = null; if (requestUri != null) { - requestMap = getControllerConfig().getRequestMapMap().get(requestUri); + try { + requestMap = getControllerConfig().getRequestMapMap().get(requestUri); + } catch (WebAppConfigurationException e) { + // If we can't read the controller.xml file, then there is no point in continuing. + Debug.logError(e, "Exception thrown while parsing controller.xml file: ", module); + return null; + } } boolean didFullSecure = false; boolean didFullStandard = false; @@ -1140,12 +1173,8 @@ public class RequestHandler { return null; } } - - String controlPath = targetControlPath; - if (UtilValidate.isEmpty(controlPath)){ - // create the path to the control servlet - controlPath = (String) request.getAttribute("_CONTROL_PATH_"); - } + // create the path to the control servlet + String controlPath = (String) request.getAttribute("_CONTROL_PATH_"); //If required by webSite parameter, surcharge control path if (webSiteProps.getWebappPath() != null) { @@ -1168,7 +1197,7 @@ public class RequestHandler { try { GenericValue webSiteValue = EntityQuery.use(delegator).from("WebSite").where("webSiteId", webSiteId).cache().queryOne(); if (webSiteValue != null) { - ServletContext application = (request.getServletContext()); + ServletContext application = ((ServletContext) request.getAttribute("servletContext")); String domainName = request.getLocalName(); if (application.getAttribute("MULTI_SITE_ENABLED") != null && UtilValidate.isNotEmpty(webSiteValue.getString("hostedPathAlias")) && !domainName.equals(webSiteValue.getString("httpHost"))) { newURL.append('/'); @@ -1199,9 +1228,9 @@ public class RequestHandler { return makeUrl(request, response, url, false, false, false); } - public static String makeUrl(HttpServletRequest request, HttpServletResponse response, String url, boolean fullPath, - boolean secure, boolean encode) { - RequestHandler rh = from(request); + public static String makeUrl(HttpServletRequest request, HttpServletResponse response, String url, boolean fullPath, boolean secure, boolean encode) { + ServletContext ctx = (ServletContext) request.getAttribute("servletContext"); + RequestHandler rh = (RequestHandler) ctx.getAttribute("_REQUEST_HANDLER_"); return rh.makeLink(request, response, url, fullPath, secure, encode); } @@ -1248,58 +1277,55 @@ public class RequestHandler { runEvents(req, resp, prod, "before-logout"); } - /** - * Checks if a request must be tracked according a global toggle and a request map predicate. - * - * @param request the request that can potentially be tracked - * @param globalToggle the global configuration toggle - * @param pred the predicate checking if each individual request map must be tracked or not. - * @return {@code true} when the request must be tracked. - * @throws NullPointerException when either {@code request} or {@code pred} is {@code null}. - */ - private boolean track(HttpServletRequest request, boolean globalToggle, Predicate<RequestMap> pred) { - if (!globalToggle) { - return false; - } - // XXX: We are basically re-implementing `resolveURI` poorly, It would be better - // to take a `request-map` as input but it is not currently not possible because this method - // is used outside `doRequest`. - String uriString = RequestHandler.getRequestUri(request.getPathInfo()); - if (uriString == null) { - uriString= ""; - } - Map<String, RequestMap> rmaps = getControllerConfig().getRequestMapMap(); - RequestMap requestMap = rmaps.get(uriString); - if (requestMap == null) { - requestMap = rmaps.get(getControllerConfig().getDefaultRequest()); - if (requestMap == null) { - return false; + public boolean trackStats(HttpServletRequest request) { + if (trackServerHit) { + String uriString = RequestHandler.getRequestUri(request.getPathInfo()); + if (uriString == null) { + uriString=""; + } + ConfigXMLReader.RequestMap requestMap = null; + try { + requestMap = getControllerConfig().getRequestMapMap().get(uriString); + if (requestMap == null) { + requestMap = getControllerConfig().getRequestMapMap().get(getControllerConfig().getDefaultRequest()); + if (requestMap == null) { + return false; + } + } + } catch (WebAppConfigurationException e) { + Debug.logError(e, "Exception thrown while parsing controller.xml file: ", module); } + return requestMap.trackServerHit; + } else { + return false; } - return pred.test(requestMap); } - /** - * Checks if server hits must be tracked for a given request. - * - * @param request the HTTP request that can potentially be tracked - * @return {@code true} when the request must be tracked. - */ - public boolean trackStats(HttpServletRequest request) { - return track(request, trackServerHit, rmap -> rmap.trackServerHit); - } - - /** - * Checks if visits must be tracked for a given request. - * - * @param request the HTTP request that can potentially be tracked - * @return {@code true} when the request must be tracked. - */ public boolean trackVisit(HttpServletRequest request) { - return track(request, trackVisit, rmap -> rmap.trackVisit); + if (trackVisit) { + String uriString = RequestHandler.getRequestUri(request.getPathInfo()); + if (uriString == null) { + uriString=""; + } + ConfigXMLReader.RequestMap requestMap = null; + try { + requestMap = getControllerConfig().getRequestMapMap().get(uriString); + if (requestMap == null) { + requestMap = getControllerConfig().getRequestMapMap().get(getControllerConfig().getDefaultRequest()); + if (requestMap == null) { + return false; + } + } + } catch (WebAppConfigurationException e) { + Debug.logError(e, "Exception thrown while parsing controller.xml file: ", module); + } + return requestMap.trackVisit; + } else { + return false; + } } - private static String showSessionId(HttpServletRequest request) { + private String showSessionId(HttpServletRequest request) { Delegator delegator = (Delegator) request.getAttribute("delegator"); boolean showSessionIdInLog = EntityUtilProperties.propertyValueEqualsIgnoreCase("requestHandler", "show-sessionId-in-log", "Y", delegator); if (showSessionIdInLog) { @@ -1307,42 +1333,4 @@ public class RequestHandler { } return " Hidden sessionId by default."; } - - /** - * Checks that the request contains some valid certificates. - * - * @param request the request to verify - * @param validator the predicate applied the certificates found - * @return true if the request contains some valid certificates, otherwise false. - */ - static boolean checkCertificates(HttpServletRequest request, Predicate<X509Certificate[]> validator) { - return Stream.of("javax.servlet.request.X509Certificate", // 2.2 spec - "javax.net.ssl.peer_certificates") // 2.1 spec - .map(request::getAttribute) - .filter(Objects::nonNull) - .map(X509Certificate[].class::cast) - .peek(certs -> { - if (Debug.infoOn()) { - for (X509Certificate cert : certs) { - Debug.logInfo(cert.getSubjectX500Principal().getName(), module); - } - } - }) - .map(validator::test) - .findFirst().orElseGet(() -> { - Debug.logWarning("Received no client certificates from browser", module); - return false; - }); - } - - /** - * Retrieves the request handler which is stored inside an HTTP request. - * - * @param request the HTTP request containing the request handler - * @return a request handler or {@code null} when absent - * @throws NullPointerException when {@code request} or the servlet context is {@code null}. - */ - public static RequestHandler from(HttpServletRequest request) { - return UtilGenerics.cast(request.getServletContext().getAttribute("_REQUEST_HANDLER_")); - } } diff --git a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/CsrfTokenAjaxTransform.java b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/CsrfTokenAjaxTransform.java deleted file mode 100644 index 6a2d89e..0000000 --- a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/CsrfTokenAjaxTransform.java +++ /dev/null @@ -1,75 +0,0 @@ -/******************************************************************************* - * 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.ftl; - -import java.io.IOException; -import java.io.Writer; -import java.util.Map; - -import javax.servlet.http.HttpServletRequest; - -import org.apache.ofbiz.security.CsrfUtil; - -import freemarker.core.Environment; -import freemarker.ext.beans.BeanModel; -import freemarker.template.TemplateModelException; -import freemarker.template.TemplateTransformModel; - -/** - * CsrfTokenAjaxTransform - Freemarker Transform for csrf token in Ajax call - */ -public class CsrfTokenAjaxTransform implements TemplateTransformModel { - - public final static String module = CsrfTokenAjaxTransform.class.getName(); - - @Override - public Writer getWriter(Writer out, @SuppressWarnings("rawtypes") Map args) - throws TemplateModelException, IOException { - - return new Writer(out) { - - @Override - public void close() throws IOException { - try { - Environment env = Environment.getCurrentEnvironment(); - BeanModel req = (BeanModel) env.getVariable("request"); - if (req != null) { - HttpServletRequest request = (HttpServletRequest) req.getWrappedObject(); - String tokenValue = CsrfUtil.generateTokenForAjax(request); - out.write(tokenValue); - } - return; - } catch (Exception e) { - throw new IOException(e.getMessage()); - } - } - - @Override - public void flush() throws IOException { - out.flush(); - } - - @Override - public void write(char cbuf[], int off, int len) { - - } - }; - - } -} diff --git a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/CsrfTokenPairNonAjaxTransform.java b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/CsrfTokenPairNonAjaxTransform.java deleted file mode 100644 index d51bd61..0000000 --- a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/CsrfTokenPairNonAjaxTransform.java +++ /dev/null @@ -1,76 +0,0 @@ -/******************************************************************************* - * 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.ftl; - -import java.io.IOException; -import java.io.Writer; -import java.util.Map; - -import javax.servlet.http.HttpServletRequest; - -import org.apache.ofbiz.security.CsrfUtil; - -import freemarker.core.Environment; -import freemarker.ext.beans.BeanModel; -import freemarker.template.TemplateModelException; -import freemarker.template.TemplateTransformModel; - -/** - * CsrfTokenPairNonAjaxTransform - Freemarker Transform for csrf token in non-Ajax call - */ -public class CsrfTokenPairNonAjaxTransform implements TemplateTransformModel { - - public final static String module = CsrfTokenPairNonAjaxTransform.class.getName(); - - @Override - public Writer getWriter(Writer out, @SuppressWarnings("rawtypes") Map args) - throws TemplateModelException, IOException { - - final StringBuffer buf = new StringBuffer(); - - return new Writer(out) { - - @Override - public void close() throws IOException { - try { - Environment env = Environment.getCurrentEnvironment(); - BeanModel req = (BeanModel) env.getVariable("request"); - if (req != null) { - HttpServletRequest request = (HttpServletRequest) req.getWrappedObject(); - String tokenValue = CsrfUtil.generateTokenForNonAjax(request, buf.toString()); - out.write(CsrfUtil.tokenNameNonAjax +"="+tokenValue); - } - return; - } catch (Exception e) { - throw new IOException(e.getMessage()); - } - } - - @Override - public void write(char cbuf[], int off, int len) { - buf.append(cbuf, off, len); - } - - @Override - public void flush() throws IOException { - out.flush(); - } - }; - } -} diff --git a/framework/webtools/groovyScripts/entity/CheckDb.groovy b/framework/webtools/groovyScripts/entity/CheckDb.groovy index 567714f..fd822de 100644 --- a/framework/webtools/groovyScripts/entity/CheckDb.groovy +++ b/framework/webtools/groovyScripts/entity/CheckDb.groovy @@ -16,9 +16,10 @@ * specific language governing permissions and limitations * under the License. */ - - +import org.apache.ofbiz.entity.Delegator +import org.apache.ofbiz.security.Security import org.apache.ofbiz.entity.jdbc.DatabaseUtil +import org.apache.ofbiz.entity.model.ModelEntity controlPath = parameters._CONTROL_PATH_ @@ -113,7 +114,7 @@ if (security.hasPermission("ENTITY_MAINT", session)) { miter = messages.iterator() context.miters = miter } - context.checkDbURL = "view/checkdb" + context.encodeURLCheckDb = response.encodeURL(controlPath + "/view/checkdb") context.groupName = groupName ?: "org.apache.ofbiz" context.entityName = entityName ?: "" } diff --git a/framework/webtools/groovyScripts/entity/EntityRef.groovy b/framework/webtools/groovyScripts/entity/EntityRef.groovy index 279e448..17933db 100644 --- a/framework/webtools/groovyScripts/entity/EntityRef.groovy +++ b/framework/webtools/groovyScripts/entity/EntityRef.groovy @@ -16,8 +16,6 @@ * specific language governing permissions and limitations * under the License. */ -import org.apache.ofbiz.security.CsrfUtil; - controlPath = parameters._CONTROL_PATH_ list = "$controlPath/view/entityref_list" main = "$controlPath/view/entityref_main" @@ -31,9 +29,5 @@ if (search) { list = "$list?forstatic=$forstatic" main = "$main?forstatic=$forstatic" } -tokenList = CsrfUtil.generateTokenForNonAjax(request, "view/entityref_list") -tokenMain = CsrfUtil.generateTokenForNonAjax(request, "view/entityref_main") -list = CsrfUtil.addOrUpdateTokenInUrl(list, tokenList) -main = CsrfUtil.addOrUpdateTokenInUrl(main, tokenMain) context.encodeUrlList = response.encodeURL(list) context.encodeUrlMain = response.encodeURL(main) diff --git a/framework/webtools/template/entity/CheckDb.ftl b/framework/webtools/template/entity/CheckDb.ftl index 91cf8d3..ac81459 100644 --- a/framework/webtools/template/entity/CheckDb.ftl +++ b/framework/webtools/template/entity/CheckDb.ftl @@ -17,7 +17,7 @@ specific language governing permissions and limitations under the License. --> <h3>${uiLabelMap.WebtoolsCheckUpdateDatabase}</h3> -<form class="basic-form" method="post" action="<@ofbizUrl>${checkDbURL}</@ofbizUrl>"> +<form class="basic-form" class="basic-form" method="post" action="${encodeURLCheckDb}"> <table class="basic-table" cellspacing="0"> <tbody> <tr> @@ -61,7 +61,7 @@ under the License. } </script> <h3>${uiLabelMap.WebtoolsRemoveAllTables}</h3> -<form class="basic-form" method="post" action="<@ofbizUrl>${checkDbURL}</@ofbizUrl>" name="TablesRemoveForm"> +<form class="basic-form" class="basic-form" method="post" action="${encodeURLCheckDb}" name="TablesRemoveForm"> <table class="basic-table" cellspacing="0"> <tbody> <tr> @@ -80,7 +80,7 @@ under the License. </tbody> </table> </form> -<form class="basic-form" method="post" action="<@ofbizUrl>${checkDbURL}</@ofbizUrl>" name="TableRemoveForm"> +<form class="basic-form" method="post" action="${encodeURLCheckDb}" name="TableRemoveForm"> <table class="basic-table" cellspacing="0"> <tbody> <tr> @@ -107,7 +107,7 @@ under the License. </table> </form> <h3>${uiLabelMap.WebtoolsCreateRemoveAllPrimaryKeys}</h3> -<form class="basic-form" method="post" action="<@ofbizUrl>${checkDbURL}</@ofbizUrl>"> +<form class="basic-form" method="post" action="${encodeURLCheckDb}"> <table class="basic-table" cellspacing="0"> <tbody> <tr> @@ -125,7 +125,7 @@ under the License. </tbody> </table> </form> -<form class="basic-form" method="post" action="<@ofbizUrl>${checkDbURL}</@ofbizUrl>"> +<form class="basic-form" method="post" action="${encodeURLCheckDb}"> <table class="basic-table" cellspacing="0"> <tbody> <tr> @@ -143,7 +143,7 @@ under the License. </table> </form> <h3>${uiLabelMap.WebtoolsCreateRemovePrimaryKey}</h3> -<form class="basic-form" method="post" action="<@ofbizUrl>${checkDbURL}</@ofbizUrl>"> +<form class="basic-form" method="post" action="${encodeURLCheckDb}"> <table class="basic-table" cellspacing="0"> <tbody> <tr> @@ -168,7 +168,7 @@ under the License. </tbody> </table> </form> -<form class="basic-form" method="post" action="<@ofbizUrl>${checkDbURL}</@ofbizUrl>"> +<form class="basic-form" method="post" action="${encodeURLCheckDb}"> <table class="basic-table" cellspacing="0"> <tbody> <tr> @@ -197,7 +197,7 @@ under the License. </table> </form> <h3>${uiLabelMap.WebtoolsCreateRemoveAllDeclaredIndices}</h3> -<form class="basic-form" method="post" action="<@ofbizUrl>${checkDbURL}</@ofbizUrl>"> +<form class="basic-form" method="post" action="${encodeURLCheckDb}"> <table class="basic-table" cellspacing="0"> <tbody> <tr> @@ -214,7 +214,7 @@ under the License. </tbody> </table> </form> -<form class="basic-form" method="post" action="<@ofbizUrl>${checkDbURL}</@ofbizUrl>"> +<form class="basic-form" method="post" action="${encodeURLCheckDb}"> <table class="basic-table" cellspacing="0"> <tbody> <tr> @@ -232,7 +232,7 @@ under the License. </table> </form> <h3>${uiLabelMap.WebtoolsCreateRemoveAllForeignKeyIndices}</h3> -<form class="basic-form" method="post" action="<@ofbizUrl>${checkDbURL}</@ofbizUrl>"> +<form class="basic-form" method="post" action="${encodeURLCheckDb}"> <table class="basic-table" cellspacing="0"> <tbody> <tr> @@ -249,7 +249,7 @@ under the License. </tbody> </table> </form> -<form class="basic-form" method="post" action="<@ofbizUrl>${checkDbURL}</@ofbizUrl>"> +<form class="basic-form" method="post" action="${encodeURLCheckDb}"> <table class="basic-table" cellspacing="0"> <tbody> <tr> @@ -268,7 +268,7 @@ under the License. </form> <h3>${uiLabelMap.WebtoolsCreateRemoveAllForeignKeys}</h3> <p>${uiLabelMap.WebtoolsNoteForeighKeysMayAlsoBeCreated}</p> -<form class="basic-form" method="post" action="<@ofbizUrl>${checkDbURL}</@ofbizUrl>"> +<form class="basic-form" method="post" action="${encodeURLCheckDb}"> <table class="basic-table" cellspacing="0"> <tbody> <tr> @@ -285,7 +285,7 @@ under the License. </tbody> </table> </form> -<form class="basic-form" method="post" action="<@ofbizUrl>${checkDbURL}</@ofbizUrl>"> +<form class="basic-form" method="post" action="${encodeURLCheckDb}"> <table class="basic-table" cellspacing="0"> <tbody> <tr> @@ -303,7 +303,7 @@ under the License. </table> </form> <h3>${uiLabelMap.WebtoolsUpdateCharacterSetAndCollate}</h3> -<form class="basic-form" method="post" action="<@ofbizUrl>${checkDbURL}</@ofbizUrl>"> +<form class="basic-form" method="post" action="${encodeURLCheckDb}"> <table class="basic-table" cellspacing="0"> <tbody> <tr> diff --git a/framework/webtools/template/entity/EntityRefList.ftl b/framework/webtools/template/entity/EntityRefList.ftl index 55e2387..1ace17f 100644 --- a/framework/webtools/template/entity/EntityRefList.ftl +++ b/framework/webtools/template/entity/EntityRefList.ftl @@ -1,4 +1,3 @@ - <#-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file @@ -55,9 +54,9 @@ under the License. <div class="section-header">${uiLabelMap.WebtoolsEntityPackages}</div> <#list packageNames as packageName> <#if forstatic> - <a href="<@ofbizUrl>view/entityref_main?forstatic=true</@ofbizUrl>#${packageName}" target="entityFrame">${packageName}</a><br /> + <a href="<@ofbizUrl>view/entityref_main?forstatic=true#${packageName}</@ofbizUrl>" target="entityFrame">${packageName}</a><br /> <#else> - <a href="<@ofbizUrl>view/entityref_main</@ofbizUrl>#${packageName}" target="entityFrame">${packageName}</a><br /> + <a href="<@ofbizUrl>view/entityref_main#${packageName}</@ofbizUrl>" target="entityFrame">${packageName}</a><br /> </#if> </#list> </#if> @@ -66,9 +65,9 @@ under the License. <div class="section-header">${uiLabelMap.WebtoolsEntitiesAlpha}</div> <#list entitiesList as entity> <#if forstatic> - <a href="<@ofbizUrl>view/entityref_main?forstatic=true</@ofbizUrl>#${entity.entityName}" target="entityFrame">${entity.entityName}</a> + <a href="<@ofbizUrl>view/entityref_main?forstatic=true#${entity.entityName}</@ofbizUrl>" target="entityFrame">${entity.entityName}</a> <#else> - <a href="<@ofbizUrl>view/entityref_main</@ofbizUrl>#${entity.entityName}${entity.url!}" target="entityFrame">${entity.entityName}</a> + <a href="<@ofbizUrl>view/entityref_main#${entity.entityName}${entity.url!}</@ofbizUrl>" target="entityFrame">${entity.entityName}</a> </#if> <br /> </#list> diff --git a/framework/webtools/template/entity/ViewGeneric.ftl b/framework/webtools/template/entity/ViewGeneric.ftl index 32b7382..f6e03f7 100644 --- a/framework/webtools/template/entity/ViewGeneric.ftl +++ b/framework/webtools/template/entity/ViewGeneric.ftl @@ -38,7 +38,6 @@ function ShowTab(lname) { } </script> -<#assign currentFindString = currentFindString?replace("/", "/")!> <div class="screenlet"> <div class="screenlet-title-bar"> <ul> diff --git a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroFormRenderer.java b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroFormRenderer.java index 697ac90..08a92a0 100644 --- a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroFormRenderer.java +++ b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/MacroFormRenderer.java @@ -40,7 +40,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -import org.apache.ofbiz.security.CsrfUtil; import org.apache.ofbiz.base.util.Debug; import org.apache.ofbiz.base.util.StringUtil; import org.apache.ofbiz.base.util.UtilCodec; @@ -1410,10 +1409,6 @@ public final class MacroFormRenderer implements FormStringRenderer { } } String focusFieldName = modelForm.getFocusFieldName(); - - // Generate CSRF name & value for form - String csrfNameValue = CsrfUtil.tokenNameNonAjax + " " +CsrfUtil.generateTokenForNonAjax(request, targ); - StringWriter sr = new StringWriter(); sr.append("<@renderFormOpen "); sr.append(" linkUrl=\""); @@ -1444,9 +1439,7 @@ public final class MacroFormRenderer implements FormStringRenderer { sr.append(Integer.toString(viewSize)); sr.append("\" useRowSubmit="); sr.append(Boolean.toString(useRowSubmit)); - sr.append(" csrfNameValue=\""); - sr.append(csrfNameValue); - sr.append("\" />"); + sr.append(" />"); executeMacro(writer, sr.toString()); } @@ -2369,11 +2362,6 @@ public final class MacroFormRenderer implements FormStringRenderer { viewSizeParam = "VIEW_SIZE" + "_" + paginatorNumber; } String str = (String) context.get("_QBESTRING_"); - - // refresh any csrf token in the query string for pagination - String tokenValue = CsrfUtil.generateTokenForNonAjax(request, targetService); - str = CsrfUtil.addOrUpdateTokenInQueryString(str, tokenValue); - // strip legacy viewIndex/viewSize params from the query string String queryString = UtilHttp.stripViewParamsFromQueryString(str, "" + paginatorNumber); // strip parameterized index/size params from the query string diff --git a/themes/bluelight/template/Header.ftl b/themes/bluelight/template/Header.ftl index 2c27eb7..7f1038b 100644 --- a/themes/bluelight/template/Header.ftl +++ b/themes/bluelight/template/Header.ftl @@ -28,10 +28,6 @@ under the License. <html lang="${docLangAttr}" dir="${langDir}" xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> - <#assign csrfDefenseStrategy = Static["org.apache.ofbiz.entity.util.EntityUtilProperties"].getPropertyValue("security", "csrf.defense.strategy", delegator)> - <#if csrfDefenseStrategy != "org.apache.ofbiz.security.NoCsrfDefenseStrategy"> - <meta name="csrf-token" content="<@csrfTokenAjax/>"/> - </#if> <title>${layoutSettings.companyName}: <#if (titleProperty)?has_content>${uiLabelMap[titleProperty]}<#else>${title!}</#if></title> <#if layoutSettings.shortcutIcon?has_content> <#assign shortcutIcon = layoutSettings.shortcutIcon/> @@ -198,7 +194,7 @@ under the License. <#--if webSiteId?? && requestAttributes._CURRENT_VIEW_?? && helpTopic??--> <#if parameters.componentName?? && requestAttributes._CURRENT_VIEW_?? && helpTopic??> <#include "component://common-theme/template/includes/HelpLink.ftl" /> - <li><a class="help-link <#if pageAvail?has_content> alert</#if>" href="javascript:lookup_popup1('<@ofbizUrl>showHelp?helpTopic=${helpTopic}&portalPageId=${(parameters.portalPageId!)?html}</@ofbizUrl>','help' ,500,500);" title="${uiLabelMap.CommonHelp}"></a></li> + <li><a class="help-link <#if pageAvail?has_content> alert</#if>" href="javascript:lookup_popup1('showHelp?helpTopic=${helpTopic}&portalPageId=${(parameters.portalPageId!)?html}','help' ,500,500);" title="${uiLabelMap.CommonHelp}"></a></li> </#if> <#if userLogin??> <#if "Y" == (userPreferences.COMPACT_HEADER)?default("N")> diff --git a/themes/common-theme/template/includes/ListLocales.ftl b/themes/common-theme/template/includes/ListLocales.ftl index 82c7ca7..647090f 100644 --- a/themes/common-theme/template/includes/ListLocales.ftl +++ b/themes/common-theme/template/includes/ListLocales.ftl @@ -36,7 +36,7 @@ under the License. </#if> <tr <#if altRow>class="alternate-row"</#if>> <td lang="${langAttr}" dir="${langDir}"> - <a href="<@ofbizUrl>setSessionLocale?newLocale=${availableLocale.toString()}</@ofbizUrl>"> + <a href="<@ofbizUrl>setSessionLocale</@ofbizUrl>?newLocale=${availableLocale.toString()}"> ${availableLocale.getDisplayName(availableLocale)} - [${langAttr}]</a> </td> diff --git a/themes/common-theme/template/macro/CsvFormMacroLibrary.ftl b/themes/common-theme/template/macro/CsvFormMacroLibrary.ftl index b371b19..cadd70e 100644 --- a/themes/common-theme/template/macro/CsvFormMacroLibrary.ftl +++ b/themes/common-theme/template/macro/CsvFormMacroLibrary.ftl @@ -54,7 +54,7 @@ under the License. <#macro renderEmptyFormDataMessage message></#macro> <#macro renderSingleFormFieldTitle></#macro> -<#macro renderFormOpen linkUrl formType targetWindow containerId containerStyle autocomplete name viewIndexField viewSizeField viewIndex viewSize useRowSubmit focusFieldName hasRequiredField csrfNameValue></#macro> +<#macro renderFormOpen linkUrl formType targetWindow containerId containerStyle autocomplete name viewIndexField viewSizeField viewIndex viewSize useRowSubmit focusFieldName hasRequiredField></#macro> <#macro renderFormClose></#macro> <#macro renderMultiFormClose></#macro> diff --git a/themes/common-theme/template/macro/FoFormMacroLibrary.ftl b/themes/common-theme/template/macro/FoFormMacroLibrary.ftl index a0d8b7e..c99efc9 100644 --- a/themes/common-theme/template/macro/FoFormMacroLibrary.ftl +++ b/themes/common-theme/template/macro/FoFormMacroLibrary.ftl @@ -80,7 +80,7 @@ under the License. <#macro renderEmptyFormDataMessage message></#macro> <#macro renderSingleFormFieldTitle><!--title form--></#macro> -<#macro renderFormOpen linkUrl formType targetWindow containerId containerStyle autocomplete name viewIndexField viewSizeField viewIndex viewSize useRowSubmit focusFieldName hasRequiredField csrfNameValue></#macro> +<#macro renderFormOpen linkUrl formType targetWindow containerId containerStyle autocomplete name viewIndexField viewSizeField viewIndex viewSize useRowSubmit focusFieldName hasRequiredField></#macro> <#macro renderFormClose></#macro> <#macro renderMultiFormClose></#macro> diff --git a/themes/common-theme/template/macro/HtmlFormMacroLibrary.ftl b/themes/common-theme/template/macro/HtmlFormMacroLibrary.ftl index 77b3b67..0923033 100644 --- a/themes/common-theme/template/macro/HtmlFormMacroLibrary.ftl +++ b/themes/common-theme/template/macro/HtmlFormMacroLibrary.ftl @@ -243,14 +243,8 @@ under the License. </#macro> <#macro renderSingleFormFieldTitle></#macro> -<#macro renderFormOpen linkUrl formType name viewIndexField viewSizeField viewIndex viewSize targetWindow="" containerId="" containerStyle="" autocomplete="" useRowSubmit="" focusFieldName="" hasRequiredField="" csrfNameValue=""> +<#macro renderFormOpen linkUrl formType name viewIndexField viewSizeField viewIndex viewSize targetWindow="" containerId="" containerStyle="" autocomplete="" useRowSubmit="" focusFieldName="" hasRequiredField=""> <form method="post" action="${linkUrl}"<#if formType=="upload"> enctype="multipart/form-data"</#if><#if targetWindow?has_content> target="${targetWindow}"</#if><#if containerId?has_content> id="${containerId}"</#if> <#if focusFieldName?has_content> data-focus-field="${focusFieldName}"</#if> class="<#if containerStyle?has_content>${containerStyle}<#else>basic-form</#if><#if hasRequiredField?has_content> requireValidation</#if>" onsubmit="javascript:submitFormDisableSubmits(this)"<#if au [...] - <#if csrfNameValue?has_content> - <#assign result = csrfNameValue?matches(r"(\w+) (\w+)")> - <#if result> - <input type="hidden" name="${result?groups[1]}" value="${result?groups[2]}"/> - </#if> - </#if> <#if useRowSubmit?has_content && useRowSubmit> <input type="hidden" name="_useRowSubmit" value="Y"/> <#if linkUrl?index_of("VIEW_INDEX") <= 0 && linkUrl?index_of(viewIndexField) <= 0> diff --git a/themes/common-theme/template/macro/TextFormMacroLibrary.ftl b/themes/common-theme/template/macro/TextFormMacroLibrary.ftl index 0e97938..228611e 100644 --- a/themes/common-theme/template/macro/TextFormMacroLibrary.ftl +++ b/themes/common-theme/template/macro/TextFormMacroLibrary.ftl @@ -54,7 +54,7 @@ under the License. <#macro renderEmptyFormDataMessage message></#macro> <#macro renderSingleFormFieldTitle></#macro> -<#macro renderFormOpen linkUrl formType targetWindow containerId containerStyle autocomplete name viewIndexField viewSizeField viewIndex viewSize useRowSubmit focusFieldName hasRequiredField csrfNameValue></#macro> +<#macro renderFormOpen linkUrl formType targetWindow containerId containerStyle autocomplete name viewIndexField viewSizeField viewIndex viewSize useRowSubmit focusFieldName hasRequiredField></#macro> <#macro renderFormClose></#macro> <#macro renderMultiFormClose></#macro> diff --git a/themes/common-theme/template/macro/XlsFormMacroLibrary.ftl b/themes/common-theme/template/macro/XlsFormMacroLibrary.ftl index 0472f2d..0998073 100644 --- a/themes/common-theme/template/macro/XlsFormMacroLibrary.ftl +++ b/themes/common-theme/template/macro/XlsFormMacroLibrary.ftl @@ -59,7 +59,7 @@ under the License. <#macro renderSingleFormFieldTitle></#macro> -<#macro renderFormOpen linkUrl formType targetWindow containerId containerStyle autocomplete name viewIndexField viewSizeField viewIndex viewSize useRowSubmit focusFieldName hasRequiredField csrfNameValue></#macro> +<#macro renderFormOpen linkUrl formType targetWindow containerId containerStyle autocomplete name viewIndexField viewSizeField viewIndex viewSize useRowSubmit focusFieldName hasRequiredField></#macro> <#macro renderFormClose></#macro> <#macro renderMultiFormClose></#macro> diff --git a/themes/common-theme/template/macro/XmlFormMacroLibrary.ftl b/themes/common-theme/template/macro/XmlFormMacroLibrary.ftl index acc2f28..b8cbc51 100644 --- a/themes/common-theme/template/macro/XmlFormMacroLibrary.ftl +++ b/themes/common-theme/template/macro/XmlFormMacroLibrary.ftl @@ -62,7 +62,7 @@ under the License. <#macro renderEmptyFormDataMessage message></#macro> <#macro renderSingleFormFieldTitle></#macro> -<#macro renderFormOpen linkUrl formType targetWindow containerId containerStyle autocomplete name viewIndexField viewSizeField viewIndex viewSize useRowSubmit focusFieldName hasRequiredField csrfNameValue></#macro> +<#macro renderFormOpen linkUrl formType targetWindow containerId containerStyle autocomplete name viewIndexField viewSizeField viewIndex viewSize useRowSubmit focusFieldName hasRequiredField></#macro> <#macro renderFormClose></#macro> <#macro renderMultiFormClose></#macro> diff --git a/themes/common-theme/webapp/common/js/util/OfbizUtil.js b/themes/common-theme/webapp/common/js/util/OfbizUtil.js index e42dc44..ce99998 100644 --- a/themes/common-theme/webapp/common/js/util/OfbizUtil.js +++ b/themes/common-theme/webapp/common/js/util/OfbizUtil.js @@ -25,16 +25,6 @@ var AJAX_REQUEST_TIMEOUT = 5000; // Add observers on DOM ready. $(document).ready(function() { - // add CSRF token to jQuery AJAX calls to the same domain - jQuery.ajaxPrefilter(function(options, _, jqXHR) { - var token; - if (!options.crossDomain) { - token = jQuery("meta[name='csrf-token']").attr("content") - if (token) { - return jqXHR.setRequestHeader("X-CSRF-Token", token); - } - } - }); //initializing UI combobox dropdown by overriding its methods. ajaxAutoCompleteDropDown(); // bindObservers will add observer on passed html section when DOM is ready. @@ -1228,7 +1218,7 @@ function getJSONuiLabels(requiredLabels, callback) { } } /** - * Read the required uiLabel from the uiLabelXml Resource + * Read the requiered uiLabel from the uiLabelXml Resource * @param uiResource String * @param errUiLabel String * @returns String with Label diff --git a/themes/flatgrey/template/Header.ftl b/themes/flatgrey/template/Header.ftl index bbe4eb3..8920f07 100644 --- a/themes/flatgrey/template/Header.ftl +++ b/themes/flatgrey/template/Header.ftl @@ -24,10 +24,6 @@ under the License. <html lang="${docLangAttr}" dir="${langDir}" xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> - <#assign csrfDefenseStrategy = Static["org.apache.ofbiz.entity.util.EntityUtilProperties"].getPropertyValue("security", "csrf.defense.strategy", delegator)> - <#if csrfDefenseStrategy != "org.apache.ofbiz.security.NoCsrfDefenseStrategy"> - <meta name="csrf-token" content="<@csrfTokenAjax/>"/> - </#if> <title>${layoutSettings.companyName}: <#if (titleProperty)?has_content>${uiLabelMap[titleProperty]}<#else>${title!}</#if></title> <#if layoutSettings.shortcutIcon?has_content> <#assign shortcutIcon = layoutSettings.shortcutIcon/> @@ -160,7 +156,7 @@ under the License. <#---if webSiteId?? && requestAttributes._CURRENT_VIEW_?? && helpTopic??--> <#if parameters.componentName?? && requestAttributes._CURRENT_VIEW_?? && helpTopic??> <#include "component://common-theme/template/includes/HelpLink.ftl" /> - <li><a <#if pageAvail?has_content>class="alert"</#if> href="javascript:lookup_popup1('<@ofbizUrl>showHelp?helpTopic=${helpTopic}&portalPageId=${(parameters.portalPageId!)?html}</@ofbizUrl>','help' ,500,500);">${uiLabelMap.CommonHelp}</a></li> + <li><a <#if pageAvail?has_content>class="alert"</#if> href="javascript:lookup_popup1('showHelp?helpTopic=${helpTopic}&portalPageId=${(parameters.portalPageId!)?html}','help' ,500,500);">${uiLabelMap.CommonHelp}</a></li> </#if> </ul> </li> diff --git a/themes/rainbowstone/template/includes/Header.ftl b/themes/rainbowstone/template/includes/Header.ftl index 93a8500..bb1ad5e 100644 --- a/themes/rainbowstone/template/includes/Header.ftl +++ b/themes/rainbowstone/template/includes/Header.ftl @@ -24,10 +24,6 @@ under the License. <html lang="${docLangAttr}" dir="${langDir}" xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> - <#assign csrfDefenseStrategy = Static["org.apache.ofbiz.entity.util.EntityUtilProperties"].getPropertyValue("security", "csrf.defense.strategy", delegator)> - <#if csrfDefenseStrategy != "org.apache.ofbiz.security.NoCsrfDefenseStrategy"> - <meta name="csrf-token" content="<@csrfTokenAjax/>"/> - </#if> <title>${layoutSettings.companyName}: <#if (titleProperty)?has_content>${uiLabelMap[titleProperty]}<#else>${title!}</#if></title> <#if layoutSettings.shortcutIcon?has_content> <#assign shortcutIcon = layoutSettings.shortcutIcon/> diff --git a/themes/rainbowstone/template/includes/TopAppBar.ftl b/themes/rainbowstone/template/includes/TopAppBar.ftl index c70a040..18f610a 100644 --- a/themes/rainbowstone/template/includes/TopAppBar.ftl +++ b/themes/rainbowstone/template/includes/TopAppBar.ftl @@ -238,7 +238,7 @@ under the License. <div id="main-nav-bar-right"> <div id="company-logo"></div> <#if parameters.componentName?exists && requestAttributes._CURRENT_VIEW_?exists && helpTopic?exists> - <a class="dark-color" title="${uiLabelMap.CommonHelp}" href="javascript:lookup_popup1('<@ofbizUrl>showHelp?helpTopic=${helpTopic}&portalPageId=${(parameters.portalPageId!)?html}</@ofbizUrl>','help' ,500,500);"><img class="appbar-btn-img" id="help-btn" src="/rainbowstone/images/help.svg" alt="Help"></a> + <a class="dark-color" title="${uiLabelMap.CommonHelp}" href="javascript:lookup_popup1('showHelp?helpTopic=${helpTopic}&portalPageId=${(parameters.portalPageId!)?html}','help' ,500,500);"><img class="appbar-btn-img" id="help-btn" src="/rainbowstone/images/help.svg" alt="Help"></a> </#if> <#include "component://rainbowstone/template/includes/Avatar.ftl"/> diff --git a/themes/tomahawk/template/AppBarClose.ftl b/themes/tomahawk/template/AppBarClose.ftl index 811bac5..af35581 100644 --- a/themes/tomahawk/template/AppBarClose.ftl +++ b/themes/tomahawk/template/AppBarClose.ftl @@ -75,7 +75,7 @@ under the License. <#--if webSiteId?? && requestAttributes._CURRENT_VIEW_?? && helpTopic??--> <#if parameters.componentName?? && requestAttributes._CURRENT_VIEW_?? && helpTopic??> <#include "component://common-theme/template/includes/HelpLink.ftl" /> - <li><a class="help-link <#if pageAvail?has_content> alert</#if>" href="javascript:lookup_popup1('<@ofbizUrl>showHelp?helpTopic=${helpTopic}&portalPageId=${(parameters.portalPageId!)?html}</@ofbizUrl>','help' ,500,500);" title="${uiLabelMap.CommonHelp}"></a></li> + <li><a class="help-link <#if pageAvail?has_content> alert</#if>" href="javascript:lookup_popup1('showHelp?helpTopic=${helpTopic}&portalPageId=${(parameters.portalPageId!)?html}','help' ,500,500);" title="${uiLabelMap.CommonHelp}"></a></li> </#if> <li><a href="<@ofbizUrl>logout</@ofbizUrl>">${uiLabelMap.CommonLogout}</a></li> <li><a href="<@ofbizUrl>ListVisualThemes</@ofbizUrl>">${uiLabelMap.CommonVisualThemes}</a></li> diff --git a/themes/tomahawk/template/Header.ftl b/themes/tomahawk/template/Header.ftl index d01ae9c..3376614 100644 --- a/themes/tomahawk/template/Header.ftl +++ b/themes/tomahawk/template/Header.ftl @@ -28,10 +28,6 @@ under the License. <html lang="${docLangAttr}" dir="${langDir}" xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> - <#assign csrfDefenseStrategy = Static["org.apache.ofbiz.entity.util.EntityUtilProperties"].getPropertyValue("security", "csrf.defense.strategy", delegator)> - <#if csrfDefenseStrategy != "org.apache.ofbiz.security.NoCsrfDefenseStrategy"> - <meta name="csrf-token" content="<@csrfTokenAjax/>"/> - </#if> <title>${layoutSettings.companyName}: <#if (titleProperty)?has_content>${uiLabelMap[titleProperty]}<#else>${title!}</#if></title> <#if layoutSettings.shortcutIcon?has_content> <#assign shortcutIcon = layoutSettings.shortcutIcon/>

