fix(server): resolve Low code-review findings (Server-004,006,008,012,014,015)
- Server-004: pass the role-derived display name to UserIdentity's base ctor (the SDK's DisplayName has no public setter) and drop the dead Display property; make RoleBasedIdentity internal sealed. - Server-006: derive a bounded CancellationToken from the SDK's OperationContext.OperationDeadline in OnReadValue / OnWriteValue so a stalled driver call can no longer pin the request thread. - Server-008: mark handled slots via CallMethodRequest.Processed = true in RouteScriptedAlarmMethodCalls (the SDK skips on Processed, not on a Good error slot). - Server-012: PeerHttpProbeLoop.ProbeAsync stops mutating client.Timeout per call; uses a per-request CancellationTokenSource linked to the shutdown token instead. - Server-014: wire SealedBootstrap into Program.cs via AddSealedBootstrap + OpcUaServerService so the generation-sealed cache + stale-config flag + resilient reader actually run; /healthz now reflects cache-fallback state. - Server-015: replace the stale 'PR 16 / PR 17 minimum-viable scope' class summaries on OtOpcUaServer and OpcUaServerOptions with the shipped LDAP + anonymous-role + configurable security-profile prose. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
using Opc.Ua;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Server.OpcUa;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Server.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Regression for Server-004 — the production
|
||||
/// <see cref="OtOpcUaServer.RoleBasedIdentity"/> must surface the LDAP-resolved display
|
||||
/// name through <see cref="IUserIdentity.DisplayName"/>, since
|
||||
/// <c>DriverNodeManager.ResolveCallUser</c> reads the base interface property when stamping
|
||||
/// audit identities on scripted-alarm Acknowledge / Confirm / Shelve calls.
|
||||
/// </summary>
|
||||
[Trait("Category", "Unit")]
|
||||
public sealed class RoleBasedIdentityTests
|
||||
{
|
||||
[Fact]
|
||||
public void DisplayName_returns_LDAP_resolved_display_name_when_present()
|
||||
{
|
||||
IUserIdentity identity = new OtOpcUaServer.RoleBasedIdentity(
|
||||
userName: "alice",
|
||||
displayName: "Alice Smith",
|
||||
roles: new[] { "WriteOperate" },
|
||||
ldapGroups: new[] { "ot_operators" });
|
||||
|
||||
identity.DisplayName.ShouldBe("Alice Smith",
|
||||
"DriverNodeManager.ResolveCallUser reads IUserIdentity.DisplayName for audit entries; "
|
||||
+ "RoleBasedIdentity must surface the LDAP-resolved name, not just the username.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DisplayName_falls_back_to_userName_when_LDAP_display_name_is_null()
|
||||
{
|
||||
IUserIdentity identity = new OtOpcUaServer.RoleBasedIdentity(
|
||||
userName: "alice",
|
||||
displayName: null,
|
||||
roles: [],
|
||||
ldapGroups: []);
|
||||
|
||||
identity.DisplayName.ShouldBe("alice",
|
||||
"absent an LDAP display name, audit entries should still carry the username.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolveCallUser_yields_LDAP_resolved_display_name()
|
||||
{
|
||||
IUserIdentity identity = new OtOpcUaServer.RoleBasedIdentity(
|
||||
userName: "alice",
|
||||
displayName: "Alice Smith",
|
||||
roles: [],
|
||||
ldapGroups: []);
|
||||
|
||||
DriverNodeManager.ResolveCallUser(identity).ShouldBe("Alice Smith");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user