1
0

Added White-/Blacklist Attributes, DateOnly/TimeOnly converters, TcpClientMoq

This commit is contained in:
2022-06-15 19:48:47 +02:00
parent f331be521f
commit 65bca0a922
15 changed files with 570 additions and 16 deletions

View File

@@ -38,11 +38,11 @@
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.1" />
<PackageReference Include="Unclassified.DeepConvert" Version="1.3.0" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.2">
<PackageReference Include="Unclassified.DeepConvert" Version="1.4.0" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
</Project>

View File

@@ -0,0 +1,103 @@
using System.Linq;
using System.Net;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace AMWD.Common.AspNetCore.Attributes
{
/// <summary>
/// Implements an IP filter. The defined addresses are blocked.
/// </summary>
public class IPBlacklistAttribute : ActionFilterAttribute
{
/// <summary>
/// Gets or sets a value indicating whether local (localhost) access is blocked (Default: false).
/// </summary>
public bool RestrictLocalAccess { get; set; }
/// <summary>
/// Gets or sets a configuration key where the blocked IP addresses are defined.
/// </summary>
/// <remarks>
/// JSON configuration example:<br/>
/// {<br/>
/// "ConfigurationKey": [<br/>
/// "10.0.0.0/8",<br/>
/// "172.16.0.0/12",<br/>
/// "fd00:123:abc::13"<br/>
/// ]<br/>
/// }
/// </remarks>
public string ConfigurationKey { get; set; }
/// <summary>
/// Gets or sets a comma separated list of blocked IP addresses.
/// </summary>
public string RestrictedIpAddresses { get; set; }
/// <inheritdoc/>
public override void OnActionExecuting(ActionExecutingContext context)
{
base.OnActionExecuting(context);
if (!RestrictLocalAccess && context.HttpContext.IsLocalRequest())
return;
var remoteIpAddress = context.HttpContext.GetRemoteIpAddress();
if (!string.IsNullOrWhiteSpace(RestrictedIpAddresses))
{
string[] ipAddresses = RestrictedIpAddresses.Split(',');
foreach (string ipAddress in ipAddresses)
{
if (string.IsNullOrWhiteSpace(ipAddress))
continue;
if (MatchesIpAddress(ipAddress, remoteIpAddress))
{
context.Result = new ForbidResult();
return;
}
}
}
var configuration = context.HttpContext.RequestServices.GetService<IConfiguration>();
if (!string.IsNullOrWhiteSpace(ConfigurationKey) && configuration != null)
{
var section = configuration.GetSection(ConfigurationKey);
if (!section.Exists())
return;
foreach (var child in section.GetChildren())
{
if (string.IsNullOrWhiteSpace(child.Value))
continue;
if (MatchesIpAddress(child.Value, remoteIpAddress))
{
context.Result = new ForbidResult();
return;
}
}
}
}
private static bool MatchesIpAddress(string configIpAddress, IPAddress remoteIpAddress)
{
if (configIpAddress.Contains('/'))
{
string[] ipNetworkParts = configIpAddress.Split('/');
var ip = IPAddress.Parse(ipNetworkParts.First());
int prefix = int.Parse(ipNetworkParts.Last());
var net = new IPNetwork(ip, prefix);
return net.Contains(remoteIpAddress);
}
return IPAddress.Parse(configIpAddress).Equals(remoteIpAddress);
}
}
}

View File

