From 2ecbac4a6fb04d8913df8d1337a2c7dff5cac1a6 Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Fri, 14 Aug 2026 00:00:19 +0000 Subject: [PATCH] fix(connection): preserve credentials for credential-free URLs Closes #5377 --- .../src/lib/connection/connectionUrl.ts | 11 ++++ packages/app-tests/connectionUrl.test.ts | 51 +++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/apps/desktop/src/lib/connection/connectionUrl.ts b/apps/desktop/src/lib/connection/connectionUrl.ts index a8fd9a691c..dd08222ff4 100644 --- a/apps/desktop/src/lib/connection/connectionUrl.ts +++ b/apps/desktop/src/lib/connection/connectionUrl.ts @@ -674,6 +674,11 @@ function zookeeperConnectStringFromUrl(parsed: URL, defaultPort: number): string return `${host}:${port}${chroot}`; } +function shouldPreserveCredentialFreeUrlCredentials(config: Omit, parsed: ParsedConnectionUrl): boolean { + const currentProfile = config.driver_profile?.trim(); + return parsed.dbType === config.db_type && (!currentProfile || parsed.driverProfile === currentProfile) && !parsed.username && !parsed.password; +} + function applyParsedUsername(config: Omit, parsed: ParsedConnectionUrl): string { if (parsed.dbType === "h2" && config.db_type === "h2" && !h2JdbcUrlHasUserParam(parsed.connectionString)) { return config.username || parsed.username; @@ -681,6 +686,9 @@ function applyParsedUsername(config: Omit, parsed: Parse if (parsed.dbType === "kingbase" && config.db_type === "kingbase" && !parsed.username) { return config.username; } + if (shouldPreserveCredentialFreeUrlCredentials(config, parsed)) { + return config.username || parsed.username; + } return parsed.username; } @@ -691,6 +699,9 @@ function applyParsedPassword(config: Omit, parsed: Parse if (parsed.dbType === "kingbase" && config.db_type === "kingbase" && !parsed.password) { return config.password; } + if (shouldPreserveCredentialFreeUrlCredentials(config, parsed)) { + return config.password || parsed.password; + } return parsed.password; } diff --git a/packages/app-tests/connectionUrl.test.ts b/packages/app-tests/connectionUrl.test.ts index 0913f4b626..f20b1f3358 100644 --- a/packages/app-tests/connectionUrl.test.ts +++ b/packages/app-tests/connectionUrl.test.ts @@ -72,6 +72,57 @@ test("applies a credential-free KingBase URL without clearing typed credentials" assert.equal(applied.password, "typed-secret"); }); +test("applies a credential-free URL without clearing same-profile typed credentials", () => { + const parsed = parseConnectionUrl("postgresql://db.example.com:5433/app?sslmode=require"); + const applied = applyParsedConnectionUrl({ name: "app", db_type: "postgres", username: "typed-user", password: "typed-secret" } as any, parsed); + + assert.equal(applied.host, "db.example.com"); + assert.equal(applied.port, 5433); + assert.equal(applied.database, "app"); + assert.equal(applied.url_params, "sslmode=require"); + assert.equal(applied.ssl, true); + assert.equal(applied.username, "typed-user"); + assert.equal(applied.password, "typed-secret"); +}); + +test("uses explicit URL credentials instead of same-profile typed credentials", () => { + const parsed = parseConnectionUrl("postgresql://url%40user:url%40secret@db.example.com/app"); + const applied = applyParsedConnectionUrl({ name: "app", db_type: "postgres", username: "typed-user", password: "typed-secret" } as any, parsed); + + assert.equal(applied.username, "url@user"); + assert.equal(applied.password, "url@secret"); +}); + +test("does not carry typed credentials to a different URL profile", () => { + const parsed = parseConnectionUrl("postgresql://db.example.com/app"); + const applied = applyParsedConnectionUrl({ name: "app", db_type: "mysql", username: "mysql-user", password: "mysql-secret" } as any, parsed); + + assert.equal(applied.db_type, "postgres"); + assert.equal(applied.username, ""); + assert.equal(applied.password, ""); +}); + +test("does not carry typed credentials between driver profiles of the same database type", () => { + const parsed = parseConnectionUrl("mariadb://db.example.com/app"); + const applied = applyParsedConnectionUrl( + { name: "app", db_type: "mysql", driver_profile: "mysql", username: "mysql-user", password: "mysql-secret" } as any, + parsed, + ); + + assert.equal(applied.db_type, "mysql"); + assert.equal(applied.driver_profile, "mariadb"); + assert.equal(applied.username, ""); + assert.equal(applied.password, ""); +}); + +test("keeps empty same-profile form credentials empty for a credential-free URL", () => { + const parsed = parseConnectionUrl("postgresql://db.example.com/app"); + const applied = applyParsedConnectionUrl({ name: "app", db_type: "postgres", username: "", password: "" } as any, parsed); + + assert.equal(applied.username, ""); + assert.equal(applied.password, ""); +}); + test("parses mysql URLs with encoded credentials", () => { const parsed = parseConnectionUrl("mysql://root:p%40ss@127.0.0.1/shop?charset=utf8mb4");