Skip to content

Commit 4ae5ed6

Browse files
committed
Add unit test
1 parent 512a318 commit 4ae5ed6

8 files changed

Lines changed: 141 additions & 8 deletions

File tree

Directory.Packages.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
<PackageVersion Include="OrchardCoreContrib.Navigation.Core" Version="1.4.0" />
5454
<PackageVersion Include="OrchardCoreContrib.Sms.Abstractions" Version="1.4.0" />
5555
<PackageVersion Include="OrchardCoreContrib.OpenApi.Abstractions" Version="1.4.0" />
56+
<PackageVersion Include="OrchardCoreContrib.Testing" Version="1.0.0-beta1" />
5657
<PackageVersion Include="Scalar.AspNetCore" Version="2.13.13" />
5758
<PackageVersion Include="SendGrid" Version="9.29.3" />
5859
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="2.1.5" />

OrchardCoreContrib.Modules.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OrchardCoreContrib.Apis.Sca
8686
EndProject
8787
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OrchardCoreContrib.Contents", "src\OrchardCoreContrib.Contents\OrchardCoreContrib.Contents.csproj", "{46481A89-4274-4126-8E23-FE4F3107AD41}"
8888
EndProject
89+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OrchardCoreContrib.HealthChecks.Tests", "test\OrchardCoreContrib.HealthChecks.Tests\OrchardCoreContrib.HealthChecks.Tests.csproj", "{ECFE5B40-2400-4332-9E54-9DBDBC618999}"
90+
EndProject
8991
Global
9092
GlobalSection(SolutionConfigurationPlatforms) = preSolution
9193
Debug|Any CPU = Debug|Any CPU
@@ -228,6 +230,10 @@ Global
228230
{46481A89-4274-4126-8E23-FE4F3107AD41}.Debug|Any CPU.Build.0 = Debug|Any CPU
229231
{46481A89-4274-4126-8E23-FE4F3107AD41}.Release|Any CPU.ActiveCfg = Release|Any CPU
230232
{46481A89-4274-4126-8E23-FE4F3107AD41}.Release|Any CPU.Build.0 = Release|Any CPU
233+
{ECFE5B40-2400-4332-9E54-9DBDBC618999}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
234+
{ECFE5B40-2400-4332-9E54-9DBDBC618999}.Debug|Any CPU.Build.0 = Debug|Any CPU
235+
{ECFE5B40-2400-4332-9E54-9DBDBC618999}.Release|Any CPU.ActiveCfg = Release|Any CPU
236+
{ECFE5B40-2400-4332-9E54-9DBDBC618999}.Release|Any CPU.Build.0 = Release|Any CPU
231237
EndGlobalSection
232238
GlobalSection(SolutionProperties) = preSolution
233239
HideSolutionNode = FALSE
@@ -267,6 +273,7 @@ Global
267273
{B2029246-BC23-4C18-B71A-55F13CB9E7AB} = {A239BFB0-9BA7-467C-AD41-405D0740633F}
268274
{3D421DD2-F842-4F5A-A95E-5AD8D3632712} = {C80A325F-F4C4-4C7B-A3CF-FB77CD8C9949}
269275
{46481A89-4274-4126-8E23-FE4F3107AD41} = {C80A325F-F4C4-4C7B-A3CF-FB77CD8C9949}
276+
{ECFE5B40-2400-4332-9E54-9DBDBC618999} = {A239BFB0-9BA7-467C-AD41-405D0740633F}
270277
EndGlobalSection
271278
GlobalSection(ExtensibilityGlobals) = postSolution
272279
SolutionGuid = {48F73B05-7D3D-4ACF-81AE-A98B2B4EFDB2}

