Skip to content

Commit adfcce4

Browse files
authored
Fixed: Ajax request fail on restful page (OFBIZ-13231) (#889)
A problem was detected with some ajax call did by js script that failed with error 405 like : https://demo-next.ofbiz.apache.org/webtools/control/entity/find/SetTimeZoneFromBrowser Reason : SetTimeZoneFromBrowser is a request define in common-controller.xml, so available on all component. In js the call is realized by : $.ajax({ url: "SetTimeZoneFromBrowser", type: "POST", async: false,... Navigator use the relative url to execute the call. In general case we have a page like https://demo-next.ofbiz.apache.org/$component/control/$request , js script realized their call with https://demo-next.ofbiz.apache.org/$component/control/$request-js. Like each request-js are present on common-controller.xml all component that include it can response. With rest url, the uri pattern is more complex and the script js generate a relative call like we have upper : _https://demo-next.ofbiz.apache.org/webtools/control/entity/find/SetTimeZoneFromBrowse_. The ControlServlet behind failed to retrieve the correct request and generate a http error 405 To fix : We remove all relative js call and create a dedicated webapp for that. $.ajax({ url: "/common-js/control/SetTimeZoneFromBrowser", type: "POST", async: false,... To pass through the authentification (we implement a new webapp), we store a jwt token with the current userLogin after the authentification that will use by common-ext to confirm authentification. This cookie is available during all the session time. For security reason, login cookie contains a jwt token generate with le JWTManager ofbiz class.
1 parent 23d8045 commit adfcce4

11 files changed

Lines changed: 200 additions & 15 deletions

File tree

applications/commonext/ofbiz-component.xml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,14 @@ under the License.
4040
location="webapp/ofbizsetup"
4141
base-permission="OFBTOOLS,SETUP"
4242
mount-point="/ofbizsetup"/>
43-
43+
44+
<webapp name="common-js"
45+
title="common-js"
46+
server="default-server"
47+
location="webapp/common-js"
48+
mount-point="/common-js"
49+
app-bar-display="false"/>
50+
4451
<webapp name="ordermgr-js"
4552
title="ordermgr-js"
4653
server="default-server"
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
Licensed to the Apache Software Foundation (ASF) under one
4+
or more contributor license agreements. See the NOTICE file
5+
distributed with this work for additional information
6+
regarding copyright ownership. The ASF licenses this file
7+
to you under the Apache License, Version 2.0 (the
8+
"License"); you may not use this file except in compliance
9+
with the License. You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing,
14+
software distributed under the License is distributed on an
15+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
KIND, either express or implied. See the License for the
17+
specific language governing permissions and limitations
18+
under the License.
19+
-->
20+
21+
<site-conf xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
22+
xmlns="http://ofbiz.apache.org/Site-Conf" xsi:schemaLocation="http://ofbiz.apache.org/Site-Conf http://ofbiz.apache.org/dtds/site-conf.xsd">
23+
<include location="component://common/webcommon/WEB-INF/common-controller.xml"/>
24+
25+
<preprocessor>
26+
<event name="securedUserLoginByJWTCookie" type="java" path="org.apache.ofbiz.webapp.control.LoginWorker" invoke="securedUserLoginByJWTCookie"/>
27+
</preprocessor>
28+
</site-conf>
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?xml version="1.0"?>
2+
3+
<!--
4+
Licensed to the Apache Software Foundation (ASF) under one
5+
or more contributor license agreements. See the NOTICE file
6+
distributed with this work for additional information
7+
regarding copyright ownership. The ASF licenses this file
8+
to you under the Apache License, Version 2.0 (the
9+
"License"); you may not use this file except in compliance
10+
with the License. You may obtain a copy of the License at
11+
12+
http://www.apache.org/licenses/LICENSE-2.0
13+
14+
Unless required by applicable law or agreed to in writing,
15+
software distributed under the License is distributed on an
16+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17+
KIND, either express or implied. See the License for the
18+
specific language governing permissions and limitations
19+
under the License.
20+
-->
21+
22+
<web-app version="4.0" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd">
23+
<display-name>Apache OFBiz - Setup Manager</display-name>
24+
<description>Common Ext Module of the Apache OFBiz Project</description>
25+
26+
<context-param>
27+
<description>The Name of the Entity Delegator to use, defined in entityengine.xml</description>
28+
<param-name>entityDelegatorName</param-name>
29+
<param-value>default</param-value>
30+
</context-param>
31+
<context-param>
32+
<description>A unique name used to identify/recognize the local dispatcher for the Service Engine</description>
33+
<param-name>localDispatcherName</param-name>
34+
<param-value>common-js</param-value>
35+
</context-param>
36+
37+
<filter>
38+
<display-name>ControlFilter</display-name>
39+
<filter-name>ControlFilter</filter-name>
40+
<filter-class>org.apache.ofbiz.webapp.control.ControlFilter</filter-class>
41+
<init-param>
42+
<param-name>allowedPaths</param-name>
43+
<param-value>/error:/control:/js</param-value>
44+
</init-param>
45+
</filter>
46+
<filter>
47+
<display-name>ContextFilter</display-name>
48+
<filter-name>ContextFilter</filter-name>
49+
<filter-class>org.apache.ofbiz.webapp.control.ContextFilter</filter-class>
50+
</filter>
51+
<filter>
52+
<display-name>SameSiteFilter</display-name>
53+
<filter-name>SameSiteFilter</filter-name>
54+
<filter-class>org.apache.ofbiz.webapp.control.SameSiteFilter</filter-class>
55+
</filter>
56+
<filter-mapping>
57+
<filter-name>ControlFilter</filter-name>
58+
<url-pattern>/*</url-pattern>
59+
</filter-mapping>
60+
<filter-mapping>
61+
<filter-name>ContextFilter</filter-name>
62+
<url-pattern>/*</url-pattern>
63+
</filter-mapping>
64+
<filter-mapping>
65+
<filter-name>SameSiteFilter</filter-name>
66+
<url-pattern>/*</url-pattern>
67+
</filter-mapping>
68+
69+
<listener><listener-class>org.apache.ofbiz.webapp.control.ControlEventListener</listener-class></listener>
70+
<listener><listener-class>org.apache.ofbiz.webapp.control.LoginEventListener</listener-class></listener>
71+
72+
<servlet>
73+
<description>Main Control Servlet</description>
74+
<display-name>ControlServlet</display-name>
75+
<servlet-name>ControlServlet</servlet-name>
76+
<servlet-class>org.apache.ofbiz.webapp.control.ControlServlet</servlet-class>
77+
<load-on-startup>1</load-on-startup>
78+
</servlet>
79+
<servlet-mapping>
80+
<servlet-name>ControlServlet</servlet-name>
81+
<url-pattern>/control/*</url-pattern>
82+
</servlet-mapping>
83+
84+
</web-app>

applications/commonext/webapp/ordermgr-js/geoAutoCompleter.js

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,8 @@ function setKeyAsParameter(event, ui) {
8080
//Generic function for fetching country's associated state list.
8181
function getAssociatedStateList(countryId, stateId, errorId, divId) {
8282
var countryGeoId = jQuery("#" + countryId).val();
83-
var requestToSend = "getAssociatedStateList";
84-
if (jQuery('#orderViewed').length) {
85-
requestToSend = "/ordermgr/control/getAssociatedStateList"
86-
}
8783
jQuery.ajax({
88-
url: requestToSend,
84+
url: '/common-js/control/getAssociatedStateList',
8985
type: "POST",
9086
data: {countryGeoId: countryGeoId},
9187
success: function(data) {

applications/party/webapp/partymgr/static/PartyProfileContent.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ function getUploadProgressStatus(event){
9797
tick: function(counter, timerId) {
9898
var timerId = timerId;
9999
jQuery.ajax({
100-
url: 'getFileUploadProgressStatus',
100+
url: '/common-js/control/getFileUploadProgressStatus',
101101
dataType: 'json',
102102
success: function(data) {
103103
if (data._ERROR_MESSAGE_LIST_ != undefined) {

framework/security/src/main/java/org/apache/ofbiz/security/SecurityUtil.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,11 @@ public static boolean authenticateUserLoginByJWT(Delegator delegator, String use
153153
if (UtilValidate.isNotEmpty(jwtToken)) {
154154
try {
155155
GenericValue userLogin = EntityQuery.use(delegator).from("UserLogin").where("userLoginId", userLoginId).queryOne();
156-
Map<String, Object> claims = JWTManager.validateToken(delegator, jwtToken,
157-
userLogin.getString("userLoginId") + userLogin.getString("currentPassword"));
158-
return (!ServiceUtil.isError(claims)) && userLoginId.equals(claims.get("userLoginId"));
156+
if (userLoginId != null) {
157+
Map<String, Object> claims = JWTManager.validateToken(delegator, jwtToken,
158+
userLogin.getString("userLoginId") + userLogin.getString("currentPassword"));
159+
return (!ServiceUtil.isError(claims)) && userLoginId.equals(claims.get("userLoginId"));
160+
}
159161
} catch (GenericEntityException e) {
160162
Debug.logWarning("failed to validate a jwToken for user " + userLoginId, MODULE);
161163
}

framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/LoginWorker.java

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
*******************************************************************************/
1919
package org.apache.ofbiz.webapp.control;
2020

21+
import com.auth0.jwt.JWT;
22+
import com.auth0.jwt.interfaces.DecodedJWT;
2123
import static org.apache.ofbiz.base.util.UtilGenerics.checkMap;
2224

2325
import java.math.BigInteger;
@@ -80,13 +82,15 @@
8082
import org.apache.ofbiz.security.Security;
8183
import org.apache.ofbiz.security.SecurityConfigurationException;
8284
import org.apache.ofbiz.security.SecurityFactory;
85+
import org.apache.ofbiz.security.SecurityUtil;
8386
import org.apache.ofbiz.service.GenericServiceException;
8487
import org.apache.ofbiz.service.LocalDispatcher;
8588
import org.apache.ofbiz.service.ModelService;
8689
import org.apache.ofbiz.service.ServiceUtil;
8790
import org.apache.ofbiz.webapp.WebAppCache;
8891
import org.apache.ofbiz.webapp.WebAppUtil;
8992
import org.apache.ofbiz.webapp.stats.VisitHandler;
93+
import org.apache.ofbiz.webapp.website.WebSiteProperties;
9094
import org.apache.ofbiz.widget.model.ThemeFactory;
9195

9296
/**
@@ -825,6 +829,9 @@ public static String doMainLogin(HttpServletRequest request, HttpServletResponse
825829
// Create a secured cookie with the correct userLoginId
826830
createSecuredLoginIdCookie(request, response);
827831

832+
// Create a secured cookie with a jwt contains the userLoginId
833+
createSecuredLoginJwtCookie(request, response);
834+
828835
// make sure the autoUserLogin is set to the same and that the client cookie has the correct userLoginId
829836
autoLoginSet(request, response);
830837

@@ -1000,6 +1007,38 @@ public static void createSecuredLoginIdCookie(HttpServletRequest request, HttpSe
10001007
}
10011008
}
10021009

1010+
/**
1011+
* Set to response a cookie that contains an identification jwt to share with some other webapp who authenticate with
1012+
* event securedUserLoginByJWTCookie
1013+
* @param request
1014+
* @param response
1015+
*/
1016+
public static void createSecuredLoginJwtCookie(HttpServletRequest request, HttpServletResponse response) {
1017+
Delegator delegator = (Delegator) request.getAttribute("delegator");
1018+
HttpSession session = request.getSession();
1019+
GenericValue userLogin = (GenericValue) session.getAttribute("userLogin");
1020+
if (userLogin != null) {
1021+
try {
1022+
String cookieName = "securedLoginToken";
1023+
Cookie securedLoginTokenCookie = new Cookie(cookieName,
1024+
SecurityUtil.generateJwtToAuthenticateUserLogin(
1025+
delegator, userLogin.getString("userLoginId")));
1026+
String cookieDomain = "";
1027+
try {
1028+
WebSiteProperties webSiteProperties = WebSiteProperties.from(request);
1029+
cookieDomain = webSiteProperties != null
1030+
? webSiteProperties.getHttpsHost()
1031+
: EntityUtilProperties.getPropertyValue("url", "cookie.domain", delegator);
1032+
} catch (GenericEntityException ignored) { }
1033+
securedLoginTokenCookie.setDomain(cookieDomain);
1034+
securedLoginTokenCookie.setPath("/");
1035+
securedLoginTokenCookie.setSecure(true);
1036+
securedLoginTokenCookie.setHttpOnly(true);
1037+
response.addCookie(securedLoginTokenCookie);
1038+
} catch (Exception ignored) { }
1039+
}
1040+
}
1041+
10031042
protected static String getAutoLoginCookieName(HttpServletRequest request) {
10041043
return UtilHttp.getApplicationName(request) + ".autoUserLoginId";
10051044
}
@@ -1040,6 +1079,27 @@ public static String getSecuredUserLoginId(HttpServletRequest request) {
10401079
}
10411080
return securedUserLoginId;
10421081
}
1082+
public static String getSecuredUserLoginByJWT(HttpServletRequest request) {
1083+
Delegator delegator = (Delegator) request.getAttribute("delegator");
1084+
Cookie[] cookies = request.getCookies();
1085+
if (cookies != null) {
1086+
Optional<Cookie> securedCookie = Arrays.stream(cookies)
1087+
.filter(cookie -> cookie.getName().equals("securedLoginToken"))
1088+
.findFirst();
1089+
if (securedCookie.isPresent()) {
1090+
try {
1091+
DecodedJWT jwt = JWT.decode(securedCookie.get().getValue());
1092+
if (SecurityUtil.authenticateUserLoginByJWT(delegator,
1093+
jwt.getClaim("userLoginId").asString(), jwt.getToken())) {
1094+
return jwt.getClaim("userLoginId").asString();
1095+
}
1096+
} catch (Exception failed) {
1097+
Debug.logWarning(failed, MODULE);
1098+
}
1099+
}
1100+
}
1101+
return null;
1102+
}
10431103

