fix(auth): move AddZbLdapAuth to Host composition root so component-lib AddSecurity() drops IConfiguration param (satisfy OptionsTests arch rule; fix pre-existing ac34dac red); behaviour-preserving

This commit is contained in:
Joseph Doherty
2026-06-02 03:50:16 -04:00
parent 7e25efa790
commit 55099b19f6
3 changed files with 55 additions and 34 deletions
@@ -6,6 +6,7 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using ZB.MOM.WW.Auth.Abstractions.Ldap;
using ZB.MOM.WW.Auth.AspNetCore;
using ZB.MOM.WW.Auth.Ldap;
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Security;
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Sites;
@@ -441,7 +442,7 @@ public class SecurityReviewRegressionTests
var services = new ServiceCollection();
services.AddLogging();
services.AddDataProtection();
services.AddSecurity(EmptyConfig());
services.AddSecurity();
using var provider = services.BuildServiceProvider();
var cookieOptions = provider
@@ -454,16 +455,6 @@ public class SecurityReviewRegressionTests
Assert.True(cookieOptions.Cookie.HttpOnly);
}
/// <summary>
/// Task 1.2: an empty <see cref="Microsoft.Extensions.Configuration.IConfiguration"/>
/// for <c>AddSecurity(config)</c>. The cookie PostConfigure under test reads only the
/// non-LDAP <see cref="SecurityOptions"/> fields (idle timeout / HTTPS-cookie policy),
/// and the library's <c>LdapOptions</c> ValidateOnStart only fires at host start (not on
/// <c>BuildServiceProvider</c>), so no LDAP config is needed to resolve the cookie wiring.
/// </summary>
private static Microsoft.Extensions.Configuration.IConfiguration EmptyConfig() =>
new Microsoft.Extensions.Configuration.ConfigurationBuilder().Build();
// --- CentralUI-005: cookie auth must use a sliding session window ---
// Documented policy (CLAUDE.md Security & Auth): sliding refresh with a
// 30-minute idle timeout. The cookie middleware must enable SlidingExpiration
@@ -475,7 +466,7 @@ public class SecurityReviewRegressionTests
var services = new ServiceCollection();
services.AddLogging();
services.AddDataProtection();
services.AddSecurity(EmptyConfig());
services.AddSecurity();
using var provider = services.BuildServiceProvider();
var cookieOptions = provider
@@ -491,7 +482,7 @@ public class SecurityReviewRegressionTests
var services = new ServiceCollection();
services.AddLogging();
services.AddDataProtection();
services.AddSecurity(EmptyConfig());
services.AddSecurity();
// The idle timeout drives the cookie's expiry window.
services.Configure<SecurityOptions>(o => o.IdleTimeoutMinutes = 30);
@@ -509,7 +500,7 @@ public class SecurityReviewRegressionTests
var services = new ServiceCollection();
services.AddLogging();
services.AddDataProtection();
services.AddSecurity(EmptyConfig());
services.AddSecurity();
services.Configure<SecurityOptions>(o => o.IdleTimeoutMinutes = 45);
using var provider = services.BuildServiceProvider();
@@ -1179,14 +1170,27 @@ public class SecurityOptionsValidatorTests
Assert.Contains(nameof(LdapOptions.ServiceAccountDn), result.FailureMessage);
}
/// <summary>
/// Verifies the security composition the Host performs at its composition root —
/// <c>AddZbLdapAuth(configuration, LdapSectionPath)</c> followed by <c>AddSecurity()</c> —
/// wires the shared <see cref="LdapOptionsValidator"/> as an
/// <see cref="IValidateOptions{TOptions}"/> for <c>LdapOptions</c> (which is what makes
/// <c>ValidateOnStart()</c> fire). The LDAP registration moved to the Host because it is
/// config-coupled; <c>AddSecurity()</c> is a component library and no longer takes
/// <c>IConfiguration</c>.
/// </summary>
[Fact]
public void AddSecurity_RegistersLdapOptionsValidator_WithValidateOnStart()
{
var services = new ServiceCollection();
services.AddLogging();
services.AddDataProtection();
services.AddSecurity(
new Microsoft.Extensions.Configuration.ConfigurationBuilder().Build());
// Mirror the Host composition order: shared LDAP auth (config-coupled) first,
// then the config-free AddSecurity().
services.AddZbLdapAuth(
new Microsoft.Extensions.Configuration.ConfigurationBuilder().Build(),
ZB.MOM.WW.ScadaBridge.Security.ServiceCollectionExtensions.LdapSectionPath);
services.AddSecurity();
using var provider = services.BuildServiceProvider();