🐛 fix(dameng): 修复特殊字符密码导致连接认证失败

- 调整达梦 DSN 生成逻辑,密码按驱动解析规则原样传入
- 移除默认 escapeProcess 参数示例,避免误导配置
- 补充特殊字符密码与问号密码的回归测试
Refs #446
This commit is contained in:
Syngnat
2026-05-10 20:26:19 +08:00
parent a11d39f981
commit 7dc9da0fd0
3 changed files with 30 additions and 14 deletions

View File

@@ -1853,7 +1853,7 @@ const ConnectionModal: React.FC<{
case "mongodb":
return "retryWrites=true&readPreference=secondaryPreferred";
case "dameng":
return "schema=SYSDBA&escapeProcess=true";
return "schema=SYSDBA";
case "tdengine":
return "timezone=Asia%2FShanghai";
default:

View File

@@ -31,7 +31,6 @@ func (d *DamengDB) getDSN(config connection.ConnectionConfig) string {
// or dm://user:password@host:port
address := net.JoinHostPort(config.Host, strconv.Itoa(config.Port))
escapedPassword := url.PathEscape(config.Password)
q := url.Values{}
if config.Database != "" {
q.Set("schema", config.Database)
@@ -44,15 +43,16 @@ func (d *DamengDB) getDSN(config connection.ConnectionConfig) string {
q.Set("SSL_KEY_PATH", keyPath)
}
}
if escapedPassword != config.Password {
// 达梦驱动要求密码包含特殊字符时password 需 PathEscape并添加 escapeProcess=true 让驱动解码。
q.Set("escapeProcess", "true")
}
mergeConnectionParamsFromConfig(q, config, "dm", "dameng")
dsn := fmt.Sprintf("dm://%s:%s@%s", config.User, escapedPassword, address)
// 当前达梦 Go 驱动使用字符串切分解析 DSN认证信息不会做 URL 反解码。
// 密码保持原样传入,避免 p%40ss 这类转义文本被当作真实密码登录。
dsn := fmt.Sprintf("dm://%s:%s@%s", config.User, config.Password, address)
encoded := q.Encode()
if encoded == "" {
if strings.Contains(config.User, "?") || strings.Contains(config.Password, "?") {
return dsn + "?"
}
return dsn
}
return dsn + "?" + encoded

View File

@@ -126,7 +126,7 @@ func TestOracleDSN_EscapesUserAndPassword(t *testing.T) {
}
}
func TestDamengDSN_EscapesPasswordAndEnablesEscapeProcess(t *testing.T) {
func TestDamengDSN_KeepsRawPasswordForDriverParser(t *testing.T) {
d := &DamengDB{}
cfg := connection.ConnectionConfig{
Type: "dameng",
@@ -138,20 +138,36 @@ func TestDamengDSN_EscapesPasswordAndEnablesEscapeProcess(t *testing.T) {
}
dsn := d.getDSN(cfg)
if strings.Contains(dsn, cfg.Password) {
t.Fatalf("dsn 包含原始密码%s", dsn)
if !strings.Contains(dsn, "SYSDBA:p@ss:wo/rd@127.0.0.1:5236") {
t.Fatalf("dsn 未保留达梦驱动可识别的原始认证信息%s", dsn)
}
if strings.Contains(dsn, "wo/rd") || !strings.Contains(dsn, "wo%2Frd") {
t.Fatalf("dsn 未按达梦驱动要求转义密码(至少应转义 '/'%s", dsn)
if strings.Contains(dsn, "p%40ss") || strings.Contains(dsn, "wo%2Frd") {
t.Fatalf("dsn 不应转义达梦密码,驱动不会反解码认证信息%s", dsn)
}
if !strings.Contains(dsn, "escapeProcess=true") {
t.Fatalf("dsn 缺少 escapeProcess=true%s", dsn)
if strings.Contains(dsn, "escapeProcess=true") {
t.Fatalf("dsn 不应自动添加 escapeProcess=true%s", dsn)
}
if !strings.Contains(dsn, "schema=DBName") {
t.Fatalf("dsn 缺少 schema 参数:%s", dsn)
}
}
func TestDamengDSN_AppendsQuerySentinelForQuestionMarkInPassword(t *testing.T) {
d := &DamengDB{}
cfg := connection.ConnectionConfig{
Type: "dameng",
Host: "127.0.0.1",
Port: 5236,
User: "SYSDBA",
Password: "p?ss",
}
dsn := d.getDSN(cfg)
if dsn != "dm://SYSDBA:p?ss@127.0.0.1:5236?" {
t.Fatalf("dsn = %q, want raw password with trailing query sentinel", dsn)
}
}
func TestDamengDSN_AppendsSSLCertAndKeyParams(t *testing.T) {
d := &DamengDB{}
cfg := connection.ConnectionConfig{