ExpressionLanguageJEXLImpl.java
- package com.gh.mygreen.xlsmapper.expression;
- import java.util.Arrays;
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.stream.Collectors;
- import org.apache.commons.jexl3.JexlBuilder;
- import org.apache.commons.jexl3.JexlEngine;
- import org.apache.commons.jexl3.JexlExpression;
- import org.apache.commons.jexl3.MapContext;
- import org.apache.commons.jexl3.introspection.JexlPermissions;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import com.gh.mygreen.xlsmapper.util.ArgUtils;
- import com.gh.mygreen.xlsmapper.util.Utils;
- /**
- * 式言語「JEXL」の実装。
- * <p>利用する際には、JEXL v3.3以上のライブラリが必要です。
- * <p>JEXL v3.3から、ELインジェクション対策として、<a href="https://commons.apache.org/proper/commons-jexl/apidocs/org/apache/commons/jexl3/introspection/JexlPermissions.html)">JexlPermissions</a>によるEL式中で参照/実行可能なクラスを制限されます。
- * <p>独自のCellConverter / FiledProcessosrなどを実装しているが場合は、システムプロパティ {@literal xlsmapper.jexlPermissions} で指定することができます。
- * 複数指定する場合はカンマ区切りで指定します。
- * </p>
- *
- * @version 2.3
- * @since 1.5
- * @author T.TSUCHIE
- *
- */
- public class ExpressionLanguageJEXLImpl implements ExpressionLanguage {
-
- private static final Logger logger = LoggerFactory.getLogger(ExpressionLanguageJEXLImpl.class);
-
- /**
- * システムプロパティ - JEXLをRESTRICTモードで使用するかどうかフラグ。
- */
- public static final String PROPERTY_JEXL_RESTRICTED = "xlsmapper.jexlRestricted";
-
- /**
- * システムプロパティ - JEXLをRESTRICTモードで使用する場合のパーミッションを指定する。
- */
- public static final String PROPERTY_JEXL_PERMISSIONS = "xlsmapper.jexlPermissions";
-
- /**
- * 本ライブラリでJEXLからアクセス許可するパッケージ指定のパーミッション。
- */
- private static final String[] LIB_PERMISSIONS =
- {"com.gh.mygreen.xlsmapper.*"};
-
- /**
- * 独自のJEXLのパーミッション。
- */
- private static final String[] USER_PERMISSIONS;
- static {
- String value = System.getProperty(PROPERTY_JEXL_PERMISSIONS);
- if(Utils.isNotEmpty(value)) {
- USER_PERMISSIONS = Arrays.stream(value.split(","))
- .map(String::trim)
- .filter(String::isEmpty)
- .collect(Collectors.toList())
- .toArray(new String[0]);
-
- } else {
- USER_PERMISSIONS = new String[] {};
- }
- }
-
- /**
- * JEXLのキャッシュサイズ。
- * <p>キャッシュする式の個数。
- */
- private static final int CACHE_SIZE = 256;
-
- private final JexlEngine jexlEngine;
-
- /**
- * デフォルトのコンストラクタ。
- * <p>パーミッションによる制限を実施する。
- */
- public ExpressionLanguageJEXLImpl() {
- this(Collections.emptyMap(), true);
-
- }
-
- /**
- * JEXLの独自のEL関数とパーミッションを指定するコンストラクタ。
- * <p>関数として{@link CustomFunctions}が登録されており、接頭語 {@literal f:}で呼び出し可能です。
- *
- * @param userFunctions 独自のEL関数を指定します。keyは接頭語、valueはメソッドが定義されたクラス。
- * @param restricted JEXLをRESTRICTEDモードでパーミッションによる制限を行うかどうか。
- * @param userPermissions JEXLのパーミッション。
- * 詳細は、<a href="https://commons.apache.org/proper/commons-jexl/apidocs/org/apache/commons/jexl3/introspection/JexlPermissions.html)">JexlPermissions</a> を参照。
- */
- public ExpressionLanguageJEXLImpl(final Map<String, Object> userFunctions, final boolean restricted, final String... userPermissions) {
-
- // EL式中で使用可能な関数の登録
- Map<String, Object> functions = new HashMap<>();
- functions.put("f", CustomFunctions.class);
-
- if (Utils.isNotEmpty(userFunctions)) {
- functions.putAll(userFunctions);
- }
- final JexlPermissions permissions;
- if(Utils.toBoolean(System.getProperty(PROPERTY_JEXL_RESTRICTED), restricted)) {
- /*
- * EL式で本ライブラリのクラス/メソッドのアクセスを許可する。
- * ・CustomFunctions以外にも、CellConverter / FieldProcessorでも参照するため。
- * ・JEXLv3からサーバーサイド・テンプレート・インジェクション、コマンドインジェクション対策のために、
- * 許可されたクラスしか参照できなくなったため、本ライブラリをEL式から参照可能に許可する。
- */
- String[] concateedUserPermission = Utils.concat(USER_PERMISSIONS, userPermissions);
- permissions = JexlPermissions.RESTRICTED
- .compose(Utils.concat(LIB_PERMISSIONS, concateedUserPermission));
- } else {
- // パーミッションによる制限を行わない。
- permissions = JexlPermissions.UNRESTRICTED;
- }
- this.jexlEngine = new JexlBuilder()
- .namespaces(functions)
- .permissions(permissions)
- .silent(true)
- .strict(false) // JEXLv2相当の文法にする。
- .cache(CACHE_SIZE)
- .create();
-
- }
-
-
- /**
- * {@link JexlEngine}を指定するコンストラクタ。
- * @param jexlEngine JEXLの処理エンジン。
- */
- public ExpressionLanguageJEXLImpl(final JexlEngine jexlEngine) {
- this.jexlEngine = jexlEngine;
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public Object evaluate(final String expression, final Map<String, ?> values) {
-
- ArgUtils.notEmpty(expression, "expression");
- ArgUtils.notNull(values, "values");
-
- if(logger.isDebugEnabled()) {
- logger.debug("Evaluating JEXL expression: {}", expression);
- }
-
- try {
- JexlExpression expr = jexlEngine.createExpression(expression);
- return expr.evaluate(new MapContext((Map<String, Object>) values));
-
- } catch(Exception ex) {
- throw new ExpressionEvaluationException(String.format("Evaluating [%s] script with JEXL failed.", expression), ex);
- }
- }
-
- /**
- * {@link JexlEngine}を取得する。
- * @return
- */
- public JexlEngine getJexlEngine() {
- return jexlEngine;
- }
-
- }