1
0

BasicAuth bezieht Realm aus dem Validator. DNS Resolve für E-Mail Validierung geändert. Moq-Package hinzugefügt.

This commit is contained in:
2022-01-30 14:25:13 +01:00
parent a885f15ebb
commit 80e5382553
11 changed files with 247 additions and 19 deletions

View File

@@ -12,19 +12,16 @@ namespace AMWD.Common.AspNetCore.BasicAuthentication
public class BasicAuthenticationMiddleware
{
private readonly RequestDelegate next;
private readonly string realm;
private readonly IBasicAuthenticationValidator validator;
/// <summary>
/// Initializes a new instance of the <see cref="BasicAuthenticationMiddleware"/> class.
/// </summary>
/// <param name="next">The following delegate in the process chain.</param>
/// <param name="realm">The realm to display when requesting for credentials.</param>
/// <param name="validator">A basic authentication validator.</param>
public BasicAuthenticationMiddleware(RequestDelegate next, string realm, IBasicAuthenticationValidator validator)
public BasicAuthenticationMiddleware(RequestDelegate next, IBasicAuthenticationValidator validator)
{
this.next = next;
this.realm = realm;
this.validator = validator;
}
@@ -53,10 +50,8 @@ namespace AMWD.Common.AspNetCore.BasicAuthentication
}
httpContext.Response.Headers["WWW-Authenticate"] = "Basic";
if (!string.IsNullOrWhiteSpace(realm))
{
httpContext.Response.Headers["WWW-Authenticate"] += $" realm=\"{realm}\"";
}
if (!string.IsNullOrWhiteSpace(validator.Realm))
httpContext.Response.Headers["WWW-Authenticate"] += $" realm=\"{validator.Realm}\"";
httpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
}

View File

@@ -9,6 +9,11 @@ namespace AMWD.Common.AspNetCore.BasicAuthentication
/// </summary>
public interface IBasicAuthenticationValidator
{
/// <summary>
/// Gets or sets the realm to use when requesting authentication.
/// </summary>
string Realm { get; set; }
/// <summary>
/// Validates a username and password for Basic Authentication.
/// </summary>

View File

@@ -66,19 +66,24 @@ namespace Microsoft.EntityFrameworkCore
return DatabaseProvider.SQLite;
if (provider.Contains("sqlclient", StringComparison.OrdinalIgnoreCase))
return DatabaseProvider.SQLServer;
if (provider.Contains("inmemory", StringComparison.OrdinalIgnoreCase))
return DatabaseProvider.InMemory;
throw new DatabaseProviderException($"The database provider '{provider}' is unknown");
}
private static async Task<bool> CreateMigrationsTable(this DbConnection connection, DatabaseMigrationOptions options, CancellationToken cancellationToken)
{
if (connection.GetProviderType() == DatabaseProvider.InMemory)
return true;
try
{
using var command = connection.CreateCommand();
#pragma warning disable CS8524 // missing default case
#pragma warning disable CS8509 // ignore missing cases
command.CommandText = connection.GetProviderType() switch
#pragma warning restore CS8524 // missing default case
#pragma warning restore CS8509 // ignore missing cases
{
DatabaseProvider.MySQL => $@"CREATE TABLE IF NOT EXISTS `{options.MigrationsTableName}` (
`id` INT NOT NULL AUTO_INCREMENT,
@@ -135,6 +140,9 @@ END;"
private static async Task<bool> Migrate(this DbConnection connection, DatabaseMigrationOptions options, CancellationToken cancellationToken)
{
if (connection.GetProviderType() == DatabaseProvider.InMemory)
return true;
try
{
List<string> availableMigrationFiles;
@@ -279,7 +287,8 @@ END;"
Oracle = 2,
PostgreSQL = 3,
SQLite = 4,
SQLServer = 5
SQLServer = 5,
InMemory = 6,
}
}
}

View File

@@ -79,6 +79,10 @@ namespace Microsoft.EntityFrameworkCore
case "mssql":
methodInfo = extensionType.GetMethod("UseSqlServer", new Type[] { typeof(DbContextOptionsBuilder), typeof(string), actionType });
break;
case "memory":
case "inmemory":
methodInfo = extensionType.GetMethod("UseInMemoryDatabase", new Type[] { typeof(DbContextOptionsBuilder), typeof(string), actionType });
break;
default:
throw new DatabaseProviderException($"Unknown database provider: {provider}");
}
@@ -122,6 +126,10 @@ namespace Microsoft.EntityFrameworkCore
case "mssql":
builderType = Type.GetType("Microsoft.EntityFrameworkCore.Infrastructure.SqlServerDbContextOptionsBuilder, Microsoft.EntityFrameworkCore.SqlServer");
break;
case "memory":
case "inmemory":
builderType = Type.GetType("Microsoft.EntityFrameworkCore.Infrastructure.InMemoryDbContextOptionsBuilder, Microsoft.EntityFrameworkCore.InMemory");
break;
default:
throw new ArgumentException($"Unknown database provider: {provider}");
}
@@ -155,6 +163,10 @@ namespace Microsoft.EntityFrameworkCore
case "mssql":
extensionType = Type.GetType("Microsoft.EntityFrameworkCore.SqlServerDbContextOptionsExtensions, Microsoft.EntityFrameworkCore.SqlServer");
break;
case "memory":
case "inmemory":
extensionType = Type.GetType("Microsoft.EntityFrameworkCore.InMemoryDbContextOptionsExtensions, Microsoft.EntityFrameworkCore.InMemory");
break;
default:
throw new ArgumentException($"Unknown database provider: {provider}");
}
@@ -219,6 +231,10 @@ namespace Microsoft.EntityFrameworkCore
}
cs.Add("Connect Timeout=15");
break;
case "memory":
case "inmemory":
cs.Add(configuration.GetValue("Name", provider));
break;
default:
throw new DatabaseProviderException($"Unknown database provider: {provider}");
}

