Skip to content

Commit

Permalink
Dev (#30)
Browse files Browse the repository at this point in the history
+ Added `HostAttribute.UseCache`
+ Added `HostAttribute.CheckIfExistsCache`
  • Loading branch information
nd1012 authored Sep 21, 2024
1 parent 6617de3 commit c74921d
Show file tree
Hide file tree
Showing 10 changed files with 86 additions and 60 deletions.
3 changes: 3 additions & 0 deletions src/ObjectValidation CountryValidator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ enumerable lengths
- XRechnung routing validation
- European VAT ID validation
- XML validation
- Hostname or IP address validation
- Conditional value requirements
- Enumeration value validation
- Validation references
Expand Down Expand Up @@ -81,6 +82,7 @@ The **ObjectValidation-CountryValidator** extension is licensed using the
| XRechnung routing validation | `XRechnungRouteAttribute` |
| European VAT ID validation | `EuVatIdAttribute` |
| XML validation | `XmlAttribute` |
| Hostname or IP address validation | `HostAttribute` |
| Conditional value requirement | `RequiredIfAttribute` |
| Allowed/denied values | `AllowedValuesAttribute`, `DeniedValuesAttribute` |
| Enumeration value | (none - using the type) |
Expand Down Expand Up @@ -298,6 +300,7 @@ These item validation adapters exist:
| `CompareAttribute` | `ItemCompareAttribute` |
| `CreditCardAttribute` | `ItemCreditCardAttribute` |
| `EmailAddressAttribute` | `ItemEmailAddressAttribute` |
| `HostAttribute` | `ItemHostAttribute` |
| `MaxLengthAttribute` | `ItemMaxLengthAttribute` |
| `MinLengthAttribute` | `ItemMinLengthAttribute` |
| `NoValidationAttribute` | `ItemNoValidationAttribute` |
Expand Down
8 changes: 4 additions & 4 deletions src/ObjectValidation/AbaValidation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,25 +93,25 @@ public static AbaFormats GetAbaFormat(string aba)
/// <summary>
/// MICR format normalizing regular expression
/// </summary>
[GeneratedRegex(@"[^\d]", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"[^\d]", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex NormalizingMicr_Generated();

/// <summary>
/// Fraction format normalizing regular expression
/// </summary>
[GeneratedRegex(@"[^\d|\-|/]", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"[^\d|\-|/]", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex NormalizingFraction_Generated();

/// <summary>
/// MICR syntax validating regular expression (<c>$1</c> is the FED routing symbol, <c>$2</c> the ABA institution identifier, and <c>$3</c> the check digit)
/// </summary>
[GeneratedRegex(@"^(\d{4})(\d{4})(\d)$", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"^(\d{4})(\d{4})(\d)$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex MicrSyntax_Generated();

/// <summary>
/// Fraction syntax validating regular expression (<c>$1</c> is the ABA prefix, <c>$2</c> the ABA institution identifier, and <c>$3</c> the FED routing symbol)
/// </summary>
[GeneratedRegex(@"^(\d{1,2})\-(\d{4})/?(\d{4})$", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"^(\d{1,2})\-(\d{4})/?(\d{4})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex FractionSyntax_Generated();
}
}
4 changes: 1 addition & 3 deletions src/ObjectValidation/CountryCodes.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System.Collections.Frozen;

namespace wan24.ObjectValidation
namespace wan24.ObjectValidation
{
/// <summary>
/// Country ISO 3166-1 alpha-2 codes
Expand Down
4 changes: 1 addition & 3 deletions src/ObjectValidation/CurrencyCodes.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System.Collections.Frozen;

namespace wan24.ObjectValidation
namespace wan24.ObjectValidation
{
/// <summary>
/// Currency ISO 4217 codes
Expand Down
58 changes: 29 additions & 29 deletions src/ObjectValidation/EuVatId.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,175 +65,175 @@ public static partial class EuVatId
/// <summary>
/// VAT ID syntax regular expression for AT
/// </summary>
[GeneratedRegex(@"^(ATU)(\d{8})$", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"^(ATU)(\d{8})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex AT_Generated();

/// <summary>
/// VAT ID syntax regular expression for BE
/// </summary>
[GeneratedRegex(@"^(BE)(\d{10})$", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"^(BE)(\d{10})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex BE_Generated();

/// <summary>
/// VAT ID syntax regular expression for BG
/// </summary>
[GeneratedRegex(@"^(BG)(\d{9,10})$", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"^(BG)(\d{9,10})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex BG_Generated();

/// <summary>
/// VAT ID syntax regular expression for CY
/// </summary>
[GeneratedRegex(@"^(CY)(\d{8}[A-Z])$", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"^(CY)(\d{8}[A-Z])$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex CY_Generated();

/// <summary>
/// VAT ID syntax regular expression for CZ
/// </summary>
[GeneratedRegex(@"^(CZ)(\d{8,10})$", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"^(CZ)(\d{8,10})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex CZ_Generated();

/// <summary>
/// VAT ID syntax regular expression for DE
/// </summary>
[GeneratedRegex(@"^(DE)(\d{9})$", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"^(DE)(\d{9})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex DE_Generated();

/// <summary>
/// VAT ID syntax regular expression for DK
/// </summary>
[GeneratedRegex(@"^(DK)(\d{8})$", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"^(DK)(\d{8})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex DK_Generated();

/// <summary>
/// VAT ID syntax regular expression for EE
/// </summary>
[GeneratedRegex(@"^(EE)(\d{9})$", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"^(EE)(\d{9})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex EE_Generated();

/// <summary>
/// VAT ID syntax regular expression for ES
/// </summary>
[GeneratedRegex(@"^(ES)([A-Z|\d]\d{7}[A-Z|\d])$", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"^(ES)([A-Z|\d]\d{7}[A-Z|\d])$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex ES_Generated();

/// <summary>
/// VAT ID syntax regular expression for FI
/// </summary>
[GeneratedRegex(@"^(FI)(\d{8})$", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"^(FI)(\d{8})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex FI_Generated();

/// <summary>
/// VAT ID syntax regular expression for FR
/// </summary>
[GeneratedRegex(@"^(FR)([A-Z|\d]{2}\d{9})$", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"^(FR)([A-Z|\d]{2}\d{9})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex FR_Generated();

/// <summary>
/// VAT ID syntax regular expression for GR
/// </summary>
[GeneratedRegex(@"^(GR)(\d{9})$", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"^(GR)(\d{9})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex GR_Generated();

/// <summary>
/// VAT ID syntax regular expression for HR
/// </summary>
[GeneratedRegex(@"^(HR)(\d{11})$", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"^(HR)(\d{11})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex HR_Generated();

/// <summary>
/// VAT ID syntax regular expression for HU
/// </summary>
[GeneratedRegex(@"^(HU)(\d{8})$", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"^(HU)(\d{8})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex HU_Generated();

/// <summary>
/// VAT ID syntax regular expression for IE
/// </summary>
[GeneratedRegex(@"^(IE)((\d[A-Z|\d]\d{5}[A-Z])|(\d{7}[A-W][A-I]))$", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"^(IE)((\d[A-Z|\d]\d{5}[A-Z])|(\d{7}[A-W][A-I]))$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex IE_Generated();

/// <summary>
/// VAT ID syntax regular expression for IT
/// </summary>
[GeneratedRegex(@"^(IT)(\d{11})$", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"^(IT)(\d{11})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex IT_Generated();

/// <summary>
/// VAT ID syntax regular expression for LT
/// </summary>
[GeneratedRegex(@"^(LT)(\d{9}|\d{12})$", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"^(LT)(\d{9}|\d{12})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex LT_Generated();

/// <summary>
/// VAT ID syntax regular expression for LU
/// </summary>
[GeneratedRegex(@"^(LU)(\d{8})$", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"^(LU)(\d{8})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex LU_Generated();

/// <summary>
/// VAT ID syntax regular expression for LV
/// </summary>
[GeneratedRegex(@"^(LV)(\d{11})$", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"^(LV)(\d{11})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex LV_Generated();

/// <summary>
/// VAT ID syntax regular expression for MT
/// </summary>
[GeneratedRegex(@"^(MT)(\d{8})$", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"^(MT)(\d{8})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex MT_Generated();

/// <summary>
/// VAT ID syntax regular expression for NL
/// </summary>
[GeneratedRegex(@"^(NL)([\d|A-Z|\+|\*]{12})$", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"^(NL)([\d|A-Z|\+|\*]{12})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex NL_Generated();

/// <summary>
/// VAT ID syntax regular expression for PL
/// </summary>
[GeneratedRegex(@"^(PL)(\d{10})$", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"^(PL)(\d{10})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex PL_Generated();

/// <summary>
/// VAT ID syntax regular expression for PT
/// </summary>
[GeneratedRegex(@"^(PT)(\d{9})$", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"^(PT)(\d{9})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex PT_Generated();

/// <summary>
/// VAT ID syntax regular expression for RO
/// </summary>
[GeneratedRegex(@"^(RO)([1-9]\d{0,9})$", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"^(RO)([1-9]\d{0,9})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex RO_Generated();

/// <summary>
/// VAT ID syntax regular expression for SE
/// </summary>
[GeneratedRegex(@"^(SE)(\d{10}01)$", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"^(SE)(\d{10}01)$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex SE_Generated();

/// <summary>
/// VAT ID syntax regular expression for SI
/// </summary>
[GeneratedRegex(@"^(SI)(\d{8})$", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"^(SI)(\d{8})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex SI_Generated();

/// <summary>
/// VAT ID syntax regular expression for SK
/// </summary>
[GeneratedRegex(@"^(SK)(\d{10})$", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"^(SK)(\d{10})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex SK_Generated();

/// <summary>
/// VAT ID syntax regular expression for XI
/// </summary>
[GeneratedRegex(@"^(XI)(\d{9}|\d{12}|(GD\d{3})|(HA\d{3}))$", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"^(XI)(\d{9}|\d{12}|(GD\d{3})|(HA\d{3}))$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex XI_Generated();

/// <summary>
/// Normalizing regular expression
/// </summary>
[GeneratedRegex(@"[^\d|A-Z]", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"[^\d|A-Z]", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex Normalizing_Generated();
}
}
49 changes: 38 additions & 11 deletions src/ObjectValidation/HostAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.Contracts;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
Expand Down Expand Up @@ -34,6 +35,36 @@ public class HostAttribute() : ValidationAttributeBase()
/// </summary>
public int PingTimeout { get; set; } = 300;

/// <summary>
/// Use a cache for <see cref="CheckIfExists"/> results?
/// </summary>
public bool UseCache { get; set; } = true;

/// <summary>
/// Check if a hostname/IP exists using a cache (may throw; for implementing a cache, this method needs to be overridden)
/// </summary>
/// <param name="hostName">Hostname</param>
/// <param name="ip">IP address</param>
/// <param name="status">IP status</param>
/// <returns>If exists</returns>
protected virtual bool CheckIfExistsCache(string? hostName, IPAddress? ip, out IPStatus status)
{
if (hostName is not null)
{
IPHostEntry hostEntry = Dns.GetHostEntry(hostName);
status = IPStatus.Unknown;
return hostEntry.AddressList.Length > 0 || hostEntry.Aliases.Length > 0;
}
Contract.Assert(ip is not null);
using Ping ping = new();
PingReply pong = ping.Send(ip, PingTimeout, RandomNumberGenerator.GetBytes(count: 32), new()
{
DontFragment = true
});
status = pong.Status;
return pong.Status == IPStatus.Success;
}

/// <inheritdoc/>
protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
{
Expand All @@ -47,15 +78,16 @@ public class HostAttribute() : ValidationAttributeBase()
if (!CheckIfExists) return null;
try
{
Dns.GetHostEntry(str);
return null;
return CheckIfExistsCache(str, ip: null, out _)
? null
: this.CreateValidationResult($"Hostname DNS lookup failed", validationContext);
}
catch (Exception ex)
{
return this.CreateValidationResult($"Hostname DNS lookup failed: {ex.Message ?? ex.GetType().ToString()}", validationContext);
}
case UriHostNameType.IPv4:
if(!IPAddress.TryParse(str, out ip)) return this.CreateValidationResult($"Host IPv4 address parsing failed", validationContext);
if (!IPAddress.TryParse(str, out ip)) return this.CreateValidationResult($"Host IPv4 address parsing failed", validationContext);
if (ip.AddressFamily != AddressFamily.InterNetwork) return this.CreateValidationResult($"Detected host IPv4 address parsed to IPv6", validationContext);
break;
case UriHostNameType.IPv6:
Expand All @@ -66,18 +98,13 @@ public class HostAttribute() : ValidationAttributeBase()
return this.CreateValidationResult($"Hostname or IP address value invalid ({type})", validationContext);
}
if (!CheckIfExists) return null;
using Ping ping = new();
try
{
PingReply pong = ping.Send(ip, PingTimeout, RandomNumberGenerator.GetBytes(count: 32), new()
{
DontFragment = true
});
return pong.Status == IPStatus.Success
return CheckIfExistsCache(hostName: null, ip, out IPStatus status)
? null
: this.CreateValidationResult($"Ping to {ip} failed: {pong.Status}", validationContext);
: this.CreateValidationResult($"Ping to {ip} failed: {status}", validationContext);
}
catch(Exception ex)
catch (Exception ex)
{
return this.CreateValidationResult($"Ping to {ip} failed exceptional: {ex.Message ?? ex.GetType().ToString()}", validationContext);
}
Expand Down
2 changes: 1 addition & 1 deletion src/ObjectValidation/LuhnChecksum.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public static bool Validate(string value)
/// <summary>
/// Normalizing regular expression
/// </summary>
[GeneratedRegex(@"[^\d]", RegexOptions.Compiled | RegexOptions.Singleline)]
[GeneratedRegex(@"[^\d]", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex Normalizing_Generated();
}
}
2 changes: 1 addition & 1 deletion src/ObjectValidation/ObjectValidation.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
<PackageReadmeFile>README.md</PackageReadmeFile>
<Version>2.7.0</Version>
<Version>2.8.0</Version>
<DebugType>embedded</DebugType>
<EmbedAllSources>true</EmbedAllSources>
<Configurations>Debug;Release;Trunk</Configurations>
Expand Down
Loading

0 comments on commit c74921d

Please sign in to comment.