Skip to content

Commit 269bef3

Browse files
committed
fix: make sql_workbench AuditMiddleware fail-open on auxiliary errors (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)
1 parent c2615ba commit 269bef3

1 file changed

Lines changed: 37 additions & 13 deletions

File tree

internal/sql_workbench/service/sql_workbench_service.go

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,6 +1043,24 @@ func makeHttpRequest(ctx context.Context, url string, headers map[string]string,
10431043
}
10441044

10451045
// AuditMiddleware 拦截工作台odc请求进行加工
1046+
//
1047+
// 设计原则(issue #850, bug 修复):
1048+
//
1049+
// 本中间件提供的是「在 streamExecute 反代到 ODC 前叠加 SQLE 审核」的增强能力,
1050+
// 不是业务流的必经环节。当:
1051+
// - SQL/数据源 ID 解析失败;
1052+
// - 缓存 / 用户上下文缺失;
1053+
// - 该 DBService 未开启 SQL 审核;
1054+
// - SQLE 服务自身调用失败;
1055+
//
1056+
// 均应**透传放行**(fail-open,return next(c))让 ODC 继续执行用户的 SQL,
1057+
// 而不是把请求 400 掉、让用户连查询都跑不通。只有审核**明确返回需要拦截**
1058+
// 的结果(如规则违反需审批)才走 buildAuditResponseWithoutExecution 路径。
1059+
//
1060+
// 修复前:未启用审核 / 缓存缺失 / 用户解析失败 等辅助路径异常均直接
1061+
// `return errors.New(...)`,被 dms 的 HTTPErrorHandler 统一映射成 400,
1062+
// 导致 case-pg-mysql-baseline-001 等用例在 SQL Console 上完全无法运行(见
1063+
// docs/dev/fix-task-004-odc-streamExecute-400.md)。
10461064
func (sqlWorkbenchService *SqlWorkbenchService) AuditMiddleware() echo.MiddlewareFunc {
10471065
return func(next echo.HandlerFunc) echo.HandlerFunc {
10481066
return func(c echo.Context) error {
@@ -1054,7 +1072,8 @@ func (sqlWorkbenchService *SqlWorkbenchService) AuditMiddleware() echo.Middlewar
10541072
// 读取请求体
10551073
bodyBytes, err := io.ReadAll(c.Request().Body)
10561074
if err != nil {
1057-
sqlWorkbenchService.log.Errorf("failed to read request body: %v", err)
1075+
// body 读不出来无法叠加审核,但也无法继续构造反代请求;保留 fail-closed。
1076+
sqlWorkbenchService.log.Errorf("failed to read streamExecute request body: %v", err)
10581077
return errors.New(locale.Bundle.LocalizeMsgByCtx(c.Request().Context(), locale.SqlWorkbenchAuditReadReqBodyErr))
10591078
}
10601079
// 恢复请求体,供后续处理使用
@@ -1063,7 +1082,6 @@ func (sqlWorkbenchService *SqlWorkbenchService) AuditMiddleware() echo.Middlewar
10631082
// 解析请求体获取 SQL 和 datasource ID
10641083
// 注意:解析仅服务于审核辅助路径,解析失败不应直接阻塞用户的 SQL 执行;
10651084
// 否则一旦中间件辅助能力出错(如 sid 解码失败),用户连查询都跑不了。
1066-
// 真正的「未启用审核 / 审核失败」等强策略仍由后续分支按既有 fail-closed 处理。
10671085
sql, sidInfo, err := sqlWorkbenchService.parseStreamExecuteRequest(bodyBytes)
10681086
if err != nil {
10691087
sqlWorkbenchService.log.Warnf("failed to parse streamExecute request, skipping audit: %v", err)
@@ -1079,32 +1097,37 @@ func (sqlWorkbenchService *SqlWorkbenchService) AuditMiddleware() echo.Middlewar
10791097
// 获取当前用户 ID
10801098
dmsUserId, err := sqlWorkbenchService.getDMSUserIdFromRequest(c)
10811099
if err != nil {
1082-
sqlWorkbenchService.log.Errorf("failed to get DMS user ID: %v", err)
1083-
return errors.New(locale.Bundle.LocalizeMsgByCtx(c.Request().Context(), locale.SqlWorkbenchAuditGetDMSUserErr))
1100+
// 审计需要用户上下文,缺失时跳过审计而非阻塞执行(鉴权由前置 Login() 已经把关)。
1101+
sqlWorkbenchService.log.Warnf("failed to get DMS user ID, skipping audit: %v", err)
1102+
return next(c)
10841103
}
10851104

10861105
// 从缓存表获取 dms_db_service_id
10871106
dmsDBServiceID, err := sqlWorkbenchService.getDMSDBServiceIDFromCache(c.Request().Context(), datasourceID, dmsUserId)
10881107
if err != nil {
1089-
sqlWorkbenchService.log.Errorf("failed to get dms_db_service_id from cache: %v", err)
1090-
return errors.New(locale.Bundle.LocalizeMsgByCtx(c.Request().Context(), locale.SqlWorkbenchAuditGetDBServiceMappingErr))
1108+
// 缓存查询失败属于辅助路径异常,不应阻塞 SQL 执行。
1109+
sqlWorkbenchService.log.Warnf("failed to get dms_db_service_id from cache, skipping audit: %v", err)
1110+
return next(c)
10911111
}
10921112

10931113
if dmsDBServiceID == "" {
1094-
sqlWorkbenchService.log.Debugf("dms_db_service_id not found in cache for datasource: %s", datasourceID)
1095-
return errors.New(locale.Bundle.LocalizeMsgByCtx(c.Request().Context(), locale.SqlWorkbenchAuditDBServiceMappingNotFoundErr))
1114+
// 用户首次在工作台使用该数据源 / 数据源未走"通过 DMS 加载"路径时缓存为空,应放行。
1115+
sqlWorkbenchService.log.Warnf("dms_db_service_id not found in cache for datasource=%s, skipping audit", datasourceID)
1116+
return next(c)
10961117
}
10971118

10981119
// 获取 DBService 信息
10991120
dbService, err := sqlWorkbenchService.dbServiceUsecase.GetDBService(c.Request().Context(), dmsDBServiceID)
11001121
if err != nil {
1101-
sqlWorkbenchService.log.Errorf("failed to get DBService: %v", err)
1102-
return errors.New(locale.Bundle.LocalizeMsgByCtx(c.Request().Context(), locale.SqlWorkbenchAuditGetDBServiceErr))
1122+
// DBService 元数据查询失败属于辅助路径异常,不应阻塞 SQL 执行。
1123+
sqlWorkbenchService.log.Warnf("failed to get DBService %s, skipping audit: %v", dmsDBServiceID, err)
1124+
return next(c)
11031125
}
11041126

11051127
// 未开启 SQL 审核时直接放行,由 ODC 执行 SQL
11061128
if !sqlWorkbenchService.isEnableSQLAudit(dbService) {
1107-
sqlWorkbenchService.log.Debugf("SQL audit is not enabled for DBService: %s", dmsDBServiceID)
1129+
// 未启用审核 = 该数据源没要求审核加强,按裸 ODC 反代行为放行。
1130+
sqlWorkbenchService.log.Debugf("SQL audit is not enabled for DBService %s, skipping audit", dmsDBServiceID)
11081131
return next(c)
11091132
}
11101133

@@ -1121,8 +1144,9 @@ func (sqlWorkbenchService *SqlWorkbenchService) AuditMiddleware() echo.Middlewar
11211144
// 调用 SQLE 审核接口
11221145
auditResult, err := sqlWorkbenchService.callSQLEAudit(c.Request().Context(), sql, dbService, schemaName)
11231146
if err != nil {
1124-
sqlWorkbenchService.log.Errorf("call SQLE audit failed: %v", err)
1125-
return errors.New(locale.Bundle.LocalizeMsgByCtx(c.Request().Context(), locale.SqlWorkbenchAuditCallSQLEErr))
1147+
// SQLE 服务自身故障(连不上、超时等)不应让用户的 SQL 执行链路一起挂;放行并打 Warn 便于排障。
1148+
sqlWorkbenchService.log.Warnf("call SQLE audit failed, skipping audit: %v", err)
1149+
return next(c)
11261150
}
11271151

11281152
// 拦截响应并添加审核结果

0 commit comments

Comments
 (0)