diff --git a/src/Libraries/Nop.Core/Configuration/CommonConfig.cs b/src/Libraries/Nop.Core/Configuration/CommonConfig.cs index d818260ab90..770cce63303 100644 --- a/src/Libraries/Nop.Core/Configuration/CommonConfig.cs +++ b/src/Libraries/Nop.Core/Configuration/CommonConfig.cs @@ -70,4 +70,11 @@ public partial class CommonConfig : IConfig /// Default status code to set on the response when a request is rejected. /// public int RejectionStatusCode { get; set; } = 503; + + /// + /// Gets or sets the collection of user identifiers that are exempt from rate limiting. + /// + /// Add user IDs or IP Addresses to this collection to allow them to bypass rate limit checks. Changes to the + /// collection take effect immediately. + public HashSet RateLimitWhitelist { get; set; } = new(); } \ No newline at end of file diff --git a/src/Presentation/Nop.Web.Framework/Infrastructure/Extensions/ServiceCollectionExtensions.cs b/src/Presentation/Nop.Web.Framework/Infrastructure/Extensions/ServiceCollectionExtensions.cs index 17d4e228f95..2d9b0214002 100644 --- a/src/Presentation/Nop.Web.Framework/Infrastructure/Extensions/ServiceCollectionExtensions.cs +++ b/src/Presentation/Nop.Web.Framework/Infrastructure/Extensions/ServiceCollectionExtensions.cs @@ -14,6 +14,7 @@ using Nop.Core; using Nop.Core.Configuration; using Nop.Core.Domain.Common; +using Nop.Core.Domain.Security; using Nop.Core.Http; using Nop.Core.Infrastructure; using Nop.Core.Security; @@ -118,17 +119,32 @@ public static void ConfigureApplicationServices(this IServiceCollection services builder.Services.AddRateLimiter(options => { var settings = Singleton.Instance.Get(); + var securitySettings = Singleton.Instance; options.GlobalLimiter = PartitionedRateLimiter.Create(httpContext => - RateLimitPartition.GetFixedWindowLimiter( - partitionKey: httpContext.User.Identity?.Name ?? httpContext.Request.Headers.Host.ToString(), + { + var username = httpContext.User.Identity?.Name; + var ip = httpContext.Connection.RemoteIpAddress?.ToString(); + + if (settings.RateLimitWhitelist.Contains(ip) || //allow from configured IP addresses + securitySettings.AdminAreaAllowedIpAddresses.Contains(ip) || //allow from admin area allowed IP addresses + httpContext.Connection.LocalIpAddress == httpContext.Connection.RemoteIpAddress) //allow from localhost + { + return RateLimitPartition.GetNoLimiter(""); + } + + var partitionKey = username ?? ip ?? httpContext.Session?.Id ?? httpContext.Request.Headers.Host; + + return RateLimitPartition.GetFixedWindowLimiter( + partitionKey: partitionKey, factory: partition => new FixedWindowRateLimiterOptions { AutoReplenishment = true, PermitLimit = settings.PermitLimit, QueueLimit = settings.QueueCount, Window = TimeSpan.FromMinutes(1) - })); + }); + }); options.RejectionStatusCode = settings.RejectionStatusCode; });