diff --git a/CHANGELOG.md b/CHANGELOG.md index c2eddec..e21f8fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +## v1.6.0 (July 2, 2025) + +### Added: +- Db Credentials file feature. +- This feature allows a database connection to be created in cases where database credentials need to be read from a file. Currently only active on PostgreSQL. +- It can be activated with the new config value `UseDbCredentialsFile`. +- This feature requires a new config section called `DbCredentialsFileSettings` with the following options: + - `FileName`: File name containing database credentials (should be full path) + - `Host`: Host information of the database + - `Database`: Db name of the database + - `ApplicationName`: App name information on database connection + - `Port`: Port of the database + - `AdditionalParameters`: Additional parameters to be added on Connection. Flags such as `Pooling`, `TrustServerCertificate` can be added. + + ## v1.5.0 (March 6, 2025) ### Added: diff --git a/example/Postgres/config/config.json b/example/Postgres/config/config.json index 4d289da..fee60da 100644 --- a/example/Postgres/config/config.json +++ b/example/Postgres/config/config.json @@ -63,5 +63,14 @@ "Threshold": 3, "DurationSc": 60, "HalfOpenMaxAttempts": 1 + }, + "UseDbCredentialsFile": true, + "DbCredentialsFileSettings": { + "FileName": "config/postgresql-test-cluster.json", + "Host": "10.85.240.242", + "Database": "test", + "ApplicationName": "PollingOutboxPublisher", + "Port": 5432, + "AdditionalParameters": "Pooling=true;TrustServerCertificate=true;" } } \ No newline at end of file diff --git a/src/ConfigOptions/DbCredentialsFileSettings.cs b/src/ConfigOptions/DbCredentialsFileSettings.cs new file mode 100644 index 0000000..74d71d0 --- /dev/null +++ b/src/ConfigOptions/DbCredentialsFileSettings.cs @@ -0,0 +1,14 @@ +using System.Diagnostics.CodeAnalysis; + +namespace PollingOutboxPublisher.ConfigOptions; + +[ExcludeFromCodeCoverage] +public class DbCredentialsFileSettings +{ + public string FileName { get; set; } + public string Host { get; set; } + public string Database { get; set; } + public int Port { get; set; } + public string ApplicationName { get; set; } + public string AdditionalParameters { get; set; } +} \ No newline at end of file diff --git a/src/Database/Providers/PostgresConnectionProvider.cs b/src/Database/Providers/PostgresConnectionProvider.cs index fcd0378..3ac6cd3 100644 --- a/src/Database/Providers/PostgresConnectionProvider.cs +++ b/src/Database/Providers/PostgresConnectionProvider.cs @@ -1,7 +1,10 @@ using System.Data; using System.Diagnostics.CodeAnalysis; +using System.IO; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Options; using Npgsql; +using PollingOutboxPublisher.ConfigOptions; using PollingOutboxPublisher.Database.Providers.Interfaces; using PollingOutboxPublisher.Exceptions; @@ -12,15 +15,25 @@ public class PostgresConnectionProvider : IPostgresConnectionProvider { private readonly string _connectionString; - public PostgresConnectionProvider(IConfiguration configuration) + public PostgresConnectionProvider(IConfiguration configuration, IOptions databaseTbpAuthenticationCredentials) { - var connectionString = configuration.GetValue("ConnectionString"); - if (string.IsNullOrWhiteSpace(connectionString)) + if (configuration.GetValue("UseDbCredentialsFile") is true) { - throw new MissingConfigurationException("ConnectionString"); + ValidateTbpAuthenticationCredentials(databaseTbpAuthenticationCredentials.Value); + var dbCredentials = databaseTbpAuthenticationCredentials.Value; + var postgresqlUsernamePassword = GetPostgresqlUsernameAndPassword(dbCredentials.FileName); + _connectionString = GenerateConnectionString(postgresqlUsernamePassword.userName, postgresqlUsernamePassword.password, dbCredentials.Host, dbCredentials.Database, dbCredentials.Port, dbCredentials.ApplicationName,dbCredentials.AdditionalParameters); } + else + { + var connectionString = configuration.GetValue("ConnectionString"); + if (string.IsNullOrWhiteSpace(connectionString)) + { + throw new MissingConfigurationException("ConnectionString"); + } - _connectionString = connectionString; + _connectionString = connectionString; + } } public IDbConnection CreateConnection() @@ -29,4 +42,58 @@ public IDbConnection CreateConnection() public IDbConnection CreateConnectionForReadOnly() => new NpgsqlConnection(_connectionString); + + private static (string userName, string password) GetPostgresqlUsernameAndPassword(string fileName) + { + if (!File.Exists(fileName)) + { + throw new FileNotFoundException($"Postgresql credentials file not found: {fileName}"); + } + + var postgresqlCredentials= new ConfigurationBuilder() + .AddJsonFile(fileName) + .Build(); + + var username = postgresqlCredentials["username"]; + var password = postgresqlCredentials["password"]; + return (username, password); + } + + private static void ValidateTbpAuthenticationCredentials(DbCredentialsFileSettings credentialses) + { + if (string.IsNullOrWhiteSpace(credentialses.FileName)) + { + throw new MissingConfigurationException("DatabaseTbpAuthenticationCredentials:FileName"); + } + + if (string.IsNullOrWhiteSpace(credentialses.Host)) + { + throw new MissingConfigurationException("DatabaseTbpAuthenticationCredentials:Host"); + } + + if (string.IsNullOrWhiteSpace(credentialses.Database)) + { + throw new MissingConfigurationException("DatabaseTbpAuthenticationCredentials:Database"); + } + + if (string.IsNullOrWhiteSpace(credentialses.ApplicationName)) + { + throw new MissingConfigurationException("DatabaseTbpAuthenticationCredentials:ApplicationName"); + } + + if (credentialses.Port == 0) + { + throw new MissingConfigurationException("DatabaseTbpAuthenticationCredentials:Port"); + } + } + + private static string GenerateConnectionString(string userName, string password, string host, string database, int port, string appName, string additionalParameters) + { + var connectionString = $"User ID={userName};Password={password};Server={host};Port={port};Database={database};ApplicationName={appName};"; + + if (!string.IsNullOrEmpty(additionalParameters)) + connectionString = string.Concat(connectionString, additionalParameters);; + + return connectionString; + } } \ No newline at end of file diff --git a/src/PollingOutboxPublisher.csproj b/src/PollingOutboxPublisher.csproj index d840d13..27612c8 100644 --- a/src/PollingOutboxPublisher.csproj +++ b/src/PollingOutboxPublisher.csproj @@ -4,7 +4,7 @@ Exe net8.0 false - 1.5.0 + 1.6.0 PollingOutboxPublisher diff --git a/src/Program.cs b/src/Program.cs index 7dcba20..f8b714c 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -103,6 +103,8 @@ private static void RegisterConfigs(IServiceCollection services, HostBuilderCont hostContext.Configuration.GetSection(nameof(ConfigOptions.Redis))); services.Configure( hostContext.Configuration.GetSection(nameof(CircuitBreakerSettings))); + services.Configure( + hostContext.Configuration.GetSection(nameof(DbCredentialsFileSettings))); } private static void RegisterSerilog(IServiceCollection services)