fix(dameng): support SSL connection settings
This commit is contained in:
parent
ca11b166f8
commit
5e7ab62066
|
|
@ -1089,7 +1089,17 @@ public final class DamengAgent extends BaseDatabaseAgent {
|
|||
private static String buildUrl(ConnectParams params) {
|
||||
String database = params.getDatabase() == null ? "" : params.getDatabase().trim();
|
||||
String suffix = database.isEmpty() ? "" : "/" + database;
|
||||
return "jdbc:dm://" + params.getHost() + ":" + params.getPort() + suffix;
|
||||
String url = "jdbc:dm://" + params.getHost() + ":" + params.getPort() + suffix;
|
||||
String urlParams = params.getUrl_params() == null ? "" : params.getUrl_params().trim();
|
||||
while (urlParams.startsWith("?") || urlParams.startsWith("&") || urlParams.startsWith(";")) {
|
||||
urlParams = urlParams.substring(1);
|
||||
}
|
||||
if (urlParams.isEmpty()) {
|
||||
return url;
|
||||
}
|
||||
|
||||
// DM8 SSL options are JDBC URL query parameters; dropping them makes the driver initialize SSL with defaults.
|
||||
return url + "?" + urlParams;
|
||||
}
|
||||
|
||||
private static String formatDataType(
|
||||
|
|
|
|||
|
|
@ -21,6 +21,25 @@ class DamengAgentUrlTest {
|
|||
Assertions.assertEquals("jdbc:dm://127.0.0.1:5236/MAIN", url);
|
||||
}
|
||||
|
||||
@Test
|
||||
void appendsDmJdbcUrlParameters() throws Exception {
|
||||
String url = invokeBuildUrl(new ConnectParams(
|
||||
"127.0.0.1",
|
||||
5236,
|
||||
"",
|
||||
"SYSDBA",
|
||||
"pwd",
|
||||
"?sslFilesPath=/Users/test/dmcert&sslkeystorePass=secret",
|
||||
"",
|
||||
false
|
||||
));
|
||||
|
||||
Assertions.assertEquals(
|
||||
"jdbc:dm://127.0.0.1:5236?sslFilesPath=/Users/test/dmcert&sslkeystorePass=secret",
|
||||
url
|
||||
);
|
||||
}
|
||||
|
||||
private static String invokeBuildUrl(ConnectParams params) throws Exception {
|
||||
Method method = DamengAgent.class.getDeclaredMethod("buildUrl", ConnectParams.class);
|
||||
method.setAccessible(true);
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ import { isLocalFileTypeDb } from "@/lib/connection/connectionFile";
|
|||
import { MQ_PINNED_VERSION_OPTIONS, pinnedVersionToSelection, selectionToPinnedVersion } from "@/lib/mq/mqPinnedVersionOptions";
|
||||
import { mongodbAuthFailureHint, mongoUrlParam, mongoUrlParamIsTrue, normalizeMongoTlsFormState, setMongoUrlParam, setMongoUrlParamBoolean } from "@/lib/mongo/mongoConnectionOptions";
|
||||
import { mysqlCleartextPasswordAuthEnabled, setMysqlCleartextPasswordAuthEnabled } from "@/lib/database/mysqlConnectionOptions";
|
||||
import { applyDamengSslUrlParams, damengSslFormConfig } from "@/lib/database/damengSslOptions";
|
||||
import { copyToClipboard } from "@/lib/common/clipboard";
|
||||
import { configuredDatabaseProductName, connectionConfigFingerprint, databaseInfoCopyText, databaseInfoRows, normalizeDatabaseConnectionInfo, type DatabaseInfoField } from "@/lib/connection/connectionDatabaseInfo";
|
||||
import { agentDriverInstallKey, appendAgentDriverUpdateHint, hasAgentDriverUpdate, showAgentDriverInstallHint, type AgentDriverInstallState, type DriverStoreFocus } from "@/lib/connection/agentDriverInstallHint";
|
||||
|
|
@ -2434,7 +2435,7 @@ const sqliteExtensionPaths = computed({
|
|||
form.value.url_params = setSqliteExtensionPaths(form.value.url_params, value);
|
||||
},
|
||||
});
|
||||
const tlsCapableDatabaseTypes = new Set<DatabaseType>(["mysql", "starrocks", "postgres", "redshift", "gaussdb", "kwdb", "opengauss", "questdb", "redis", "etcd", "clickhouse", "elasticsearch", "qdrant", "milvus", "weaviate", "chromadb", "influxdb"]);
|
||||
const tlsCapableDatabaseTypes = new Set<DatabaseType>(["mysql", "starrocks", "postgres", "redshift", "gaussdb", "kwdb", "opengauss", "questdb", "dameng", "redis", "etcd", "clickhouse", "elasticsearch", "qdrant", "milvus", "weaviate", "chromadb", "influxdb"]);
|
||||
const supportsTlsToggle = computed(() => tlsCapableDatabaseTypes.has(form.value.db_type));
|
||||
const supportsCaCertificatePath = computed(() => form.value.db_type === "clickhouse");
|
||||
const supportsGenericUrlParams = computed(() => form.value.db_type !== "manticoresearch");
|
||||
|
|
@ -2447,6 +2448,37 @@ const mysqlCleartextPasswordAuth = computed({
|
|||
form.value.url_params = setMysqlCleartextPasswordAuthEnabled(form.value.url_params, value);
|
||||
},
|
||||
});
|
||||
// DM8 configures SSL through JDBC URL parameters, so the TLS form and Advanced tab share one source of truth.
|
||||
const tlsEnabled = computed({
|
||||
get: () => !!form.value.ssl || (form.value.db_type === "dameng" && damengSslFormConfig(form.value.url_params).enabled),
|
||||
set: (enabled: boolean) => {
|
||||
form.value.ssl = enabled;
|
||||
if (form.value.db_type === "dameng" && !enabled) {
|
||||
form.value.url_params = applyDamengSslUrlParams(form.value.url_params, false, "", "", "");
|
||||
}
|
||||
},
|
||||
});
|
||||
const damengSslFilesPath = computed({
|
||||
get: () => damengSslFormConfig(form.value.url_params).sslFilesPath,
|
||||
set: (value: string) => {
|
||||
const current = damengSslFormConfig(form.value.url_params);
|
||||
form.value.url_params = applyDamengSslUrlParams(form.value.url_params, true, value, current.sslKeystorePassword, current.sslProtocol);
|
||||
},
|
||||
});
|
||||
const damengSslKeystorePassword = computed({
|
||||
get: () => damengSslFormConfig(form.value.url_params).sslKeystorePassword,
|
||||
set: (value: string) => {
|
||||
const current = damengSslFormConfig(form.value.url_params);
|
||||
form.value.url_params = applyDamengSslUrlParams(form.value.url_params, true, current.sslFilesPath, value, current.sslProtocol);
|
||||
},
|
||||
});
|
||||
const damengSslProtocol = computed({
|
||||
get: () => damengSslFormConfig(form.value.url_params).sslProtocol,
|
||||
set: (value: string) => {
|
||||
const current = damengSslFormConfig(form.value.url_params);
|
||||
form.value.url_params = applyDamengSslUrlParams(form.value.url_params, true, current.sslFilesPath, current.sslKeystorePassword, value);
|
||||
},
|
||||
});
|
||||
const mysqlTlsMode = computed({
|
||||
get: () => mysqlTlsModeFromParams(form.value.url_params, form.value.ssl),
|
||||
set: (value: string) => {
|
||||
|
|
@ -3020,6 +3052,11 @@ function connectionConfigForSubmit(id: string, generatedName = ""): ConnectionCo
|
|||
if (config.db_type === "manticoresearch") {
|
||||
config.url_params = "";
|
||||
}
|
||||
if (config.db_type === "dameng") {
|
||||
const damengSsl = damengSslFormConfig(config.url_params);
|
||||
config.ssl = !!config.ssl || damengSsl.enabled;
|
||||
config.url_params = applyDamengSslUrlParams(config.url_params, config.ssl, damengSsl.sslFilesPath, damengSsl.sslKeystorePassword, damengSsl.sslProtocol);
|
||||
}
|
||||
if (config.db_type === "hive") {
|
||||
if (hiveAuthMode.value === "kerberos" && !hivePrincipal.value.trim()) {
|
||||
throw new Error(t("connection.hiveKerberosPrincipalRequired"));
|
||||
|
|
@ -4201,6 +4238,20 @@ async function browseCaCertPath() {
|
|||
}
|
||||
}
|
||||
|
||||
async function browseDamengSslFilesPath() {
|
||||
if (isTauriRuntime()) {
|
||||
const { open } = await import("@tauri-apps/plugin-dialog");
|
||||
const selected = await open({
|
||||
title: t("connection.damengSslFilesPathBrowse"),
|
||||
directory: true,
|
||||
multiple: false,
|
||||
});
|
||||
if (selected && typeof selected === "string") {
|
||||
damengSslFilesPath.value = selected;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function browseMysqlTlsFile(target: "cert" | "key") {
|
||||
if (isTauriRuntime()) {
|
||||
const { open } = await import("@tauri-apps/plugin-dialog");
|
||||
|
|
@ -6044,11 +6095,46 @@ function openExternalUrl(url: string) {
|
|||
<div v-if="!supportsPostgresTlsOptions && !supportsMysqlTlsOptions" class="grid grid-cols-4 items-center gap-4">
|
||||
<Label :class="connectionLabelSmallClass">SSL/TLS</Label>
|
||||
<label class="col-span-3 flex items-center gap-2 cursor-pointer">
|
||||
<input type="checkbox" v-model="form.ssl" class="mr-0" />
|
||||
<input type="checkbox" v-model="tlsEnabled" class="mr-0" />
|
||||
<span class="text-xs text-muted-foreground">{{ t("connection.sslEnable") }}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<template v-if="form.db_type === 'dameng'">
|
||||
<div class="grid grid-cols-4 items-start gap-4">
|
||||
<Label :class="connectionLabelSmallPaddedClass">{{ t("connection.damengSslFilesPath") }}</Label>
|
||||
<div class="col-span-3 space-y-1.5">
|
||||
<div class="flex items-center gap-1">
|
||||
<Input v-model="damengSslFilesPath" class="flex-1" :placeholder="t('connection.damengSslFilesPathPlaceholder')" :disabled="!tlsEnabled" />
|
||||
<Tooltip v-if="isDesktop">
|
||||
<TooltipTrigger as-child>
|
||||
<Button variant="outline" size="icon" class="h-9 w-9 shrink-0" :disabled="!tlsEnabled" @click="browseDamengSslFilesPath">
|
||||
<FolderOpen class="h-4 w-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{{ t("connection.damengSslFilesPathBrowse") }}</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<p class="text-[11px] leading-4 text-muted-foreground">{{ t("connection.damengSslHint") }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label :class="connectionLabelSmallClass">{{ t("connection.damengSslKeystorePassword") }}</Label>
|
||||
<PasswordInput v-model="damengSslKeystorePassword" class="col-span-3" :disabled="!tlsEnabled" />
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label :class="connectionLabelSmallClass">{{ t("connection.damengSslProtocol") }}</Label>
|
||||
<Input v-model="damengSslProtocol" class="col-span-3" :placeholder="t('connection.damengSslProtocolPlaceholder')" :disabled="!tlsEnabled" />
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-4 items-start gap-4">
|
||||
<span />
|
||||
<p class="col-span-3 text-[11px] leading-4 text-muted-foreground">{{ t("connection.damengSslVerificationHint") }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-if="form.db_type === 'redis'" class="grid grid-cols-4 items-start gap-4">
|
||||
<Label :class="connectionLabelSmallClass">{{ t("connection.redisTlsInsecure") }}</Label>
|
||||
<label class="col-span-3 flex items-start gap-2 cursor-pointer">
|
||||
|
|
|
|||
|
|
@ -250,6 +250,14 @@ export default {
|
|||
caCertPath: "CA Certificate",
|
||||
caCertPathPlaceholder: "Optional, e.g. ~/.yandex/RootCA.crt",
|
||||
caCertPathBrowse: "Browse certificate",
|
||||
damengSslFilesPath: "Certificate directory",
|
||||
damengSslFilesPathPlaceholder: "/path/to/dmcert",
|
||||
damengSslFilesPathBrowse: "Select certificate directory",
|
||||
damengSslKeystorePassword: "Keystore password",
|
||||
damengSslHint: "DM8 JDBC reads the SSL certificates from this directory and passes both fields through URL parameters.",
|
||||
damengSslProtocol: "SSL protocol",
|
||||
damengSslProtocolPlaceholder: "Optional, e.g. TLSv1.2",
|
||||
damengSslVerificationHint: "Certificate verification is controlled by the DM8 server ENABLE_ENCRYPT setting. Mode 5 verifies the server certificate; mode 4 encrypts without certificate verification.",
|
||||
redisTlsInsecure: "Skip certificate verification",
|
||||
redisTlsInsecureHint: "Equivalent to redis-cli --tls --insecure, for self-signed certificates or private CAs.",
|
||||
mysqlTlsMode: "TLS Mode",
|
||||
|
|
|
|||
|
|
@ -237,6 +237,14 @@ export default withEnglishFallback({
|
|||
caCertPath: "Certificado CA",
|
||||
caCertPathPlaceholder: "Opcional, p. ej. ~/.yandex/RootCA.crt",
|
||||
caCertPathBrowse: "Buscar certificado",
|
||||
damengSslFilesPath: "Directorio de certificados",
|
||||
damengSslFilesPathPlaceholder: "/path/to/dmcert",
|
||||
damengSslFilesPathBrowse: "Seleccionar directorio de certificados",
|
||||
damengSslKeystorePassword: "Contraseña del almacén de claves",
|
||||
damengSslHint: "DM8 JDBC lee los certificados SSL de este directorio y pasa ambos valores mediante parámetros URL.",
|
||||
damengSslProtocol: "Protocolo SSL",
|
||||
damengSslProtocolPlaceholder: "Opcional, p. ej. TLSv1.2",
|
||||
damengSslVerificationHint: "La verificación del certificado depende de ENABLE_ENCRYPT en el servidor DM8. El modo 5 verifica el certificado del servidor; el modo 4 cifra sin verificar certificados.",
|
||||
redisTlsInsecure: "Omitir verificación del certificado",
|
||||
redisTlsInsecureHint: "Equivale a redis-cli --tls --insecure, para certificados autofirmados o CA privadas.",
|
||||
mysqlTlsMode: "Modo TLS",
|
||||
|
|
|
|||
|
|
@ -236,6 +236,14 @@ export default withEnglishFallback({
|
|||
caCertPath: "Certificato CA",
|
||||
caCertPathPlaceholder: "Opzionale, es. ~/.yandex/RootCA.crt",
|
||||
caCertPathBrowse: "Sfoglia certificato",
|
||||
damengSslFilesPath: "Directory certificati",
|
||||
damengSslFilesPathPlaceholder: "/path/to/dmcert",
|
||||
damengSslFilesPathBrowse: "Seleziona directory certificati",
|
||||
damengSslKeystorePassword: "Password keystore",
|
||||
damengSslHint: "DM8 JDBC legge i certificati SSL da questa directory e passa entrambi i valori tramite parametri URL.",
|
||||
damengSslProtocol: "Protocollo SSL",
|
||||
damengSslProtocolPlaceholder: "Opzionale, es. TLSv1.2",
|
||||
damengSslVerificationHint: "La verifica del certificato dipende da ENABLE_ENCRYPT sul server DM8. La modalità 5 verifica il certificato del server; la modalità 4 cifra senza verificare i certificati.",
|
||||
redisTlsInsecure: "Salta verifica certificato",
|
||||
redisTlsInsecureHint: "Equivalente a redis-cli --tls --insecure, per certificati auto-firmati o CA private.",
|
||||
mysqlTlsMode: "Modalità TLS",
|
||||
|
|
|
|||
|
|
@ -236,6 +236,14 @@ export default withEnglishFallback({
|
|||
caCertPath: "CA証明書",
|
||||
caCertPathPlaceholder: "任意、例: ~/.yandex/RootCA.crt",
|
||||
caCertPathBrowse: "証明書を参照",
|
||||
damengSslFilesPath: "証明書ディレクトリ",
|
||||
damengSslFilesPathPlaceholder: "/path/to/dmcert",
|
||||
damengSslFilesPathBrowse: "証明書ディレクトリを選択",
|
||||
damengSslKeystorePassword: "キーストアパスワード",
|
||||
damengSslHint: "DM8 JDBC はこのディレクトリから SSL 証明書を読み取り、両方の設定を URL パラメータとして渡します。",
|
||||
damengSslProtocol: "SSL プロトコル",
|
||||
damengSslProtocolPlaceholder: "任意、例: TLSv1.2",
|
||||
damengSslVerificationHint: "証明書の検証方法は DM8 サーバーの ENABLE_ENCRYPT 設定で決まります。モード 5 はサーバー証明書を検証し、モード 4 は証明書を検証せず暗号化のみ行います。",
|
||||
redisTlsInsecure: "証明書の検証をスキップ",
|
||||
redisTlsInsecureHint: "自己署名証明書やプライベートCA用の、redis-cli --tls --insecure 相当の設定です。",
|
||||
mysqlTlsMode: "TLSモード",
|
||||
|
|
|
|||
|
|
@ -237,6 +237,14 @@ export default withEnglishFallback({
|
|||
caCertPath: "Certificado CA",
|
||||
caCertPathPlaceholder: "Opcional, ex.: ~/.yandex/RootCA.crt",
|
||||
caCertPathBrowse: "Procurar certificado",
|
||||
damengSslFilesPath: "Diretório de certificados",
|
||||
damengSslFilesPathPlaceholder: "/path/to/dmcert",
|
||||
damengSslFilesPathBrowse: "Selecionar diretório de certificados",
|
||||
damengSslKeystorePassword: "Senha do keystore",
|
||||
damengSslHint: "O DM8 JDBC lê os certificados SSL deste diretório e envia os dois valores por parâmetros de URL.",
|
||||
damengSslProtocol: "Protocolo SSL",
|
||||
damengSslProtocolPlaceholder: "Opcional, ex.: TLSv1.2",
|
||||
damengSslVerificationHint: "A verificação do certificado depende de ENABLE_ENCRYPT no servidor DM8. O modo 5 verifica o certificado do servidor; o modo 4 criptografa sem verificar certificados.",
|
||||
redisTlsInsecure: "Ignorar verificação de certificado",
|
||||
redisTlsInsecureHint: "Equivalente a redis-cli --tls --insecure, para certificados autoassinados ou CAs privadas.",
|
||||
mysqlTlsMode: "Modo TLS",
|
||||
|
|
|
|||
|
|
@ -252,6 +252,14 @@ export default withEnglishFallback({
|
|||
caCertPath: "CA 证书",
|
||||
caCertPathPlaceholder: "可选,例如 ~/.yandex/RootCA.crt",
|
||||
caCertPathBrowse: "选择证书",
|
||||
damengSslFilesPath: "证书目录",
|
||||
damengSslFilesPathPlaceholder: "/path/to/dmcert",
|
||||
damengSslFilesPathBrowse: "选择证书目录",
|
||||
damengSslKeystorePassword: "密钥库密码",
|
||||
damengSslHint: "DM8 JDBC 从该目录读取 SSL 证书,并通过 URL 参数传递这两项配置。",
|
||||
damengSslProtocol: "SSL 协议",
|
||||
damengSslProtocolPlaceholder: "可选,例如 TLSv1.2",
|
||||
damengSslVerificationHint: "证书验证方式由 DM8 服务端 ENABLE_ENCRYPT 决定:模式 5 验证服务端证书,模式 4 仅加密而不验证证书。",
|
||||
redisTlsInsecure: "跳过证书验证",
|
||||
redisTlsInsecureHint: "等价于 redis-cli --tls --insecure,适用于自签名证书或私有 CA。",
|
||||
mysqlTlsMode: "TLS 模式",
|
||||
|
|
|
|||
|
|
@ -237,6 +237,14 @@ export default withEnglishFallback({
|
|||
caCertPath: "CA 憑證",
|
||||
caCertPathPlaceholder: "可選,例如 ~/.yandex/RootCA.crt",
|
||||
caCertPathBrowse: "瀏覽憑證",
|
||||
damengSslFilesPath: "憑證目錄",
|
||||
damengSslFilesPathPlaceholder: "/path/to/dmcert",
|
||||
damengSslFilesPathBrowse: "選擇憑證目錄",
|
||||
damengSslKeystorePassword: "金鑰庫密碼",
|
||||
damengSslHint: "DM8 JDBC 會從此目錄讀取 SSL 憑證,並透過 URL 參數傳遞這兩項設定。",
|
||||
damengSslProtocol: "SSL 協定",
|
||||
damengSslProtocolPlaceholder: "可選,例如 TLSv1.2",
|
||||
damengSslVerificationHint: "憑證驗證方式由 DM8 伺服器端 ENABLE_ENCRYPT 決定:模式 5 驗證伺服器憑證,模式 4 僅加密而不驗證憑證。",
|
||||
redisTlsInsecure: "跳過憑證驗證",
|
||||
redisTlsInsecureHint: "等價於 redis-cli --tls --insecure,適用於自簽名憑證或私有 CA。",
|
||||
mysqlTlsMode: "TLS 模式",
|
||||
|
|
|
|||
15
apps/desktop/src/lib/__tests__/connection/connectionDialog.damengSsl.spec.ts
vendored
Normal file
15
apps/desktop/src/lib/__tests__/connection/connectionDialog.damengSsl.spec.ts
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import { readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const dialogSource = readFileSync(new URL("../../../components/connection/ConnectionDialog.vue", import.meta.url), "utf8");
|
||||
|
||||
describe("Dameng SSL connection form", () => {
|
||||
it("shows Dameng in the TLS tab with certificate directory and password fields", () => {
|
||||
expect(dialogSource).toContain('"questdb", "dameng", "redis"');
|
||||
expect(dialogSource).toContain("<template v-if=\"form.db_type === 'dameng'\">");
|
||||
expect(dialogSource).toContain('v-model="damengSslFilesPath"');
|
||||
expect(dialogSource).toContain('v-model="damengSslKeystorePassword"');
|
||||
expect(dialogSource).toContain('v-model="damengSslProtocol"');
|
||||
expect(dialogSource).toContain('t("connection.damengSslVerificationHint")');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { parseConnectionUrl } from "@/lib/connection/connectionUrl";
|
||||
|
||||
describe("Dameng connection URLs", () => {
|
||||
it("parses JDBC SSL parameters and enables the TLS form", () => {
|
||||
const parsed = parseConnectionUrl("jdbc:dm://dm.example.com:5236/MAIN?sslFilesPath=/Users/test/dmcert&sslkeystorePass=secret");
|
||||
|
||||
expect(parsed).toMatchObject({
|
||||
dbType: "dameng",
|
||||
driverProfile: "dm",
|
||||
host: "dm.example.com",
|
||||
port: 5236,
|
||||
database: "MAIN",
|
||||
urlParams: "sslFilesPath=/Users/test/dmcert&sslkeystorePass=secret",
|
||||
ssl: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { applyDamengSslUrlParams, damengSslFormConfig } from "@/lib/database/damengSslOptions";
|
||||
|
||||
describe("Dameng SSL URL parameters", () => {
|
||||
it("reads SSL fields without changing parameter casing requirements", () => {
|
||||
expect(damengSslFormConfig("schema=APP&sslFilesPath=/Users/test/dmcert&sslkeystorePass=secret&sslProtocol=TLSv1.2")).toEqual({
|
||||
enabled: true,
|
||||
sslFilesPath: "/Users/test/dmcert",
|
||||
sslKeystorePassword: "secret",
|
||||
sslProtocol: "TLSv1.2",
|
||||
});
|
||||
});
|
||||
|
||||
it("updates SSL fields while preserving unrelated URL parameters", () => {
|
||||
expect(applyDamengSslUrlParams("?schema=APP;compatMode=oracle", true, "/opt/dm/cert", "changeit", "TLSv1.2")).toBe("schema=APP&compatMode=oracle&sslFilesPath=/opt/dm/cert&sslkeystorePass=changeit&sslProtocol=TLSv1.2");
|
||||
});
|
||||
|
||||
it("removes only managed SSL fields when SSL is disabled", () => {
|
||||
expect(applyDamengSslUrlParams("schema=APP&sslFilesPath=/opt/dm/cert&sslkeystorePass=secret&sslProtocol=TLSv1.2&foo=", false, "", "", "")).toBe("schema=APP&foo=");
|
||||
});
|
||||
});
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import type { ConnectionConfig, DatabaseType } from "@/types/database";
|
||||
import { h2JdbcUrlHasPasswordParam, h2JdbcUrlHasUserParam, parseH2JdbcUrl } from "@/lib/database/h2Connection";
|
||||
import { damengSslFormConfig } from "@/lib/database/damengSslOptions";
|
||||
|
||||
export interface ParsedConnectionUrl {
|
||||
name?: string;
|
||||
|
|
@ -226,6 +227,10 @@ function extractMysqlCredentialParams(params: string): { username?: string; pass
|
|||
}
|
||||
|
||||
function urlParamsRequireTls(dbType: DatabaseType, params: string): boolean {
|
||||
if (dbType === "dameng") {
|
||||
return damengSslFormConfig(params).enabled;
|
||||
}
|
||||
|
||||
if (dbType === "mysql") {
|
||||
const requireSsl = queryParamValue(params, "require_ssl")?.toLowerCase();
|
||||
if (requireSsl === "true" || requireSsl === "1" || requireSsl === "yes") return true;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
export interface DamengSslFormConfig {
|
||||
enabled: boolean;
|
||||
sslFilesPath: string;
|
||||
sslKeystorePassword: string;
|
||||
sslProtocol: string;
|
||||
}
|
||||
|
||||
interface DamengUrlParam {
|
||||
key: string;
|
||||
value: string;
|
||||
raw: string;
|
||||
}
|
||||
|
||||
const SSL_FILES_PATH_KEY = "sslfilespath";
|
||||
const SSL_KEYSTORE_PASSWORD_KEY = "sslkeystorepass";
|
||||
const SSL_PROTOCOL_KEY = "sslprotocol";
|
||||
|
||||
export function damengSslFormConfig(urlParams?: string): DamengSslFormConfig {
|
||||
const params = parseDamengUrlParams(urlParams);
|
||||
const sslFilesPath = getDamengUrlParam(params, SSL_FILES_PATH_KEY);
|
||||
const sslKeystorePassword = getDamengUrlParam(params, SSL_KEYSTORE_PASSWORD_KEY);
|
||||
const sslProtocol = getDamengUrlParam(params, SSL_PROTOCOL_KEY);
|
||||
|
||||
return {
|
||||
enabled: params.some((param) => isManagedDamengSslParam(param.key)),
|
||||
sslFilesPath,
|
||||
sslKeystorePassword,
|
||||
sslProtocol,
|
||||
};
|
||||
}
|
||||
|
||||
export function applyDamengSslUrlParams(urlParams: string | undefined, enabled: boolean, sslFilesPath: string, sslKeystorePassword: string, sslProtocol: string): string {
|
||||
const parts = parseDamengUrlParams(urlParams)
|
||||
.filter((param) => !isManagedDamengSslParam(param.key))
|
||||
.map((param) => param.raw);
|
||||
|
||||
if (enabled) {
|
||||
const normalizedFilesPath = sslFilesPath.trim();
|
||||
const normalizedProtocol = sslProtocol.trim();
|
||||
if (normalizedFilesPath) parts.push(`sslFilesPath=${normalizedFilesPath}`);
|
||||
if (sslKeystorePassword) parts.push(`sslkeystorePass=${sslKeystorePassword}`);
|
||||
if (normalizedProtocol) parts.push(`sslProtocol=${normalizedProtocol}`);
|
||||
}
|
||||
|
||||
return parts.join("&");
|
||||
}
|
||||
|
||||
function parseDamengUrlParams(urlParams?: string): DamengUrlParam[] {
|
||||
return (urlParams || "")
|
||||
.trim()
|
||||
.replace(/^[?&;]+/, "")
|
||||
.replace(/[?&;]+$/, "")
|
||||
.split(/[&;]/)
|
||||
.map((part) => part.trim())
|
||||
.filter(Boolean)
|
||||
.map((raw) => {
|
||||
const equals = raw.indexOf("=");
|
||||
if (equals < 0) return { key: raw, value: "", raw };
|
||||
return {
|
||||
key: raw.slice(0, equals).trim(),
|
||||
value: raw.slice(equals + 1).trim(),
|
||||
raw,
|
||||
};
|
||||
})
|
||||
.filter((param) => !!param.key);
|
||||
}
|
||||
|
||||
function getDamengUrlParam(params: DamengUrlParam[], key: string): string {
|
||||
return params.find((param) => param.key.toLowerCase() === key)?.value || "";
|
||||
}
|
||||
|
||||
function isManagedDamengSslParam(key: string): boolean {
|
||||
const normalizedKey = key.trim().toLowerCase();
|
||||
return normalizedKey === SSL_FILES_PATH_KEY || normalizedKey === SSL_KEYSTORE_PASSWORD_KEY || normalizedKey === SSL_PROTOCOL_KEY;
|
||||
}
|
||||
Loading…
Reference in New Issue