diff --git a/src/OrchardCoreContrib.Garnet/README.md b/src/OrchardCoreContrib.Garnet/README.md index 8f955e8..d76e138 100644 --- a/src/OrchardCoreContrib.Garnet/README.md +++ b/src/OrchardCoreContrib.Garnet/README.md @@ -70,22 +70,22 @@ This module has no dependencies. 2. Go to the admin site 3. Select **Configuration -> Features** menu. -## Garnet +#### Garnet 4. Enable the `Garnet` feature. -## Garnet Cache +#### Garnet Cache 4. Enable the `Garnet Cache` feature. -## Garnet Bus +#### Garnet Bus 4. Enable the `Garnet Bus` feature. -## Garnet DataProtection +#### Garnet DataProtection 4. Enable the `Garnet DataProtection` feature. -## Garnet Lock +#### Garnet Lock 4. Enable the `Garnet Lock` feature. diff --git a/src/OrchardCoreContrib.HealthChecks/Constants.cs b/src/OrchardCoreContrib.HealthChecks/Constants.cs index ea6d488..233cabd 100644 --- a/src/OrchardCoreContrib.HealthChecks/Constants.cs +++ b/src/OrchardCoreContrib.HealthChecks/Constants.cs @@ -2,5 +2,16 @@ public class Constants { - public const string ConfigurationKey = "OrchardCoreContrib_HealthChecks"; + /// + /// Defines the configuration key for health checks settings in the Orchard Core configuration system. + /// + public const string HealthChecksConfigurationKey = "OrchardCoreContrib_HealthChecks"; + /// + /// Defines the configuration key for health checks access settings in the Orchard Core configuration system. + /// + public const string HealthChecksAccessConfigurationKey = "OrchardCoreContrib_HealthChecks:Access"; + /// + /// Defines the configuration key for health checks rate limiting settings in the Orchard Core configuration system. + /// + public const string HealthChecksRateLimitingConfigurationKey = "OrchardCoreContrib_HealthChecks:RateLimiting"; } diff --git a/src/OrchardCoreContrib.HealthChecks/HealthChecksAccessOptions.cs b/src/OrchardCoreContrib.HealthChecks/HealthChecksAccessOptions.cs index 025d8b2..c77775b 100644 --- a/src/OrchardCoreContrib.HealthChecks/HealthChecksAccessOptions.cs +++ b/src/OrchardCoreContrib.HealthChecks/HealthChecksAccessOptions.cs @@ -1,6 +1,12 @@ namespace OrchardCoreContrib.HealthChecks; +/// +/// Provides configuration options for controlling access to health check endpoints, such as allowed IP addresses. +/// public class HealthChecksAccessOptions { + /// + /// Gets or sets the collection of IP addresses that are permitted access. + /// public HashSet AllowedIPs { get; set; } = []; } diff --git a/src/OrchardCoreContrib.HealthChecks/HealthChecksBlockingRateLimitingMiddleware.cs b/src/OrchardCoreContrib.HealthChecks/HealthChecksBlockingRateLimitingMiddleware.cs index c3817dc..3744ea8 100644 --- a/src/OrchardCoreContrib.HealthChecks/HealthChecksBlockingRateLimitingMiddleware.cs +++ b/src/OrchardCoreContrib.HealthChecks/HealthChecksBlockingRateLimitingMiddleware.cs @@ -6,6 +6,9 @@ namespace OrchardCoreContrib.HealthChecks; +/// +/// Middleware that enforces rate limiting and temporary blocking for health check endpoints based on client IP address. +/// public class HealthChecksBlockingRateLimitingMiddleware { private static readonly ConcurrentDictionary _blockedIPs = new(); @@ -39,6 +42,7 @@ public HealthChecksBlockingRateLimitingMiddleware( }); } + /// public async Task InvokeAsync(HttpContext context) { if (context.Request.Path.Equals(_healthChecksOptions.Url)) diff --git a/src/OrchardCoreContrib.HealthChecks/HealthChecksBlockingRateLimitingOptions.cs b/src/OrchardCoreContrib.HealthChecks/HealthChecksBlockingRateLimitingOptions.cs index 1524d12..782ca65 100644 --- a/src/OrchardCoreContrib.HealthChecks/HealthChecksBlockingRateLimitingOptions.cs +++ b/src/OrchardCoreContrib.HealthChecks/HealthChecksBlockingRateLimitingOptions.cs @@ -1,6 +1,12 @@ namespace OrchardCoreContrib.HealthChecks; +/// +/// Provides configuration options for blocking rate limiting applied to health check endpoints. +/// public class HealthChecksBlockingRateLimitingOptions : HealthChecksRateLimitingOptions { + /// + /// Gets or sets the duration for which a client is blocked when the rate limit is exceeded. Defaults to 1 minute. + /// public TimeSpan BlockDuration { get; set; } = TimeSpan.FromMinutes(1); } diff --git a/src/OrchardCoreContrib.HealthChecks/HealthChecksIPRestrictionMiddleware.cs b/src/OrchardCoreContrib.HealthChecks/HealthChecksIPRestrictionMiddleware.cs index 5b74ae2..371272f 100644 --- a/src/OrchardCoreContrib.HealthChecks/HealthChecksIPRestrictionMiddleware.cs +++ b/src/OrchardCoreContrib.HealthChecks/HealthChecksIPRestrictionMiddleware.cs @@ -4,6 +4,13 @@ namespace OrchardCoreContrib.HealthChecks; +/// +/// Middleware that restricts access to health check endpoints based on allowed IP addresses. +/// +/// The representing the next middleware in the pipeline. +/// The containing health check configuration. +/// The containing IP access configuration. +/// The used for logging. public class HealthChecksIPRestrictionMiddleware( RequestDelegate next, IOptions healthChecksOptions, @@ -12,6 +19,7 @@ public class HealthChecksIPRestrictionMiddleware( { private readonly HealthChecksOptions _healthChecksOptions = healthChecksOptions.Value; + /// public async Task InvokeAsync(HttpContext context) { if (context.Request.Path.Equals(_healthChecksOptions.Url)) diff --git a/src/OrchardCoreContrib.HealthChecks/HealthChecksRateLimitingMiddleware.cs b/src/OrchardCoreContrib.HealthChecks/HealthChecksRateLimitingMiddleware.cs index f9ebb8c..d7cc499 100644 --- a/src/OrchardCoreContrib.HealthChecks/HealthChecksRateLimitingMiddleware.cs +++ b/src/OrchardCoreContrib.HealthChecks/HealthChecksRateLimitingMiddleware.cs @@ -5,6 +5,9 @@ namespace OrchardCoreContrib.HealthChecks; +/// +/// Middleware that enforces rate limiting on health check endpoints to prevent excessive requests. +/// public class HealthChecksRateLimitingMiddleware { private readonly RequestDelegate _next; @@ -33,6 +36,7 @@ public HealthChecksRateLimitingMiddleware( }); } + /// public async Task InvokeAsync(HttpContext context) { if (context.Request.Path.Equals(_healthChecksOptions.Url)) diff --git a/src/OrchardCoreContrib.HealthChecks/HealthChecksRateLimitingOptions.cs b/src/OrchardCoreContrib.HealthChecks/HealthChecksRateLimitingOptions.cs index d0ce9bf..59eeb1a 100644 --- a/src/OrchardCoreContrib.HealthChecks/HealthChecksRateLimitingOptions.cs +++ b/src/OrchardCoreContrib.HealthChecks/HealthChecksRateLimitingOptions.cs @@ -1,13 +1,28 @@ namespace OrchardCoreContrib.HealthChecks; +/// +/// Provides configuration options for rate limiting applied to health check endpoints. +/// public class HealthChecksRateLimitingOptions { + /// + /// Gets or sets the maximum number of concurrent permits that can be acquired. Defaults to 5. + /// public int PermitLimit { get; set; } = 5; + /// + /// Gets or sets the time window for which the rate limiting is applied. Defaults to 10 seconds. + /// public TimeSpan Window { get; set; } = TimeSpan.FromSeconds(10); + /// + /// Gets or sets the number of segments the time window is divided into for rate limiting purposes. Defaults to 10. + /// public int SegmentsPerWindow { get; set; } = 10; + /// + /// Gets or sets the maximum number of requests that can be queued when the rate limit is exceeded. Defaults to 0. + /// public int QueueLimit { get; set; } = 0; } diff --git a/src/OrchardCoreContrib.HealthChecks/Startups/BlockingRateLimitingStartup.cs b/src/OrchardCoreContrib.HealthChecks/Startups/BlockingRateLimitingStartup.cs new file mode 100644 index 0000000..3048721 --- /dev/null +++ b/src/OrchardCoreContrib.HealthChecks/Startups/BlockingRateLimitingStartup.cs @@ -0,0 +1,31 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; +using OrchardCore.Environment.Shell.Configuration; +using OrchardCore.Modules; + +namespace OrchardCoreContrib.HealthChecks; + +/// +/// Configures blocking rate limiting for health check endpoints during application startup. +/// +/// The . +[Feature("OrchardCoreContrib.HealthChecks.BlockingRateLimiting")] +public class BlockingRateLimitingStartup(IShellConfiguration shellConfiguration) : StartupBase +{ + /// + public override int Order => 20; + + /// + public override void ConfigureServices(IServiceCollection services) + { + var rateLimitingSection = shellConfiguration.GetSection(Constants.HealthChecksRateLimitingConfigurationKey); + + services.Configure(rateLimitingSection); + services.Configure(rateLimitingSection); + } + + /// + public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + => app.UseMiddleware(); +} diff --git a/src/OrchardCoreContrib.HealthChecks/Startups/IPRestrictionStartup.cs b/src/OrchardCoreContrib.HealthChecks/Startups/IPRestrictionStartup.cs new file mode 100644 index 0000000..c9ae3d8 --- /dev/null +++ b/src/OrchardCoreContrib.HealthChecks/Startups/IPRestrictionStartup.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; +using OrchardCore.Environment.Shell.Configuration; +using OrchardCore.Modules; + +namespace OrchardCoreContrib.HealthChecks; + +/// +/// Configures IP-based access restrictions for health check endpoints during application startup. +/// +/// The . +[Feature("OrchardCoreContrib.HealthChecks.IPRestriction")] +public class IPRestrictionStartup(IShellConfiguration shellConfiguration) : StartupBase +{ + /// + public override int Order => 10; + + /// + public override void ConfigureServices(IServiceCollection services) + => services.Configure(shellConfiguration.GetSection(Constants.HealthChecksAccessConfigurationKey)); + + /// + public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + => app.UseMiddleware(); +} diff --git a/src/OrchardCoreContrib.HealthChecks/Startups/RateLimitingStartup.cs b/src/OrchardCoreContrib.HealthChecks/Startups/RateLimitingStartup.cs new file mode 100644 index 0000000..abede72 --- /dev/null +++ b/src/OrchardCoreContrib.HealthChecks/Startups/RateLimitingStartup.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; +using OrchardCore.Environment.Shell.Configuration; +using OrchardCore.Modules; + +namespace OrchardCoreContrib.HealthChecks; + +/// +/// Configures rate limiting for health check endpoints during application startup. +/// +/// The . +[Feature("OrchardCoreContrib.HealthChecks.RateLimiting")] +public class RateLimitingStartup(IShellConfiguration shellConfiguration) : StartupBase +{ + /// + public override int Order => 30; + + /// + public override void ConfigureServices(IServiceCollection services) + => services.Configure(shellConfiguration.GetSection(Constants.HealthChecksRateLimitingConfigurationKey)); + + /// + public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) + => app.UseMiddleware(); +} diff --git a/src/OrchardCoreContrib.HealthChecks/Startup.cs b/src/OrchardCoreContrib.HealthChecks/Startups/Startup.cs similarity index 55% rename from src/OrchardCoreContrib.HealthChecks/Startup.cs rename to src/OrchardCoreContrib.HealthChecks/Startups/Startup.cs index 4d1f89b..04d3d06 100644 --- a/src/OrchardCoreContrib.HealthChecks/Startup.cs +++ b/src/OrchardCoreContrib.HealthChecks/Startups/Startup.cs @@ -13,17 +13,23 @@ namespace OrchardCoreContrib.HealthChecks; +/// +/// Configures health check endpoints during application startup. +/// +/// The . public class Startup(IShellConfiguration shellConfiguration) : StartupBase { private static readonly JsonSerializerOptions _jsonSerializerOptions = new() { WriteIndented = true }; + /// public override void ConfigureServices(IServiceCollection services) { services.AddHealthChecks(); - services.Configure(shellConfiguration.GetSection(Constants.ConfigurationKey)); + services.Configure(shellConfiguration.GetSection(Constants.HealthChecksConfigurationKey)); } + /// public override void Configure(IApplicationBuilder builder, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) { var healthChecksOptions = serviceProvider.GetService>().Value; @@ -67,45 +73,3 @@ private static async Task WriteResponse(HttpContext context, HealthReport report await context.Response.WriteAsync(JsonSerializer.Serialize(response, response.GetType(), options: _jsonSerializerOptions)); } } - -[Feature("OrchardCoreContrib.HealthChecks.IPRestriction")] -public class IPRestrictionStartup(IShellConfiguration shellConfiguration) : StartupBase -{ - public override int Order => 10; - - public override void ConfigureServices(IServiceCollection services) - => services.Configure(shellConfiguration.GetSection($"{Constants.ConfigurationKey}:Access")); - - public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - => app.UseMiddleware(); -} - -[Feature("OrchardCoreContrib.HealthChecks.RateLimiting")] -public class RateLimitingStartup(IShellConfiguration shellConfiguration) : StartupBase -{ - public override int Order => 30; - - public override void ConfigureServices(IServiceCollection services) - => services.Configure(shellConfiguration.GetSection($"{Constants.ConfigurationKey}:RateLimiting")); - - public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - => app.UseMiddleware(); -} - -[Feature("OrchardCoreContrib.HealthChecks.BlockingRateLimiting")] -public class BlockingRateLimitingStartup(IShellConfiguration shellConfiguration) : StartupBase -{ - public override int Order => 20; - - public override void ConfigureServices(IServiceCollection services) - { - var rateLimitingSection = shellConfiguration.GetSection($"{Constants.ConfigurationKey}:RateLimiting"); - - services.Configure(rateLimitingSection); - services.Configure(rateLimitingSection); - } - - public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) - => app.UseMiddleware(); -} - diff --git a/test/OrchardCoreContrib.HealthChecks.Tests/OrchardCoreStartup.cs b/test/OrchardCoreContrib.HealthChecks.Tests/OrchardCoreStartup.cs index 4e7a79d..f715af0 100644 --- a/test/OrchardCoreContrib.HealthChecks.Tests/OrchardCoreStartup.cs +++ b/test/OrchardCoreContrib.HealthChecks.Tests/OrchardCoreStartup.cs @@ -48,9 +48,9 @@ private IConfigurationRoot AddHealthChecksConfiguration() { var newConfiguration = new Dictionary { - { $"{Constants.ConfigurationKey}:{nameof(HealthChecksOptions.Url)}", "/health" }, - { $"{Constants.ConfigurationKey}:Access:AllowedIPs:0", "127.0.0.1" }, - { $"{Constants.ConfigurationKey}:Access:AllowedIPs:1", "::1" } + { $"{Constants.HealthChecksConfigurationKey}:{nameof(HealthChecksOptions.Url)}", "/health" }, + { $"{Constants.HealthChecksAccessConfigurationKey}:AllowedIPs:0", "127.0.0.1" }, + { $"{Constants.HealthChecksAccessConfigurationKey}:AllowedIPs:1", "::1" } }; return new ConfigurationBuilder()