10441104
public static String autoLoginCheck(HttpServletRequest request, HttpServletResponse response) {
10451105
Delegator delegator = (Delegator) request.getAttribute("delegator");
@@ -1051,6 +1111,14 @@ public static String autoLoginCheck(HttpServletRequest request, HttpServletRespo
10511111
return autoLoginCheck(delegator, session, getAutoUserLoginId(request));
10521112
}
10531113

1114+
public static String securedUserLoginByJWTCookie(HttpServletRequest request, HttpServletResponse response) {
1115+
String userLoginId = getSecuredUserLoginByJWT(request);
1116+
if (userLoginId != null) {
1117+
return loginUserWithUserLoginId(request, response, userLoginId);
1118+
}
1119+
return "success";
1120+
}
1121+
10541122
private static String autoLoginCheck(Delegator delegator, HttpSession session, String autoUserLoginId) {
10551123
if (autoUserLoginId != null) {
10561124
if (Debug.infoOn()) {

framework/webtools/widget/GeoManagementScreens.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@
168168
<set field="asm_title" from-field="uiLabelMap.WebtoolsGeosSelect"/>
169169
<!-- selectMultipleRelatedValues parameters -->
170170
<set field="asm_relatedField" value="LinkGeos_geoId"/>
171-
<set field="asm_requestName" value="getRelatedGeos"/>
171+
<set field="asm_requestName" value="/common-js/control/getRelatedGeos"/>
172172
<set field="asm_paramKey" value="geoId"/>
173173
<set field="asm_type" value="geoAssocTypeId"/>
174174
<set field="asm_typeField" value="LinkGeos_geoAssocTypeId"/>

themes/common-theme/webapp/common-theme/js/util/OfbizUtil.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1437,7 +1437,7 @@ function getJSONuiLabels(requiredLabels, callback) {
14371437

14381438
if (requiredLabels != null && requiredLabels != "") {
14391439
jQuery.ajax({
1440-
url: "getUiLabels",
1440+
url: "/common-js/control/getUiLabels",
14411441
type: "POST",
14421442
async: false,
14431443
data: { "requiredLabels": requiredLabelsStr, "widgetVerbose": false },
@@ -1547,7 +1547,7 @@ function submitPagination(obj, url) {
15471547
function loadJWT() {
15481548
var JwtToken = "";
15491549
jQuery.ajax({
1550-
url: "loadJWT",
1550+
url: "/common-js/control/loadJWT",
15511551
type: "POST",
15521552
async: false,
15531553
dataType: "text",

themes/common-theme/webapp/common-theme/js/util/miscAjaxFunctions.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ function getServiceResult(){
141141
var data;
142142
jQuery.ajax({
143143
type: 'POST',
144-
url: request,
144+
url: '/common-js/control/' + request,
145145
data: prepareAjaxData(arguments),
146146
async: false,
147147
cache: false,

0 commit comments

Comments
 (0)