@@ -0,0 +1,102 @@
using System.Linq;
using System.Net;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace AMWD.Common.AspNetCore.Attributes
{
/// <summary>
/// Implements an IP filter. Only defined addresses are allowed to access.
/// </summary>
public class IPWhitelistAttribute : ActionFilterAttribute
{
/// <summary>
/// Gets or sets a value indicating whether local (localhost) access is granted (Default: true).
/// </summary>
public bool AllowLocalAccess { get; set; } = true;
/// <summary>
/// Gets or sets a configuration key where the allowed IP addresses are allowed.
/// </summary>
/// <remarks>
/// JSON configuration example:<br/>
/// {<br/>
/// "ConfigurationKey": [<br/>
/// "10.0.0.0/8",<br/>
/// "172.16.0.0/12",<br/>
/// "fd00:123:abc::13"<br/>
/// ]<br/>
/// }
/// </remarks>
public string ConfigurationKey { get; set; }
/// <summary>
/// Gets or sets a comma separated list of allowed IP addresses.
/// </summary>
public string AllowedIpAddresses { get; set; }
/// <inheritdoc/>
public override void OnActionExecuting(ActionExecutingContext context)
{
base.OnActionExecuting(context);
if (AllowLocalAccess && context.HttpContext.IsLocalRequest())
return;
var remoteIpAddress = context.HttpContext.GetRemoteIpAddress();
if (!string.IsNullOrWhiteSpace(AllowedIpAddresses))
{
string[] ipAddresses = AllowedIpAddresses.Split(',');
foreach (string ipAddress in ipAddresses)
{
if (string.IsNullOrWhiteSpace(ipAddress))
continue;
if (MatchesIpAddress(ipAddress, remoteIpAddress))
return;
}
}
var configuration = context.HttpContext.RequestServices.GetService<IConfiguration>();
if (!string.IsNullOrWhiteSpace(ConfigurationKey) && configuration != null)
{
var section = configuration.GetSection(ConfigurationKey);
if (!section.Exists())
{
context.Result = new ForbidResult();
return;
}
foreach (var child in section.GetChildren())
{
if (string.IsNullOrWhiteSpace(child.Value))
continue;
if (MatchesIpAddress(child.Value, remoteIpAddress))
return;
}
}
context.Result = new ForbidResult();
}
private static bool MatchesIpAddress(string configIpAddress, IPAddress remoteIpAddress)
{
if (configIpAddress.Contains('/'))
{
string[] ipNetworkParts = configIpAddress.Split('/');
var ip = IPAddress.Parse(ipNetworkParts.First());
int prefix = int.Parse(ipNetworkParts.Last());
var net = new IPNetwork(ip, prefix);
return net.Contains(remoteIpAddress);
}
return IPAddress.Parse(configIpAddress).Equals(remoteIpAddress);
}
}
}

View File

@@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.Http
/// <summary>
/// Retrieves the antiforgery token.
/// </summary>
/// <param name="httpContext">The web context.</param>
/// <param name="httpContext">The current <see cref="HttpContext"/>.</param>
/// <returns>Name and value of the token.</returns>
public static (string Name, string Value) GetAntiforgeryToken(this HttpContext httpContext)
{
@@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.Http
/// <summary>
/// Returns the remote ip address.
/// </summary>
/// <param name="httpContext">The web context.</param>
/// <param name="httpContext">The current <see cref="HttpContext"/>.</param>
/// <param name="headerName">The name of the header to resolve the <see cref="IPAddress"/> when behind a proxy (Default: X-Forwarded-For).</param>
/// <returns>The ip address of the client.</returns>
public static IPAddress GetRemoteIpAddress(this HttpContext httpContext, string headerName = "X-Forwarded-For")
@@ -39,10 +39,22 @@ namespace Microsoft.AspNetCore.Http
return remote;
}
/// <summary>
/// Returns whether the request was made locally.
/// </summary>
/// <param name="httpContext">The current <see cref="HttpContext"/>.</param>
/// <param name="headerName">The name of the header to resolve the <see cref="IPAddress"/> when behind a proxy (Default: X-Forwarded-For).</param>
/// <returns></returns>
public static bool IsLocalRequest(this HttpContext httpContext, string headerName = "X-Forwarded-For")
{
var remoteIpAddress = httpContext.GetRemoteIpAddress(headerName);
return httpContext.Connection.LocalIpAddress.Equals(remoteIpAddress);
}
/// <summary>
/// Tries to retrieve the return url.
/// </summary>
/// <param name="httpContext"></param>
/// <param name="httpContext">The current <see cref="HttpContext"/>.</param>
/// <returns></returns>
public static string GetReturnUrl(this HttpContext httpContext)
{