From e9be1ca10430b92a881eb9c5b0836cfc84ad2868 Mon Sep 17 00:00:00 2001 From: Mark Otter Date: Tue, 9 Jul 2024 15:46:19 +0200 Subject: [PATCH] Added support for supplying a custom CertificateValidationCallback on the connection --- .../NoPublicApiChanges.Run.DotNet.verified.cs | 9 +++ .../NoPublicApiChanges.Run.Net.verified.cs | 9 +++ .../PostgresqlConnectionManager.cs | 16 ++++- .../PostgresqlConnectionOptions.cs | 22 +++++++ src/dbup-postgresql/PostgresqlExtensions.cs | 59 ++++++++++++++++--- 5 files changed, 105 insertions(+), 10 deletions(-) create mode 100644 src/dbup-postgresql/PostgresqlConnectionOptions.cs diff --git a/src/Tests/ApprovalFiles/NoPublicApiChanges.Run.DotNet.verified.cs b/src/Tests/ApprovalFiles/NoPublicApiChanges.Run.DotNet.verified.cs index 165f98e..c35ea89 100644 --- a/src/Tests/ApprovalFiles/NoPublicApiChanges.Run.DotNet.verified.cs +++ b/src/Tests/ApprovalFiles/NoPublicApiChanges.Run.DotNet.verified.cs @@ -11,8 +11,10 @@ public static DbUp.Builder.UpgradeEngineBuilder PostgresqlDatabase(this DbUp.Bui public static DbUp.Builder.UpgradeEngineBuilder PostgresqlDatabase(DbUp.Engine.Transactions.IConnectionManager connectionManager, string schema) { } public static DbUp.Builder.UpgradeEngineBuilder PostgresqlDatabase(this DbUp.Builder.SupportedDatabases supported, string connectionString, string schema) { } public static DbUp.Builder.UpgradeEngineBuilder PostgresqlDatabase(this DbUp.Builder.SupportedDatabases supported, string connectionString, string schema, System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) { } + public static DbUp.Builder.UpgradeEngineBuilder PostgresqlDatabase(this DbUp.Builder.SupportedDatabases supported, string connectionString, string schema, DbUp.Postgresql.PostgresqlConnectionOptions connectionOptions) { } public static void PostgresqlDatabase(this DbUp.SupportedDatabasesForEnsureDatabase supported, string connectionString) { } public static void PostgresqlDatabase(this DbUp.SupportedDatabasesForEnsureDatabase supported, string connectionString, System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) { } + public static void PostgresqlDatabase(this DbUp.SupportedDatabasesForEnsureDatabase supported, string connectionString, DbUp.Postgresql.PostgresqlConnectionOptions connectionOptions) { } public static void PostgresqlDatabase(this DbUp.SupportedDatabasesForEnsureDatabase supported, string connectionString, DbUp.Engine.Output.IUpgradeLog logger) { } } namespace DbUp.Postgresql @@ -21,8 +23,15 @@ public class PostgresqlConnectionManager : DbUp.Engine.Transactions.DatabaseConn { public PostgresqlConnectionManager(string connectionString) { } public PostgresqlConnectionManager(string connectionString, System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) { } + public PostgresqlConnectionManager(string connectionString, DbUp.Postgresql.PostgresqlConnectionOptions connectionOptions) { } public override System.Collections.Generic.IEnumerable SplitScriptIntoCommands(string scriptContents) { } } + public class PostgresqlConnectionOptions + { + public PostgresqlConnectionOptions() { } + public System.Security.Cryptography.X509Certificates.X509Certificate2 ClientCertificate { get; set; } + public System.Net.Security.RemoteCertificateValidationCallback UserCertificateValidationCallback { get; set; } + } public class PostgresqlObjectParser : DbUp.Support.SqlObjectParser, DbUp.Engine.ISqlObjectParser { public PostgresqlObjectParser() { } diff --git a/src/Tests/ApprovalFiles/NoPublicApiChanges.Run.Net.verified.cs b/src/Tests/ApprovalFiles/NoPublicApiChanges.Run.Net.verified.cs index 165f98e..c35ea89 100644 --- a/src/Tests/ApprovalFiles/NoPublicApiChanges.Run.Net.verified.cs +++ b/src/Tests/ApprovalFiles/NoPublicApiChanges.Run.Net.verified.cs @@ -11,8 +11,10 @@ public static DbUp.Builder.UpgradeEngineBuilder PostgresqlDatabase(this DbUp.Bui public static DbUp.Builder.UpgradeEngineBuilder PostgresqlDatabase(DbUp.Engine.Transactions.IConnectionManager connectionManager, string schema) { } public static DbUp.Builder.UpgradeEngineBuilder PostgresqlDatabase(this DbUp.Builder.SupportedDatabases supported, string connectionString, string schema) { } public static DbUp.Builder.UpgradeEngineBuilder PostgresqlDatabase(this DbUp.Builder.SupportedDatabases supported, string connectionString, string schema, System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) { } + public static DbUp.Builder.UpgradeEngineBuilder PostgresqlDatabase(this DbUp.Builder.SupportedDatabases supported, string connectionString, string schema, DbUp.Postgresql.PostgresqlConnectionOptions connectionOptions) { } public static void PostgresqlDatabase(this DbUp.SupportedDatabasesForEnsureDatabase supported, string connectionString) { } public static void PostgresqlDatabase(this DbUp.SupportedDatabasesForEnsureDatabase supported, string connectionString, System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) { } + public static void PostgresqlDatabase(this DbUp.SupportedDatabasesForEnsureDatabase supported, string connectionString, DbUp.Postgresql.PostgresqlConnectionOptions connectionOptions) { } public static void PostgresqlDatabase(this DbUp.SupportedDatabasesForEnsureDatabase supported, string connectionString, DbUp.Engine.Output.IUpgradeLog logger) { } } namespace DbUp.Postgresql @@ -21,8 +23,15 @@ public class PostgresqlConnectionManager : DbUp.Engine.Transactions.DatabaseConn { public PostgresqlConnectionManager(string connectionString) { } public PostgresqlConnectionManager(string connectionString, System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) { } + public PostgresqlConnectionManager(string connectionString, DbUp.Postgresql.PostgresqlConnectionOptions connectionOptions) { } public override System.Collections.Generic.IEnumerable SplitScriptIntoCommands(string scriptContents) { } } + public class PostgresqlConnectionOptions + { + public PostgresqlConnectionOptions() { } + public System.Security.Cryptography.X509Certificates.X509Certificate2 ClientCertificate { get; set; } + public System.Net.Security.RemoteCertificateValidationCallback UserCertificateValidationCallback { get; set; } + } public class PostgresqlObjectParser : DbUp.Support.SqlObjectParser, DbUp.Engine.ISqlObjectParser { public PostgresqlObjectParser() { } diff --git a/src/dbup-postgresql/PostgresqlConnectionManager.cs b/src/dbup-postgresql/PostgresqlConnectionManager.cs index 8a10b3d..098d1fc 100644 --- a/src/dbup-postgresql/PostgresqlConnectionManager.cs +++ b/src/dbup-postgresql/PostgresqlConnectionManager.cs @@ -27,11 +27,23 @@ public PostgresqlConnectionManager(string connectionString) /// The PostgreSQL connection string. /// Certificate for securing connection. public PostgresqlConnectionManager(string connectionString, X509Certificate2 certificate) + : this(connectionString, new PostgresqlConnectionOptions + { + ClientCertificate = certificate + }) + { + } + + /// + /// Create a new PostgreSQL database connection + /// + /// The PostgreSQL connection string. + /// Custom options to apply on the created connection + public PostgresqlConnectionManager(string connectionString, PostgresqlConnectionOptions connectionOptions) : base(new DelegateConnectionFactory(l => { NpgsqlConnection databaseConnection = new NpgsqlConnection(connectionString); - databaseConnection.ProvideClientCertificatesCallback += - certs => certs.Add(certificate); + databaseConnection.ApplyConnectionOptions(connectionOptions); return databaseConnection; } diff --git a/src/dbup-postgresql/PostgresqlConnectionOptions.cs b/src/dbup-postgresql/PostgresqlConnectionOptions.cs new file mode 100644 index 0000000..e503223 --- /dev/null +++ b/src/dbup-postgresql/PostgresqlConnectionOptions.cs @@ -0,0 +1,22 @@ +using System.Net.Security; +using System.Security.Cryptography.X509Certificates; + +namespace DbUp.Postgresql +{ + /// + /// Options that will be applied on the created connection + /// + public class PostgresqlConnectionOptions + { + /// + /// Certificate for securing connection. + /// + public X509Certificate2 ClientCertificate { get; set; } + + /// + // Custom handler to verify the remote SSL certificate. + // Ignored if Npgsql.NpgsqlConnectionStringBuilder.TrustServerCertificate is set. + /// + public RemoteCertificateValidationCallback UserCertificateValidationCallback { get; set; } + } +} diff --git a/src/dbup-postgresql/PostgresqlExtensions.cs b/src/dbup-postgresql/PostgresqlExtensions.cs index 8ff517a..8a5a77c 100644 --- a/src/dbup-postgresql/PostgresqlExtensions.cs +++ b/src/dbup-postgresql/PostgresqlExtensions.cs @@ -51,6 +51,19 @@ public static UpgradeEngineBuilder PostgresqlDatabase(this SupportedDatabases su public static UpgradeEngineBuilder PostgresqlDatabase(this SupportedDatabases supported, string connectionString, string schema, X509Certificate2 certificate) => PostgresqlDatabase(new PostgresqlConnectionManager(connectionString, certificate), schema); + /// + /// Creates an upgrader for PostgreSQL databases that use SSL. + /// + /// Fluent helper type. + /// PostgreSQL database connection string. + /// The schema in which to check for changes + /// Connection options to set SSL parameters + /// + /// A builder for a database upgrader designed for PostgreSQL databases. + /// + public static UpgradeEngineBuilder PostgresqlDatabase(this SupportedDatabases supported, string connectionString, string schema, PostgresqlConnectionOptions connectionOptions) + => PostgresqlDatabase(new PostgresqlConnectionManager(connectionString, connectionOptions), schema); + /// /// Creates an upgrader for PostgreSQL databases. /// @@ -113,6 +126,18 @@ public static void PostgresqlDatabase(this SupportedDatabasesForEnsureDatabase s PostgresqlDatabase(supported, connectionString, new ConsoleUpgradeLog(), certificate); } + /// + /// Ensures that the database specified in the connection string exists using SSL for the connection. + /// + /// Fluent helper type. + /// The connection string. + /// Connection SSL to customize SSL behaviour + /// + public static void PostgresqlDatabase(this SupportedDatabasesForEnsureDatabase supported, string connectionString, PostgresqlConnectionOptions connectionOptions) + { + PostgresqlDatabase(supported, connectionString, new ConsoleUpgradeLog(), connectionOptions); + } + /// /// Ensures that the database specified in the connection string exists. /// @@ -122,10 +147,19 @@ public static void PostgresqlDatabase(this SupportedDatabasesForEnsureDatabase s /// public static void PostgresqlDatabase(this SupportedDatabasesForEnsureDatabase supported, string connectionString, IUpgradeLog logger) { - PostgresqlDatabase(supported, connectionString, logger, null); + PostgresqlDatabase(supported, connectionString, logger, (PostgresqlConnectionOptions)null); } - + private static void PostgresqlDatabase(this SupportedDatabasesForEnsureDatabase supported, string connectionString, IUpgradeLog logger, X509Certificate2 certificate) + { + var options = new PostgresqlConnectionOptions + { + ClientCertificate = certificate + }; + PostgresqlDatabase(supported, connectionString, logger, options); + } + + private static void PostgresqlDatabase(this SupportedDatabasesForEnsureDatabase supported, string connectionString, IUpgradeLog logger, PostgresqlConnectionOptions connectionOptions) { if (supported == null) throw new ArgumentNullException("supported"); @@ -137,7 +171,7 @@ private static void PostgresqlDatabase(this SupportedDatabasesForEnsureDatabase if (logger == null) throw new ArgumentNullException("logger"); var masterConnectionStringBuilder = new NpgsqlConnectionStringBuilder(connectionString); - + var databaseName = masterConnectionStringBuilder.Database; if (string.IsNullOrEmpty(databaseName) || databaseName.Trim() == string.Empty) @@ -157,11 +191,7 @@ private static void PostgresqlDatabase(this SupportedDatabasesForEnsureDatabase using (var connection = new NpgsqlConnection(masterConnectionStringBuilder.ConnectionString)) { - if (certificate != null) - { - connection.ProvideClientCertificatesCallback += - certs => certs.Add(certificate); - } + connection.ApplyConnectionOptions(connectionOptions); connection.Open(); var sqlCommandText = string.Format @@ -216,4 +246,17 @@ public static UpgradeEngineBuilder JournalToPostgresqlTable(this UpgradeEngineBu builder.Configure(c => c.Journal = new PostgresqlTableJournal(() => c.ConnectionManager, () => c.Log, schema, table)); return builder; } + + internal static void ApplyConnectionOptions(this NpgsqlConnection connection, PostgresqlConnectionOptions connectionOptions) + { + if (connectionOptions?.ClientCertificate != null) + { + connection.ProvideClientCertificatesCallback += + certs => certs.Add(connectionOptions.ClientCertificate); + } + if (connectionOptions?.UserCertificateValidationCallback != null) + { + connection.UserCertificateValidationCallback = connectionOptions.UserCertificateValidationCallback; + } + } }