From 52af3d984451a2401c364fae302a25180b9007be Mon Sep 17 00:00:00 2001 From: k0st1x Date: Fri, 13 Mar 2026 15:24:46 +0300 Subject: [PATCH 1/3] #76 clear Data before refill --- .../VaultConfigurationProvider.cs | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/Source/VaultSharp.Extensions.Configuration/VaultConfigurationProvider.cs b/Source/VaultSharp.Extensions.Configuration/VaultConfigurationProvider.cs index 2fc6ddb..0d7fb09 100644 --- a/Source/VaultSharp.Extensions.Configuration/VaultConfigurationProvider.cs +++ b/Source/VaultSharp.Extensions.Configuration/VaultConfigurationProvider.cs @@ -127,6 +127,9 @@ public override void Load() private async Task LoadVaultDataAsync(IVaultClient vaultClient) { var hasChanges = false; + var currentKeys = new HashSet(); + var keysToUpdate = new Dictionary(); + await foreach (var secretData in this.ReadKeysAsync(vaultClient, this.ConfigurationSource.BasePath)) { this.logger?.LogDebug($"VaultConfigurationProvider: got Vault data with key `{secretData.Key}`"); @@ -146,8 +149,9 @@ private async Task LoadVaultDataAsync(IVaultClient vaultClient) { key = this.ConfigurationSource.Options.KeyPrefix + ":" + key; } - } + + currentKeys.Add(key); var data = secretData.SecretData.Data; var shouldSetValue = true; @@ -160,9 +164,26 @@ private async Task LoadVaultDataAsync(IVaultClient vaultClient) if (shouldSetValue) { - this.SetData(data, this.ConfigurationSource.Options.OmitVaultKeyName ? string.Empty : key); - hasChanges = true; - this.versionsCache[key] = secretData.SecretData.Metadata.Version; + keysToUpdate[key] = (data, secretData.SecretData.Metadata.Version); + } + } + + var keysToRemove = this.versionsCache.Keys.Where(k => !currentKeys.Contains(k)).ToList(); + if (keysToUpdate.Count > 0 || keysToRemove.Count > 0) + { + this.Data.Clear(); + hasChanges = true; + + foreach (var kvp in keysToUpdate) + { + this.SetData((IDictionary)kvp.Value.Data, this.ConfigurationSource.Options.OmitVaultKeyName ? string.Empty : kvp.Key); + this.versionsCache[kvp.Key] = kvp.Value.Version; + } + + foreach (var removedKey in keysToRemove) + { + this.versionsCache.Remove(removedKey); + this.logger?.LogDebug($"VaultConfigurationProvider: key `{removedKey}` was removed from Vault"); } } From c71936ba69d2d77c055909e513b2f53012ddb654 Mon Sep 17 00:00:00 2001 From: k0st1x Date: Mon, 23 Mar 2026 14:46:07 +0300 Subject: [PATCH 2/3] test added DeletedKeyRemovedFromVersionsCache additional property Vaultconfigurationprovider.VersionsCache_TEST only for testing purpose --- .../VaultConfigurationProvider.cs | 4 ++ ...VaultSharp.Extensions.Configuration.csproj | 2 +- .../IntegrationTests.cs | 56 +++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/Source/VaultSharp.Extensions.Configuration/VaultConfigurationProvider.cs b/Source/VaultSharp.Extensions.Configuration/VaultConfigurationProvider.cs index 0d7fb09..0cfd3a1 100644 --- a/Source/VaultSharp.Extensions.Configuration/VaultConfigurationProvider.cs +++ b/Source/VaultSharp.Extensions.Configuration/VaultConfigurationProvider.cs @@ -27,6 +27,10 @@ public class VaultConfigurationProvider : ConfigurationProvider private IVaultClient? vaultClient; private readonly Dictionary versionsCache; +#if DEBUG + public Dictionary VersionsCache_TEST => this.versionsCache; +#endif + /// /// Initializes a new instance of the class. /// diff --git a/Source/VaultSharp.Extensions.Configuration/VaultSharp.Extensions.Configuration.csproj b/Source/VaultSharp.Extensions.Configuration/VaultSharp.Extensions.Configuration.csproj index 4205b9d..3cbb179 100644 --- a/Source/VaultSharp.Extensions.Configuration/VaultSharp.Extensions.Configuration.csproj +++ b/Source/VaultSharp.Extensions.Configuration/VaultSharp.Extensions.Configuration.csproj @@ -3,7 +3,7 @@ default True - SA1633;SA1028;SA1309;CA1303;CS1591;IDE0057 + SA1633;SA1028;SA1309;CA1303;CS1591;IDE0057;IDE0032 VaultSharp.Extensions.Configuration MIT enable diff --git a/Tests/VaultSharp.Extensions.Configuration.Test/IntegrationTests.cs b/Tests/VaultSharp.Extensions.Configuration.Test/IntegrationTests.cs index c0ec754..522c64c 100644 --- a/Tests/VaultSharp.Extensions.Configuration.Test/IntegrationTests.cs +++ b/Tests/VaultSharp.Extensions.Configuration.Test/IntegrationTests.cs @@ -3,6 +3,7 @@ namespace VaultSharp.Extensions.Configuration.Test using System; using System.Collections.Generic; using System.IO; + using System.Linq; using System.Net; using System.Net.Http; using System.Threading; @@ -999,6 +1000,61 @@ public async Task Success_TokenNoAuthMethod() await container.DisposeAsync().ConfigureAwait(false); } } + + [Fact] + public async Task Success_DeletedKeyRemovedFromVersionsCache() + { + // arrange + var values = new Dictionary>> + { + { "test", new[] { new KeyValuePair("option1", "value1") } }, + { "test/subsection", new[] { new KeyValuePair("option2", "value2") } }, + }; + + var container = this.PrepareVaultContainer(); + try + { + await container.StartAsync(); + await this.LoadDataAsync("http://localhost:8200", values); + + var builder = new ConfigurationBuilder(); + builder.AddVaultConfiguration( + () => new VaultOptions("http://localhost:8200", "root"), + "test", + "secret", + this.logger); + var configurationRoot = builder.Build(); + + // assert initial state + configurationRoot.GetValue("option1").Should().Be("value1"); + configurationRoot.GetSection("subsection").GetValue("option2").Should().Be("value2"); + + var provider = configurationRoot.Providers.OfType().First(); + + provider.VersionsCache_TEST.Should().ContainKey("subsection"); + + // delete test/subsection from Vault + var vaultClientSettings = new VaultClientSettings("http://localhost:8200", new TokenAuthMethodInfo("root")) + { + SecretsEngineMountPoints = { KeyValueV2 = "secret" } + }; + IVaultClient vaultClient = new VaultClient(vaultClientSettings); + await vaultClient.V1.Secrets.KeyValue.V2.DeleteSecretAsync("test/subsection", "secret"); + + // reload the configuration provider + provider.Load(); + + // assert the deleted key is removed from versionsCache + provider.VersionsCache_TEST.Should().NotContainKey("subsection"); + + // assert the deleted key's value is no longer present in configuration + configurationRoot.GetSection("subsection").GetValue("option2").Should().BeNull(); + } + finally + { + await container.DisposeAsync(); + } + } } public class TestConfigObject From 63105d3bd636b91e428bc9f4ca04e2b8252fd53f Mon Sep 17 00:00:00 2001 From: k0st1x Date: Wed, 25 Mar 2026 10:25:56 +0300 Subject: [PATCH 3/3] test with reflection --- .../VaultConfigurationProvider.cs | 4 ---- .../IntegrationTests.cs | 7 +++++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Source/VaultSharp.Extensions.Configuration/VaultConfigurationProvider.cs b/Source/VaultSharp.Extensions.Configuration/VaultConfigurationProvider.cs index 0cfd3a1..0d7fb09 100644 --- a/Source/VaultSharp.Extensions.Configuration/VaultConfigurationProvider.cs +++ b/Source/VaultSharp.Extensions.Configuration/VaultConfigurationProvider.cs @@ -27,10 +27,6 @@ public class VaultConfigurationProvider : ConfigurationProvider private IVaultClient? vaultClient; private readonly Dictionary versionsCache; -#if DEBUG - public Dictionary VersionsCache_TEST => this.versionsCache; -#endif - /// /// Initializes a new instance of the class. /// diff --git a/Tests/VaultSharp.Extensions.Configuration.Test/IntegrationTests.cs b/Tests/VaultSharp.Extensions.Configuration.Test/IntegrationTests.cs index 522c64c..ba70ba2 100644 --- a/Tests/VaultSharp.Extensions.Configuration.Test/IntegrationTests.cs +++ b/Tests/VaultSharp.Extensions.Configuration.Test/IntegrationTests.cs @@ -6,6 +6,7 @@ namespace VaultSharp.Extensions.Configuration.Test using System.Linq; using System.Net; using System.Net.Http; + using System.Reflection; using System.Threading; using System.Threading.Tasks; using DotNet.Testcontainers.Builders; @@ -1031,7 +1032,9 @@ public async Task Success_DeletedKeyRemovedFromVersionsCache() var provider = configurationRoot.Providers.OfType().First(); - provider.VersionsCache_TEST.Should().ContainKey("subsection"); + var versionsCacheField = typeof(VaultConfigurationProvider) + .GetField("versionsCache", BindingFlags.NonPublic | BindingFlags.Instance); + var versionsCache = (Dictionary)versionsCacheField!.GetValue(provider)!; // delete test/subsection from Vault var vaultClientSettings = new VaultClientSettings("http://localhost:8200", new TokenAuthMethodInfo("root")) @@ -1045,7 +1048,7 @@ public async Task Success_DeletedKeyRemovedFromVersionsCache() provider.Load(); // assert the deleted key is removed from versionsCache - provider.VersionsCache_TEST.Should().NotContainKey("subsection"); + versionsCache.Should().NotContainKey("subsection"); // assert the deleted key's value is no longer present in configuration configurationRoot.GetSection("subsection").GetValue("option2").Should().BeNull();