View File

@@ -0,0 +1,46 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>10.0</LangVersion>
<AssemblyName>AMWD.Common.Moq</AssemblyName>
<RootNamespace>AMWD.Common.Moq</RootNamespace>
<NrtRevisionFormat>{semvertag:master:+chash}{!:-dirty}</NrtRevisionFormat>
<AppendTargetFrameworkToOutputPath>true</AppendTargetFrameworkToOutputPath>
<CopyRefAssembliesToPublishDirectory>false</CopyRefAssembliesToPublishDirectory>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<PackageId>AMWD.Common.Moq</PackageId>
<PackageIcon>icon.png</PackageIcon>
<PackageProjectUrl>https://wiki.am-wd.de/libs/common</PackageProjectUrl>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://git.am-wd.de/AM.WD/common.git</RepositoryUrl>
<Product>AM.WD Common Library for Moq</Product>
<Description>Library with classes and extensions used frequently on AM.WD projects.</Description>
<Company>AM.WD</Company>
<Authors>Andreas Müller</Authors>
<Copyright>© {copyright:2020-} AM.WD</Copyright>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup>
<ItemGroup>
<None Include="../icon.png" Pack="true" PackagePath="/" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Moq" Version="4.16.1" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,130 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using Moq;
using Moq.Protected;
namespace AMWD.Common.Moq
{
/// <summary>
/// Wrapps the <see cref="Mock{HttpMessageHandler}"/> including the setup.
/// </summary>
public class HttpMessageHandlerMoq
{
/// <summary>
/// Initializes a new instance of the <see cref="HttpMessageHandlerMoq"/> class.
/// </summary>
public HttpMessageHandlerMoq()
{
Response = new() { StatusCode = HttpStatusCode.OK };
Callbacks = new();
Mock = new();
Mock.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.Callback<HttpRequestMessage, CancellationToken>(async (req, _) =>
{
var callback = new HttpMessageRequestCallback
{
Headers = req.Headers,
Method = req.Method,
Properties = req.Properties,
RequestUri = req.RequestUri,
Version = req.Version
};
if (req.Content != null)
{
callback.ContentBytes = await req.Content.ReadAsByteArrayAsync();
callback.ContentString = await req.Content.ReadAsStringAsync();
}
Callbacks.Add(callback);
})
.ReturnsAsync(Response);
}
/// <summary>
/// Gets the mocked <see cref="HttpMessageHandler"/>.
/// </summary>
public Mock<HttpMessageHandler> Mock { get; }
/// <summary>
/// Gets the placed request.
/// </summary>
public List<HttpMessageRequestCallback> Callbacks { get; private set; }
/// <summary>
/// Gets the HTTP response, that should be "sent".
/// </summary>
public HttpResponseMessage Response { get; private set; }
/// <summary>
/// Disposes and resets the <see cref="Response"/> and <see cref="Callbacks"/>.
/// </summary>
public void Reset()
{
Response.Dispose();
Response = new() { StatusCode = HttpStatusCode.OK };
Callbacks.Clear();
}
/// <summary>
/// Verifies the number of calls to the HTTP request.
/// </summary>
/// <param name="times"></param>
public void Verify(Times times)
=> Mock.Protected().Verify("SendAsync", times, ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>());
/// <summary>
/// Represents the placed HTTP request.
/// </summary>
public class HttpMessageRequestCallback
{
/// <summary>
/// Gets the contents of the HTTP message.
/// </summary>
public byte[] ContentBytes { get; internal set; }
/// <summary>
/// Gets the contents of the HTTP message.
/// </summary>
public string ContentString { get; internal set; }
/// <summary>
/// Gets the collection of HTTP request headers.
/// </summary>
public HttpRequestHeaders Headers { get; internal set; }
/// <summary>
/// Gets the HTTP method used by the HTTP request message.
/// </summary>
public HttpMethod Method { get; internal set; }
/// <summary>
/// Gets of properties for the HTTP request.
/// </summary>
public IDictionary<string, object> Properties { get; internal set; }
/// <summary>
/// Gets the <see cref="Uri"/> used for the HTTP request.
/// </summary>
public Uri RequestUri { get; internal set; }
/// <summary>
/// Gets the <see cref="RequestUri"/> string representation.
/// </summary>
public string RequestUrl => RequestUri?.ToString();
/// <summary>
/// Gets the HTTP message version.
/// </summary>
public Version Version { get; internal set; }
}
}
}

