From fe7d1ce1ece79ee3bb50fd0f1672572f012d809c Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Mon, 1 Jun 2026 07:19:22 -0400 Subject: [PATCH] feat(gateway): validate MxGateway:Tls options --- .../Configuration/GatewayOptionsValidator.cs | 26 ++++++++ .../GatewayOptionsValidatorTests.cs | 59 +++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 src/ZB.MOM.WW.MxGateway.Tests/Configuration/GatewayOptionsValidatorTests.cs diff --git a/src/ZB.MOM.WW.MxGateway.Server/Configuration/GatewayOptionsValidator.cs b/src/ZB.MOM.WW.MxGateway.Server/Configuration/GatewayOptionsValidator.cs index 26f05ca..214a21f 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Configuration/GatewayOptionsValidator.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Configuration/GatewayOptionsValidator.cs @@ -26,6 +26,7 @@ public sealed class GatewayOptionsValidator : IValidateOptions ValidateDashboard(options.Dashboard, failures); ValidateProtocol(options.Protocol, failures); ValidateAlarms(options.Alarms, failures); + ValidateTls(options.Tls, failures); return failures.Count == 0 ? ValidateOptionsResult.Success @@ -262,6 +263,31 @@ public sealed class GatewayOptionsValidator : IValidateOptions } } + private const int MinimumCertValidityYears = 1; + private const int MaximumCertValidityYears = 100; + + private static void ValidateTls(TlsOptions options, List failures) + { + if (options.ValidityYears is < MinimumCertValidityYears or > MaximumCertValidityYears) + { + failures.Add( + $"MxGateway:Tls:ValidityYears must be between {MinimumCertValidityYears} and {MaximumCertValidityYears}."); + } + + AddIfInvalidPath( + options.SelfSignedCertPath, + "MxGateway:Tls:SelfSignedCertPath must be a valid filesystem path.", + failures); + + foreach (string dns in options.AdditionalDnsNames) + { + if (string.IsNullOrWhiteSpace(dns)) + { + failures.Add("MxGateway:Tls:AdditionalDnsNames entries must be non-blank."); + } + } + } + private static void ValidateProtocol(ProtocolOptions options, List failures) { if (options.WorkerProtocolVersion != GatewayContractInfo.WorkerProtocolVersion) diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Configuration/GatewayOptionsValidatorTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Configuration/GatewayOptionsValidatorTests.cs new file mode 100644 index 0000000..e4ed269 --- /dev/null +++ b/src/ZB.MOM.WW.MxGateway.Tests/Configuration/GatewayOptionsValidatorTests.cs @@ -0,0 +1,59 @@ +using Microsoft.Extensions.Options; +using ZB.MOM.WW.MxGateway.Server.Configuration; + +namespace ZB.MOM.WW.MxGateway.Tests.Configuration; + +public sealed class GatewayOptionsValidatorTests +{ + // Constructs the minimal valid GatewayOptions by relying on each sub-option's + // design-default values; those defaults are validated separately in GatewayOptionsTests. + private static GatewayOptions ValidOptions() => new(); + + private static GatewayOptions CloneWithTls(GatewayOptions source, TlsOptions tls) + => new() + { + Authentication = source.Authentication, + Ldap = source.Ldap, + Worker = source.Worker, + Sessions = source.Sessions, + Events = source.Events, + Dashboard = source.Dashboard, + Protocol = source.Protocol, + Alarms = source.Alarms, + Tls = tls, + }; + + [Fact] + public void Validate_Succeeds_WithDefaultTlsOptions() + { + ValidateOptionsResult result = new GatewayOptionsValidator().Validate(null, ValidOptions()); + Assert.True(result.Succeeded); + } + + [Fact] + public void Validate_Fails_WhenTlsValidityYearsOutOfRange() + { + GatewayOptions withBadTls = CloneWithTls(ValidOptions(), new TlsOptions { ValidityYears = 0 }); + ValidateOptionsResult result = new GatewayOptionsValidator().Validate(null, withBadTls); + Assert.True(result.Failed); + Assert.Contains(result.Failures!, f => f.Contains("MxGateway:Tls:ValidityYears")); + } + + [Fact] + public void Validate_Fails_WhenTlsValidityYearsTooLarge() + { + GatewayOptions withBadTls = CloneWithTls(ValidOptions(), new TlsOptions { ValidityYears = 101 }); + ValidateOptionsResult result = new GatewayOptionsValidator().Validate(null, withBadTls); + Assert.True(result.Failed); + Assert.Contains(result.Failures!, f => f.Contains("MxGateway:Tls:ValidityYears")); + } + + [Fact] + public void Validate_Fails_WhenAdditionalDnsNameBlank() + { + GatewayOptions options = CloneWithTls(ValidOptions(), new TlsOptions { AdditionalDnsNames = [" "] }); + ValidateOptionsResult result = new GatewayOptionsValidator().Validate(null, options); + Assert.True(result.Failed); + Assert.Contains(result.Failures!, f => f.Contains("MxGateway:Tls:AdditionalDnsNames")); + } +}