diff --git a/lib/core/database/postgres_connection.dart b/lib/core/database/postgres_connection.dart index 8507de0..e008370 100644 --- a/lib/core/database/postgres_connection.dart +++ b/lib/core/database/postgres_connection.dart @@ -160,7 +160,7 @@ class PostgresConnection { if ((sslRootCert != null && sslRootCert!.trim().isNotEmpty) || (sslCert != null && sslCert!.trim().isNotEmpty) || (sslKey != null && sslKey!.trim().isNotEmpty)) { - securityContext = SecurityContext(); + securityContext = SecurityContext(withTrustedRoots: true); if (sslCert != null && sslCert!.trim().isNotEmpty) { securityContext.useCertificateChain(sslCert!.trim()); } diff --git a/lib/core/security/ssl_certificate_support.dart b/lib/core/security/ssl_certificate_support.dart index d97d63f..15dc022 100644 --- a/lib/core/security/ssl_certificate_support.dart +++ b/lib/core/security/ssl_certificate_support.dart @@ -88,7 +88,7 @@ Uri syncSslParamsIntoUri(String uriText, SslCertificatePaths paths) { SecurityContext? buildSecurityContext(SslCertificatePaths paths) { if (!paths.hasAny) return null; - final context = SecurityContext(); + final context = SecurityContext(withTrustedRoots: true); if (SslCertificatePaths._nonEmpty(paths.clientCert)) { context.useCertificateChain(paths.clientCert!.trim()); } diff --git a/test/core/security/ssl_certificate_support_test.dart b/test/core/security/ssl_certificate_support_test.dart index e96886c..239e545 100644 --- a/test/core/security/ssl_certificate_support_test.dart +++ b/test/core/security/ssl_certificate_support_test.dart @@ -109,5 +109,91 @@ void main() { expect(await orphanDir.exists(), isFalse); }); + + test('buildSecurityContext returns null when paths.hasAny is false', () { + const paths = SslCertificatePaths(); + final ctx = buildSecurityContext(paths); + expect(ctx, isNull); + }); + + const validCertPem = '''-----BEGIN CERTIFICATE----- +MIIDCTCCAfGgAwIBAgIUaf9MPYX80yFJlevK8mydpFPRUSUwDQYJKoZIhvcNAQEL +BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI2MDkyNTA3MjQzMVoXDTI3MDky +NTA3MjQzMVowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAsqAeJVJ6+kJnPFp9Bzjm8pjxTLOSJ0DvkJ+BsHF/rW0H +hDU5rFY6xgMUyM4Y2bXBa+BVSpnhzVEr6zOr5hebDq7fbTrtL3YSiCtxEVqlqjZg +njHl9/A90162YvbR1FOOshYGcARbP/hWsuJUNJRzX9Loqk+DDGHG1HdYF8ra81aQ +naWiImvXGWD6oQYG0HlSgzhMsFJrYXxc04z/PA8MTKiKtiEhNttyrexp7MEL7zMu +xD11Nwmxsi77p1IJ88ijri73ck9Rogz0LwhY/SUM8JAN8bCX+HaHcwH15SrXY9Yy +ec6gKXM4U1Jr0ZQ3An1eUNVg4SWpLbbn9oxrBms2vwIDAQABo1MwUTAdBgNVHQ4E +FgQUz0yjkIl5wxdfbvjBtmOTokQgCM0wHwYDVR0jBBgwFoAUz0yjkIl5wxdfbvjB +tmOTokQgCM0wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAlt5X +t/a9GmtPaw8cGZ4Y/xGT3aFoAQ+TBHLY+pDRR7erIEmWhwqf33uW+67DKW9jldeN +NofYn0sO4b4pshqBxhhcbnOWW+z37+njqdd/xUZ30siSDAf4FhwnaVNdPhAjo5uP ++UQ8dpr4NH9RKcNf0PDUmO1WCgjTIa1/XMqOQ5e4qfz+bbsQNVlAAXbXP6jJ3XmY +opXp+c8iH9E/Uv/wr9P5biqY9BzcrLciA08kUZBl4gF9dgBqsUQeSOnXOMeHNY2j +Bq/8MW8ajmUnR+/RHEOQNdVPSpXfeiyhHC1yC0RClPs0jYaEMboczJ/Z0AbagDuv +3dcW4xSuDp00JS6xLg== +-----END CERTIFICATE-----'''; + + const validKeyPem = '''-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCyoB4lUnr6Qmc8 +Wn0HOObymPFMs5InQO+Qn4GwcX+tbQeENTmsVjrGAxTIzhjZtcFr4FVKmeHNUSvr +M6vmF5sOrt9tOu0vdhKIK3ERWqWqNmCeMeX38D3TXrZi9tHUU46yFgZwBFs/+Fay +4lQ0lHNf0uiqT4MMYcbUd1gXytrzVpCdpaIia9cZYPqhBgbQeVKDOEywUmthfFzT +jP88DwxMqIq2ISE223Kt7GnswQvvMy7EPXU3CbGyLvunUgnzyKOuLvdyT1GiDPQv +CFj9JQzwkA3xsJf4dodzAfXlKtdj1jJ5zqApczhTUmvRlDcCfV5Q1WDhJakttuf2 +jGsGaza/AgMBAAECggEAAl3Kz02fyHMa5bq3uuHV4o67bVcz8/h1wbrMX6490UWQ +bP+88O4IXycJJ4Zwlo3EXP/+vvtVC9Lr92JayUbG7JgWVl7BZ8rqJTirQJT16nT+ +9kBoQypBGtqIt5gzoLZlz+gEXNDTGhMQg4rhCyD+VgIlo1tkco2d6OvyZppKTUWp +kXmRE3I4BR7hinTGcKGq2b5GHb408ZumCgjgZfwtGHdqhQzoRw/j3GZV4r+7p/Y8 +ZmBtonapsHNicgGn0NK488hkTIk2G8kzww5Xuo4UFr4ZEmy6dGLCGOGDzwTBTBho +uoiSapd9Xi/F4UBH9sjMn6UJBn/3AzUD6tCl2WA0HQKBgQDd6Q+zK2p5mSZZSaGb +RyGXk0y9fGurQ02sB/fOWEjzia85LrkmdC7BZykdgwJ2iOJaYYXTpgpMqgf1nCRq +S8WbC5ik0NclE7SYnYnTc5gW6QjU1cHX0QOzjzu58DPUc2ZBu2sQlV5Ffwhzsr+I +o2uBxDlSavHexY2GPnUMmMmiCwKBgQDOENC+HrQEX+3bOUQELJQDTx9j+uChNmAp +jHynO1iDGaM+uyd6NhqtGuXBPHhEUcVMFfp/VOKfO5q+ckO6LcLHXVduQ21+1+JA +vo47z5K14ynZkYN1NJdaXgCKBW/k+JG7hId2zeFxzEPiSzj+7rxGRbvPE/RJhjU5 +P0kb/nRCnQKBgECsV+MD2DgwJjkHeI3koSmnyEnBJS/4oX5tpA8DM9+mVOb5cwR9 +/9Jl2lm7gNBC/JUSrwoL7hyBwWgXZZWFF8YkDwyZwNoRcCS6ZRy3J7AlomlFEwVu +6QE/0UxTcQeNylOF56IhpiPi7feqNKAB4KclJP+cI3mlYaWqNjrBnKIZAoGAXVWY +dsSJXQHmRkll2U4nrGgGG87iN6LdY3RScZybtqXCHwO+GcivxIBOWHv/LVKsPo4l +686S5vSkXmZ67rUTaCGLHFJGIhG+VPz6h3S5StEdf4I9PLUZaMDzFZDo4ZkEyR56 +DQGrf1O526GeqzmO5XVoX572Iuc67DcR8jAKkLECgYAkPigDPHZ5d5kFcS/YEya2 +Rew/doODqiEstN7GO89KNLi1dDbuUOljs8ii2WY71XE7KQFBN2Fv4qVxdl3AfgnJ +kbu2ounmhZaVEbvHOw1zCcCYo25vBxcAa8zoBX2z2CAfN7/vkdOHKVDPr+wSnwaS +eQ6/4mA+Eqf0w+s+IOflBw== +-----END PRIVATE KEY-----'''; + + test('buildSecurityContext initializes SecurityContext with system trusted roots', () async { + final testDir = await Directory.systemTemp.createTemp('querya_sec_ctx_'); + final certFile = File('${testDir.path}/client.crt'); + final keyFile = File('${testDir.path}/client.key'); + await certFile.writeAsString(validCertPem); + await keyFile.writeAsString(validKeyPem); + + final paths = SslCertificatePaths( + clientCert: certFile.path, + clientKey: keyFile.path, + ); + final ctx = buildSecurityContext(paths); + expect(ctx, isNotNull); + + await testDir.delete(recursive: true); + }); + + test('buildSecurityContext applies custom root CA certificate', () async { + final testDir = await Directory.systemTemp.createTemp('querya_sec_ctx_root_'); + final rootFile = File('${testDir.path}/root.crt'); + await rootFile.writeAsString(validCertPem); + + final paths = SslCertificatePaths( + rootCert: rootFile.path, + ); + final ctx = buildSecurityContext(paths); + expect(ctx, isNotNull); + + await testDir.delete(recursive: true); + }); }); }