src/OrchardCoreContrib.HealthChecks/HealthCheckIPRestrictionMiddleware.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ public class HealthCheckIPRestrictionMiddleware(
1313
ILogger<HealthCheckIPRestrictionMiddleware> logger)
1414
{
1515
private readonly HealthChecksOptions _healthChecksOptions = healthChecksOptions.Value;
16-
private readonly HashSet<string> _allowedIPs = shellConfiguration.GetSection($"{Constants.ConfigurationKey}:AllowedIPs")
17-
.Get<string[]>()?.ToHashSet() ?? [];
16+
private readonly HashSet<string> _allowedIPs =
17+
shellConfiguration.GetSection($"{Constants.ConfigurationKey}:AllowedIPs").Get<string[]>()?.ToHashSet(StringComparer.OrdinalIgnoreCase)
18+
?? [];
1819

1920
public async Task InvokeAsync(HttpContext context)
2021
{
@@ -24,6 +25,7 @@ public async Task InvokeAsync(HttpContext context)
2425
if (!_allowedIPs.Contains(remoteIP))
2526
{
2627
logger.LogWarning("Unauthorized IP {IP} tried to access {HealthCheckEndpoint}.", remoteIP, _healthChecksOptions.Url);
28+
2729
context.Response.StatusCode = StatusCodes.Status403Forbidden;
2830

2931
await context.Response.WriteAsync("Forbidden");

src/OrchardCoreContrib.Modules.Web/appsettings.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,15 @@
3838
"RequestUrlPrefix": "blog"
3939
}
4040
]
41-
}
41+
},
4242
//"OrchardCoreContrib_Diagnostics_Elm": {
4343
// "Path": "/elm"
4444
//},
45-
//"OrchardCoreContrib_HealthChecks": {
46-
// "Url": "/health",
47-
// "ShowDetails": true,
48-
// "AllowedIPs": [ "127.0.0.1", "::1", "192.168.1.100" ]
49-
//},
45+
"OrchardCoreContrib_HealthChecks": {
46+
"Url": "/health",
47+
"ShowDetails": true,
48+
"AllowedIPs": [ "127.0.0.1", "::1" ]
49+
},
5050
//"OrchardCoreContrib_Garnet": {
5151
// "Host": "127.0.0.1",
5252
// "Port": 3278,
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using OrchardCoreContrib.HealthChecks.Tests.Tests;
2+
using System.Net;
3+
4+
namespace OrchardCoreContrib.HealthChecks.Tests;
5+
6+
public class IPRestrictionTests
7+
{
8+
[Theory]
9+
[InlineData("10.0.0.1", HttpStatusCode.Forbidden)]
10+
[InlineData("127.0.0.1", HttpStatusCode.OK)]
11+
public async Task HealthCheck_RestrictIP_IfClientIPNotInAllowedIPs(string clientIP, HttpStatusCode expectedStatus)
12+
{
13+
// Arrange
14+
using var context = new SaasSiteContext();
15+
16+
await context.InitializeAsync();
17+
18+
// Act
19+
using var request = new HttpRequestMessage(HttpMethod.Get, "health");
20+
21+
request.Headers.TryAddWithoutValidation("X-Forwarded-For", clientIP);
22+
23+
var httpResponse = await context.Client.SendAsync(request);
24+
25+
// Assert
26+
Assert.Equal(expectedStatus, httpResponse.StatusCode);
27+
}
28+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<ImplicitUsings>enable</ImplicitUsings>
5+
<IsPackable>false</IsPackable>
6+
<IsTestProject>true</IsTestProject>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="coverlet.collector" />
11+
<PackageReference Include="Microsoft.NET.Test.Sdk" />
12+
<PackageReference Include="OrchardCoreContrib.Testing" />
13+
<PackageReference Include="xunit" />
14+
<PackageReference Include="xunit.runner.visualstudio" />
15+
</ItemGroup>
16+
17+
<ItemGroup>
18+
<ProjectReference Include="..\..\src\OrchardCoreContrib.HealthChecks\OrchardCoreContrib.HealthChecks.csproj" />
19+
<ProjectReference Include="..\..\src\OrchardCoreContrib.Modules.Web\OrchardCoreContrib.Modules.Web.csproj" />
20+
</ItemGroup>
21+
22+
<ItemGroup>
23+
<Using Include="Xunit" />
24+
</ItemGroup>
25+
26+
</Project>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
using Microsoft.AspNetCore.Authorization;
2+
using Microsoft.AspNetCore.Builder;
3+
using Microsoft.AspNetCore.Http;
4+
using Microsoft.AspNetCore.HttpOverrides;
5+
using Microsoft.Extensions.Configuration;
6+
using Microsoft.Extensions.DependencyInjection;
7+
using OrchardCore.Modules;
8+
using OrchardCoreContrib.Modules.Web;
9+
using OrchardCoreContrib.Testing;
10+
using OrchardCoreContrib.Testing.Security;
11+
12+
namespace OrchardCoreContrib.HealthChecks.Tests.Tests;
13+
14+
public class OrchardCoreStartup(IConfiguration configuration)
15+
{
16+
public void ConfigureServices(IServiceCollection services)
17+
{
18+
services.AddOrchardCms(builder => builder
19+
.AddSetupFeatures("OrchardCore.Tenants")
20+
.AddTenantFeatures("OrchardCoreContrib.HealthChecks.IPRestriction")
21+
.ConfigureServices(serviceCollection =>
22+
{
23+
serviceCollection.AddScoped<IAuthorizationHandler, PermissionContextAuthorizationHandler>(sp =>
24+
new PermissionContextAuthorizationHandler(sp.GetRequiredService<IHttpContextAccessor>(), SiteContextOptions.PermissionsContexts));
25+
26+
serviceCollection.AddSingleton<IConfiguration>(AddHealthChecksConfiguration());
27+
})
28+
.Configure(appBuilder => appBuilder.UseAuthorization()));
29+
30+
services.AddSingleton<IModuleNamesProvider>(new ModuleNamesProvider(typeof(Program).Assembly));
31+
}
32+
33+
public void Configure(IApplicationBuilder app)
34+
{
35+
var forwardedHeadersOptions = new ForwardedHeadersOptions
36+
{
37+
ForwardedHeaders = ForwardedHeaders.XForwardedFor
38+
};
39+
40+
forwardedHeadersOptions.KnownNetworks.Clear();
41+
forwardedHeadersOptions.KnownProxies.Clear();
42+
43+
app.UseForwardedHeaders(forwardedHeadersOptions);
44+
app.UseOrchardCore();
45+
}
46+
47+
private IConfigurationRoot AddHealthChecksConfiguration()
48+
{
49+
var newConfiguration = new Dictionary<string, string>
50+
{
51+
{ $"{Constants.ConfigurationKey}:{nameof(HealthChecksOptions.Url)}", "/health" },
52+
{ $"{Constants.ConfigurationKey}:AllowedIPs:0", "127.0.0.1" },
53+
{ $"{Constants.ConfigurationKey}:AllowedIPs:1", "::1" }
54+
};
55+
56+
return new ConfigurationBuilder()
57+
.AddConfiguration(configuration)
58+
.AddInMemoryCollection(newConfiguration)
59+
.Build();
60+
}
61+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
using OrchardCoreContrib.Testing;
2+
3+
namespace OrchardCoreContrib.HealthChecks.Tests.Tests;
4+
5+
public class SaasSiteContext : SiteContextBase<OrchardCoreStartup>
6+
{
7+
public SaasSiteContext() => Options.RecipeName = "SaaS";
8+
}

0 commit comments

Comments
 (0)