diff --git a/AMWD.Common.AspNetCore/AMWD.Common.AspNetCore.csproj b/AMWD.Common.AspNetCore/AMWD.Common.AspNetCore.csproj index 191b0d1..8aeae5d 100644 --- a/AMWD.Common.AspNetCore/AMWD.Common.AspNetCore.csproj +++ b/AMWD.Common.AspNetCore/AMWD.Common.AspNetCore.csproj @@ -35,30 +35,8 @@ - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/AMWD.Common.AspNetCore/AuthenticationHandler/BasicAuthenticationHandler.cs b/AMWD.Common.AspNetCore/AuthenticationHandler/BasicAuthenticationHandler.cs new file mode 100644 index 0000000..22634dc --- /dev/null +++ b/AMWD.Common.AspNetCore/AuthenticationHandler/BasicAuthenticationHandler.cs @@ -0,0 +1,70 @@ +using System; +using System.Net.Http.Headers; +using System.Security.Claims; +using System.Text; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Implements the for Basic Authentication. + /// + public class BasicAuthenticationHandler : AuthenticationHandler + { + private readonly ILogger logger; + private readonly IBasicAuthenticationValidator validator; + + /// + /// Initializes a new instance of the class. + /// + /// The authentication scheme options. + /// The logger factory. + /// The URL encoder. + /// The system clock. + /// An basic autentication validator implementation. + public BasicAuthenticationHandler(IOptionsMonitor options, ILoggerFactory loggerFactory, UrlEncoder encoder, ISystemClock clock, IBasicAuthenticationValidator validator) + : base(options, loggerFactory, encoder, clock) + { + logger = loggerFactory.CreateLogger(); + this.validator = validator; + } + + /// + protected override async Task HandleAuthenticateAsync() + { + var endpoint = Context.GetEndpoint(); + if (endpoint?.Metadata?.GetMetadata() != null) + return AuthenticateResult.NoResult(); + + if (!Request.Headers.ContainsKey("Authorization")) + return AuthenticateResult.Fail("Authorization header missing"); + + ClaimsPrincipal principal; + try + { + var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]); + string plain = Encoding.UTF8.GetString(Convert.FromBase64String(authHeader.Parameter)); + string[] credentials = plain.Split(':', 2); + + var ipAddress = Context.GetRemoteIpAddress(); + principal = await validator.ValidateAsync(credentials[0], credentials[1], ipAddress); + } + catch (Exception ex) + { + logger.LogError(ex, $"Handling the Basic Authentication failed: {ex.Message}"); + return AuthenticateResult.Fail("Authorization header invalid"); + } + + if (principal == null) + return AuthenticateResult.Fail("Invalid credentials"); + + var ticket = new AuthenticationTicket(principal, Scheme.Name); + return AuthenticateResult.Success(ticket); + } + } +} diff --git a/AMWD.Common.AspNetCore/AuthenticationHandler/IBasicAuthenticationValidator.cs b/AMWD.Common.AspNetCore/AuthenticationHandler/IBasicAuthenticationValidator.cs new file mode 100644 index 0000000..6993641 --- /dev/null +++ b/AMWD.Common.AspNetCore/AuthenticationHandler/IBasicAuthenticationValidator.cs @@ -0,0 +1,21 @@ +using System.Net; +using System.Security.Claims; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Interface representing the validation of a basic authentication. + /// + public interface IBasicAuthenticationValidator + { + /// + /// Validates a username and password for Basic Authentication. + /// + /// A username. + /// A password. + /// The remote client's IP address. + /// The validated user principal or null. + Task ValidateAsync(string username, string password, IPAddress remoteAddress); + } +} diff --git a/AMWD.Common/AMWD.Common.csproj b/AMWD.Common/AMWD.Common.csproj index c451611..aaf036e 100644 --- a/AMWD.Common/AMWD.Common.csproj +++ b/AMWD.Common/AMWD.Common.csproj @@ -36,7 +36,7 @@ - + diff --git a/AMWD.Common/Extensions/StringExtensions.cs b/AMWD.Common/Extensions/StringExtensions.cs index 96cb18b..df766bd 100644 --- a/AMWD.Common/Extensions/StringExtensions.cs +++ b/AMWD.Common/Extensions/StringExtensions.cs @@ -5,7 +5,8 @@ using System.Net; using System.Net.Mail; using System.Text; using System.Text.RegularExpressions; -using DnsClient; +using DNS.Client; +using DNS.Protocol; namespace System { @@ -148,10 +149,10 @@ namespace System /// You can enhance the check by requesting the MX record of the domain. /// /// The email address as string to validate. - /// A value indicating whether to resolve a MX record. + /// A value indicating whether to resolve a MX record (Google DNS is used). /// true when the email address is valid, other wise false. public static bool IsValidEmailAddress(this string email, bool checkRecordExists = false) - => email.IsValidEmailAddress(checkRecordExists ? new LookupClient() : null); + => email.IsValidEmailAddress(checkRecordExists ? new[] { new IPEndPoint(IPAddress.Parse("8.8.8.8"), 53) } : null); /// /// Checks whether the given is a valid . @@ -163,19 +164,36 @@ namespace System /// A list of s of nameservers. /// true when the email address is valid, other wise false. public static bool IsValidEmailAddress(this string email, IEnumerable nameservers) - => email.IsValidEmailAddress(new LookupClient(nameservers.ToArray())); - - private static bool IsValidEmailAddress(this string email, LookupClient lookupClient) { try { var mailAddress = new MailAddress(email); bool isValid = mailAddress.Address == email; - if (isValid && lookupClient != null) + if (isValid && nameservers != null) { - var result = lookupClient.Query(mailAddress.Host, QueryType.MX); - isValid &= result.Answers.MxRecords().Any(); + 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 response = clientRequest.Resolve() + .ConfigureAwait(false) + .GetAwaiter() + .GetResult(); + + if (response.ResponseCode != ResponseCode.NoError) + continue; + + exists = response.AnswerRecords + .Where(r => r.Type == RecordType.MX) + .Any(); + + break; + } + + isValid &= exists; } return isValid;