Skip to content

Commit 7d47e22

Browse files
committed
TD-3730: No Rate Limiting Enabled
1 parent c07874e commit 7d47e22

File tree

7 files changed

+122
-13
lines changed

7 files changed

+122
-13
lines changed

LearningHub.Nhs.WebUI/Controllers/HomeController.cs

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -133,11 +133,12 @@ public IActionResult CreateAccount()
133133
public IActionResult Error(int? httpStatusCode)
134134
{
135135
string originalPathUrlMessage = null;
136-
136+
string originalPath = null;
137137
if (httpStatusCode.HasValue && httpStatusCode.Value == 404)
138138
{
139139
var exceptionHandlerPathFeature = this.HttpContext.Features.Get<IStatusCodeReExecuteFeature>();
140-
originalPathUrlMessage = $"Page Not Found url: {exceptionHandlerPathFeature?.OriginalPath}. ";
140+
originalPath = exceptionHandlerPathFeature?.OriginalPath;
141+
originalPathUrlMessage = $"Page Not Found url: {originalPath}. ";
141142
}
142143

143144
if (this.User.Identity.IsAuthenticated)
@@ -165,16 +166,23 @@ public IActionResult Error(int? httpStatusCode)
165166
}
166167
else
167168
{
168-
this.ViewBag.ErrorHeader = httpStatusCode.Value switch
169+
if (originalPath == "/TooManyRequests")
169170
{
170-
401 => "You do not have permission to access this page",
171-
404 => "We cannot find the page you are looking for",
172-
_ => "We cannot find the page you are looking for",
173-
};
171+
return this.View("TooManyRequests");
172+
}
173+
else
174+
{
175+
this.ViewBag.ErrorHeader = httpStatusCode.Value switch
176+
{
177+
401 => "You do not have permission to access this page",
178+
404 => "We cannot find the page you are looking for",
179+
_ => "We cannot find the page you are looking for",
180+
};
174181

175-
this.ViewBag.HttpStatusCode = httpStatusCode.Value;
176-
this.ViewBag.HomePageUrl = "/home";
177-
return this.View("CustomError");
182+
this.ViewBag.HttpStatusCode = httpStatusCode.Value;
183+
this.ViewBag.HomePageUrl = "/home";
184+
return this.View("CustomError");
185+
}
178186
}
179187
}
180188

LearningHub.Nhs.WebUI/LearningHub.Nhs.WebUI.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@
104104

105105

106106
<ItemGroup>
107+
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
107108
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.1" />
108109
<PackageReference Include="Azure.Storage.Blobs" Version="12.23.0" />
109110
<PackageReference Include="elfhHub.Nhs.Models" Version="3.0.9" />
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
namespace LearningHub.Nhs.WebUI.Middleware
2+
{
3+
using System.Threading.Tasks;
4+
using AspNetCoreRateLimit;
5+
using Microsoft.AspNetCore.Http;
6+
using Microsoft.Extensions.Logging;
7+
using Microsoft.Extensions.Options;
8+
9+
/// <summary>
10+
/// Defines the <see cref="LHIPRateLimitMiddleware" />.
11+
/// </summary>
12+
public class LHIPRateLimitMiddleware : IpRateLimitMiddleware
13+
{
14+
/// <summary>
15+
/// Initializes a new instance of the <see cref="LHIPRateLimitMiddleware"/> class.
16+
/// </summary>
17+
/// <param name="next">The next.</param>
18+
/// <param name="processingStrategy">The processingStrategy.</param>
19+
/// <param name="options">The options.</param>
20+
/// <param name="policyStore">The policyStore.</param>
21+
/// <param name="config">The config.</param>
22+
/// <param name="logger">The logger.</param>
23+
public LHIPRateLimitMiddleware(
24+
RequestDelegate next,
25+
IProcessingStrategy processingStrategy,
26+
IOptions<IpRateLimitOptions> options,
27+
IIpPolicyStore policyStore,
28+
IRateLimitConfiguration config,
29+
ILogger<IpRateLimitMiddleware> logger)
30+
: base(
31+
next,
32+
processingStrategy,
33+
options,
34+
policyStore,
35+
config,
36+
logger)
37+
{
38+
}
39+
40+
/// <summary>
41+
/// The ReturnQuotaExceededResponse method.
42+
/// </summary>
43+
/// <param name="httpContext">The httpContext.</param>
44+
/// <param name="rule">The rule.</param>
45+
/// <param name="retryAfter">The retryAfter.</param>
46+
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
47+
public override Task ReturnQuotaExceededResponse(
48+
HttpContext httpContext,
49+
RateLimitRule rule,
50+
string retryAfter)
51+
{
52+
httpContext.Response.Headers["Location"] = "/TooManyRequests";
53+
httpContext.Response.StatusCode = 302;
54+
return httpContext.Response.WriteAsync(string.Empty);
55+
}
56+
}
57+
}

