fix(security): wrap the ConfigDb key store on admin nodes + registration guard

This commit is contained in:
Joseph Doherty
2026-08-20 09:45:08 -04:00
parent 6048257dfe
commit 39a10a277a
7 changed files with 100 additions and 9 deletions
@@ -7,6 +7,7 @@ using Xunit;
using ZB.MOM.WW.OtOpcUa.Cluster;
using ZB.MOM.WW.OtOpcUa.Configuration;
using ZB.MOM.WW.OtOpcUa.Core.Authorization;
using ZB.MOM.WW.OtOpcUa.Host.OpcUa;
using ZB.MOM.WW.OtOpcUa.Host.Configuration;
using ZB.MOM.WW.OtOpcUa.OpcUaServer.Security;
@@ -40,6 +41,41 @@ public sealed class ApiKeyStoreRegistrationTests
authorizer.Clock.ShouldBeSameAs(registered);
}
/// <summary>
/// An admin (or fused admin+driver) node's key store is the <see cref="BoundedApiKeyStore"/>
/// decorator, wrapping the ConfigDb store.
/// </summary>
/// <remarks>
/// The decorator lives in ONE branch of a role conditional, which is the "built but never wired"
/// shape OVERALL theme 3 names. Unwired, every unit test in <c>BoundedApiKeyStoreTests</c> still
/// passes and the deadline protects nothing.
/// </remarks>
[Fact]
public void An_admin_node_wraps_the_ConfigDb_store_in_the_bound()
{
using var sp = BuildNode(hasAdmin: true, hasDriver: true);
var store = sp.GetRequiredService<IApiKeyStore>().ShouldBeOfType<BoundedApiKeyStore>();
store.Inner.ShouldBeOfType<ConfigDbApiKeyStore>();
store.Clock.ShouldBeSameAs(sp.GetRequiredService<TimeProvider>());
}
/// <summary>
/// A driver-only node's key store is the bare artifact store — deliberately NOT wrapped.
/// </summary>
/// <remarks>
/// It is two dictionary probes on an in-memory snapshot, so it can neither hang nor be worth
/// caching; wrapping it would add a deadline to a call that cannot miss one and a second copy of
/// data the snapshot already holds.
/// </remarks>
[Fact]
public void A_driver_only_node_gets_the_bare_artifact_store()
{
using var sp = BuildNode(hasAdmin: false, hasDriver: true);
sp.GetRequiredService<IApiKeyStore>().ShouldBeOfType<ArtifactApiKeyStore>();
}
/// <summary>Builds the data-plane authentication wiring for one node role combination.</summary>
/// <param name="hasAdmin">Whether the node carries the <c>admin</c> role.</param>
/// <param name="hasDriver">Whether the node carries the <c>driver</c> role.</param>