feat: harden data access and simplify user management

This commit is contained in:
xuelong committed 2026-08-25 21:02:30 -07:00
1 parent 3dd5731751
commit adb780bc82
51 files changed
+3239 -2121

No files matched your search

+36 -14
View File
@@ -14,6 +14,14 @@ const serverComposeSource = readFileSync(
"utf8",
);
const dockerfileSource = readFileSync(new URL("../../Dockerfile", import.meta.url), "utf8");
const backupScriptSource = readFileSync(
new URL("../../scripts/backup_mysql_encrypted.sh", import.meta.url),
"utf8",
);
const storageGuardSource = readFileSync(
new URL("../../docker/mysql-storage-guard.sh", import.meta.url),
"utf8",
);
const dataCenterSource = readFileSync(
new URL("../src/panels/data-platform/DataCenterPanel.tsx", import.meta.url),
"utf8",
@@ -30,11 +38,11 @@ test("Interface Center is available under System", () => {
});
test("Interface Center is designed for inbound server access", () => {
for (const label of ["DBeaver 管理接入", "HTTPS API", "接入信息", "安全设置"]) {
for (const label of ["DBeaver 管理接入", "选择服务器数据库", "连接参数", "生成密钥与数据库账号"]) {
assert.match(panelSource, new RegExp(label));
}
assert.match(panelSource, /SSH 隧道 \+ 独立 MySQL 账号/);
assert.match(panelSource, /不要在 Main 页填写服务器公网 IP/);
assert.match(panelSource, /SSH 加密隧道/);
assert.match(panelSource, /Main 页不要填写服务器公网 IP/);
assert.match(panelSource, /Show all databases/);
assert.doesNotMatch(panelSource, /JDBC URL/);
assert.doesNotMatch(panelSource, /测试连接/);
@@ -62,28 +70,42 @@ test("production deployment fails closed and preserves recoverability", () => {
assert.match(serverComposeSource, /SECURITY_STRICT_MODE: "true"/);
assert.match(serverComposeSource, /API_BIND_HOST:-127\.0\.0\.1/);
assert.match(serverComposeSource, /必须设置至少 32 位 AUTH_SECRET/);
assert.match(serverComposeSource, /mysql-storage-guard/);
assert.match(serverComposeSource, /MYSQL_STORAGE_MOUNT:\?必须设置 MYSQL_STORAGE_MOUNT/);
assert.match(serverComposeSource, /MYSQL_DATA_DIR:\?必须设置 MYSQL_DATA_DIR/);
assert.match(serverComposeSource, /create_host_path: false/);
assert.match(serverComposeSource, /127\.0\.0\.1:\$\{MYSQL_PORT:-3307\}:3306/);
assert.match(storageGuardSource, /storage identity mismatch/);
assert.match(backupScriptSource, /拒绝将生产备份写入代码仓库/);
assert.match(backupScriptSource, /verify_mysql_backup\.sh/);
assert.match(backupScriptSource, /DATA_BACKUP_RETENTION_DAYS/);
assert.match(dockerfileSource, /USER appuser/);
assert.match(dataCenterSource, /安全只读模式/);
});
test("Interface Center keeps clients, credentials, policies, docs and logs", () => {
for (const label of ["客户端与密钥", "权限策略", "接口说明", "调用日志"]) {
test("Interface Center keeps the DBeaver flow focused", () => {
for (const label of ["私钥不会上传", "一人一号、单库授权", "一键撤销", "复制全部连接参数"]) {
assert.match(panelSource, new RegExp(label));
}
assert.match(panelSource, /完整密钥只在签发时显示一次/);
assert.match(panelSource, /留空默认 30 天,最长 90 天/);
assert.match(panelSource, /加密备份待部署/);
assert.match(panelSource, /接口不接受任意 SQL/);
for (const removedLabel of ["HTTPS API", "客户端与密钥", "权限策略", "调用日志"]) {
assert.doesNotMatch(panelSource, new RegExp(removedLabel));
}
});
test("frontend uses the administrative Interface Center API", () => {
test("frontend uses only the managed DBeaver endpoints on this page", () => {
for (const path of [
"/interface-center/summary",
"/interface-center/clients",
"/interface-center/credentials",
"/interface-center/policies",
"/interface-center/logs",
"/interface-center/catalog",
"/interface-center/dbeaver-access",
]) {
assert.match(apiSource, new RegExp(path));
}
for (const unusedCall of [
"listInterfaceClients",
"listInterfaceCredentials",
"listInterfacePolicies",
"listInterfaceCallLogs",
]) {
assert.doesNotMatch(panelSource, new RegExp(unusedCall));
}
});