blob: 6313fbf74c596d330fafbcd4d5c87484a7869144 [file] [log] [blame]
/*******************************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*******************************************************************************/
package org.apache.ofbiz.common;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.ofbiz.base.lang.JSON;
import org.apache.ofbiz.base.location.FlexibleLocation;
import org.apache.ofbiz.base.util.Debug;
import org.apache.ofbiz.base.util.UtilGenerics;
import org.apache.ofbiz.base.util.UtilHttp;
import org.apache.ofbiz.base.util.UtilProperties;
import org.apache.ofbiz.base.util.UtilValidate;
import org.apache.ofbiz.base.util.string.FlexibleStringExpander;
import org.apache.ofbiz.entity.Delegator;
import org.apache.ofbiz.entity.GenericEntityException;
import org.apache.ofbiz.entity.GenericValue;
import org.apache.ofbiz.entity.util.EntityUtilProperties;
import org.apache.ofbiz.webapp.control.JWTManager;
import org.apache.ofbiz.webapp.control.LoginWorker;
import org.apache.ofbiz.widget.model.ModelWidget;
import org.apache.ofbiz.widget.model.MultiBlockHtmlTemplateUtil;
import org.apache.ofbiz.widget.model.ThemeFactory;
import org.apache.ofbiz.widget.renderer.VisualTheme;
/**
* Common Services
*/
public class CommonEvents {
private static final String MODULE = CommonEvents.class.getName();
// Attributes removed for security reason; _ERROR_MESSAGE_ and _ERROR_MESSAGE_LIST are kept
private static final String[] IGNOREATTRS = new String[] {
"javax.servlet.request.key_size",
"_CONTEXT_ROOT_",
"_FORWARDED_FROM_SERVLET_",
"javax.servlet.request.ssl_session",
"javax.servlet.request.ssl_session_id",
"multiPartMap",
"javax.servlet.request.cipher_suite",
"targetRequestUri",
"_SERVER_ROOT_URL_",
"_CONTROL_PATH_",
"thisRequestUri",
"org.apache.tomcat.util.net.secure_protocol_version",
"userLogin",
"impersonateLogin",
"requestMapMap" // requestMapMap is used by CSRFUtil
};
/**
* Simple event to set the users per-session locale setting. The user's locale setting should be passed as a
* "newLocale" request parameter.
*/
public static String setSessionLocale(HttpServletRequest request, HttpServletResponse response) {
String localeString = request.getParameter("newLocale");
if (UtilValidate.isNotEmpty(localeString)) {
UtilHttp.setLocale(request, localeString);
// update the UserLogin object
GenericValue userLogin = (GenericValue) request.getSession().getAttribute("userLogin");
if (userLogin == null) {
userLogin = (GenericValue) request.getSession().getAttribute("autoUserLogin");
}
if (userLogin != null) {
GenericValue ulUpdate = GenericValue.create(userLogin);
ulUpdate.set("lastLocale", localeString);
try {
ulUpdate.store();
userLogin.refreshFromCache();
} catch (GenericEntityException e) {
Debug.logWarning(e, MODULE);
}
}
}
return "success";
}
/** Simple event to set the user's per-session time zone setting. */
public static String setSessionTimeZone(HttpServletRequest request, HttpServletResponse response) {
String tzString = request.getParameter("tzId");
if (UtilValidate.isNotEmpty(tzString)) {
UtilHttp.setTimeZone(request, tzString);
// update the UserLogin object
GenericValue userLogin = (GenericValue) request.getSession().getAttribute("userLogin");
if (userLogin == null) {
userLogin = (GenericValue) request.getSession().getAttribute("autoUserLogin");
}
if (userLogin != null) {
GenericValue ulUpdate = GenericValue.create(userLogin);
ulUpdate.set("lastTimeZone", tzString);
try {
ulUpdate.store();
userLogin.refreshFromCache();
} catch (GenericEntityException e) {
Debug.logWarning(e, MODULE);
}
}
}
return "success";
}
/** Simple event to set the user's per-session theme setting. */
public static String setSessionTheme(HttpServletRequest request, HttpServletResponse response) {
String visualThemeId = request.getParameter("userPrefValue");
if (UtilValidate.isNotEmpty(visualThemeId)) {
VisualTheme visualTheme = ThemeFactory.getVisualThemeFromId(visualThemeId);
if (visualTheme != null) {
UtilHttp.setVisualTheme(request, visualTheme);
}
}
return "success";
}
/** Simple event to set the users per-session currency uom value */
public static String setSessionCurrencyUom(HttpServletRequest request, HttpServletResponse response) {
String currencyUom = request.getParameter("currencyUom");
if (UtilValidate.isNotEmpty(currencyUom)) {
// update the session
UtilHttp.setCurrencyUom(request.getSession(), currencyUom);
// update the UserLogin object
GenericValue userLogin = (GenericValue) request.getSession().getAttribute("userLogin");
if (userLogin == null) {
userLogin = (GenericValue) request.getSession().getAttribute("autoUserLogin");
}
if (userLogin != null) {
GenericValue ulUpdate = GenericValue.create(userLogin);
ulUpdate.set("lastCurrencyUom", currencyUom);
try {
ulUpdate.store();
userLogin.refreshFromCache();
} catch (GenericEntityException e) {
Debug.logWarning(e, MODULE);
}
}
}
return "success";
}
public static String jsResponseFromRequest(HttpServletRequest request, HttpServletResponse response) {
String fileName = request.getParameter("name");
String script = MultiBlockHtmlTemplateUtil.getScriptFromCache(request.getSession(), fileName);
// return the JS String
Writer out;
try {
// set the JS content type
response.setContentType("application/javascript");
// script.length is not reliable for unicode characters
response.setContentLength(script.getBytes("UTF8").length);
// return 404 if script is empty
if (UtilValidate.isEmpty(script)) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
}
out = response.getWriter();
out.write(script);
out.flush();
} catch (IOException e) {
Debug.logError(e, MODULE);
return "error";
}
return "success";
}
public static String jsonResponseFromRequestAttributes(HttpServletRequest request, HttpServletResponse response) {
// pull out the service response from the request attribute
Map<String, Object> attrMap = UtilHttp.getJSONAttributeMap(request);
for (String ignoreAttr : IGNOREATTRS) {
if (attrMap.containsKey(ignoreAttr)) {
attrMap.remove(ignoreAttr);
}
}
try {
JSON json = JSON.from(attrMap);
writeJSONtoResponse(json, request, response);
} catch (IOException e) {
return "error";
}
return "success";
}
private static void writeJSONtoResponse(JSON json, HttpServletRequest request, HttpServletResponse response)
throws UnsupportedEncodingException {
String jsonStr = json.toString();
String httpMethod = request.getMethod();
// This was added for security reason (OFBIZ-5409), you might need to remove the "//" prefix when handling the
// JSON response
// Though normally you simply have to access the data you want, so should not be annoyed by the "//" prefix
if ("GET".equalsIgnoreCase(httpMethod)) {
Debug.logWarning("for security reason (OFBIZ-5409) the '//' prefix was added handling the JSON response. "
+ "Normally you simply have to access the data you want, so should not be annoyed by the '//' prefix."
+ "You might need to remove it if you use Ajax GET responses (not recommended)."
+ "In case, the util.js scrpt is there to help you."
+ "This can be customized in general.properties with the http.json.xssi.prefix property", MODULE);
Delegator delegator = (Delegator) request.getAttribute("delegator");
String xssiPrefix = EntityUtilProperties.getPropertyValue("general", "http.json.xssi.prefix", delegator);
jsonStr = xssiPrefix + jsonStr;
}
// set the JSON content type
response.setContentType("application/json");
// jsonStr.length is not reliable for unicode characters
response.setContentLength(jsonStr.getBytes("UTF8").length);
// return the JSON String
Writer out;
try {
out = response.getWriter();
out.write(jsonStr);
out.flush();
} catch (IOException e) {
Debug.logError(e, MODULE);
}
}
public static String getJSONuiLabelArray(HttpServletRequest request, HttpServletResponse response)
throws UnsupportedEncodingException, IOException {
// Format - {resource1 : [key1, key2 ...], resource2 : [key1, key2, ...], ...}
String jsonString = request.getParameter("requiredLabels");
Map<String, List<String>> uiLabelObject = null;
if (UtilValidate.isNotEmpty(jsonString)) {
JSON json = JSON.from(jsonString);
uiLabelObject = UtilGenerics.<Map<String, List<String>>> cast(json.toObject(Map.class));
}
if (UtilValidate.isEmpty(uiLabelObject)) {
Debug.logError("No resource and labels found in JSON string: " + jsonString, MODULE);
return "error";
}
Locale locale = UtilHttp.getLocale(request);
Map<String, List<String>> uiLabelMap = new HashMap<>();
Set<Map.Entry<String, List<String>>> entrySet = uiLabelObject.entrySet();
for (Map.Entry<String, List<String>> entry : entrySet) {
String resource = entry.getKey();
List<String> resourceKeys = entry.getValue();
if (resourceKeys != null) {
List<String> labels = new ArrayList<>(resourceKeys.size());
for (String resourceKey : resourceKeys) {
String label = UtilProperties.getMessage(resource, resourceKey, locale);
labels.add(label);
}
uiLabelMap.put(resource, labels);
}
}
writeJSONtoResponse(JSON.from(uiLabelMap), request, response);
return "success";
}
public static String getJSONuiLabel(HttpServletRequest request, HttpServletResponse response)
throws UnsupportedEncodingException, IOException {
// Format - {resource : key}
String jsonString = request.getParameter("requiredLabel");
Map<String, String> uiLabelObject = null;
if (UtilValidate.isNotEmpty(jsonString)) {
JSON json = JSON.from(jsonString);
uiLabelObject = UtilGenerics.<Map<String, String>> cast(json.toObject(Map.class));
}
if (UtilValidate.isEmpty(uiLabelObject)) {
Debug.logError("No resource and labels found in JSON string: " + jsonString, MODULE);
return "error";
} else if (uiLabelObject.size() > 1) {
Debug.logError("More than one resource found, please use the method: getJSONuiLabelArray", MODULE);
return "error";
}
Locale locale = UtilHttp.getLocale(request);
Map<String, String> uiLabelMap = new HashMap<>();
Set<Map.Entry<String, String>> entrySet = uiLabelObject.entrySet();
for (Map.Entry<String, String> entry : entrySet) {
String resource = entry.getKey();
String resourceKey = entry.getValue();
if (resourceKey != null) {
String label = UtilProperties.getMessage(resource, resourceKey, locale);
uiLabelMap.put(resource, label);
}
}
writeJSONtoResponse(JSON.from(uiLabelMap), request, response);
return "success";
}
public static String getCaptcha(HttpServletRequest request, HttpServletResponse response) {
try {
Delegator delegator = (Delegator) request.getAttribute("delegator");
final String captchaSizeConfigName = StringUtils.defaultIfEmpty(request.getParameter("captchaSize"),
"default");
final String captchaSizeConfig = EntityUtilProperties.getPropertyValue("captcha",
"captcha." + captchaSizeConfigName, delegator);
final String[] captchaSizeConfigs = captchaSizeConfig.split("\\|");
// this is used to uniquely identify in the user session the attribute where the captcha code
// for the last captcha for the form is stored
final String captchaCodeId = StringUtils.defaultIfEmpty(request.getParameter("captchaCodeId"), "");
final int fontSize = Integer.parseInt(captchaSizeConfigs[0]);
final int height = Integer.parseInt(captchaSizeConfigs[1]);
final int width = Integer.parseInt(captchaSizeConfigs[2]);
final int charsToPrint = UtilProperties.getPropertyAsInteger("captcha", "captcha.code_length", 6);
final char[] availableChars = EntityUtilProperties
.getPropertyValue("captcha", "captcha.characters", delegator).toCharArray();
// It is possible to pass the font size, image width and height with the request as well
Color backgroundColor = Color.gray;
Color borderColor = Color.DARK_GRAY;
Color textColor = Color.ORANGE;
Color circleColor = new Color(160, 160, 160);
Font textFont = new Font("Arial", Font.PLAIN, fontSize);
int circlesToDraw = 6;
float horizMargin = 20.0f;
double rotationRange = 0.7; // in radians
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = (Graphics2D) bufferedImage.getGraphics();
g.setColor(backgroundColor);
g.fillRect(0, 0, width, height);
// Generating some circles for background noise
g.setColor(circleColor);
for (int i = 0; i < circlesToDraw; i++) {
int circleRadius = (int) (Math.random() * height / 2.0);
int circleX = (int) (Math.random() * width - circleRadius);
int circleY = (int) (Math.random() * height - circleRadius);
g.drawOval(circleX, circleY, circleRadius * 2, circleRadius * 2);
}
g.setColor(textColor);
g.setFont(textFont);
FontMetrics fontMetrics = g.getFontMetrics();
int maxAdvance = fontMetrics.getMaxAdvance();
int fontHeight = fontMetrics.getHeight();
String captchaCode = RandomStringUtils.random(6, availableChars);
float spaceForLetters = -horizMargin * 2 + width;
float spacePerChar = spaceForLetters / (charsToPrint - 1.0f);
for (int i = 0; i < captchaCode.length(); i++) {
// this is a separate canvas used for the character so that
// we can rotate it independently
int charWidth = fontMetrics.charWidth(captchaCode.charAt(i));
int charDim = Math.max(maxAdvance, fontHeight);
int halfCharDim = (charDim / 2);
BufferedImage charImage = new BufferedImage(charDim, charDim, BufferedImage.TYPE_INT_ARGB);
Graphics2D charGraphics = charImage.createGraphics();
charGraphics.translate(halfCharDim, halfCharDim);
double angle = (Math.random() - 0.5) * rotationRange;
charGraphics.transform(AffineTransform.getRotateInstance(angle));
charGraphics.translate(-halfCharDim, -halfCharDim);
charGraphics.setColor(textColor);
charGraphics.setFont(textFont);
int charX = (int) (0.5 * charDim - 0.5 * charWidth);
charGraphics.drawString("" + captchaCode.charAt(i), charX,
((charDim - fontMetrics.getAscent()) / 2 + fontMetrics.getAscent()));
float x = horizMargin + spacePerChar * (i) - charDim / 2.0f;
int y = ((height - charDim) / 2);
g.drawImage(charImage, (int) x, y, charDim, charDim, null, null);
charGraphics.dispose();
}
// Drawing the image border
g.setColor(borderColor);
g.drawRect(0, 0, width - 1, height - 1);
g.dispose();
response.setContentType("image/jpeg");
ImageIO.write(bufferedImage, "jpg", response.getOutputStream());
HttpSession session = request.getSession();
Map<String, String> captchaCodeMap = UtilGenerics.cast(session.getAttribute("_CAPTCHA_CODE_"));
if (captchaCodeMap == null) {
captchaCodeMap = new HashMap<>();
session.setAttribute("_CAPTCHA_CODE_", captchaCodeMap);
}
captchaCodeMap.put(captchaCodeId, captchaCode);
} catch (IOException | IllegalArgumentException | IllegalStateException ioe) {
Debug.logError(ioe.getMessage(), MODULE);
}
return "success";
}
public static String loadJWT(HttpServletRequest request, HttpServletResponse response)
throws UnsupportedEncodingException {
Delegator delegator = (Delegator) request.getAttribute("delegator");
Map<String, String> types = new HashMap<>();
String securedUserLoginId = LoginWorker.getSecuredUserLoginId(request);
if (securedUserLoginId != null) {
types.put("userLoginId", securedUserLoginId);
// 30 seconds seems plenty enough OOTB to compensate for possible time difference
// If you cross issue with this value you should use the same NTP server for both sides
// Custom projects might want set a lower value for security reason
int ttlSeconds = (int) Long.parseLong(EntityUtilProperties.getPropertyValue("security",
"security.jwt.token.expireTime", "30", delegator));
String token = JWTManager.createJwt(delegator, types, ttlSeconds);
writeJSONtoResponse(JSON.from(token), request, response);
} else {
Debug.logWarning("No securedUserLoginId cookie was found for this application", MODULE);
}
return "success";
}
public static String openSourceFile(HttpServletRequest request, HttpServletResponse response) {
ModelWidget.NamedBorderType namedBorderType = ModelWidget.widgetNamedBorderEnabled();
if (namedBorderType == ModelWidget.NamedBorderType.SOURCE) {
String sourceLocation = request.getParameter("sourceLocation");
if (UtilValidate.isNotEmpty(sourceLocation) && sourceLocation.startsWith("component:")) {
try {
// find absolute path of file
URL sourceFileUrl = FlexibleLocation.resolveLocation(sourceLocation);
String location = sourceFileUrl.getFile();
// prepare content map for string expansion
Map<String, Object> sourceMap = new HashMap<>();
sourceMap.put("sourceLocation", location);
// get command to run
String cmdTemplate = UtilProperties.getPropertyValue("widget", "widget.dev.cmd.openSourceFile");
String cmd = (String) FlexibleStringExpander.getInstance(cmdTemplate).expand(sourceMap);
// run command
Process process = Runtime.getRuntime().exec(String.format(cmd, location));
// print result
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = "";
while ((line = reader.readLine()) != null) {
Debug.logInfo(line, MODULE);
}
return "success";
} catch (IOException e) {
Debug.logError(e, MODULE);
}
}
}
return "error";
}
}