Merge branch 'main' into cmd
This commit is contained in:
@@ -4,7 +4,7 @@ using System.Net.Http.Headers;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using AMWD.Common.AspNetCore.BasicAuthentication;
|
||||
using AMWD.Common.AspNetCore.Security.BasicAuthentication;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.Mvc.Filters
|
||||
/// <summary>
|
||||
/// Implements an IP filter. Only defined addresses are allowed to access.
|
||||
/// </summary>
|
||||
public class IPWhitelistAttribute : ActionFilterAttribute
|
||||
public class IPAllowListAttribute : ActionFilterAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether local (localhost) access is granted (Default: true).
|
||||
@@ -10,12 +10,12 @@ namespace Microsoft.AspNetCore.Mvc.Filters
|
||||
/// <summary>
|
||||
/// Implements an IP filter. The defined addresses are blocked.
|
||||
/// </summary>
|
||||
public class IPBlacklistAttribute : ActionFilterAttribute
|
||||
public class IPBlockListAttribute : ActionFilterAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether local (localhost) access is blocked (Default: false).
|
||||
/// </summary>
|
||||
public bool RestrictLocalAccess { get; set; }
|
||||
public bool BlockLocalAccess { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a configuration key where the blocked IP addresses are defined.
|
||||
@@ -35,7 +35,7 @@ namespace Microsoft.AspNetCore.Mvc.Filters
|
||||
/// <summary>
|
||||
/// Gets or sets a comma separated list of blocked IP addresses.
|
||||
/// </summary>
|
||||
public string RestrictedIpAddresses { get; set; }
|
||||
public string BlockedIpAddresses { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void OnActionExecuting(ActionExecutingContext context)
|
||||
@@ -43,13 +43,13 @@ namespace Microsoft.AspNetCore.Mvc.Filters
|
||||
base.OnActionExecuting(context);
|
||||
context.HttpContext.Items["RemoteAddress"] = context.HttpContext.GetRemoteIpAddress();
|
||||
|
||||
if (!RestrictLocalAccess && context.HttpContext.IsLocalRequest())
|
||||
if (!BlockLocalAccess && context.HttpContext.IsLocalRequest())
|
||||
return;
|
||||
|
||||
var remoteIpAddress = context.HttpContext.GetRemoteIpAddress();
|
||||
if (!string.IsNullOrWhiteSpace(RestrictedIpAddresses))
|
||||
if (!string.IsNullOrWhiteSpace(BlockedIpAddresses))
|
||||
{
|
||||
string[] ipAddresses = RestrictedIpAddresses.Split(',');
|
||||
string[] ipAddresses = BlockedIpAddresses.Split(',');
|
||||
foreach (string ipAddress in ipAddresses)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(ipAddress))
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Net;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Antiforgery;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
@@ -9,30 +10,57 @@ namespace Microsoft.AspNetCore.Http
|
||||
/// </summary>
|
||||
public static class HttpContextExtensions
|
||||
{
|
||||
// Search these additional headers for a remote client ip address.
|
||||
private static readonly string[] defaultIpHeaderNames = new[]
|
||||
{
|
||||
"X-Forwarded-For", // commonly used on all known proxies
|
||||
"X-Real-IP", // wide-spread alternative to X-Forwarded-For
|
||||
"CF-Connecting-IP" // set by Cloudflare
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the antiforgery token.
|
||||
/// </summary>
|
||||
/// <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)
|
||||
/// <returns>FormName, HeaderName and Value of the antiforgery token.</returns>
|
||||
public static (string FormName, string HeaderName, string Value) GetAntiforgeryToken(this HttpContext httpContext)
|
||||
{
|
||||
var af = httpContext.RequestServices.GetService<IAntiforgery>();
|
||||
var set = af?.GetAndStoreTokens(httpContext);
|
||||
var antiforgery = httpContext.RequestServices.GetService<IAntiforgery>();
|
||||
var tokenSet = antiforgery?.GetAndStoreTokens(httpContext);
|
||||
|
||||
return (Name: set?.FormFieldName, Value: set?.RequestToken);
|
||||
return (tokenSet?.FormFieldName, tokenSet?.HeaderName, tokenSet?.RequestToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the remote ip address.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Searches for additional headers in the following order:
|
||||
/// <list type="number">
|
||||
/// <item>X-Forwarded-For</item>
|
||||
/// <item>X-Real-IP</item>
|
||||
/// <item>CF-Connecting-IP</item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
/// <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>
|
||||
/// <param name="ipHeaderName">The name of the header to resolve the <see cref="IPAddress"/> when behind a proxy.</param>
|
||||
/// <returns>The ip address of the client.</returns>
|
||||
public static IPAddress GetRemoteIpAddress(this HttpContext httpContext, string headerName = "X-Forwarded-For")
|
||||
public static IPAddress GetRemoteIpAddress(this HttpContext httpContext, string ipHeaderName = null)
|
||||
{
|
||||
string forwardedHeader = httpContext.Request.Headers[headerName].ToString();
|
||||
if (!string.IsNullOrWhiteSpace(forwardedHeader) && IPAddress.TryParse(forwardedHeader, out var forwarded))
|
||||
return forwarded;
|
||||
string forwardedForAddress = null;
|
||||
|
||||
var headerNames = string.IsNullOrWhiteSpace(ipHeaderName) ? defaultIpHeaderNames : new[] { ipHeaderName }.Concat(defaultIpHeaderNames);
|
||||
foreach (string headerName in headerNames)
|
||||
{
|
||||
if (!httpContext.Request.Headers.ContainsKey(headerName))
|
||||
continue;
|
||||
|
||||
forwardedForAddress = httpContext.Request.Headers[headerName].ToString();
|
||||
break;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(forwardedForAddress) && IPAddress.TryParse(forwardedForAddress, out var remoteAddress))
|
||||
return remoteAddress;
|
||||
|
||||
return httpContext.Connection.RemoteIpAddress;
|
||||
}
|
||||
@@ -40,12 +68,20 @@ namespace Microsoft.AspNetCore.Http
|
||||
/// <summary>
|
||||
/// Returns whether the request was made locally.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Searches for additional headers in the following order:
|
||||
/// <list type="number">
|
||||
/// <item>X-Forwarded-For</item>
|
||||
/// <item>X-Real-IP</item>
|
||||
/// <item>CF-Connecting-IP</item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
/// <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>
|
||||
/// <param name="ipHeaderName">The name of the header to resolve the <see cref="IPAddress"/> when behind a proxy.</param>
|
||||
/// <returns></returns>
|
||||
public static bool IsLocalRequest(this HttpContext httpContext, string headerName = "X-Forwarded-For")
|
||||
public static bool IsLocalRequest(this HttpContext httpContext, string ipHeaderName = null)
|
||||
{
|
||||
var remoteIpAddress = httpContext.GetRemoteIpAddress(headerName);
|
||||
var remoteIpAddress = httpContext.GetRemoteIpAddress(ipHeaderName);
|
||||
return httpContext.Connection.LocalIpAddress.Equals(remoteIpAddress);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace AMWD.Common.AspNetCore.BasicAuthentication
|
||||
namespace AMWD.Common.AspNetCore.Security.BasicAuthentication
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements the <see cref="AuthenticationHandler{TOptions}"/> for Basic Authentication.
|
||||
@@ -6,7 +6,7 @@ using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace AMWD.Common.AspNetCore.BasicAuthentication
|
||||
namespace AMWD.Common.AspNetCore.Security.BasicAuthentication
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements a basic authentication.
|
||||
@@ -3,7 +3,7 @@ using System.Security.Claims;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AMWD.Common.AspNetCore.BasicAuthentication
|
||||
namespace AMWD.Common.AspNetCore.Security.BasicAuthentication
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface representing the validation of a basic authentication.
|
||||
@@ -0,0 +1,19 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
|
||||
namespace AMWD.Common.AspNetCore.Security.PathProtection
|
||||
{
|
||||
/// <summary>
|
||||
/// Extnsion for <see cref="IApplicationBuilder"/> to enable folder protection.
|
||||
/// </summary>
|
||||
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
||||
public static class ProtectedPathExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Provide protected paths even for static files.
|
||||
/// </summary>
|
||||
/// <param name="app">The <see cref="IApplicationBuilder"/>.</param>
|
||||
/// <param name="options">The <see cref="ProtectedPathOptions"/> with path and policy name.</param>
|
||||
public static IApplicationBuilder UseProtectedPath(this IApplicationBuilder app, ProtectedPathOptions options)
|
||||
=> app.UseMiddleware<ProtectedPathMiddleware>(options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace AMWD.Common.AspNetCore.Security.PathProtection
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements a check to provide protected paths.
|
||||
/// </summary>
|
||||
public class ProtectedPathMiddleware
|
||||
{
|
||||
private readonly RequestDelegate next;
|
||||
private readonly PathString path;
|
||||
private readonly string policyName;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ProtectedPathExtensions"/> class.
|
||||
/// </summary>
|
||||
/// <param name="next">The following delegate in the process chain.</param>
|
||||
/// <param name="options">The options to configure the middleware.</param>
|
||||
public ProtectedPathMiddleware(RequestDelegate next, ProtectedPathOptions options)
|
||||
{
|
||||
this.next = next;
|
||||
path = options.Path;
|
||||
policyName = options.PolicyName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The delegate invokation.
|
||||
/// Performs the protection check.
|
||||
/// </summary>
|
||||
/// <param name="httpContext">The corresponding HTTP context.</param>
|
||||
/// <param name="authorizationService">The <see cref="IAuthorizationService"/>.</param>
|
||||
/// <returns>An awaitable task.</returns>
|
||||
public async Task InvokeAsync(HttpContext httpContext, IAuthorizationService authorizationService)
|
||||
{
|
||||
if (httpContext.Request.Path.StartsWithSegments(path))
|
||||
{
|
||||
var result = await authorizationService.AuthorizeAsync(httpContext.User, null, policyName);
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
await httpContext.ChallengeAsync();
|
||||
return;
|
||||
}
|
||||
}
|
||||
await next.Invoke(httpContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace AMWD.Common.AspNetCore.Security.PathProtection
|
||||
{
|
||||
/// <summary>
|
||||
/// Options to define which folder should be protected.
|
||||
/// </summary>
|
||||
public class ProtectedPathOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the path to the protected folder.
|
||||
/// </summary>
|
||||
public PathString Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the policy name to use.
|
||||
/// </summary>
|
||||
public string PolicyName { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user