LearningHub.Nhs.WebUI/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@
8484
app.UseAuthorization();
8585

8686
app.UseMiddleware<NLogMiddleware>();
87-
87+
app.UseMiddleware<LHIPRateLimitMiddleware>();
8888
app.UseStaticFiles();
8989

9090
app.Map(TimezoneInfoMiddleware.TimezoneInfoUrl, b => b.UseMiddleware<TimezoneInfoMiddleware>());

LearningHub.Nhs.WebUI/ServiceCollectionExtension.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System;
44
using System.IdentityModel.Tokens.Jwt;
55
using System.Net;
6+
using AspNetCoreRateLimit;
67
using LearningHub.Nhs.Caching;
78
using LearningHub.Nhs.Models.Binders;
89
using LearningHub.Nhs.Models.Enums;
@@ -110,6 +111,8 @@ public static void ConfigureServices(this IServiceCollection services, IConfigur
110111
}
111112
});
112113

114+
ConfigureIpRateLimiting(services, configuration);
115+
113116
// this method setup so httpcontext is available from controllers
114117
services.AddHttpContextAccessor();
115118
services.AddSingleton(learningHubAuthSvcConf);
@@ -136,5 +139,17 @@ public static void ConfigureServices(this IServiceCollection services, IConfigur
136139

137140
services.AddFeatureManagement();
138141
}
142+
143+
/// <summary>
144+
/// ConfigureIpRateLimiting.
145+
/// </summary>
146+
/// <param name="services">The services.</param>
147+
/// <param name="configuration">The configuration.</param>
148+
private static void ConfigureIpRateLimiting(IServiceCollection services, IConfiguration configuration)
149+
{
150+
services.Configure<IpRateLimitOptions>(configuration.GetSection("IpRateLimiting"));
151+
services.AddInMemoryRateLimiting();
152+
services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
153+
}
139154
}
140155
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
@{
2+
ViewData["Title"] = "Too many requests";
3+
}
4+
<div class="bg-white">
5+
<div class="nhsuk-width-container app-width-container">
6+
<div class="nhsuk-grid-row">
7+
<div class="nhsuk-grid-column-full nhsuk-u-padding-top-9 nhsuk-u-padding-bottom-7">
8+
<h1 class="nhsuk-heading-xl"> @ViewData["Title"]</h1>
9+
<p>Too many requests have been made.</p>
10+
<p>If you need help, please contact the <a href="@ViewBag.SupportFormUrl" target="_blank">support team</a>.</p>
11+
<p>@DateTimeOffset.Now.ToString("d MMMM yyyy HH:mm:ss")</p>
12+
</div>
13+
</div>
14+
</div>
15+
</div>

LearningHub.Nhs.WebUI/appsettings.json

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@
113113
},
114114
"EnableTempDebugging": "false",
115115
"LimitScormToAdmin": "false"
116-
116+
117117
},
118118
"LearningHubAuthServiceConfig": {
119119
"Authority": "",
@@ -158,5 +158,18 @@
158158
"FeatureManagement": {
159159
"ContributeAudioVideoResource": true,
160160
"DisplayAudioVideoResource": true
161-
}
161+
},
162+
"IpRateLimiting": {
163+
"EnableEndpointRateLimiting": true,
164+
"StackBlockedRequests": false,
165+
"RealIpHeader": "X-Real-IP",
166+
"HttpStatusCode": 429,
167+
"GeneralRules": [
168+
{
169+
"Endpoint": "post:/Account/ForgotPassword",
170+
"Period": "1m",
171+
"Limit": 1
172+
}
173+
]
174+
}
162175
}

0 commit comments

Comments
 (0)