Skip to content

feat(workbench): support PostgreSQL + fix AuditMiddleware fail-open (issue #850)#630

Open
LordofAvernus wants to merge 3 commits into
mainfrom
dms/feature-850
Open

feat(workbench): support PostgreSQL + fix AuditMiddleware fail-open (issue #850)#630
LordofAvernus wants to merge 3 commits into
mainfrom
dms/feature-850

Conversation

@LordofAvernus
Copy link
Copy Markdown
Collaborator

@LordofAvernus LordofAvernus commented May 22, 2026

User description

Summary

  • 工作台支持 PostgreSQL:在 SqlWorkbenchService.SupportDBType 白名单加入 postgresql(compat-RISK-2),打开工作台对 PG 数据源的入口。
  • 部署侧把 sqle-pg-plugin 二进制随 RPM 打包并落到 SQLE plugin_path(compat-RISK-4 决策 B 的必补覆盖)。
  • 修复 AuditMiddleware 在辅助路径(用户解析失败 / 缓存缺失 / DBService 元数据失败 / 未启用审核 / SQLE 调用失败)上把请求 400 掉的 bug,统一改为 fail-open 透传,让 ODC SQL Console 能正常执行 SQL;read-body 与「审核结果要求拦截」两个强约束仍保留 fail-closed。

关联 issue: actiontech/dms-ee#850
关联文档: docs/dev/compat_risks.md §compat-RISK-2 / §compat-RISK-4 / docs/dev/fix-task-004-odc-streamExecute-400.md

Test plan

  • make EDITION=ce docker_install 构建通过
  • go vet ./internal/sql_workbench/service/... 通过(本地已通过)
  • ODC SQL Console 上对 MySQL / PG 实例执行 SELECT 不再 400(fix-task-004 复测证据)
  • PG 数据源建表 / 工作台目录树展示不回归(compat-RISK-2 覆盖用例)
  • RPM 打包后 /usr/local/dms/plugins 含 sqle-pg-plugin 可执行文件(compat-RISK-4 覆盖用例)

Description

  • 增加 PostgreSQL 白名单支持

  • 优化 AuditMiddleware 容错策略

  • 更新单元测试验证逻辑

  • 同步 Makefile 与 RPM 部署文件


Diagram Walkthrough

flowchart LR
  A["添加 PostgreSQL 支持"] --> B["调整 AuditMiddleware 错误处理"]
  B --> C["更新单元测试用例"]
  C --> D["同步 Makefile 与 RPM spec"]
Loading

File Walkthrough

Relevant files
Enhancement
sql_workbench_service.go
调整数据库类型支持与审核中间件                                                                                   

internal/sql_workbench/service/sql_workbench_service.go

  • 增加 PostgreSQL 白名单判断
  • 添加审计中间件详细注释
  • 调整错误处理由阻塞改为跳过审计
  • 使用 warn 记录非关键错误
+39/-14 
Makefile
添加 PG 插件构建与复制步骤                                                                                   

Makefile

  • 新增 builddir/plugins 目录创建
  • 增加拷贝 sqle-pg-plugin 逻辑
  • 同步构建流程文件复制
+4/-0     
dms_sqle_provision.spec
同步 RPM 文件复制逻辑                                                                                       

build/dms_sqle_provision.spec

  • 添加复制 sqle-pg-plugin 至 RPM
  • 确保部署插件覆盖要求
+2/-0     
Tests
sql_workbench_service_test.go
更新数据库支持测试用例                                                                                           

internal/sql_workbench/service/sql_workbench_service_test.go

  • 调整测试用例支持 PostgreSQL
  • 扩充测试验证白名单逻辑
+14/-10 

…issue #850, compat-RISK-2)

Refs: dms-ee#850
Risk: compat-RISK-2

把 DBTypePostgreSQL 加入 SqlWorkbenchService.SupportDBType 白名单,使 PG 数据源
与 MySQL / Oracle / OceanBaseMySQL 等已支持类型一致走 ResetDbServiceByAuth 临时
账号路径进入 ODC 工作台(设计文档 §3.1 / §8.3)。

同步更新 sql_workbench_service_test.go::Test_SupportDBType 的 map case:
- 9 项 expected=true:MySQL / Oracle / OceanBaseMySQL / DM / TiDB /
  TDSQLForInnoDB / GoldenDB / PolarDBForMySQL / PostgreSQL
- 3 项 expected=false:SQL Server / 空字符串 / 未知类型字符串

(cherry picked from commit ba55d54f5d52adf9a85929a48958a1e334a67885)
…#850, compat-RISK-4)

Refs: #850
Risk: compat-RISK-4

在 dms-ee 部署流程中加入 sqle-pg-plugin 二进制拷贝,保证 SQLE plugin_path
在部署后包含 PG 审核插件二进制,闭环 compat-RISK-4 决策 B(必补覆盖)。

1) Makefile dms_sqle_provision_rpm_pre target
   - 新增 mkdir -p ./builddir/plugins,与既有 bin/config/static/scripts/
     neo4j-community/lib mkdir 行同级
   - 新增 cp ${PRE_DIR}sqle-pg-plugin/bin/sqle-pg-plugin
     ./builddir/plugins/sqle-pg-plugin,复用 sqle-pg-plugin Makefile install
     target 默认产物路径 $(GOBIN)/sqle-pg-plugin(GOBIN=$(shell pwd)/bin),
     模式与 sqle/bin/sqled、provision/bin/provision 等同仓 plugin 上游拷贝一致

2) build/dms_sqle_provision.spec %install 阶段
   - 新增 cp builddir/plugins/sqle-pg-plugin -> $RPM_BUILD_ROOT/usr/local/
     %{name}/plugins/sqle-pg-plugin
   - 走单文件 cp 而非 cp -R 整目录,避免与已有
     mkdir -p $RPM_BUILD_ROOT/usr/local/%{name}/plugins(行 40)冲突产生
     plugins/plugins/ 嵌套;语义与 sqle-ee/build/sqled.spec 的 plugin 拷贝模式
     等价,落地路径仍为 SQLE plugin_path 根
   - 不动 %files / %pre / %post / %preun / %postun;既有
     find $RPM_INSTALL_PREFIX/plugins -type f -exec chmod 0750 自然覆盖新二进制

