1
0

Moved all UnitTests to a single project. Implemented parts of AspNetCore UnitTests.

This commit is contained in:
2022-07-17 12:21:05 +02:00
parent 73038bbe5a
commit a26d6a0036
46 changed files with 2411 additions and 105 deletions

View File

@@ -16,6 +16,7 @@ namespace Microsoft.AspNetCore.Authorization
/// <summary>
/// A basic authentication as attribute to use for specific actions.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class BasicAuthenticationAttribute : Attribute, IAsyncAuthorizationFilter
{
/// <summary>
@@ -53,11 +54,14 @@ namespace Microsoft.AspNetCore.Authorization
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, StringSplitOptions.RemoveEmptyEntries);
// See: https://www.rfc-editor.org/rfc/rfc2617, page 6
string username = plain.Split(':').First();
string password = plain[(username.Length + 1)..];
if (!string.IsNullOrWhiteSpace(Username) && !string.IsNullOrWhiteSpace(Password))
{
if (Username == credentials.First() && Password == credentials.Last())
if (Username == username && Password == password)
return;
}
@@ -82,7 +86,7 @@ namespace Microsoft.AspNetCore.Authorization
context.HttpContext.Response.Headers["WWW-Authenticate"] = "Basic";
if (!string.IsNullOrWhiteSpace(realm))
context.HttpContext.Response.Headers["WWW-Authenticate"] += $" realm=\"{realm.Trim().Replace("\"", "")}\"";
context.HttpContext.Response.Headers["WWW-Authenticate"] = $"Basic realm=\"{realm.Trim().Replace("\"", "")}\"";
context.HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
context.Result = new StatusCodeResult(StatusCodes.Status401Unauthorized);
@@ -98,22 +102,29 @@ namespace Microsoft.AspNetCore.Authorization
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, StringSplitOptions.RemoveEmptyEntries);
// See: https://www.rfc-editor.org/rfc/rfc2617, page 6
string username = plain.Split(':').First();
string password = plain[(username.Length + 1)..];
var validator = context.HttpContext.RequestServices.GetService<IBasicAuthenticationValidator>();
var result = await validator?.ValidateAsync(credentials.First(), credentials.Last(), context.HttpContext.GetRemoteIpAddress());
if (result != null)
context.HttpContext.User = result;
if (validator == null)
return null;
var result = await validator.ValidateAsync(username, password, context.HttpContext.GetRemoteIpAddress());
if (result == null)
return null;
context.HttpContext.User = result;
return result;
}
return null;
}
catch (Exception ex)
{
logger?.LogError(ex, $"Using validator to get HTTP user failed: {ex.InnerException?.Message ?? ex.Message}");
return null;
}
return null;
}
}
}

View File

@@ -30,7 +30,7 @@ namespace Microsoft.AspNetCore.Mvc.Filters
/// <br/>
/// The score from google can be found on HttpContext.Items[GoogleReCaptchaAttribute.ScoreKey].
/// </remarks>
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public class GoogleReCaptchaAttribute : ActionFilterAttribute
{
/// <summary>

View File

@@ -16,6 +16,7 @@ namespace AMWD.Common.AspNetCore.BasicAuthentication
/// <summary>
/// Implements the <see cref="AuthenticationHandler{TOptions}"/> for Basic Authentication.
/// </summary>
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
private readonly ILogger logger;
@@ -51,10 +52,13 @@ 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, StringSplitOptions.RemoveEmptyEntries);
// See: https://www.rfc-editor.org/rfc/rfc2617, page 6
string username = plain.Split(':').First();
string password = plain[(username.Length + 1)..];
var ipAddress = Context.GetRemoteIpAddress();
principal = await validator.ValidateAsync(credentials.First(), credentials.Last(), ipAddress);
principal = await validator.ValidateAsync(username, password, ipAddress);
}
catch (Exception ex)
{

View File

@@ -4,6 +4,7 @@ using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
namespace AMWD.Common.AspNetCore.BasicAuthentication
{
@@ -45,9 +46,12 @@ namespace AMWD.Common.AspNetCore.BasicAuthentication
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());
// See: https://www.rfc-editor.org/rfc/rfc2617, page 6
string username = plain.Split(':').First();
string password = plain[(username.Length + 1)..];
var principal = await validator.ValidateAsync(username, password, httpContext.GetRemoteIpAddress());
if (principal == null)
{
SetAuthenticateRequest(httpContext, validator.Realm);
@@ -56,9 +60,11 @@ namespace AMWD.Common.AspNetCore.BasicAuthentication
await next.Invoke(httpContext);
}
catch
catch (Exception ex)
{
SetAuthenticateRequest(httpContext, validator.Realm);
var logger = (ILogger<BasicAuthenticationMiddleware>)httpContext.RequestServices.GetService(typeof(ILogger<BasicAuthenticationMiddleware>));
logger?.LogError(ex, $"Falied to execute basic authentication middleware: {ex.InnerException?.Message ?? ex.Message}");
httpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;
}
}
@@ -66,7 +72,7 @@ namespace AMWD.Common.AspNetCore.BasicAuthentication
{
httpContext.Response.Headers["WWW-Authenticate"] = "Basic";
if (!string.IsNullOrWhiteSpace(realm))
httpContext.Response.Headers["WWW-Authenticate"] += $" realm=\"{realm.Replace("\"", "")}\"";
httpContext.Response.Headers["WWW-Authenticate"] = $"Basic realm=\"{realm.Replace("\"", "")}\"";
httpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
}

View File

