Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM jumpserver/chen-base:20250708_060644 AS stage-build
FROM jumpserver/chen-base:20251011_081112 AS stage-build
ENV LANG=en_US.UTF-8

WORKDIR /opt/chen/
Expand Down
9 changes: 5 additions & 4 deletions Dockerfile-base
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ RUN set -ex \
&& rm -rf /var/lib/apt/lists/*

# Install tools and dependencies
ARG CHECK_VERSION=v1.0.4
ARG CHECK_VERSION=v1.0.5
RUN set -ex \
&& wget https://github.com/jumpserver-dev/healthcheck/releases/download/${CHECK_VERSION}/check-${CHECK_VERSION}-linux-${TARGETARCH}.tar.gz \
&& tar -xf check-${CHECK_VERSION}-linux-${TARGETARCH}.tar.gz -C /usr/local/bin/ check \
&& chown root:root /usr/local/bin/check \
&& chmod 755 /usr/local/bin/check \
&& rm -f check-${CHECK_VERSION}-linux-${TARGETARCH}.tar.gz

ARG WISP_VERSION=v0.2.7
ARG WISP_VERSION=v0.2.8
RUN set -ex \
&& wget https://github.com/jumpserver/wisp/releases/download/${WISP_VERSION}/wisp-${WISP_VERSION}-linux-${TARGETARCH}.tar.gz \
&& tar -xf wisp-${WISP_VERSION}-linux-${TARGETARCH}.tar.gz -C /usr/local/bin/ --strip-components=1 \
Expand All @@ -43,7 +43,7 @@ RUN --mount=type=cache,target=/usr/local/share/.cache/yarn,sharing=locked,id=che
npm install

# Install Maven dependencies
ARG MAVEN_VERSION=3.9.10
ARG MAVEN_VERSION=3.9.11
ARG USER_HOME_DIR="/root"
ARG BASE_URL=https://downloads.apache.org/maven/maven-3/${MAVEN_VERSION}/binaries
ARG MAVEN_MIRROR=https://repo.maven.apache.org/maven2
Expand All @@ -68,5 +68,6 @@ RUN set -ex \
&& mkdir -p /root/.m2 \
&& mkdir -p /opt/chen/frontend/dist \
&& sed -i "s@https://repo.maven.apache.org/maven2@${MAVEN_MIRROR}@g" settings.xml \
&& \cp -f settings.xml /root/.m2/ \
&& cp -f settings.xml /root/.m2/ \
&& mvn clean install

Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,12 @@ public void export(String scope, String format) throws SQLException {
SessionManager.getCurrentSession().getController().showMessage(MessageLevel.ERROR, MessageUtils.get("DownloadNotAllowed"));
return;
}

if (SessionManager.getCurrentSession().isLocked()) {
SessionManager.getCurrentSession().getController().showMessage(MessageLevel.ERROR, MessageUtils.get("SessionLockedMessage", SessionManager.getCurrentSession().getLockCreator()));
return;
}

File f = null;
switch (scope) {
case "current":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,17 @@
import org.jumpserver.chen.framework.utils.HexUtils;
import org.jumpserver.chen.framework.utils.PageUtils;
import org.jumpserver.chen.framework.utils.ReflectUtils;
import org.jumpserver.wisp.Common;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

@Slf4j
public abstract class BaseSQLActuator implements SQLActuator {
Expand Down Expand Up @@ -151,6 +155,8 @@ private void executeStatement(SQLExecutePlan plan, Statement statement, SQLQuery
var fieldName = StringUtils.isNotEmpty(resultSet.getMetaData().getColumnLabel(i)) ?
resultSet.getMetaData().getColumnLabel(i) : resultSet.getMetaData().getColumnName(i);
field.setName(fieldName);
field.setColumnName(resultSet.getMetaData().getColumnName(i));
field.setLabel(resultSet.getMetaData().getColumnLabel(i));
result.getFields().add(field);
}

Expand All @@ -167,7 +173,7 @@ private void executeStatement(SQLExecutePlan plan, Statement statement, SQLQuery
fs.add(HexUtils.bytesToHex((byte[]) obj));
} else if (obj instanceof Blob) {
fs.add(HexUtils.bytesToHex(((Blob) obj).getBytes(1, (int) ((Blob) obj).length())));
} else if (obj!= null && obj.getClass().getSimpleName().equalsIgnoreCase("pgobject")) {
} else if (obj != null && obj.getClass().getSimpleName().equalsIgnoreCase("pgobject")) {
fs.add(obj.toString());
} else {
fs.add(obj);
Expand All @@ -181,6 +187,9 @@ private void executeStatement(SQLExecutePlan plan, Statement statement, SQLQuery
resultSet.close();
result.setFetchFinishedTime(new Time(System.currentTimeMillis()));

// 数据脱敏
this.handleDataMasking(result);

var total = this.count(plan);
if (total < 0) {
result.setTotal(result.getData().size());
Expand All @@ -198,6 +207,120 @@ private void executeStatement(SQLExecutePlan plan, Statement statement, SQLQuery
}
}

private void handleDataMasking(SQLQueryResult result) {
var rules = SessionManager.getCurrentSession().getDataMaskingRules();
var maskIndexes = new ArrayList<>();
var maskRules = new HashMap<Integer, Common.DataMaskingRule>();
for (var i = 0; i < result.getFields().size(); i++) {
for (Common.DataMaskingRule rule : rules) {
if (this.matchField(result.getFields().get(i), rule.getFieldsPattern())) {
maskIndexes.add(i);
maskRules.put(i, rule);
}
}
}

for (var i = 0; i < result.getData().size(); i++) {
for (var j = 0; j < result.getData().get(i).size(); j++) {
if (maskIndexes.contains(j)) {
var rule = maskRules.get(j);
var val = result.getData().get(i).get(j);
if (val instanceof String) {
var rep = this.replaceColumnVal(rule, (String) val);
result.getData().get(i).set(j, rep);
} else {
result.getData().get(i).set(j, rule.getMaskPattern());
}
}
}
}
}

private boolean matchField(Field field, String pattern) {
List<String> names = List.of(field.getColumnName(), field.getLabel());
String[] ps = pattern.split(",");

for (String name : names) {
for (String p : ps) {
p = p.trim();
if (p.isEmpty()) continue;

try {
// 先整体转义,避免用户写的正则符号被误解释
String regex = Pattern.quote(p);
// 把被转义的 \* 恢复为 .*
regex = regex.replace("\\*", ".*");
// 加上锚点,实现整串匹配
regex = "^" + regex + "$";

int flags = Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE;
Pattern pa = Pattern.compile(regex, flags);

if (pa.matcher(name).matches()) { // 注意:用 matches() 而不是 find()
return true;
}
} catch (PatternSyntaxException e) {
// 忽略坏模式,继续下一个
continue;
}
}
}
return false;
}


private String replaceColumnVal(Common.DataMaskingRule rule, String val) {
if (rule == null) {
return "####";
} else {
rule.getMaskingMethod();
}

String method = rule.getMaskingMethod();
rule.getMaskPattern();
String pattern = rule.getMaskPattern();

switch (method) {
case "fixed_char":
// 固定字符替换
if (pattern.isEmpty()) {
return "####";
}
return pattern;

case "hide_middle":
// 隐藏中间
if (val == null || val.length() < 3) {
return pattern.isEmpty() ? "####" : pattern;
}
return val.charAt(0)
+ "*".repeat(val.length() - 2)
+ val.substring(val.length() - 1);

case "keep_prefix":
// 保留前缀
int prefix = 2;
if (val == null || prefix >= val.length()) {
return "####";
}
return val.substring(0, prefix)
+ "*".repeat(val.length() - prefix);

case "keep_suffix":
// 保留后缀
int suffix = 2;
if (val == null || suffix >= val.length()) {
return "####";
}
return "*".repeat(val.length() - suffix)
+ val.substring(val.length() - suffix);

default:
// 未知策略
return pattern.isEmpty() ? "####" : pattern;
}
}

@Override
public SQLQueryResult executeWithAudit(SQL sql) throws SQLException {
var plan = this.createPlan(sql);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
public class Field implements ResourceNode {

private String name;
private String label;
private String columnName;
private String schema;
private String table;
private String type;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,25 @@
import org.jumpserver.chen.framework.jms.exception.CommandRejectException;
import org.jumpserver.chen.framework.session.controller.Controller;
import org.jumpserver.chen.framework.ws.io.PacketIO;
import org.jumpserver.wisp.Common;

import java.io.File;
import java.nio.file.Path;
import java.sql.Connection;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Locale;
import java.util.Map;

public interface Session {

String getLockCreator();

boolean isLocked();

List<Common.DataMaskingRule> getDataMaskingRules();

void refreshLastActiveTime();

LocalDateTime getLastActiveTime();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@
import org.jumpserver.chen.framework.session.controller.Controller;
import org.jumpserver.chen.framework.session.controller.impl.BaseController;
import org.jumpserver.chen.framework.ws.io.PacketIO;
import org.jumpserver.wisp.Common;

import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
Expand Down Expand Up @@ -61,13 +63,24 @@ public class BaseSession implements Session {
@Getter
private LocalDateTime lastActiveTime;

@Getter
private boolean locked = false;

@Getter
private String lockCreator;


public BaseSession(Datasource datasource, String remoteAddr) {
this.datasource = datasource;
this.remoteAddr = remoteAddr;
}


@Override
public List<Common.DataMaskingRule> getDataMaskingRules() {
return List.of();
}

@Override
public void refreshLastActiveTime() {
this.lastActiveTime = LocalDateTime.now();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ public class JMSSession extends BaseSession {
@Getter
private final Common.Session jmsSession;

@Getter
private List<Common.DataMaskingRule> dataMaskingRules;
private ACLFilter aclFilter;
private CommandHandler commandHandler;
private ReplayHandler replayHandler;
Expand All @@ -60,8 +62,13 @@ public class JMSSession extends BaseSession {
@Setter
private String gatewayId;


@Getter
private boolean locked = false;

@Getter
private String lockCreator;

private boolean canUpload = false;
private boolean canDownload = false;

Expand All @@ -70,16 +77,21 @@ public class JMSSession extends BaseSession {

private boolean closed = false;


public void lockSession(String creator) {
SessionManager.setContext(this.getWebToken());
this.getController().showMessage(MessageLevel.ERROR, MessageUtils.get("SessionLockedMessage", creator));

this.lockCreator = creator;
this.locked = true;
this.getController().showMessage(MessageLevel.ERROR, MessageUtils.get("SessionLockedMessage", this.lockCreator));
}

public void unloadSession(String creator) {
SessionManager.setContext(this.getWebToken());

this.getController().showMessage(MessageLevel.SUCCESS, MessageUtils.get("SessionUnlockedMessage", creator));
this.locked = false;
this.lockCreator = null;
}


Expand All @@ -103,6 +115,7 @@ public JMSSession(Common.Session session,
this.canDownload = tokenResp.getData().getPermission().getEnableDownload();
this.canCopy = tokenResp.getData().getPermission().getEnableCopy();
this.canPaste = tokenResp.getData().getPermission().getEnablePaste();
this.dataMaskingRules = tokenResp.getData().getDataMaskingRulesList();
}


Expand Down
3 changes: 0 additions & 3 deletions backend/web/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,5 @@
<artifactId>modules</artifactId>
<version>${revision}</version>
</dependency>

</dependencies>


</project>
2 changes: 1 addition & 1 deletion backend/wisp/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>4.28.0</version>
<version>4.32.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
Expand Down
Loading