From 75919cec31164888975922a6b5d338462d785ec4 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Tue, 16 Jun 2026 08:52:28 -0400 Subject: [PATCH] =?UTF-8?q?test(security):=20DL-3=20review=20nits=20?= =?UTF-8?q?=E2=80=94=20assert=20OnValidatePrincipal=20on=20prod=20path=20+?= =?UTF-8?q?=20warning/doc=20polish?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Auth/AuthDisableLoginOptions.cs | 10 ++++++- .../ServiceCollectionExtensions.cs | 2 +- .../DisableLoginRegistrationTests.cs | 26 +++++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/ZB.MOM.WW.ScadaBridge.Security/Auth/AuthDisableLoginOptions.cs b/src/ZB.MOM.WW.ScadaBridge.Security/Auth/AuthDisableLoginOptions.cs index ff8b1606..ba7d9fa8 100644 --- a/src/ZB.MOM.WW.ScadaBridge.Security/Auth/AuthDisableLoginOptions.cs +++ b/src/ZB.MOM.WW.ScadaBridge.Security/Auth/AuthDisableLoginOptions.cs @@ -8,7 +8,15 @@ namespace ZB.MOM.WW.ScadaBridge.Security.Auth; /// public sealed class AuthDisableLoginOptions { - /// Configuration section name (ScadaBridge:Security:Auth). + /// + /// Configuration section name (ScadaBridge:Security:Auth). + /// This is a CHILD sub-section of ScadaBridge:Security (where + /// binds the parent fields) — not a sibling. + /// In appsettings.json nest it under the existing Security object: + /// + /// "ScadaBridge": { "Security": { "Auth": { "DisableLogin": true } } } + /// + /// public const string SectionName = "ScadaBridge:Security:Auth"; /// When true, disable login and auto-authenticate every request. Default false. diff --git a/src/ZB.MOM.WW.ScadaBridge.Security/ServiceCollectionExtensions.cs b/src/ZB.MOM.WW.ScadaBridge.Security/ServiceCollectionExtensions.cs index f9c3022b..94edacac 100644 --- a/src/ZB.MOM.WW.ScadaBridge.Security/ServiceCollectionExtensions.cs +++ b/src/ZB.MOM.WW.ScadaBridge.Security/ServiceCollectionExtensions.cs @@ -127,7 +127,7 @@ public static class ServiceCollectionExtensions "AUTH DISABLED (ScadaBridge:Security:Auth:DisableLogin=true) — every request is " + "authenticated as '{User}' with FULL permissions ({Roles}) across ALL sites. This " + "is a SCADA control surface; dev/test ONLY — never enable in production.", - opts.User, string.Join(",", Roles.All))); + opts.User, string.Join(", ", Roles.All))); } else { diff --git a/tests/ZB.MOM.WW.ScadaBridge.Security.Tests/DisableLoginRegistrationTests.cs b/tests/ZB.MOM.WW.ScadaBridge.Security.Tests/DisableLoginRegistrationTests.cs index c84fb094..c4075ba3 100644 --- a/tests/ZB.MOM.WW.ScadaBridge.Security.Tests/DisableLoginRegistrationTests.cs +++ b/tests/ZB.MOM.WW.ScadaBridge.Security.Tests/DisableLoginRegistrationTests.cs @@ -32,4 +32,30 @@ public class DisableLoginRegistrationTests var scheme = await ResolveCookieSchemeAsync(disableLogin: false); Assert.Equal(typeof(CookieAuthenticationHandler), scheme!.HandlerType); } + + /// + /// When disableLogin: false (the production path) the M2.19 idle-timeout / + /// role-refresh hook MUST be wired on the cookie scheme's + /// . + /// This pin-test ensures a future refactor cannot silently drop the hook without + /// a red test. + /// + [Fact] + public async Task FlagFalse_CookieScheme_OnValidatePrincipalIsWired() + { + var services = new ServiceCollection(); + services.AddLogging(); + // Provide default SecurityOptions so the PostConfigure that reads + // IOptions (cookie-hardening + name) can resolve successfully. + services.Configure(_ => { }); + services.AddSecurity(disableLogin: false); + + await using var sp = services.BuildServiceProvider(); + + var options = sp + .GetRequiredService>() + .Get(CookieAuthenticationDefaults.AuthenticationScheme); + + Assert.NotNull(options.Events?.OnValidatePrincipal); + } }