@@ -8,6 +8,7 @@ namespace Microsoft.AspNetCore.Builder
/// <summary>
/// Extensions for the <see cref="IApplicationBuilder"/>.
/// </summary>
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public static class ApplicationBuilderExtensions
{
/// <summary>

View File

@@ -7,6 +7,7 @@ namespace AMWD.Common.AspNetCore.Extensions
/// <summary>
/// Extensions for the HTML (e.g. <see cref="IHtmlHelper"/>).
/// </summary>
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public static class HtmlExtensions
{
/// <summary>

View File

@@ -30,13 +30,11 @@ namespace Microsoft.AspNetCore.Http
/// <returns>The ip address of the client.</returns>
public static IPAddress GetRemoteIpAddress(this HttpContext httpContext, string headerName = "X-Forwarded-For")
{
var remote = httpContext.Connection.RemoteIpAddress;
string forwardedHeader = httpContext.Request.Headers[headerName].ToString();
if (!string.IsNullOrWhiteSpace(forwardedHeader) && IPAddress.TryParse(forwardedHeader, out var forwarded))
return forwarded;
return remote;
return httpContext.Connection.RemoteIpAddress;
}
/// <summary>
@@ -58,11 +56,13 @@ namespace Microsoft.AspNetCore.Http
/// <returns></returns>
public static string GetReturnUrl(this HttpContext httpContext)
{
string url = httpContext.Items["OriginalRequest"]?.ToString();
if (string.IsNullOrWhiteSpace(url))
url = httpContext.Request.Query["ReturnUrl"].ToString();
if (httpContext.Items.ContainsKey("OriginalRequest"))
return httpContext.Items["OriginalRequest"].ToString();
return url;
if (httpContext.Request.Query.ContainsKey("ReturnUrl"))
return httpContext.Request.Query["ReturnUrl"].ToString();
return null;
}
/// <summary>
@@ -70,6 +70,6 @@ namespace Microsoft.AspNetCore.Http
/// </summary>
/// <param name="httpContext">The current <see cref="HttpContext"/>.</param>
public static void ClearSession(this HttpContext httpContext)
=> httpContext?.Session?.Clear();
=> httpContext.Session?.Clear();
}
}

View File

@@ -6,6 +6,7 @@ namespace Microsoft.Extensions.Logging
/// <summary>
/// Extensions for the <see cref="ILogger"/>.
/// </summary>
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
internal static class LoggerExtensions
{
// Found here:

View File

@@ -5,6 +5,7 @@ namespace Microsoft.Extensions.DependencyInjection
/// <summary>
/// Extensions for the <see cref="IServiceCollection"/>.
/// </summary>
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public static class ServiceCollectionExtensions
{
/// <summary>

View File

@@ -9,6 +9,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
/// <summary>
/// Custom floating point ModelBinder as the team of Microsoft is not capable of fixing their <see href="https://github.com/dotnet/aspnetcore/issues/6566">issue</see> with other cultures than en-US.
/// </summary>
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public class InvariantFloatingPointModelBinder : IModelBinder
{
private readonly NumberStyles supportedNumberStyles;

View File

@@ -20,6 +20,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
/// options.ModelBinderProviders.Insert(0, new CustomFloatingPointModelBinderProvider());<br/>
/// });</code>
/// </remarks>
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public class InvariantFloatingPointModelBinderProvider : IModelBinderProvider
{
/// <summary>

View File

@@ -9,6 +9,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
/// <summary>
/// A tag helper that adds a CSS class attribute based on a condition.
/// </summary>
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
[HtmlTargetElement(Attributes = ClassPrefix + "*")]
public class ConditionClassTagHelper : TagHelper
{

View File

@@ -8,6 +8,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
/// <summary>
/// A tag helper to create a obfuscated email link.
/// </summary>
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
[HtmlTargetElement("email", TagStructure = TagStructure.WithoutEndTag)]
public class EmailTagHelper : TagHelper
{

View File

@@ -13,6 +13,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
/// <summary>
/// A tag helper to dynamically create integrity checks for linked sources.
/// </summary>
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
[HtmlTargetElement("link")]
[HtmlTargetElement("script")]
public class IntegrityHashTagHelper : TagHelper

View File

@@ -9,6 +9,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
/// <summary>
/// Adds additional behavior to the modelbinding for numeric properties.
/// </summary>
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
[HtmlTargetElement("input", Attributes = "asp-for")]
public class NumberInputTagHelper : InputTagHelper
{

View File

@@ -7,6 +7,7 @@ namespace Microsoft.Extensions.Hosting
/// Wrapper class to start a background service.
/// </summary>
/// <typeparam name="TService">The service type.</typeparam>
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public class BackgroundServiceStarter<TService> : IHostedService
where TService : class, IHostedService
{

View File

@@ -17,7 +17,7 @@ namespace AMWD.Common.AspNetCore.Utilities
public static bool IsDarkColor(string color)
{
if (string.IsNullOrWhiteSpace(color))
return false;
throw new ArgumentNullException(nameof(color));
int r, g, b;
@@ -27,9 +27,9 @@ namespace AMWD.Common.AspNetCore.Utilities
if (rgbMatch.Success)
{
r = Convert.ToInt32(rgbMatch.Groups[1].Value);
g = Convert.ToInt32(rgbMatch.Groups[2].Value);
b = Convert.ToInt32(rgbMatch.Groups[3].Value);
r = Convert.ToInt32(rgbMatch.Groups[1].Value, 10);
g = Convert.ToInt32(rgbMatch.Groups[2].Value, 10);
b = Convert.ToInt32(rgbMatch.Groups[3].Value, 10);
}
else if (hexMatchFull.Success)
{
@@ -45,7 +45,7 @@ namespace AMWD.Common.AspNetCore.Utilities
}
else
{
return false;
throw new NotSupportedException($"Unknown color value '{color}'");
}
double luminance = (r * 0.299 + g * 0.587 + b * 0.114) / 255;