From 0992b6545110664aedaabeacafcbf2c3c3a285cd Mon Sep 17 00:00:00 2001 From: David Eberhart <64981864+deberhar@users.noreply.github.com> Date: Wed, 31 Jan 2024 11:59:32 -0500 Subject: [PATCH] Expose VaultSharp's PostProcessHttpClientHandlerAction hook Expose PostProcessHttpClientHandlerAction via the VaultSharp configuration provider settings. This allows arbitrary customizations to the underlying HTTP client, such as customizing HTTP proxy settings. --- .../VaultConfigurationProvider.cs | 2 + .../VaultOptions.cs | 12 +++++ .../IntegrationTests.cs | 52 +++++++++++++++++++ 3 files changed, 66 insertions(+) diff --git a/Source/VaultSharp.Extensions.Configuration/VaultConfigurationProvider.cs b/Source/VaultSharp.Extensions.Configuration/VaultConfigurationProvider.cs index aae4df1..10af75f 100644 --- a/Source/VaultSharp.Extensions.Configuration/VaultConfigurationProvider.cs +++ b/Source/VaultSharp.Extensions.Configuration/VaultConfigurationProvider.cs @@ -92,6 +92,8 @@ public override void Load() { clientHandler.ServerCertificateCustomValidationCallback = this.ConfigurationSource.Options.ServerCertificateCustomValidationCallback; } + + this.ConfigurationSource.Options.PostProcessHttpClientHandlerAction?.Invoke(clientHandler); } } }; diff --git a/Source/VaultSharp.Extensions.Configuration/VaultOptions.cs b/Source/VaultSharp.Extensions.Configuration/VaultOptions.cs index bf060d6..fa4f66d 100644 --- a/Source/VaultSharp.Extensions.Configuration/VaultOptions.cs +++ b/Source/VaultSharp.Extensions.Configuration/VaultOptions.cs @@ -163,5 +163,17 @@ public VaultOptions( /// An optional action to post-process the HttpClientHandler. Used to manually validate the server certificate. Ignored if AcceptInsecureConnection is true. /// public Func? ServerCertificateCustomValidationCallback { get; set;} + + /// + /// An optional hook to allow custom configuration of the HttpClientHandler. + /// This is useful if you need to customize the HTTP client's proxy settings, for example. + /// + /// + /// The action will be invoked after the VaultSharp provider applies the AcceptInsecureConnection and ServerCertificateCustomValidationCallback + /// customizations, if you enabled them. Be aware that if you overwrite the HttpMessageHandler's ServerCertificateCustomValidationCallback + /// in your action-handler method, you will cancel out the effect of enabling the AcceptInsecureConnection and/or + /// ServerCertificateCustomValidationCallback options. + /// + public Action? PostProcessHttpClientHandlerAction { get; set; } } } diff --git a/Tests/VaultSharp.Extensions.Configuration.Test/IntegrationTests.cs b/Tests/VaultSharp.Extensions.Configuration.Test/IntegrationTests.cs index c05d31f..ac85b13 100644 --- a/Tests/VaultSharp.Extensions.Configuration.Test/IntegrationTests.cs +++ b/Tests/VaultSharp.Extensions.Configuration.Test/IntegrationTests.cs @@ -616,6 +616,58 @@ public async Task Failure_PermissionDenied() It.Is>((v, t) => true)), Times.Once); } + + [Fact] + public async Task Success_Proxy_Verify_Custom_Hook_Invoked() + { + // arrange + using CancellationTokenSource cts = new CancellationTokenSource(); + var values = + new Dictionary>> + { + { + "myservice-config", new[] + { + new KeyValuePair("option1", "value1"), + new KeyValuePair("subsection", new {option2 = "value2"}), + } + }, + }; + + var container = this.PrepareVaultContainer(); + try + { + await container.StartAsync(cts.Token).ConfigureAwait(false); + await this.LoadDataAsync("http://localhost:8200", values).ConfigureAwait(false); + + // Moq mock of PostProcessHttpClientHandlerAction implementation: + var mockConfigureProxyAction = new Mock>(); + + // act + ConfigurationBuilder builder = new ConfigurationBuilder(); + builder.AddVaultConfiguration( + () => new VaultOptions("http://localhost:8200", new TokenAuthMethodInfo("root"), reloadOnChange: true, reloadCheckIntervalSeconds: 10, omitVaultKeyName: true) + { + PostProcessHttpClientHandlerAction = mockConfigureProxyAction.Object + }, + "myservice-config", + "secret", + this.logger); + var configurationRoot = builder.Build(); + + // assert secrets were loaded successfully: + configurationRoot.GetValue("option1").Should().Be("value1"); + configurationRoot.GetSection("subsection").GetValue("option2").Should().Be("value2"); + + // assert that PostProcessHttpClientHandlerAction was actually invoked, and a HttpMessageHandler was passed: + mockConfigureProxyAction.Verify(x => x(It.IsAny()), Times.Once); + } + finally + { + cts.Cancel(); + await container.DisposeAsync().ConfigureAwait(false); + } + } } public class TestConfigObject