diff --git a/@rbv b/@rbv
index 017c99f74..f256231e8 160000
--- a/@rbv
+++ b/@rbv
@@ -1 +1 @@
-Subproject commit 017c99f7496cc86adb2e0b5cc4ca277f0dcc6d61
+Subproject commit f256231e8f0d67ee326d47b65ed9408a0ce9320f
diff --git a/SECURITY.md b/SECURITY.md
index 83a6fd9da..90c9c5e75 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -4,9 +4,10 @@
| Version | Supported |
| ------- | ------------------ |
-| 2.x | :white_check_mark: |
+| 3.x | :white_check_mark: |
| 1.x | :x: |
+| 2.x | :x: |
## Reporting a Vulnerability
-Please report security issues to `rebuild@ruifang-tech.com`
\ No newline at end of file
+Please report security issues to `rebuild@ruifang-tech.com`
diff --git a/pom.xml b/pom.xml
index d7b2e01bf..a1c70df9a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,7 +10,7 @@
com.rebuild
rebuild
- 3.1.0
+ 3.1.1
rebuild
Building your business-systems freely!
diff --git a/src/main/java/com/rebuild/api/ApiGateway.java b/src/main/java/com/rebuild/api/ApiGateway.java
index 3025c94b9..93f9f34b6 100644
--- a/src/main/java/com/rebuild/api/ApiGateway.java
+++ b/src/main/java/com/rebuild/api/ApiGateway.java
@@ -156,7 +156,16 @@ public class ApiGateway extends Controller implements Initialization {
final ConfigBean apiConfig = RebuildApiManager.instance.getApp(appid);
if (apiConfig == null) {
- throw new ApiInvokeException(ApiInvokeException.ERR_BADAUTH, "Invalid [appid] " + appid);
+ throw new ApiInvokeException(ApiInvokeException.ERR_BADAUTH, "Invalid [appid] : " + appid);
+ }
+
+ // v3.1.1
+ final String bindIps = apiConfig.getString("bindIps");
+ if (StringUtils.isNotBlank(bindIps)) {
+ String clientIp = ServletUtils.getRemoteAddr(request);
+ if (!bindIps.contains(clientIp)) {
+ throw new ApiInvokeException(ApiInvokeException.ERR_BADAUTH, "Client ip not in whitelist : " + clientIp);
+ }
}
// 验证签名
diff --git a/src/main/java/com/rebuild/api/user/AuthTokenManager.java b/src/main/java/com/rebuild/api/user/AuthTokenManager.java
index 73bea6bbc..536654125 100644
--- a/src/main/java/com/rebuild/api/user/AuthTokenManager.java
+++ b/src/main/java/com/rebuild/api/user/AuthTokenManager.java
@@ -61,7 +61,7 @@ public class AuthTokenManager {
System.nanoTime());
String token = EncryptUtils.toSHA1Hex(desc);
- Application.getCommonsCache().putx(TOKEN_PREFIX + token, desc, expires);
+ Application.getCommonsCache().put(TOKEN_PREFIX + token, desc, expires);
return token;
}
diff --git a/src/main/java/com/rebuild/core/Application.java b/src/main/java/com/rebuild/core/Application.java
index 702fdbdf6..ef1a7c97d 100644
--- a/src/main/java/com/rebuild/core/Application.java
+++ b/src/main/java/com/rebuild/core/Application.java
@@ -67,11 +67,11 @@ public class Application implements ApplicationListener
/**
* Rebuild Version
*/
- public static final String VER = "3.1.0";
+ public static final String VER = "3.1.1";
/**
* Rebuild Build [MAJOR]{1}[MINOR]{2}[PATCH]{2}[BUILD]{2}
*/
- public static final int BUILD = 3010008;
+ public static final int BUILD = 3010109;
static {
// Driver for DB
diff --git a/src/main/java/com/rebuild/core/configuration/RebuildApiService.java b/src/main/java/com/rebuild/core/configuration/RebuildApiService.java
index 3c9d8e5fa..34a49bc49 100644
--- a/src/main/java/com/rebuild/core/configuration/RebuildApiService.java
+++ b/src/main/java/com/rebuild/core/configuration/RebuildApiService.java
@@ -7,11 +7,14 @@ See LICENSE and COMMERCIAL in the project root for license information.
package com.rebuild.core.configuration;
+import cn.devezhao.commons.CodecUtils;
import cn.devezhao.persist4j.PersistManagerFactory;
+import cn.devezhao.persist4j.Record;
import cn.devezhao.persist4j.engine.ID;
import com.rebuild.core.Application;
import com.rebuild.core.metadata.EntityHelper;
import com.rebuild.core.privileges.AdminGuard;
+import org.apache.commons.lang.math.RandomUtils;
import org.springframework.stereotype.Service;
/**
@@ -32,6 +35,20 @@ public class RebuildApiService extends BaseConfigurationService implements Admin
return EntityHelper.RebuildApi;
}
+ @Override
+ public Record create(Record record) {
+ record.setString("appId", (100000000 + RandomUtils.nextInt(899999999)) + "");
+ record.setString("appSecret", CodecUtils.randomCode(40));
+ return super.create(record);
+ }
+
+ @Override
+ public Record update(Record record) {
+ record.removeValue("appId");
+ record.removeValue("appSecret");
+ return super.update(record);
+ }
+
@Override
protected void cleanCache(ID cfgid) {
Object[] cfg = Application.createQueryNoFilter(
diff --git a/src/main/java/com/rebuild/core/configuration/general/FormsBuilder.java b/src/main/java/com/rebuild/core/configuration/general/FormsBuilder.java
index 2bc686967..dfb610537 100644
--- a/src/main/java/com/rebuild/core/configuration/general/FormsBuilder.java
+++ b/src/main/java/com/rebuild/core/configuration/general/FormsBuilder.java
@@ -678,6 +678,13 @@ public class FormsBuilder extends FormsManager {
fieldParent = dtf.getName() + "." + pfs[0].split("\\.")[1];
}
+ // v3.1.1 父级已删除
+ Entity entity = MetadataHelper.getEntity(record.getEntityCode());
+ if (MetadataHelper.getLastJoinField(entity, fieldParent) == null) {
+ log.warn("Unknow field : {} in {}", fieldParent, entity.getName());
+ return null;
+ }
+
Object[] o = Application.getQueryFactory().uniqueNoFilter(record, fieldParent);
return o == null ? null : (ID) o[0];
}
diff --git a/src/main/java/com/rebuild/core/metadata/EntityRecordCreator.java b/src/main/java/com/rebuild/core/metadata/EntityRecordCreator.java
index 07520022b..fdcccf145 100644
--- a/src/main/java/com/rebuild/core/metadata/EntityRecordCreator.java
+++ b/src/main/java/com/rebuild/core/metadata/EntityRecordCreator.java
@@ -58,7 +58,7 @@ public class EntityRecordCreator extends JsonRecordCreator {
final boolean isNew = record.getPrimary() == null;
// 明细关联主记录
- if (isNew && isDTF(field)) return true;
+ if (isNew && isDtmField(field)) return true;
// 公共字段前台可能会布局出来
// 此处忽略检查没问题,因为最后还会复写,即 #bindCommonsFieldsValue
@@ -107,7 +107,7 @@ public class EntityRecordCreator extends JsonRecordCreator {
}
} else {
if (field.isCreatable()) {
- if (!matchsPattern(easyField, hasVal)) {
+ if (!patternMatches(easyField, hasVal)) {
notWells.add(easyField.getLabel());
}
} else {
@@ -135,7 +135,7 @@ public class EntityRecordCreator extends JsonRecordCreator {
}
} else {
if (field.isUpdatable()) {
- if (!matchsPattern(easyField, hasVal)) {
+ if (!patternMatches(easyField, hasVal)) {
notWells.add(easyField.getLabel());
}
} else {
@@ -157,7 +157,7 @@ public class EntityRecordCreator extends JsonRecordCreator {
}
// 明细关联主记录字段
- private boolean isDTF(Field field) {
+ private boolean isDtmField(Field field) {
if (field.getType() == FieldType.REFERENCE && entity.getMainEntity() != null) {
return field.equals(MetadataHelper.getDetailToMainField(entity));
}
@@ -166,7 +166,7 @@ public class EntityRecordCreator extends JsonRecordCreator {
// 强制可新建
private boolean isForceCreateable(Field field) {
- if (isDTF(field)) return true;
+ if (isDtmField(field)) return true;
// 自定定位
EasyField easyField = EasyMetaFactory.valueOf(field);
@@ -178,7 +178,8 @@ public class EntityRecordCreator extends JsonRecordCreator {
}
// 正则匹配
- private boolean matchsPattern(EasyField easyField, Object val) {
+ @SuppressWarnings("BooleanMethodIsAlwaysInverted")
+ private boolean patternMatches(EasyField easyField, Object val) {
if (!(easyField instanceof EasyText)) return true;
Pattern patt = ((EasyText) easyField).getPattern();
diff --git a/src/main/java/com/rebuild/core/privileges/UserHelper.java b/src/main/java/com/rebuild/core/privileges/UserHelper.java
index db1d8407b..e87bdfee3 100644
--- a/src/main/java/com/rebuild/core/privileges/UserHelper.java
+++ b/src/main/java/com/rebuild/core/privileges/UserHelper.java
@@ -314,18 +314,17 @@ public class UserHelper {
*/
public static File generateAvatar(String name, boolean forceMake) {
if (StringUtils.isBlank(name)) name = "RB";
- File avatarFile = RebuildConfiguration.getFileOfData("avatar-" + name + "29.jpg");
- if (avatarFile.exists()) {
+
+ File avatar = RebuildConfiguration.getFileOfData("avatar-" + name + "29.jpg");
+ if (avatar.exists()) {
if (forceMake) {
- FileUtils.deleteQuietly(avatarFile);
+ FileUtils.deleteQuietly(avatar);
} else {
- return avatarFile;
+ return avatar;
}
}
- if (name.length() > 2) {
- name = name.substring(name.length() - 2);
- }
+ if (name.length() > 2) name = name.substring(name.length() - 2);
name = name.toUpperCase();
BufferedImage bi = new BufferedImage(200, 200, BufferedImage.TYPE_INT_RGB);
@@ -347,7 +346,7 @@ public class UserHelper {
g2d.drawString("wbr", 0, 62);
g2d.dispose();
- try (FileOutputStream fos = new FileOutputStream(avatarFile)) {
+ try (FileOutputStream fos = new FileOutputStream(avatar)) {
ImageIO.write(bi, "png", fos);
fos.flush();
}
@@ -360,7 +359,7 @@ public class UserHelper {
is = CommonsUtils.getStreamOfRes("/web" + DEFAULT_AVATAR);
bi = ImageIO.read(is);
- try (FileOutputStream fos = new FileOutputStream(avatarFile)) {
+ try (FileOutputStream fos = new FileOutputStream(avatar)) {
ImageIO.write(bi, "png", fos);
fos.flush();
}
@@ -370,7 +369,7 @@ public class UserHelper {
}
}
- return avatarFile;
+ return avatar;
}
private static Font createFont() {
@@ -408,7 +407,7 @@ public class UserHelper {
* @see #sortUsers(boolean)
*/
public static User[] sortUsers() {
- return sortUsers(false);
+ return sortUsers(Boolean.FALSE);
}
/**
diff --git a/src/main/java/com/rebuild/core/service/dashboard/ChartManager.java b/src/main/java/com/rebuild/core/service/dashboard/ChartManager.java
index 6cee05566..e7058e4c5 100644
--- a/src/main/java/com/rebuild/core/service/dashboard/ChartManager.java
+++ b/src/main/java/com/rebuild/core/service/dashboard/ChartManager.java
@@ -144,11 +144,12 @@ public class ChartManager implements ConfigManager {
}
ch.put("title", e.getString("title"));
- ch.put("type", e.getString("type"));
+ String type = e.getString("type");
+ ch.put("type", type);
try {
String c = ((JSONObject) e.getJSON("config")).getJSONObject("option").getString("useColor");
- if (StringUtils.isNotBlank(c)) ch.put("color", c);
+ if ("INDEX".equals(type) && StringUtils.isNotBlank(c)) ch.put("color", c);
} catch (Exception ignore) {}
if (user != null) {
diff --git a/src/main/java/com/rebuild/core/service/dashboard/charts/TableBuilder.java b/src/main/java/com/rebuild/core/service/dashboard/charts/TableBuilder.java
index 74aeb1629..ee8b2856a 100644
--- a/src/main/java/com/rebuild/core/service/dashboard/charts/TableBuilder.java
+++ b/src/main/java/com/rebuild/core/service/dashboard/charts/TableBuilder.java
@@ -90,33 +90,35 @@ public class TableBuilder {
}
// 合并纬度单元格
- for (int i = 0; i < chart.getDimensions().length; i++) {
- // 行号
- if (chart.isShowLineNumber() && i == 0) continue;
+ if (chart.isMergeCell()) {
+ for (int i = 0; i < chart.getDimensions().length; i++) {
+ // 行号
+ if (chart.isShowLineNumber() && i == 0) continue;
- TD last = null;
- int sumMinus = 0; // 合并的单元格
- int childrenLen = tbody.children.size();
- for (TR tr : tbody.children) {
- TD current = tr.children.get(i);
- if (last == null || childrenLen-- <= 1) {
- last = current;
- continue;
+ TD last = null;
+ int sumMinus = 0; // 合并的单元格
+ int childrenLen = tbody.children.size();
+ for (TR tr : tbody.children) {
+ TD current = tr.children.get(i);
+ if (last == null || childrenLen-- <= 1) {
+ last = current;
+ continue;
+ }
+
+ if (last.content.equals(current.content)) {
+ last.rowspan++;
+ current.rowspan = 0;
+ sumMinus++;
+ } else {
+ last = current;
+ }
}
- if (last.content.equals(current.content)) {
- last.rowspan++;
- current.rowspan = 0;
- sumMinus++;
- } else {
- last = current;
+ if (chart.isShowSums() && sumMinus > 0 && StringUtils.isNotBlank(last.content)) {
+ int num = ObjectUtils.toInt(last.content) - sumMinus;
+ last.content = String.valueOf(num);
}
}
-
- if (chart.isShowSums() && sumMinus > 0 && StringUtils.isNotBlank(last.content)) {
- int num = ObjectUtils.toInt(last.content) - sumMinus;
- last.content = String.valueOf(num);
- }
}
String tClazz = (chart.isShowLineNumber() ? "line-number " : "") + (chart.isShowSums() ? "sums" : "");
diff --git a/src/main/java/com/rebuild/core/service/dashboard/charts/TableChart.java b/src/main/java/com/rebuild/core/service/dashboard/charts/TableChart.java
index 3e1d260c8..a5651a060 100644
--- a/src/main/java/com/rebuild/core/service/dashboard/charts/TableChart.java
+++ b/src/main/java/com/rebuild/core/service/dashboard/charts/TableChart.java
@@ -28,6 +28,7 @@ public class TableChart extends ChartData {
private boolean showLineNumber = false;
private boolean showSums = false;
+ private boolean mergeCell = true;
protected TableChart(JSONObject config) {
super(config);
@@ -36,6 +37,7 @@ public class TableChart extends ChartData {
if (option != null) {
this.showLineNumber = option.getBooleanValue("showLineNumber");
this.showSums = option.getBooleanValue("showSums");
+ if (option.containsKey("mergeCell")) this.mergeCell = option.getBooleanValue("mergeCell");
}
}
@@ -96,6 +98,10 @@ public class TableChart extends ChartData {
return showSums;
}
+ protected boolean isMergeCell() {
+ return mergeCell;
+ }
+
protected String wrapSumValue(Axis sumAxis, Object value) {
if (ChartsHelper.isZero(value)) {
return ChartsHelper.VALUE_ZERO;
diff --git a/src/main/java/com/rebuild/core/service/dataimport/RecordCheckout.java b/src/main/java/com/rebuild/core/service/dataimport/RecordCheckout.java
index 8d4623c10..1c92014aa 100644
--- a/src/main/java/com/rebuild/core/service/dataimport/RecordCheckout.java
+++ b/src/main/java/com/rebuild/core/service/dataimport/RecordCheckout.java
@@ -180,11 +180,11 @@ public class RecordCheckout {
// 支持ID
if (ID.isId(val) && ID.valueOf(val).getEntityCode() == EntityHelper.ClassificationData) {
- ID iid = ID.valueOf(val);
- if (ClassificationManager.instance.getName(iid) != null) {
- return iid;
+ ID cid = ID.valueOf(val);
+ if (ClassificationManager.instance.getName(cid) != null) {
+ return cid;
} else {
- log.warn("No item of Classification found by ID : " + iid);
+ log.warn("No item of Classification found by ID : " + cid);
return null;
}
} else {
diff --git a/src/main/java/com/rebuild/core/service/query/AdvFilterParser.java b/src/main/java/com/rebuild/core/service/query/AdvFilterParser.java
index 98a7b8562..2d5ec69fb 100644
--- a/src/main/java/com/rebuild/core/service/query/AdvFilterParser.java
+++ b/src/main/java/com/rebuild/core/service/query/AdvFilterParser.java
@@ -486,7 +486,7 @@ public class AdvFilterParser extends SetUser {
if (VF_ACU.equals(field)) {
return String.format(
- "(exists (select recordId from RobotApprovalStep where ^%s = recordId and state = 1 and %s) and approvalState = 2)",
+ "(exists (select recordId from RobotApprovalStep where ^%s = recordId and state = 1 and isCanceled = 'F' and %s) and approvalState = 2)",
specRootEntity.getPrimaryField().getName(), sb.toString().replace(VF_ACU, "approver"));
} else {
return sb.toString();
diff --git a/src/main/java/com/rebuild/core/service/trigger/ActionContext.java b/src/main/java/com/rebuild/core/service/trigger/ActionContext.java
index 0298137b3..5ddb12a99 100644
--- a/src/main/java/com/rebuild/core/service/trigger/ActionContext.java
+++ b/src/main/java/com/rebuild/core/service/trigger/ActionContext.java
@@ -73,4 +73,9 @@ public class ActionContext {
public ID getConfigId() {
return configId;
}
+
+ @Override
+ public String toString() {
+ return super.toString() + "#" + getConfigId();
+ }
}
diff --git a/src/main/java/com/rebuild/core/service/trigger/ActionType.java b/src/main/java/com/rebuild/core/service/trigger/ActionType.java
index 90a90886a..760b2e21e 100644
--- a/src/main/java/com/rebuild/core/service/trigger/ActionType.java
+++ b/src/main/java/com/rebuild/core/service/trigger/ActionType.java
@@ -33,6 +33,7 @@ public enum ActionType {
AUTOASSIGN("自动分派", AutoAssign.class),
SENDNOTIFICATION("发送通知", SendNotification.class),
HOOKURL("回调 URL", "com.rebuild.rbv.trigger.HookUrl"),
+ PROXYTRIGGERACTION("自定义触发器", "com.rebuild.rbv.trigger.ProxyTriggerAction"),
;
diff --git a/src/main/java/com/rebuild/core/service/trigger/aviator/AviatorUtils.java b/src/main/java/com/rebuild/core/service/trigger/aviator/AviatorUtils.java
index 827ab591e..6b142a53a 100644
--- a/src/main/java/com/rebuild/core/service/trigger/aviator/AviatorUtils.java
+++ b/src/main/java/com/rebuild/core/service/trigger/aviator/AviatorUtils.java
@@ -49,9 +49,10 @@ public class AviatorUtils {
addCustomFunction(new CurrentBizunitFunction());
addCustomFunction(new CurrentDateFunction());
addCustomFunction(new LocationDistanceFunction());
- addCustomFunction(new RequestFunctuin());
- addCustomFunction(new TextFunction());
addCustomFunction(new ChineseYuanFunction());
+ addCustomFunction(new TextFunction());
+ addCustomFunction(new RequestFunctuin());
+ addCustomFunction(new SqlQueryFunction());
}
/**
diff --git a/src/main/java/com/rebuild/core/service/trigger/aviator/SqlQueryFunction.java b/src/main/java/com/rebuild/core/service/trigger/aviator/SqlQueryFunction.java
new file mode 100644
index 000000000..89780b421
--- /dev/null
+++ b/src/main/java/com/rebuild/core/service/trigger/aviator/SqlQueryFunction.java
@@ -0,0 +1,101 @@
+/*!
+Copyright (c) REBUILD and/or its owners. All rights reserved.
+
+rebuild is dual-licensed under commercial and open source licenses (GPLv3).
+See LICENSE and COMMERCIAL in the project root for license information.
+*/
+
+package com.rebuild.core.service.trigger.aviator;
+
+import cn.devezhao.persist4j.Query;
+import cn.devezhao.persist4j.engine.ID;
+import com.googlecode.aviator.runtime.function.AbstractFunction;
+import com.googlecode.aviator.runtime.type.AviatorDecimal;
+import com.googlecode.aviator.runtime.type.AviatorNil;
+import com.googlecode.aviator.runtime.type.AviatorObject;
+import com.googlecode.aviator.runtime.type.AviatorString;
+import com.rebuild.core.Application;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * Usage: SQLQUERY($sql, [ $param...{5} ])
+ * Return: Number|Date|String
+ *
+ * @author RB
+ * @since 2022/11/10
+ */
+@Slf4j
+public class SqlQueryFunction extends AbstractFunction {
+ private static final long serialVersionUID = 6408510455471045309L;
+
+ @Override
+ public AviatorObject call(Map env, AviatorObject arg1, AviatorObject arg2, AviatorObject arg3, AviatorObject arg4, AviatorObject arg5, AviatorObject arg6) {
+ String $sql = arg1.getValue(env).toString();
+ Query query = Application.createQueryNoFilter($sql);
+
+ Object param1 = getValue(env, arg2);
+ if (param1 != null) query.setParameter(1, param1);
+ Object param2 = getValue(env, arg3);
+ if (param2 != null) query.setParameter(2, param2);
+ Object param3 = getValue(env, arg4);
+ if (param3 != null) query.setParameter(3, param3);
+ Object param4 = getValue(env, arg5);
+ if (param4 != null) query.setParameter(4, param4);
+ Object param5 = getValue(env, arg6);
+ if (param5 != null) query.setParameter(5, param5);
+
+ Object[] o = query.unique();
+ if (o == null || o[0] == null) return AviatorNil.NIL;
+
+ final Object value = o[0];
+
+ if (value instanceof Number) {
+ return new AviatorDecimal((Number) value);
+ } else if (value instanceof Date) {
+ return new AviatorDate((Date) value);
+ } else {
+ return new AviatorString(value.toString());
+ }
+ }
+
+ private Object getValue(Map env, AviatorObject arg) {
+ Object value = arg.getValue(env);
+ if (value == null) return null;
+
+ if (value instanceof Number || value instanceof Date || value instanceof ID) return value;
+ else return value.toString();
+ }
+
+ @Override
+ public AviatorObject call(Map env, AviatorObject arg1, AviatorObject arg2, AviatorObject arg3, AviatorObject arg4, AviatorObject arg5) {
+ return this.call(env, arg1, arg2, arg3, arg4, arg5, AviatorNil.NIL);
+ }
+
+ @Override
+ public AviatorObject call(Map env, AviatorObject arg1, AviatorObject arg2, AviatorObject arg3, AviatorObject arg4) {
+ return this.call(env, arg1, arg2, arg3, arg4, AviatorNil.NIL);
+ }
+
+ @Override
+ public AviatorObject call(Map env, AviatorObject arg1, AviatorObject arg2, AviatorObject arg3) {
+ return this.call(env, arg1, arg2, arg3, AviatorNil.NIL);
+ }
+
+ @Override
+ public AviatorObject call(Map env, AviatorObject arg1, AviatorObject arg2) {
+ return this.call(env, arg1, arg2, AviatorNil.NIL);
+ }
+
+ @Override
+ public AviatorObject call(Map env, AviatorObject arg1) {
+ return this.call(env, arg1, AviatorNil.NIL);
+ }
+
+ @Override
+ public String getName() {
+ return "SQLQUERY";
+ }
+}
diff --git a/src/main/java/com/rebuild/core/support/setup/Installer.java b/src/main/java/com/rebuild/core/support/setup/Installer.java
index d2062153c..a8546c5d6 100644
--- a/src/main/java/com/rebuild/core/support/setup/Installer.java
+++ b/src/main/java/com/rebuild/core/support/setup/Installer.java
@@ -37,6 +37,7 @@ import org.apache.commons.lang.StringUtils;
import org.springframework.util.Assert;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.args.FlushMode;
import javax.sql.DataSource;
import java.io.File;
@@ -518,9 +519,12 @@ public class Installer implements InstallState {
public static void clearAllCache() {
if (isUseRedis()) {
try (Jedis jedis = Application.getCommonsCache().getJedisPool().getResource()) {
-// // Delete all the keys of the currently selected DB
-// jedis.flushDB(FlushMode.SYNC);
- jedis.flushAll();
+ // https://redis.io/commands/flushdb/
+ try {
+ jedis.flushDB(FlushMode.SYNC); // v6.2.0
+ } catch (Exception v620) {
+ jedis.flushDB();
+ }
}
} else {
Application.getCommonsCache().getEhcacheCache().clear();
diff --git a/src/main/java/com/rebuild/utils/AppUtils.java b/src/main/java/com/rebuild/utils/AppUtils.java
index c60c9dce8..b880b3cde 100644
--- a/src/main/java/com/rebuild/utils/AppUtils.java
+++ b/src/main/java/com/rebuild/utils/AppUtils.java
@@ -92,9 +92,7 @@ public class AppUtils {
*/
public static ID getRequestUser(HttpServletRequest request, boolean refreshToken) {
Object user = request.getSession().getAttribute(WebUtils.CURRENT_USER);
- if (user == null) {
- user = getRequestUserViaToken(request, refreshToken);
- }
+ if (user == null) user = getRequestUserViaToken(request, refreshToken);
return user == null ? null : (ID) user;
}
diff --git a/src/main/java/com/rebuild/utils/CommonsUtils.java b/src/main/java/com/rebuild/utils/CommonsUtils.java
index eb83c3ef2..15cda14e0 100644
--- a/src/main/java/com/rebuild/utils/CommonsUtils.java
+++ b/src/main/java/com/rebuild/utils/CommonsUtils.java
@@ -39,7 +39,7 @@ public class CommonsUtils {
private static final char[] SPECIAL_CHARS = "`~!@#$%^&*()_+=-{}|[];':\",./<>?".toCharArray();
/**
- * 不含特殊字符。不允许除 数字 字母 中文 及 _ - 以外的字符,包括空格
+ * 不含特殊字符。不允许除 `数字` `字母` `中文` `_` `-` 及空格以外的字符
*
* @param text
* @return
diff --git a/src/main/java/com/rebuild/web/admin/AdminCLI2.java b/src/main/java/com/rebuild/web/admin/AdminCLI2.java
index 15e64a585..ea1016402 100644
--- a/src/main/java/com/rebuild/web/admin/AdminCLI2.java
+++ b/src/main/java/com/rebuild/web/admin/AdminCLI2.java
@@ -7,6 +7,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
package com.rebuild.web.admin;
+import com.rebuild.core.Application;
import com.rebuild.core.support.ConfigurationItem;
import com.rebuild.core.support.RebuildConfiguration;
import com.rebuild.core.support.setup.DatabaseBackup;
@@ -64,7 +65,7 @@ public class AdminCLI2 {
case C_HELP:
case "?" : {
result = " Usage : " +
- " \ncache clean" +
+ " \ncache [clean|get] [KEY]" +
" \nsyscfg NAME [VALUE]" +
" \nsyscfg clean-qiniu|clean-sms|clean-email" +
" \nbackup [database|datafile]" +
@@ -108,6 +109,12 @@ public class AdminCLI2 {
String name = commands[1];
if ("clean".equals(name)) {
Installer.clearAllCache();
+ } else if ("get".equals(name)) {
+ if (commands.length < 3) return "WRAN: Bad arguments";
+ String key = commands[2];
+ Object value = Application.getCommonsCache().getx(key);
+ if (value == null) result = "/NULL/";
+ else result = value.toString();
} else {
result = "WRAN: Bad arguments";
}
diff --git a/src/main/java/com/rebuild/web/admin/ApisManagerController.java b/src/main/java/com/rebuild/web/admin/ApisManagerController.java
index 3fe4dc184..54fef73ed 100644
--- a/src/main/java/com/rebuild/web/admin/ApisManagerController.java
+++ b/src/main/java/com/rebuild/web/admin/ApisManagerController.java
@@ -17,8 +17,8 @@ import com.rebuild.core.configuration.RebuildApiService;
import com.rebuild.core.metadata.EntityHelper;
import com.rebuild.core.support.i18n.I18nUtils;
import com.rebuild.web.BaseController;
-import org.apache.commons.lang.math.RandomUtils;
import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
@@ -39,10 +39,10 @@ public class ApisManagerController extends BaseController {
return createModelAndView("/admin/integration/apis-manager");
}
- @RequestMapping("apis-manager/app-list")
+ @GetMapping("apis-manager/app-list")
public RespBody appList() {
Object[][] apps = Application.createQueryNoFilter(
- "select uniqueId,appId,appSecret,bindUser,bindUser.fullName,createdOn,appId from RebuildApi")
+ "select uniqueId,appId,appSecret,bindUser,bindUser.fullName,createdOn,appId,bindIps from RebuildApi order by createdOn")
.array();
// 近30日用量
@@ -60,24 +60,15 @@ public class ApisManagerController extends BaseController {
return RespBody.ok(apps);
}
- @RequestMapping("apis-manager/app-create")
- public RespBody appCreate(HttpServletRequest request) {
- ID user = getRequestUser(request);
- ID bindUser = getIdParameter(request, "bind");
-
- Record record = EntityHelper.forNew(EntityHelper.RebuildApi, user);
- record.setString("appId", (100000000 + RandomUtils.nextInt(899999999)) + "");
+ @PostMapping("apis-manager/reset-secret")
+ public RespBody resetSecret(HttpServletRequest request) {
+ ID appId = getIdParameterNotNull(request, "id");
+ Record record = EntityHelper.forUpdate(appId, getRequestUser(request));
record.setString("appSecret", CodecUtils.randomCode(40));
- record.setID("bindUser", bindUser);
- Application.getBean(RebuildApiService.class).create(record);
+ Application.getCommonsService().update(record, false);
- return RespBody.ok();
- }
-
- @RequestMapping("apis-manager/app-delete")
- public RespBody appDelete(HttpServletRequest request) {
- ID id = getIdParameterNotNull(request, "id");
- Application.getBean(RebuildApiService.class).delete(id);
+ // cache
+ Application.getBean(RebuildApiService.class).update(record);
return RespBody.ok();
}
}
diff --git a/src/main/java/com/rebuild/web/admin/setup/InstallController.java b/src/main/java/com/rebuild/web/admin/setup/InstallController.java
index 57ed90959..132de4cf9 100644
--- a/src/main/java/com/rebuild/web/admin/setup/InstallController.java
+++ b/src/main/java/com/rebuild/web/admin/setup/InstallController.java
@@ -13,6 +13,7 @@ import cn.devezhao.commons.web.ServletUtils;
import com.alibaba.fastjson.JSONObject;
import com.rebuild.api.RespBody;
import com.rebuild.core.Application;
+import com.rebuild.core.RebuildException;
import com.rebuild.core.support.License;
import com.rebuild.core.support.i18n.Language;
import com.rebuild.core.support.setup.InstallState;
@@ -34,7 +35,6 @@ import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
@@ -50,13 +50,17 @@ import java.sql.SQLException;
@RequestMapping("/setup/")
public class InstallController extends BaseController implements InstallState {
- @GetMapping("install")
- public ModelAndView index(HttpServletRequest request, HttpServletResponse response) throws IOException {
- if (Application.isReady() && !Application.devMode()) {
- response.sendError(404);
- return null;
+ @Override
+ public boolean checkInstalled() {
+ if (InstallState.super.checkInstalled() && Application.isReady()) {
+ throw new RebuildException("NOT ALLOWED");
}
+ return false;
+ }
+ @GetMapping("install")
+ public ModelAndView index(HttpServletRequest request) throws IOException {
+ checkInstalled();
ModelAndView mv = createModelAndView("/admin/setup/install");
mv.getModel().put("Version", Application.VER);
@@ -68,6 +72,7 @@ public class InstallController extends BaseController implements InstallState {
@PostMapping("test-connection")
public RespBody testConnection(HttpServletRequest request) {
+ checkInstalled();
JSONObject dbProps = (JSONObject) ServletUtils.getRequestJson(request);
JSONObject props = JSONUtils.toJSONObject("databaseProps", dbProps);
@@ -108,6 +113,7 @@ public class InstallController extends BaseController implements InstallState {
@PostMapping("test-cache")
public RespBody testCache(HttpServletRequest request) {
+ checkInstalled();
JSONObject cacheProps = (JSONObject) ServletUtils.getRequestJson(request);
JedisPool pool = new JedisPool(new JedisPoolConfig(),
@@ -133,6 +139,7 @@ public class InstallController extends BaseController implements InstallState {
@PostMapping("install-rebuild")
public RespBody installExec(HttpServletRequest request) {
+ checkInstalled();
JSONObject installProps = (JSONObject) ServletUtils.getRequestJson(request);
try {
@@ -146,6 +153,7 @@ public class InstallController extends BaseController implements InstallState {
@GetMapping("request-sn")
public RespBody requestSn() {
+ checkInstalled();
return RespBody.ok(License.SN());
}
}
diff --git a/src/main/java/com/rebuild/web/user/signup/LoginAction.java b/src/main/java/com/rebuild/web/user/signup/LoginAction.java
index d48d6799a..62e919ac6 100644
--- a/src/main/java/com/rebuild/web/user/signup/LoginAction.java
+++ b/src/main/java/com/rebuild/web/user/signup/LoginAction.java
@@ -16,14 +16,15 @@ import cn.devezhao.persist4j.Record;
import cn.devezhao.persist4j.engine.ID;
import com.rebuild.api.user.AuthTokenManager;
import com.rebuild.core.Application;
+import com.rebuild.core.cache.CommonsCache;
import com.rebuild.core.metadata.EntityHelper;
import com.rebuild.core.privileges.UserService;
import com.rebuild.core.privileges.bizz.User;
import com.rebuild.core.support.KVStorage;
import com.rebuild.core.support.License;
import com.rebuild.core.support.task.TaskExecutors;
-import com.rebuild.utils.AES;
import com.rebuild.web.BaseController;
+import com.rebuild.web.user.UserAvatar;
import eu.bitwalker.useragentutils.DeviceType;
import eu.bitwalker.useragentutils.OperatingSystem;
import eu.bitwalker.useragentutils.UserAgent;
@@ -49,7 +50,8 @@ public class LoginAction extends BaseController {
protected static final String SK_NEED_VCODE = "needLoginVCode";
protected static final String SK_START_TOUR = "needStartTour";
- protected static final String PREFIX_2FA = "2FA";
+ protected static final String PREFIX_2FA = "2FA:";
+ protected static final String PREFIX_ALT = "ALT:";
/**
* 登录成功
@@ -63,8 +65,9 @@ public class LoginAction extends BaseController {
protected Integer loginSuccessed(HttpServletRequest request, HttpServletResponse response, ID user, boolean autoLogin) {
// 自动登录
if (autoLogin) {
- String alt = user + "," + System.currentTimeMillis() + ",v1";
- ServletUtils.addCookie(response, CK_AUTOLOGIN, AES.encrypt(alt));
+ final String altToken = CodecUtils.randomCode(60);
+ Application.getCommonsCache().putx(PREFIX_ALT + altToken, user, CommonsCache.TS_DAY * 14);
+ ServletUtils.addCookie(response, CK_AUTOLOGIN, altToken);
} else {
ServletUtils.removeCookie(request, response, CK_AUTOLOGIN);
}
@@ -75,6 +78,9 @@ public class LoginAction extends BaseController {
ServletUtils.setSessionAttribute(request, SK_USER_THEME, KVStorage.getCustomValue("THEME." + user));
Application.getSessionStore().storeLoginSuccessed(request);
+ // 头像缓存
+ ServletUtils.setSessionAttribute(request, UserAvatar.SK_DAVATAR, System.currentTimeMillis());
+
// TOUR 显示规则
Object[] initLoginTimes = Application.createQueryNoFilter(
"select count(loginTime) from LoginLog where user = ? and loginTime > '2022-01-01'")
diff --git a/src/main/java/com/rebuild/web/user/signup/LoginController.java b/src/main/java/com/rebuild/web/user/signup/LoginController.java
index 27e6c13d3..d544cbbab 100644
--- a/src/main/java/com/rebuild/web/user/signup/LoginController.java
+++ b/src/main/java/com/rebuild/web/user/signup/LoginController.java
@@ -8,7 +8,6 @@ See LICENSE and COMMERCIAL in the project root for license information.
package com.rebuild.web.user.signup;
import cn.devezhao.commons.CodecUtils;
-import cn.devezhao.commons.ObjectUtils;
import cn.devezhao.commons.RegexUtils;
import cn.devezhao.commons.web.ServletUtils;
import cn.devezhao.persist4j.Record;
@@ -28,9 +27,7 @@ import com.rebuild.core.service.DataSpecificationException;
import com.rebuild.core.support.*;
import com.rebuild.core.support.i18n.Language;
import com.rebuild.core.support.integration.SMSender;
-import com.rebuild.utils.AES;
import com.rebuild.utils.AppUtils;
-import com.rebuild.web.user.UserAvatar;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
@@ -85,28 +82,9 @@ public class LoginController extends LoginAction {
// 记住登录
final String useAlt = ServletUtils.readCookie(request, CK_AUTOLOGIN);
if (StringUtils.isNotBlank(useAlt)) {
- ID altUser = null;
- try {
- String[] alts = AES.decrypt(useAlt).split(",");
- altUser = alts.length == 3 && ID.isId(alts[0]) ? ID.valueOf(alts[0]) : null;
+ final ID altUser = (ID) Application.getCommonsCache().getx(PREFIX_ALT + useAlt);
- // 最大30天有效期
- if (altUser != null) {
- long t = ObjectUtils.toLong(alts[1]);
- if ((System.currentTimeMillis() - t) / 1000 > 30 * 24 * 60 * 60) {
- altUser = null;
- }
- if (altUser != null && !UserHelper.isActive(altUser)) {
- altUser = null;
- }
- }
-
- } catch (Exception ex) {
- ServletUtils.removeCookie(request, response, CK_AUTOLOGIN);
- log.error("Cannot decode User from alt : {}", useAlt, ex);
- }
-
- if (altUser != null && Application.getUserStore().existsUser(altUser)) {
+ if (altUser != null && UserHelper.isActive(altUser)) {
Integer ed = loginSuccessed(request, response, altUser, true);
String nexturl = getParameter(request, "nexturl", homeUrl);
@@ -183,8 +161,6 @@ public class LoginController extends LoginAction {
// 清理验证码
getLoginRetryTimes(user, -1);
ServletUtils.setSessionAttribute(request, SK_NEED_VCODE, null);
- // 头像缓存
- ServletUtils.setSessionAttribute(request, UserAvatar.SK_DAVATAR, System.currentTimeMillis());
final User loginUser = Application.getUserStore().getUser(user);
final boolean isRbMobile = AppUtils.isRbMobile(request);
@@ -197,8 +173,8 @@ public class LoginController extends LoginAction {
if (faMode > 0 && !faModeSkip) {
resMap.put("login2FaMode", faMode);
- String userToken = CodecUtils.randomCode(40);
- Application.getCommonsCache().putx(PREFIX_2FA + userToken, loginUser.getId(), 15 * 60); // 15m
+ final String userToken = CodecUtils.randomCode(40);
+ Application.getCommonsCache().putx(PREFIX_2FA + userToken, loginUser.getId(), CommonsCache.TS_MINTE * 15);
resMap.put("login2FaUserToken", userToken);
if (isRbMobile) {
diff --git a/src/main/resources/i18n/lang.zh_CN.json b/src/main/resources/i18n/lang.zh_CN.json
index c8748de7c..821c3438d 100644
--- a/src/main/resources/i18n/lang.zh_CN.json
+++ b/src/main/resources/i18n/lang.zh_CN.json
@@ -2369,5 +2369,15 @@
"批量添加字段":"批量添加字段",
"支持 H5":"支持 H5",
"手机登录":"手机登录",
- "批量添加":"批量添加"
+ "批量添加":"批量添加",
+ "触发类 (TriggerAction)":"触发类 (TriggerAction)",
+ "无 (不限制)":"无 (不限制)",
+ "重置 APP SECRET":"重置 APP SECRET",
+ "自定义触发器":"自定义触发器",
+ "白名单内的 IP 才可以通过此 API 秘钥调用接口,如有多个 IP 请使用逗号或空格分开,留空则不限制":"白名单内的 IP 才可以通过此 API 秘钥调用接口,如有多个 IP 请使用逗号或空格分开,留空则不限制",
+ "配色":"配色",
+ "APP SECRET 已重置":"APP SECRET 已重置",
+ "修改 API 秘钥":"修改 API 秘钥",
+ "自动合并单元格":"自动合并单元格",
+ "重置后第三方应用需更换新的 APP SECRET 使用":"重置后第三方应用需更换新的 APP SECRET 使用"
}
diff --git a/src/main/resources/web/admin/integration/apis-manager.html b/src/main/resources/web/admin/integration/apis-manager.html
index cb54849bf..e76505665 100644
--- a/src/main/resources/web/admin/integration/apis-manager.html
+++ b/src/main/resources/web/admin/integration/apis-manager.html
@@ -27,12 +27,13 @@
- APP ID |
- APP SECRET |
- [[${bundle.L('绑定用户 (权限)')}]] |
- [[${bundle.L('调用量 (30 天)')}]] |
+ APP ID |
+ APP SECRET |
+ [[${bundle.L('绑定用户 (权限)')}]] |
+ [[${bundle.L('IP 白名单')}]] |
+ [[${bundle.L('调用量 (30 天)')}]] |
[[${bundle.L('创建时间')}]] |
- |
+ |
@@ -57,6 +58,7 @@
+