ctx) {
+ if (value instanceof VariableSource) {
+ return ((VariableSource) value).resolveStatic(ctx);
+ }
+ if (value instanceof Map) {
+ Map, ?> mapVal = ((Map, ?>) value);
+ if (!mapVal.containsKey("$noVariable")) {
+ Object sourceName = mapVal.get("source");
+ if (sourceName != null && VariableSource.Source.of(String.valueOf(sourceName)).isPresent()) {
+ VariableSource source = FastBeanCopier.copy(mapVal, new VariableSource());
+ if (source.getSource() != null) {
+ return source.resolveStatic(ctx);
+ }
+ }
+ }
+ }
+ return value;
+ }
+
public static VariableSource of(Object value) {
if (value instanceof VariableSource) {
return ((VariableSource) value);
diff --git a/jetlinks-components/rule-engine-component/src/main/java/org/jetlinks/community/rule/engine/commons/TermsConditionEvaluator.java b/jetlinks-components/rule-engine-component/src/main/java/org/jetlinks/community/rule/engine/commons/TermsConditionEvaluator.java
index 4fbbc61b6..a169de553 100644
--- a/jetlinks-components/rule-engine-component/src/main/java/org/jetlinks/community/rule/engine/commons/TermsConditionEvaluator.java
+++ b/jetlinks-components/rule-engine-component/src/main/java/org/jetlinks/community/rule/engine/commons/TermsConditionEvaluator.java
@@ -1,8 +1,12 @@
package org.jetlinks.community.rule.engine.commons;
+import com.alibaba.fastjson.JSONObject;
+import org.apache.commons.collections4.CollectionUtils;
import org.hswebframework.ezorm.core.param.Term;
+import org.hswebframework.ezorm.rdb.operator.builder.fragments.NativeSql;
import org.hswebframework.web.api.crud.entity.TermExpressionParser;
import org.hswebframework.web.bean.FastBeanCopier;
+import org.hswebframework.web.exception.I18nSupportException;
import org.jetlinks.community.relation.utils.VariableSource;
import org.jetlinks.community.utils.ReactorUtils;
import org.jetlinks.reactor.ql.utils.CastUtils;
@@ -14,6 +18,7 @@
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -35,14 +40,20 @@
* }
*
* }
+ *
+ * 建议使用{@link TermsConditionEvaluator#createCondition(List)}来创建用户输入的条件
*
* @author zhouhao
+ * @see TermsConditionEvaluator#createCondition(List)
* @since 2.0
*/
public class TermsConditionEvaluator implements ConditionEvaluatorStrategy {
public static final String TYPE = "terms";
public static Condition createCondition(List terms) {
+ //校验条件是否合法
+ validateUnsafeTerm(terms);
+
Condition condition = new Condition();
condition.setType(TYPE);
condition.setConfiguration(Collections.singletonMap("terms", terms));
@@ -59,6 +70,49 @@ public boolean evaluate(Condition condition, RuleData context) {
return false;
}
+ public static void validateUnsafeTerm(List term) {
+ if (CollectionUtils.isNotEmpty(term)) {
+ term.forEach(TermsConditionEvaluator::validateUnsafeTerm);
+ }
+ }
+
+ public static void validateUnsafeTerm(Term term) {
+ Object val = term.getValue();
+ //如果值是Map,并且包含sql字段,说明是有用户输入了sql参数.
+ //程序构造应当使用NativeSql
+ if (val instanceof Map) {
+ @SuppressWarnings("all")
+ JSONObject map = new JSONObject((Map) val);
+ if (map.containsKey("sql")) {
+ throw new I18nSupportException("error.illegal_term_value");
+ }
+ }
+ validateUnsafeTerm(term.getTerms());
+ }
+
+ private Term convertTermValue(Term term) {
+ Object val = term.getValue();
+ if (val instanceof Map) {
+ @SuppressWarnings("all")
+ JSONObject map = new JSONObject((Map) val);
+ //nativeSql,这里存在缺陷? 用户在特殊情况下可以传入任意sql(不使用 createCondition 方法创建的条件)
+ //但是此sql使用reactorQL执行,不会直接执行到数据库,所以不会有sql注入风险.
+ if (map.containsKey("sql")) {
+ String sql = map.getString("sql");
+ Object[] params = map.containsKey("parameters") ? map
+ .getJSONArray("parameters")
+ .toArray() : new Object[0];
+ term.setValue(NativeSql.of(sql, params));
+ }
+ }
+ if (CollectionUtils.isNotEmpty(term.getTerms())) {
+ for (Term termTerm : term.getTerms()) {
+ convertTermValue(termTerm);
+ }
+ }
+ return term;
+ }
+
@Override
public Function> prepare(Condition condition) {
List terms = condition
@@ -66,7 +120,7 @@ public Function> prepare(Condition condition) {
.map(val -> CastUtils
.castArray(val)
.stream()
- .map(obj -> FastBeanCopier.copy(obj, new Term()))
+ .map(obj -> convertTermValue(FastBeanCopier.copy(obj, new Term())))
.collect(Collectors.toList()))
.orElseGet(() -> condition
.getConfig("where")
@@ -77,6 +131,6 @@ public Function> prepare(Condition condition) {
return ReactorUtils.createFilter(terms,
RuleDataHelper::toContextMap,
- (arg, map) -> VariableSource.of(arg).resolveStatic(map));
+ VariableSource::resolveStatic);
}
}
diff --git a/jetlinks-components/rule-engine-component/src/main/java/org/jetlinks/community/rule/engine/commons/things/ThingInfoSpec.java b/jetlinks-components/rule-engine-component/src/main/java/org/jetlinks/community/rule/engine/commons/things/ThingInfoSpec.java
new file mode 100644
index 000000000..bde283315
--- /dev/null
+++ b/jetlinks-components/rule-engine-component/src/main/java/org/jetlinks/community/rule/engine/commons/things/ThingInfoSpec.java
@@ -0,0 +1,265 @@
+package org.jetlinks.community.rule.engine.commons.things;
+
+import com.google.common.collect.Maps;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.commons.collections4.CollectionUtils;
+import org.hswebframework.web.bean.FastBeanCopier;
+import org.hswebframework.web.i18n.LocaleUtils;
+import org.jetlinks.core.Values;
+import org.jetlinks.core.metadata.EventMetadata;
+import org.jetlinks.core.metadata.PropertyMetadata;
+import org.jetlinks.core.metadata.types.DateTimeType;
+import org.jetlinks.core.metadata.types.ObjectType;
+import org.jetlinks.core.metadata.types.StringType;
+import org.jetlinks.core.things.*;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ *
+ * @author zhangji 2025/1/22
+ * @since 2.3
+ */
+@Getter
+@Setter
+public class ThingInfoSpec {
+
+ private Set configs;
+ private Set properties;
+ private Set tags;
+ private Set events;
+
+ public Mono