设计依据:design.md §3.2 行 130-136 / §3.3 行 138-143 / §9.4 行 406
风险登记:docs/dev/compat_risks.md §1 compat-RISK-4 决策 B + case_ids
关联用例:case-pg-compat-010 / 011 / sqlaudit-002 / sqlaudit-003 /
         deploy-001 / deploy-002

不动:sqle-ee / sqle / sqle-pg-plugin 代码;vendor / go.mod / go.sum;
     skills/dms/script/**;任何 _ee.go / _ce.go;odc / odc-client;CE 仓库

(cherry picked from commit 12c0128b09fa0685a66dd7530eb44abba2657382)
… (issue #850, bug)

streamExecute 反代到 ODC 时,AuditMiddleware 在以下辅助路径异常时直接
`return errors.New(...)`,被 dms 的 HTTPErrorHandler 默认映射为
HTTP 400 BadRequest,导致 ODC SQL Console 完全无法执行 SQL:

- 解析用户 ID 失败
- 缓存表 sql_workbench_datasource_caches 查询失败
- 缓存中找不到 dms_db_service_id(用户首次走工作台 / 数据源未经 DMS
  加载路径时缓存为空)
- 获取 DBService 元数据失败
- 该 DBService 未启用 SQL 审核(最常见命中分支)
- SQLE 审核服务调用失败(网络 / 超时 / 5xx)

修复:把上述 6 个分支统一改为 `return next(c)`(fail-open)— 审核能力是
增强项,未启用 / 缓存缺失 / SQLE 故障时应按裸 ODC 反代行为透传,而不应
阻塞用户的 SQL 执行。read body err 与「审核结果要求拦截」两条强约束分支
仍保留 fail-closed。同时把 Errorf / Debugf 调整为 Warnf 以便后续排障。

不影响:
- 已启用 SQL 审核 + SQLE 调用成功 + 命中需审批规则的路径仍走
  buildAuditResponseWithoutExecution,行为不变;
- MySQL/PG/Oracle/OB-Mysql 等所有数据源类型均一致受益;
- 不动 odc / odc-client / vendor / go.mod / pnpm-lock。

证据:docs/dev/fix-task-004-odc-streamExecute-400.md

(cherry picked from commit fd802f139ec88ff33e26d21814039677092d9de0 of dms-ee, adapted to CE's sidInfo-based parseStreamExecuteRequest signature)
@actiontech-bot actiontech-bot requested a review from iwanghc May 22, 2026 10:10
@github-actions
Copy link
Copy Markdown

PR Reviewer Guide 🔍

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

日志不一致

在 AuditMiddleware 中,对于错误处理的日志记录级别不够统一。例如,读取请求体失败时使用了 error 级别日志,而获取用户 ID、缓存数据或 DBService 信息失败时则使用 warn 级别。建议统一日志级别,以便于后续问题排查和监控。

	// body 读不出来无法叠加审核,但也无法继续构造反代请求;保留 fail-closed。
	sqlWorkbenchService.log.Errorf("failed to read streamExecute request body: %v", err)
	return errors.New(locale.Bundle.LocalizeMsgByCtx(c.Request().Context(), locale.SqlWorkbenchAuditReadReqBodyErr))
}
// 恢复请求体,供后续处理使用
c.Request().Body = io.NopCloser(bytes.NewBuffer(bodyBytes))

// 解析请求体获取 SQL 和 datasource ID
// 注意:解析仅服务于审核辅助路径,解析失败不应直接阻塞用户的 SQL 执行;
// 否则一旦中间件辅助能力出错(如 sid 解码失败),用户连查询都跑不了。
sql, sidInfo, err := sqlWorkbenchService.parseStreamExecuteRequest(bodyBytes)
if err != nil {
	sqlWorkbenchService.log.Warnf("failed to parse streamExecute request, skipping audit: %v", err)
	return next(c)
}

if sql == "" || sidInfo == nil || sidInfo.datasourceID == "" {
	sqlWorkbenchService.log.Warnf("SQL or datasource ID is empty, skipping audit")
	return next(c)
}
datasourceID := sidInfo.datasourceID

// 获取当前用户 ID
dmsUserId, err := sqlWorkbenchService.getDMSUserIdFromRequest(c)
if err != nil {
	// 审计需要用户上下文,缺失时跳过审计而非阻塞执行(鉴权由前置 Login() 已经把关)。
	sqlWorkbenchService.log.Warnf("failed to get DMS user ID, skipping audit: %v", err)
	return next(c)
}

@github-actions
Copy link
Copy Markdown

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
改为fail-open处理

建议对读取请求体失败的情况采用fail-open策略,而不是阻断SQL执行。这样可以确保在不可预知的读取错误时,仍能继续执行SQL,避免整体服务中断。

internal/sql_workbench/service/sql_workbench_service.go [1073-1077]

 bodyBytes, err := io.ReadAll(c.Request().Body)
 if err != nil {
-	sqlWorkbenchService.log.Errorf("failed to read streamExecute request body: %v", err)
-	return errors.New(locale.Bundle.LocalizeMsgByCtx(c.Request().Context(), locale.SqlWorkbenchAuditReadReqBodyErr))
+	sqlWorkbenchService.log.Warnf("failed to read streamExecute request body, skipping audit: %v", err)
+	return next(c)
 }
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly recommends switching from a fail-closed to a fail-open strategy by logging a warning and calling next(c), which increases service resilience. This error-handling improvement is contextually relevant and aligns with similar adjustments elsewhere in the code.

Medium

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant