From 071dc0ddee0dfdabab8d8d6e283fb7aabd296479 Mon Sep 17 00:00:00 2001 From: mac Date: Wed, 13 May 2026 17:06:23 -0700 Subject: [PATCH 1/2] fix: default MSSQL SSL mode to DISABLE to prevent handshake failures MSSQL connections failed with SSL handshake errors when connecting to servers that do not require encryption. The root cause was that the SSL mode defaulted to NO_VERIFY (encrypt=true), forcing a TLS handshake. Changes: - Set default SSL mode to DISABLE (encrypt=false) in form.json - Handle missing SSL config gracefully by defaulting to encrypt=false instead of throwing an exception Users who need SSL can still explicitly select "Enabled with no verify". Fixes #41627 Co-Authored-By: Claude Opus 4.6 (1M context) --- .../main/java/com/external/plugins/MssqlPlugin.java | 12 ++++-------- .../mssqlPlugin/src/main/resources/form.json | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/app/server/appsmith-plugins/mssqlPlugin/src/main/java/com/external/plugins/MssqlPlugin.java b/app/server/appsmith-plugins/mssqlPlugin/src/main/java/com/external/plugins/MssqlPlugin.java index c546fa03c856..9c3247428a64 100644 --- a/app/server/appsmith-plugins/mssqlPlugin/src/main/java/com/external/plugins/MssqlPlugin.java +++ b/app/server/appsmith-plugins/mssqlPlugin/src/main/java/com/external/plugins/MssqlPlugin.java @@ -643,20 +643,16 @@ private static HikariDataSource createConnectionPool( private static void addSslOptionsToUrlBuilder( DatasourceConfiguration datasourceConfiguration, StringBuilder urlBuilder) throws AppsmithPluginException { /* - * - Ideally, it is never expected to be null because the SSL dropdown is set to a initial value. + * - When SSL configuration is absent, default to disabling encryption so that + * connections to servers that do not require encryption work out of the box. */ if (datasourceConfiguration.getConnection() == null || datasourceConfiguration.getConnection().getSsl() == null || datasourceConfiguration.getConnection().getSsl().getAuthType() == null) { - throw new AppsmithPluginException( - AppsmithPluginError.PLUGIN_ERROR, - "Appsmith server has failed to fetch SSL configuration from datasource configuration form. " - + "Please reach out to Appsmith customer support to resolve this."); + urlBuilder.append("encrypt=false;"); + return; } - /* - * - By default, the driver configures SSL in the no verify mode. - */ SSLDetails.AuthType sslAuthType = datasourceConfiguration.getConnection().getSsl().getAuthType(); switch (sslAuthType) { diff --git a/app/server/appsmith-plugins/mssqlPlugin/src/main/resources/form.json b/app/server/appsmith-plugins/mssqlPlugin/src/main/resources/form.json index 7731ac9421aa..9cacb47d1b7b 100644 --- a/app/server/appsmith-plugins/mssqlPlugin/src/main/resources/form.json +++ b/app/server/appsmith-plugins/mssqlPlugin/src/main/resources/form.json @@ -82,7 +82,7 @@ "label": "SSL mode", "configProperty": "datasourceConfiguration.connection.ssl.authType", "controlType": "DROP_DOWN", - "initialValue": "NO_VERIFY", + "initialValue": "DISABLE", "options": [ { "label": "Disable", From d6d977171d6c437033417ff3bfad9c3883316a01 Mon Sep 17 00:00:00 2001 From: mac Date: Mon, 8 Jun 2026 16:39:29 -0700 Subject: [PATCH 2/2] feat: add configurable connection timeout for MSSQL datasource --- .../com/external/plugins/MssqlPlugin.java | 31 +++++ .../exceptions/MssqlErrorMessages.java | 3 + .../mssqlPlugin/src/main/resources/form.json | 15 +++ .../com/external/plugins/MssqlPluginTest.java | 112 ++++++++++++++++++ 4 files changed, 161 insertions(+) diff --git a/app/server/appsmith-plugins/mssqlPlugin/src/main/java/com/external/plugins/MssqlPlugin.java b/app/server/appsmith-plugins/mssqlPlugin/src/main/java/com/external/plugins/MssqlPlugin.java index 9c3247428a64..58ecfc0293a8 100644 --- a/app/server/appsmith-plugins/mssqlPlugin/src/main/java/com/external/plugins/MssqlPlugin.java +++ b/app/server/appsmith-plugins/mssqlPlugin/src/main/java/com/external/plugins/MssqlPlugin.java @@ -102,6 +102,8 @@ public class MssqlPlugin extends BasePlugin { private static final long MS_SQL_DEFAULT_PORT = 1433L; + private static final int DEFAULT_CONNECTION_TIMEOUT_SECONDS = 60; + public static final MssqlDatasourceUtils mssqlDatasourceUtils = new MssqlDatasourceUtils(); public MssqlPlugin(PluginWrapper wrapper) { @@ -417,6 +419,11 @@ public Set validateDatasource(@NonNull DatasourceConfiguration datasourc } } + int connectionTimeout = getConnectionTimeoutSeconds(datasourceConfiguration); + if (connectionTimeout < 0) { + invalids.add(MssqlErrorMessages.DS_INVALID_CONNECTION_TIMEOUT_ERROR_MSG); + } + return invalids; } @@ -626,6 +633,10 @@ private static HikariDataSource createConnectionPool( addSslOptionsToUrlBuilder(datasourceConfiguration, urlBuilder); + int timeoutSeconds = getConnectionTimeoutSeconds(datasourceConfiguration); + urlBuilder.append("loginTimeout=").append(timeoutSeconds).append(";"); + hikariConfig.setConnectionTimeout(timeoutSeconds * 1000L); + hikariConfig.setJdbcUrl(urlBuilder.toString()); try { @@ -640,6 +651,26 @@ private static HikariDataSource createConnectionPool( return hikariDatasource; } + /** + * Extracts the connection timeout (in seconds) from the datasource properties. + * Returns the default timeout if no value is configured or the value is not a valid number. + */ + static int getConnectionTimeoutSeconds(DatasourceConfiguration datasourceConfiguration) { + List properties = datasourceConfiguration.getProperties(); + if (properties != null && !properties.isEmpty()) { + Property timeoutProperty = properties.get(0); + if (timeoutProperty != null && timeoutProperty.getValue() != null) { + try { + return Integer.parseInt( + String.valueOf(timeoutProperty.getValue()).trim()); + } catch (NumberFormatException e) { + // fall through to default + } + } + } + return DEFAULT_CONNECTION_TIMEOUT_SECONDS; + } + private static void addSslOptionsToUrlBuilder( DatasourceConfiguration datasourceConfiguration, StringBuilder urlBuilder) throws AppsmithPluginException { /* diff --git a/app/server/appsmith-plugins/mssqlPlugin/src/main/java/com/external/plugins/exceptions/MssqlErrorMessages.java b/app/server/appsmith-plugins/mssqlPlugin/src/main/java/com/external/plugins/exceptions/MssqlErrorMessages.java index 6be7871af6d2..39da23c5d67f 100644 --- a/app/server/appsmith-plugins/mssqlPlugin/src/main/java/com/external/plugins/exceptions/MssqlErrorMessages.java +++ b/app/server/appsmith-plugins/mssqlPlugin/src/main/java/com/external/plugins/exceptions/MssqlErrorMessages.java @@ -34,4 +34,7 @@ public class MssqlErrorMessages extends BasePluginErrorMessages { public static final String DS_MISSING_USERNAME_ERROR_MSG = "Missing username for authentication."; public static final String DS_MISSING_PASSWORD_ERROR_MSG = "Missing password for authentication."; + + public static final String DS_INVALID_CONNECTION_TIMEOUT_ERROR_MSG = + "Connection timeout must be a non-negative number (in seconds)."; } diff --git a/app/server/appsmith-plugins/mssqlPlugin/src/main/resources/form.json b/app/server/appsmith-plugins/mssqlPlugin/src/main/resources/form.json index 9cacb47d1b7b..87b416138d1b 100644 --- a/app/server/appsmith-plugins/mssqlPlugin/src/main/resources/form.json +++ b/app/server/appsmith-plugins/mssqlPlugin/src/main/resources/form.json @@ -47,6 +47,21 @@ "controlType": "INPUT_TEXT", "placeholderText": "Database name", "initialValue": "admin" + }, + { + "label": "Connection timeout (seconds)", + "configProperty": "datasourceConfiguration.properties[0].key", + "initialValue": "connectionTimeout", + "hidden": true, + "controlType": "INPUT_TEXT" + }, + { + "label": "Connection timeout (seconds)", + "configProperty": "datasourceConfiguration.properties[0].value", + "controlType": "INPUT_TEXT", + "dataType": "NUMBER", + "initialValue": "60", + "placeholderText": "60" } ] }, diff --git a/app/server/appsmith-plugins/mssqlPlugin/src/test/java/com/external/plugins/MssqlPluginTest.java b/app/server/appsmith-plugins/mssqlPlugin/src/test/java/com/external/plugins/MssqlPluginTest.java index 7be16681867a..52d5f035a15e 100755 --- a/app/server/appsmith-plugins/mssqlPlugin/src/test/java/com/external/plugins/MssqlPluginTest.java +++ b/app/server/appsmith-plugins/mssqlPlugin/src/test/java/com/external/plugins/MssqlPluginTest.java @@ -15,6 +15,7 @@ import com.appsmith.external.models.PsParameterDTO; import com.appsmith.external.models.RequestParamDTO; import com.appsmith.external.models.SSLDetails; +import com.external.plugins.exceptions.MssqlErrorMessages; import com.external.plugins.exceptions.MssqlPluginError; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -773,4 +774,115 @@ public void testGetEndpointIdentifierForRateLimit_HostPresentPortAbsent_ReturnsC }) .verifyComplete(); } + + @Test + public void testSslDefaultsToDisable_whenConnectionIsNull() { + DatasourceConfiguration dsConfig = createDatasourceConfiguration(container); + dsConfig.setConnection(null); + + Mono dsConnectionMono = mssqlPluginExecutor.datasourceCreate(dsConfig); + + StepVerifier.create(dsConnectionMono) + .assertNext(hikariDataSource -> { + assertNotNull(hikariDataSource); + assertTrue(hikariDataSource.getJdbcUrl().contains("encrypt=false")); + }) + .verifyComplete(); + } + + @Test + public void testSslDefaultsToDisable_whenSslDetailsAreNull() { + DatasourceConfiguration dsConfig = createDatasourceConfiguration(container); + dsConfig.getConnection().setSsl(null); + + Mono dsConnectionMono = mssqlPluginExecutor.datasourceCreate(dsConfig); + + StepVerifier.create(dsConnectionMono) + .assertNext(hikariDataSource -> { + assertNotNull(hikariDataSource); + assertTrue(hikariDataSource.getJdbcUrl().contains("encrypt=false")); + }) + .verifyComplete(); + } + + @Test + public void testSslDefaultsToDisable_whenAuthTypeIsNull() { + DatasourceConfiguration dsConfig = createDatasourceConfiguration(container); + dsConfig.getConnection().getSsl().setAuthType(null); + + Mono dsConnectionMono = mssqlPluginExecutor.datasourceCreate(dsConfig); + + StepVerifier.create(dsConnectionMono) + .assertNext(hikariDataSource -> { + assertNotNull(hikariDataSource); + assertTrue(hikariDataSource.getJdbcUrl().contains("encrypt=false")); + }) + .verifyComplete(); + } + + @Test + public void testConnectionTimeout_defaultAppliedWhenNoPropertySet() { + DatasourceConfiguration dsConfig = createDatasourceConfiguration(container); + // No properties set — default timeout should be used + + Mono dsConnectionMono = mssqlPluginExecutor.datasourceCreate(dsConfig); + + StepVerifier.create(dsConnectionMono) + .assertNext(hikariDataSource -> { + assertNotNull(hikariDataSource); + String jdbcUrl = hikariDataSource.getJdbcUrl(); + assertTrue( + jdbcUrl.contains("loginTimeout=60"), + "JDBC URL should contain default loginTimeout=60 but was: " + jdbcUrl); + assertEquals( + 60000L, + hikariDataSource.getConnectionTimeout(), + "HikariCP connectionTimeout should be 60000ms"); + }) + .verifyComplete(); + } + + @Test + public void testConnectionTimeout_customValueApplied() { + DatasourceConfiguration dsConfig = createDatasourceConfiguration(container); + dsConfig.setProperties(List.of(new Property("connectionTimeout", "30"))); + + Mono dsConnectionMono = mssqlPluginExecutor.datasourceCreate(dsConfig); + + StepVerifier.create(dsConnectionMono) + .assertNext(hikariDataSource -> { + assertNotNull(hikariDataSource); + String jdbcUrl = hikariDataSource.getJdbcUrl(); + assertTrue( + jdbcUrl.contains("loginTimeout=30"), + "JDBC URL should contain loginTimeout=30 but was: " + jdbcUrl); + assertEquals( + 30000L, + hikariDataSource.getConnectionTimeout(), + "HikariCP connectionTimeout should be 30000ms"); + }) + .verifyComplete(); + } + + @Test + public void testConnectionTimeout_negativeValueFailsValidation() { + DatasourceConfiguration dsConfig = createDatasourceConfiguration(container); + dsConfig.setProperties(List.of(new Property("connectionTimeout", "-5"))); + + Set invalids = mssqlPluginExecutor.validateDatasource(dsConfig); + assertTrue( + invalids.contains(MssqlErrorMessages.DS_INVALID_CONNECTION_TIMEOUT_ERROR_MSG), + "Validation should reject negative timeout values"); + } + + @Test + public void testConnectionTimeout_zeroValuePassesValidation() { + DatasourceConfiguration dsConfig = createDatasourceConfiguration(container); + dsConfig.setProperties(List.of(new Property("connectionTimeout", "0"))); + + Set invalids = mssqlPluginExecutor.validateDatasource(dsConfig); + assertTrue( + !invalids.contains(MssqlErrorMessages.DS_INVALID_CONNECTION_TIMEOUT_ERROR_MSG), + "Validation should accept zero timeout (meaning no timeout)"); + } }