diff --git a/AMWD.Common.AspNetCore/BasicAuthentication/BasicAuthenticationAttribute.cs b/AMWD.Common.AspNetCore/Attributes/BasicAuthenticationAttribute.cs
similarity index 71%
rename from AMWD.Common.AspNetCore/BasicAuthentication/BasicAuthenticationAttribute.cs
rename to AMWD.Common.AspNetCore/Attributes/BasicAuthenticationAttribute.cs
index 61e3825..337aefc 100644
--- a/AMWD.Common.AspNetCore/BasicAuthentication/BasicAuthenticationAttribute.cs
+++ b/AMWD.Common.AspNetCore/Attributes/BasicAuthenticationAttribute.cs
@@ -1,31 +1,30 @@
using System;
+using System.Linq;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
+using AMWD.Common.AspNetCore.BasicAuthentication;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
-namespace AMWD.Common.AspNetCore.BasicAuthentication
+namespace AMWD.Common.AspNetCore.Attributes
{
///
/// A basic authentication as attribute to use for specific actions.
///
public class BasicAuthenticationAttribute : ActionFilterAttribute
{
- private readonly ILogger logger;
private readonly IServiceScopeFactory serviceScopeFactory;
///
/// Initializes a new instance of the class.
///
- /// A logger.
/// A service scope factory.
- public BasicAuthenticationAttribute(ILogger logger, IServiceScopeFactory serviceScopeFactory)
+ public BasicAuthenticationAttribute(IServiceScopeFactory serviceScopeFactory)
{
- this.logger = logger;
this.serviceScopeFactory = serviceScopeFactory;
}
@@ -62,29 +61,30 @@ namespace AMWD.Common.AspNetCore.BasicAuthentication
return;
}
+ using var scope = serviceScopeFactory.CreateScope();
+ var logger = scope.ServiceProvider.GetService>();
+
try
{
var authHeader = AuthenticationHeaderValue.Parse(context.HttpContext.Request.Headers["Authorization"]);
byte[] decoded = Convert.FromBase64String(authHeader.Parameter);
string plain = Encoding.UTF8.GetString(decoded);
- string[] credentials = plain.Split(':', 2);
+ string[] credentials = plain.Split(':', 2, StringSplitOptions.RemoveEmptyEntries);
if (!string.IsNullOrWhiteSpace(Username) && !string.IsNullOrWhiteSpace(Password))
{
- if (Username == credentials[0] && Password == credentials[1])
+ if (Username == credentials.First() && Password == credentials.Last())
return;
}
- using var scope = serviceScopeFactory.CreateScope();
var validator = scope.ServiceProvider.GetService();
-
- var principal = await validator?.ValidateAsync(credentials[0], credentials[1], context.HttpContext.GetRemoteIpAddress());
+ var principal = await validator?.ValidateAsync(credentials.First(), credentials.Last(), context.HttpContext.GetRemoteIpAddress());
if (principal == null)
SetAuthenticateRequest(context);
}
catch (Exception ex)
{
- logger.LogError(ex, $"Failed to execute the basic authentication attribute: {ex.Message}");
+ logger?.LogError(ex, $"Failed to execute the basic authentication attribute: {ex.Message}");
context.Result = new StatusCodeResult(StatusCodes.Status500InternalServerError);
}
}
@@ -95,8 +95,8 @@ namespace AMWD.Common.AspNetCore.BasicAuthentication
if (!string.IsNullOrWhiteSpace(Realm))
context.HttpContext.Response.Headers["WWW-Authenticate"] += $" realm=\"{Realm.Replace("\"", "")}\"";
- context.HttpContext.Response.StatusCode = 401;
- context.Result = new UnauthorizedResult();
+ context.HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
+ context.Result = new StatusCodeResult(StatusCodes.Status401Unauthorized);
}
}
}
diff --git a/AMWD.Common.AspNetCore/Attributes/IPBlacklistAttribute.cs b/AMWD.Common.AspNetCore/Attributes/IPBlacklistAttribute.cs
index 9dbd87a..c18dca5 100644
--- a/AMWD.Common.AspNetCore/Attributes/IPBlacklistAttribute.cs
+++ b/AMWD.Common.AspNetCore/Attributes/IPBlacklistAttribute.cs
@@ -58,7 +58,7 @@ namespace AMWD.Common.AspNetCore.Attributes
if (MatchesIpAddress(ipAddress, remoteIpAddress))
{
- context.Result = new ForbidResult();
+ context.Result = new StatusCodeResult(StatusCodes.Status403Forbidden);
return;
}
}
@@ -78,7 +78,7 @@ namespace AMWD.Common.AspNetCore.Attributes
if (MatchesIpAddress(child.Value, remoteIpAddress))
{
- context.Result = new ForbidResult();
+ context.Result = new StatusCodeResult(StatusCodes.Status403Forbidden);
return;
}
}
@@ -87,17 +87,24 @@ namespace AMWD.Common.AspNetCore.Attributes
private static bool MatchesIpAddress(string configIpAddress, IPAddress remoteIpAddress)
{
- if (configIpAddress.Contains('/'))
+ try
{
- string[] ipNetworkParts = configIpAddress.Split('/');
- var ip = IPAddress.Parse(ipNetworkParts.First());
- int prefix = int.Parse(ipNetworkParts.Last());
+ 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);
+ var net = new IPNetwork(ip, prefix);
+ return net.Contains(remoteIpAddress);
+ }
+
+ return IPAddress.Parse(configIpAddress).Equals(remoteIpAddress);
+ }
+ catch
+ {
+ return false;
}
-
- return IPAddress.Parse(configIpAddress).Equals(remoteIpAddress);
}
}
}
diff --git a/AMWD.Common.AspNetCore/Attributes/IPWhitelistAttribute.cs b/AMWD.Common.AspNetCore/Attributes/IPWhitelistAttribute.cs
index 568fe6d..27a7d48 100644
--- a/AMWD.Common.AspNetCore/Attributes/IPWhitelistAttribute.cs
+++ b/AMWD.Common.AspNetCore/Attributes/IPWhitelistAttribute.cs
@@ -67,7 +67,7 @@ namespace AMWD.Common.AspNetCore.Attributes
var section = configuration.GetSection(ConfigurationKey);
if (!section.Exists())
{
- context.Result = new ForbidResult();
+ context.Result = new StatusCodeResult(StatusCodes.Status403Forbidden);
return;
}
@@ -81,22 +81,29 @@ namespace AMWD.Common.AspNetCore.Attributes
}
}
- context.Result = new ForbidResult();
+ context.Result = new StatusCodeResult(StatusCodes.Status403Forbidden);
}
private static bool MatchesIpAddress(string configIpAddress, IPAddress remoteIpAddress)
{
- if (configIpAddress.Contains('/'))
+ try
{
- string[] ipNetworkParts = configIpAddress.Split('/');
- var ip = IPAddress.Parse(ipNetworkParts.First());
- int prefix = int.Parse(ipNetworkParts.Last());
+ 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);
+ var net = new IPNetwork(ip, prefix);
+ return net.Contains(remoteIpAddress);
+ }
+
+ return IPAddress.Parse(configIpAddress).Equals(remoteIpAddress);
+ }
+ catch
+ {
+ return false;
}
-
- return IPAddress.Parse(configIpAddress).Equals(remoteIpAddress);
}
}
}
diff --git a/AMWD.Common.AspNetCore/BasicAuthentication/BasicAuthenticationHandler.cs b/AMWD.Common.AspNetCore/BasicAuthentication/BasicAuthenticationHandler.cs
index 05dcd28..fbde971 100644
--- a/AMWD.Common.AspNetCore/BasicAuthentication/BasicAuthenticationHandler.cs
+++ b/AMWD.Common.AspNetCore/BasicAuthentication/BasicAuthenticationHandler.cs
@@ -1,4 +1,5 @@
using System;
+using System.Linq;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text;
@@ -50,10 +51,10 @@ namespace AMWD.Common.AspNetCore.BasicAuthentication
{
var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
string plain = Encoding.UTF8.GetString(Convert.FromBase64String(authHeader.Parameter));
- string[] credentials = plain.Split(':', 2);
+ string[] credentials = plain.Split(':', 2, StringSplitOptions.RemoveEmptyEntries);
var ipAddress = Context.GetRemoteIpAddress();
- principal = await validator.ValidateAsync(credentials[0], credentials[1], ipAddress);
+ principal = await validator.ValidateAsync(credentials.First(), credentials.Last(), ipAddress);
}
catch (Exception ex)
{
diff --git a/AMWD.Common.AspNetCore/BasicAuthentication/BasicAuthenticationMiddleware.cs b/AMWD.Common.AspNetCore/BasicAuthentication/BasicAuthenticationMiddleware.cs
index b96c046..ea574a1 100644
--- a/AMWD.Common.AspNetCore/BasicAuthentication/BasicAuthenticationMiddleware.cs
+++ b/AMWD.Common.AspNetCore/BasicAuthentication/BasicAuthenticationMiddleware.cs
@@ -1,5 +1,6 @@
using System;
using System.Linq;
+using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
@@ -33,25 +34,39 @@ namespace AMWD.Common.AspNetCore.BasicAuthentication
/// An awaitable task.
public async Task InvokeAsync(HttpContext httpContext)
{
- if (httpContext.Request.Headers.TryGetValue("Authorization", out var authHeader)
- && ((string)authHeader).StartsWith("Basic "))
+ if (!httpContext.Request.Headers.ContainsKey("Authorization"))
{
- string encoded = ((string)authHeader).Split(' ', StringSplitOptions.RemoveEmptyEntries).LastOrDefault() ?? "";
-
- string decoded = Encoding.UTF8.GetString(Convert.FromBase64String(encoded));
- string[] parts = decoded.Split(':', 2);
-
- var principal = validator.ValidateAsync(parts[0], parts[1], httpContext.GetRemoteIpAddress());
- if (principal != null)
- {
- await next.Invoke(httpContext);
- return;
- }
+ SetAuthenticateRequest(httpContext, validator.Realm);
+ return;
}
+ try
+ {
+ var authHeader = AuthenticationHeaderValue.Parse(httpContext.Request.Headers["Authorization"]);
+ byte[] decoded = Convert.FromBase64String(authHeader.Parameter);
+ string plain = Encoding.UTF8.GetString(decoded);
+ string[] credentials = plain.Split(':', 2, StringSplitOptions.RemoveEmptyEntries);
+
+ var principal = await validator.ValidateAsync(credentials.First(), credentials.Last(), httpContext.GetRemoteIpAddress());
+ if (principal == null)
+ {
+ SetAuthenticateRequest(httpContext, validator.Realm);
+ return;
+ }
+
+ await next.Invoke(httpContext);
+ }
+ catch
+ {
+ SetAuthenticateRequest(httpContext, validator.Realm);
+ }
+ }
+
+ private static void SetAuthenticateRequest(HttpContext httpContext, string realm)
+ {
httpContext.Response.Headers["WWW-Authenticate"] = "Basic";
- if (!string.IsNullOrWhiteSpace(validator.Realm))
- httpContext.Response.Headers["WWW-Authenticate"] += $" realm=\"{validator.Realm}\"";
+ if (!string.IsNullOrWhiteSpace(realm))
+ httpContext.Response.Headers["WWW-Authenticate"] += $" realm=\"{realm.Replace("\"", "")}\"";
httpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8b5d4ef..dcd479b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,10 +4,19 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-## [Unreleased](https://git.am-wd.de/AM.WD/common/compare/v1.5.2...master) - 0000-00-00
+## [Unreleased](https://git.am-wd.de/AM.WD/common/compare/v1.5.3...master) - 0000-00-00
_nothing changed yet_
+## [v1.5.3](https://git.am-wd.de/AM.WD/common/compare/v1.5.2...v1.5.3) - 2022-06-22
+### Fixed
+- Fixed problem with `ForbidResult` without having an authentication schema defined.
+ Now only HTTP Status 403 (Forbid) is returned.
+
+### Changed
+- `BasicAuthenticationAttribute` is now in namespace `AMWD.Common.AspNetCore.Attributes`.
+
+
## [v1.5.2](https://git.am-wd.de/AM.WD/common/compare/v1.5.1...v1.5.2) - 2022-06-20
### Removed
- Removed support for .NET 5.0 due to EOL (2022-05-10, see [.NET and .NET Core release lifecycle](https://dotnet.microsoft.com/en-us/platform/support/policy/dotnet-core#lifecycle))