From 7fe32c61a92094ab8f1a68fff64f4282aa31a0cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20K=C3=BCrzd=C3=B6rfer?= Date: Wed, 21 Feb 2024 10:36:54 +0100 Subject: [PATCH] fix ordering of appsettings --- PiBox.Hosting/WebHost/README.md | 30 ++++++++--- .../Configurators/HostConfigurator.cs | 26 ++++----- .../ConfigurationManagerExtensions.cs | 15 ++++++ .../PiBox.Hosting.WebHost.Tests.csproj | 6 +++ .../PluginWebHostBuilderTests.cs | 17 +++--- .../appsettings.test.secrets.json | 5 ++ .../appsettings.test.yml | 53 +++++++++++++++++++ 7 files changed, 125 insertions(+), 27 deletions(-) create mode 100644 PiBox.Hosting/WebHost/src/PiBox.Hosting.WebHost/Extensions/ConfigurationManagerExtensions.cs create mode 100644 PiBox.Hosting/WebHost/test/PiBox.Hosting.WebHost.Tests/appsettings.test.secrets.json create mode 100644 PiBox.Hosting/WebHost/test/PiBox.Hosting.WebHost.Tests/appsettings.test.yml diff --git a/PiBox.Hosting/WebHost/README.md b/PiBox.Hosting/WebHost/README.md index 0defeb0..48dbfaf 100644 --- a/PiBox.Hosting/WebHost/README.md +++ b/PiBox.Hosting/WebHost/README.md @@ -38,13 +38,29 @@ PluginWebHostBuilder.RunDefault(PiBox.Generated.PiBoxPluginTypes.All); PiBox will try to load config files and values in the following order -1. `appsettings*.json` -2. `appsettings*.yaml` -3. `appsettings*.yml` -4. `appsettings.json` -5. `appsettings.yaml` -6. `appsettings.yml` -7. ENV variables +1. `appsettings.json` +2. `appsettings.yaml` +3. `appsettings.yml` +4. `appsettings.*.json` +5. `appsettings.*.yaml` +6. `appsettings.*.yml` +7. `appsettings.secrets.yml` +8. `appsettings.*.secrets.yml` +9`ENV variables` + +**NOTE:** +All settings (does not matter which file extension) will be ordered by node length. And the secret files will be loaded as last (except the environment variables). + +Example: + +1. appsettings.yaml +2. appsettings.api.yaml +3. appsettings.api.host.yaml +4. appsettings.secrets.yaml +5. apssettings.api.secrets.yaml +6. appsettings.api.host.secrets.yaml +7. ENV Variables + File names must always start with `appsettings` or they will be ignored! diff --git a/PiBox.Hosting/WebHost/src/PiBox.Hosting.WebHost/Configurators/HostConfigurator.cs b/PiBox.Hosting/WebHost/src/PiBox.Hosting.WebHost/Configurators/HostConfigurator.cs index 26660cd..967fc6b 100644 --- a/PiBox.Hosting/WebHost/src/PiBox.Hosting.WebHost/Configurators/HostConfigurator.cs +++ b/PiBox.Hosting/WebHost/src/PiBox.Hosting.WebHost/Configurators/HostConfigurator.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using PiBox.Hosting.Abstractions.Configuration; +using PiBox.Hosting.WebHost.Extensions; using PiBox.Hosting.WebHost.Logging; namespace PiBox.Hosting.WebHost.Configurators @@ -30,27 +31,28 @@ internal static void ConfigureAppConfiguration(ConfigurationManager configuratio configurationManager.Sources.Clear(); - foreach (var jsonConfig in FindAppSettingFiles(".json")) - configurationManager.AddJsonFile(jsonConfig, optional: true, reloadOnChange: true); + var settings = FindAppSettingFiles("json", "yaml", "yml").ToList(); + foreach (var file in settings.Where(x => !IsSecretsFile(x))) + configurationManager.AddFile(file); - foreach (var yamlFile in FindAppSettingFiles(".yaml", ".yml")) - configurationManager.AddYamlFile(yamlFile, optional: true, reloadOnChange: true); + foreach (var file in settings.Where(IsSecretsFile)) + configurationManager.AddFile(file); - configurationManager.AddJsonFile("appsettings.json", true, true) - .AddYamlFile("appsettings.yaml", true, true) - .AddYamlFile("appsettings.yml", true, true) - .AddEnvVariables(); + configurationManager.AddEnvVariables(); #pragma warning restore ASP0013 } + private static bool IsSecretsFile(string filename) => filename.EndsWith(".secrets.yaml") || + filename.EndsWith(".secrets.yml") || + filename.EndsWith(".secrets.json"); + private static IEnumerable FindAppSettingFiles(params string[] extensions) { var files = Directory.GetFiles(Environment.CurrentDirectory) .Select(x => x.Split(Path.DirectorySeparatorChar).Last()) - .Where(x => !string.IsNullOrEmpty(x) && x.StartsWith("appsettings", StringComparison.InvariantCulture)) - .Where(x => extensions.Any(e => x.EndsWith(e, StringComparison.InvariantCulture))) - .Where(x => !extensions.Any( - e => string.Equals(x, "appsettings" + e, StringComparison.OrdinalIgnoreCase))) + .Where(x => !string.IsNullOrEmpty(x) && x.StartsWith("appsettings.", StringComparison.InvariantCulture)) + .Where(x => extensions.Any(e => x.EndsWith(e, StringComparison.InvariantCultureIgnoreCase))) + .OrderBy(x => x.Split('.').Length) .ToList(); return files; diff --git a/PiBox.Hosting/WebHost/src/PiBox.Hosting.WebHost/Extensions/ConfigurationManagerExtensions.cs b/PiBox.Hosting/WebHost/src/PiBox.Hosting.WebHost/Extensions/ConfigurationManagerExtensions.cs new file mode 100644 index 0000000..ea3f8c8 --- /dev/null +++ b/PiBox.Hosting/WebHost/src/PiBox.Hosting.WebHost/Extensions/ConfigurationManagerExtensions.cs @@ -0,0 +1,15 @@ +using Microsoft.Extensions.Configuration; + +namespace PiBox.Hosting.WebHost.Extensions +{ + internal static class ConfigurationManagerExtensions + { + public static void AddFile(this ConfigurationManager configurationManager, string file, bool optional = true, bool reloadOnChange = true) + { + if (file.EndsWith(".json")) + configurationManager.AddJsonFile(file, optional, reloadOnChange); + else if (file.EndsWith(".yaml") || file.EndsWith(".yml")) + configurationManager.AddYamlFile(file, optional, reloadOnChange); + } + } +} diff --git a/PiBox.Hosting/WebHost/test/PiBox.Hosting.WebHost.Tests/PiBox.Hosting.WebHost.Tests.csproj b/PiBox.Hosting/WebHost/test/PiBox.Hosting.WebHost.Tests/PiBox.Hosting.WebHost.Tests.csproj index a26c35e..91b3ac5 100644 --- a/PiBox.Hosting/WebHost/test/PiBox.Hosting.WebHost.Tests/PiBox.Hosting.WebHost.Tests.csproj +++ b/PiBox.Hosting/WebHost/test/PiBox.Hosting.WebHost.Tests/PiBox.Hosting.WebHost.Tests.csproj @@ -15,6 +15,12 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + diff --git a/PiBox.Hosting/WebHost/test/PiBox.Hosting.WebHost.Tests/PluginWebHostBuilderTests.cs b/PiBox.Hosting/WebHost/test/PiBox.Hosting.WebHost.Tests/PluginWebHostBuilderTests.cs index 86fd6cd..f46046b 100644 --- a/PiBox.Hosting/WebHost/test/PiBox.Hosting.WebHost.Tests/PluginWebHostBuilderTests.cs +++ b/PiBox.Hosting/WebHost/test/PiBox.Hosting.WebHost.Tests/PluginWebHostBuilderTests.cs @@ -48,19 +48,20 @@ public async Task ShouldWork() webApplication.Logger.GetType().Name.Should().Be("SerilogLogger"); var configurationProviders = webApplication.Configuration.As().Providers.ToList(); - configurationProviders[0].Should().BeOfType(); - configurationProviders[0].As().Source.Path.Should().Be("appsettings.json"); + configurationProviders[0].Should().BeOfType(); + configurationProviders[0].As().Source.Path.Should().Be("appsettings.yml"); configurationProviders[1].Should().BeOfType(); - configurationProviders[1].As().Source.Path.Should().Be("appsettings.yaml"); + configurationProviders[1].As().Source.Path.Should().Be("appsettings.test.yml"); - configurationProviders[2].Should().BeOfType(); - configurationProviders[2].As().Source.Path.Should().Be("appsettings.yml"); - configurationProviders[2].TryGet("serilog:minimumLevel", out var loglevel); - loglevel.Should().Be("Debug"); + configurationProviders[2].Should().BeOfType(); + configurationProviders[2].As().Source.Path.Should().Be("appsettings.test.secrets.json"); configurationProviders[3].Should().BeOfType(); + var logLevel = webApplication.Configuration.GetValue("serilog:minimumLevel"); + logLevel.Should().Be("Information"); // overriden from appsettings.test.secrets.json + var ipRateLimitOptions = webApplication.Configuration.BindToSection("IpRateLimiting"); ipRateLimitOptions.EnableEndpointRateLimiting.Should().BeFalse(); ipRateLimitOptions.StackBlockedRequests.Should().BeFalse(); @@ -124,7 +125,7 @@ public async Task ShouldWork() apiBehaviourOptions.InvalidModelStateResponseFactory.Should().Be(modelStateResponseFactory); var sampleConfig = webApplication.Configuration.BindToSection("sampleConfig"); - sampleConfig.Name.Should().Be("example"); + sampleConfig.Name.Should().Be("example1"); // overridden from appsettings.test.yml var unitTestPluginConfig = host.Services.GetRequiredService(); unitTestPluginConfig.Should().NotBeNull(); diff --git a/PiBox.Hosting/WebHost/test/PiBox.Hosting.WebHost.Tests/appsettings.test.secrets.json b/PiBox.Hosting/WebHost/test/PiBox.Hosting.WebHost.Tests/appsettings.test.secrets.json new file mode 100644 index 0000000..40fe18c --- /dev/null +++ b/PiBox.Hosting/WebHost/test/PiBox.Hosting.WebHost.Tests/appsettings.test.secrets.json @@ -0,0 +1,5 @@ +{ + "serilog": { + "minimumLevel": "Information" + } +} diff --git a/PiBox.Hosting/WebHost/test/PiBox.Hosting.WebHost.Tests/appsettings.test.yml b/PiBox.Hosting/WebHost/test/PiBox.Hosting.WebHost.Tests/appsettings.test.yml new file mode 100644 index 0000000..9a1f6c0 --- /dev/null +++ b/PiBox.Hosting/WebHost/test/PiBox.Hosting.WebHost.Tests/appsettings.test.yml @@ -0,0 +1,53 @@ +IpRateLimiting: + EnableEndpointRateLimiting: false + StackBlockedRequests: false + RealIpHeader: X-Real-IP + ClientIdHeader: X-ClientId + HttpStatusCode: 429 + IpWhitelist: + - 127.0.0.1 + - "::1/10" + - 192.168.0.0/24 + EndpointWhitelist: + - get:/api/license + - "*:/api/status" + ClientWhitelist: + - dev-id-1 + - dev-id-2 + GeneralRules: + - Endpoint: "*" + Period: 1s + Limit: 2 + - Endpoint: "*" + Period: 15m + Limit: 100 + - Endpoint: "*" + Period: 12h + Limit: 1000 + - Endpoint: "*" + Period: 7d + Limit: 10000 + +IpRateLimitPolicies: + IpRules: + - Ip: 84.247.85.224 + Rules: + - Endpoint: "*" + Period: 1s + Limit: 10 + - Ip: 192.168.3.22/25 + Rules: + - Endpoint: "*" + Period: 12h + Limit: 500 + +CorsPolicy: + Origins: + - "http://localhost:4200" + - "http://localhost:4201" + Methods: + - "POST" + SupportsCredentials: "true" + +sampleConfig: + name: example1