View File

@@ -5,6 +5,7 @@ using System.Net;
using System.Net.Mail;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using DNS.Client;
using DNS.Protocol;
@@ -170,18 +171,17 @@ namespace System
var mailAddress = new MailAddress(email);
bool isValid = mailAddress.Address == email;
if (isValid && nameservers != null)
if (isValid && nameservers?.Any() == true)
{
bool exists = false;
foreach (var nameserver in nameservers)
{
var clientRequest = new ClientRequest(nameserver) { RecursionDesired = true };
clientRequest.Questions.Add(new Question(Domain.FromString(mailAddress.Host), RecordType.MX));
var client = new DnsClient(nameserver);
var waitTask = Task.Run(async () => await client.Resolve(mailAddress.Host, RecordType.MX));
waitTask.Wait();
var response = clientRequest.Resolve()
.ConfigureAwait(false)
.GetAwaiter()
.GetResult();
var response = waitTask.Result;
waitTask.Dispose();
if (response.ResponseCode != ResponseCode.NoError)
continue;

View File

@@ -6,10 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased](https://git.am-wd.de/AM.WD/common/compare/v1.3.0...master) - 0000-00-00
### Added
- New NuGet package `AMWD.Common.Moq` with `HttpRequestHandlerMoq` class
- Supporting `InMemory` Database as provider
### Changed
- Enhanced Stopwatch/Timer delta due to unsharp resolution using timers
- `IBasicAuthenticationValidator` now requires also the realm (resolving a string on DI is bad style)
## [v1.3.0](https://git.am-wd.de/AM.WD/common/compare/v1.2.0...v1.3.0) - 2021-12-21

View File

@@ -26,6 +26,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F2C7556A-99E
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{E5DF156A-6C8B-4004-BA4C-A8DDE6FD3ECD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AMWD.Common.Moq", "AMWD.Common.Moq\AMWD.Common.Moq.csproj", "{6EBA2792-0B66-4C90-89A1-4E1D26D16443}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -48,6 +50,10 @@ Global
{086E3C11-454A-4C8F-AEAA-215BAE9C443F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{086E3C11-454A-4C8F-AEAA-215BAE9C443F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{086E3C11-454A-4C8F-AEAA-215BAE9C443F}.Release|Any CPU.Build.0 = Release|Any CPU
{6EBA2792-0B66-4C90-89A1-4E1D26D16443}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6EBA2792-0B66-4C90-89A1-4E1D26D16443}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6EBA2792-0B66-4C90-89A1-4E1D26D16443}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6EBA2792-0B66-4C90-89A1-4E1D26D16443}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -57,6 +63,7 @@ Global
{725F40C9-8172-487F-B3D0-D7E38B4DB197} = {F2C7556A-99EB-43EB-8954-56A24AFE928F}
{7091CECF-C981-4FB9-9CC6-91C4E65A6356} = {F2C7556A-99EB-43EB-8954-56A24AFE928F}
{086E3C11-454A-4C8F-AEAA-215BAE9C443F} = {E5DF156A-6C8B-4004-BA4C-A8DDE6FD3ECD}
{6EBA2792-0B66-4C90-89A1-4E1D26D16443} = {F2C7556A-99EB-43EB-8954-56A24AFE928F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {961E8DF8-DDF5-4D10-A510-CE409E9962AC}

View File

@@ -40,3 +40,14 @@ rmdir /S /Q bin
dotnet build -c %Configuration% --nologo --no-incremental
move bin\%Configuration%\*.nupkg ..\artifacts
move bin\%Configuration%\*.snupkg ..\artifacts
cd "%~dp0"
cd "AMWD.Common.Moq"
powershell write-host -fore Blue Building AMWD.Common.Moq
rmdir /S /Q bin
dotnet build -c %Configuration% --nologo --no-incremental
move bin\%Configuration%\*.nupkg ..\artifacts
move bin\%Configuration%\*.snupkg ..\artifacts

View File

@@ -28,3 +28,10 @@ dotnet build -c ${CONFIGURATION} --nologo --no-incremental
mv bin/${CONFIGURATION}/*.nupkg ../artifacts
mv bin/${CONFIGURATION}/*.snupkg ../artifacts
popd
pushd AMWD.Common.Moq
rm -rf bin
dotnet build -c ${CONFIGURATION} --nologo --no-incremental
mv bin/${CONFIGURATION}/*.nupkg ../artifacts
mv bin/${CONFIGURATION}/*.snupkg ../artifacts
popd