126 lines
4.9 KiB
C#
126 lines
4.9 KiB
C#
using Microsoft.Extensions.Configuration;
|
|
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.Host.Configuration;
|
|
using ZB.MOM.WW.OtOpcUa.Security.Ldap;
|
|
using LdapTransport = ZB.MOM.WW.Auth.Abstractions.Ldap.LdapTransport;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Host.IntegrationTests;
|
|
|
|
/// <summary>
|
|
/// Regression guard for the LDAP config-section fix. The real config (admin/driver/Development
|
|
/// overlays) lives under <c>Security:Ldap</c>, and <see cref="LdapOptions.SectionName"/> must point
|
|
/// there so the configured <c>DevStubMode</c> actually binds. Previously the binders used the
|
|
/// nonexistent <c>"Ldap"</c>/<c>"Authentication:Ldap"</c> sections, so the dev stub never activated.
|
|
/// </summary>
|
|
public sealed class LdapOptionsBindingTests
|
|
{
|
|
/// <summary><see cref="LdapOptions.SectionName"/> resolves to the real overlay section.</summary>
|
|
[Fact]
|
|
public void SectionName_is_Security_Ldap()
|
|
{
|
|
LdapOptions.SectionName.ShouldBe("Security:Ldap");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Binding from <see cref="LdapOptions.SectionName"/> reads the configured <c>DevStubMode</c>
|
|
/// from the real <c>Security:Ldap</c> section — proving the dev stub now takes effect.
|
|
/// </summary>
|
|
[Fact]
|
|
public void Binding_from_SectionName_reads_Security_Ldap_DevStubMode()
|
|
{
|
|
var configuration = new ConfigurationBuilder()
|
|
.AddInMemoryCollection(new Dictionary<string, string?>
|
|
{
|
|
["Security:Ldap:DevStubMode"] = "true",
|
|
})
|
|
.Build();
|
|
|
|
var options = configuration.GetSection(LdapOptions.SectionName).Get<LdapOptions>();
|
|
|
|
options.ShouldNotBeNull();
|
|
options.DevStubMode.ShouldBeTrue();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Negative control: binding from the old (nonexistent) <c>"Ldap"</c> section against the same
|
|
/// <c>Security:Ldap</c> config does NOT pick up <c>DevStubMode</c> — it falls back to the C#
|
|
/// default (false). This is the pre-fix behaviour the change corrects.
|
|
/// </summary>
|
|
[Fact]
|
|
public void Binding_from_old_Ldap_section_does_not_read_DevStubMode()
|
|
{
|
|
var configuration = new ConfigurationBuilder()
|
|
.AddInMemoryCollection(new Dictionary<string, string?>
|
|
{
|
|
["Security:Ldap:DevStubMode"] = "true",
|
|
})
|
|
.Build();
|
|
|
|
var options = configuration.GetSection("Ldap").Get<LdapOptions>() ?? new LdapOptions();
|
|
|
|
options.DevStubMode.ShouldBeFalse();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// End-to-end guard for the shipped production overlays: binds each of the three prod overlay
|
|
/// files' real <c>Security:Ldap</c> section (the same files the host loads at boot, copied into the
|
|
/// test output via the Host project reference) and runs the <see cref="LdapOptionsValidator"/> the
|
|
/// host wires via <c>AddValidatedOptions</c>. Proves each prod overlay declares a TLS transport and
|
|
/// therefore PASSES startup validation — i.e. the host actually boots with these overlays after the
|
|
/// insecure-transport guard was added. The <c>Development</c> overlay (DevStubMode) is verified to
|
|
/// pass via the guard exemption.
|
|
/// </summary>
|
|
public sealed class ProdOverlayValidationTests
|
|
{
|
|
private static readonly LdapOptionsValidator Sut = new();
|
|
|
|
private static LdapOptions BindOverlay(string fileName)
|
|
{
|
|
var path = Path.Combine(AppContext.BaseDirectory, fileName);
|
|
File.Exists(path).ShouldBeTrue($"overlay '{fileName}' should be copied to the test output");
|
|
|
|
var configuration = new ConfigurationBuilder()
|
|
.AddJsonFile(path, optional: false, reloadOnChange: false)
|
|
.Build();
|
|
|
|
return configuration.GetSection(LdapOptions.SectionName).Get<LdapOptions>() ?? new LdapOptions();
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("appsettings.admin.json")]
|
|
[InlineData("appsettings.driver.json")]
|
|
[InlineData("appsettings.admin-driver.json")]
|
|
public void Prod_overlay_declares_ldaps_transport(string fileName)
|
|
{
|
|
var options = BindOverlay(fileName);
|
|
|
|
options.DevStubMode.ShouldBeFalse();
|
|
options.Transport.ShouldBe(LdapTransport.Ldaps);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("appsettings.admin.json")]
|
|
[InlineData("appsettings.driver.json")]
|
|
[InlineData("appsettings.admin-driver.json")]
|
|
public void Prod_overlay_passes_startup_validation(string fileName)
|
|
{
|
|
var options = BindOverlay(fileName);
|
|
|
|
// Match the host: these overlays only set Security:Ldap fields, so backfill the required
|
|
// Server/SearchBase/Port the way the base C# defaults do (LdapOptions defaults are valid),
|
|
// then validate exactly as AddValidatedOptions would at boot.
|
|
Sut.Validate(null, options).Succeeded.ShouldBeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void Development_overlay_passes_startup_validation_via_devstub_exemption()
|
|
{
|
|
var options = BindOverlay("appsettings.Development.json");
|
|
|
|
options.DevStubMode.ShouldBeTrue();
|
|
Sut.Validate(null, options).Succeeded.ShouldBeTrue();
|
|
}
|
|
}
|