test: prove trusted channels PKI and scoped audit compatibility

This commit is contained in:
Joseph Doherty
2026-11-10 11:43:59 -05:00
parent ab25bd5318
commit dc9e777bff
7 changed files with 6539 additions and 4158 deletions
@@ -137,6 +137,7 @@ public sealed class AclServerObjectModelChangeTests
rig.EnsureEquipment();
rig.Sink.EnsureFolder(RawDeviceFolder, parentNodeId: null, displayName: "dev1", realm: AddressSpaceRealm.Raw);
rig.Sink.EnsureVariable(RawVariable, RawDeviceFolder, "Speed", "Int32", writable: true, AddressSpaceRealm.Raw);
rig.Sink.WriteValue(RawVariable, 7, OpcUaStatus.Good, DateTime.UtcNow, AddressSpaceRealm.Raw);
rig.Nm.NodeWriteGateway = new RejectingGateway();
var (granted, grantedEvents, grantedItem, grantedSubscription) =
@@ -145,6 +146,15 @@ public sealed class AclServerObjectModelChangeTests
await ServerObjectAsync(rig, anonymous: true, ct, encrypted: true);
var variable = new NodeId(RawVariable, (ushort)granted.NamespaceUris.GetIndex(V3NodeIds.RawNamespaceUri));
var serverSession = rig.Server.CurrentInstance.SessionManager.GetSessions().Single(s =>
s.Identity is ZB.MOM.WW.OtOpcUa.OpcUaServer.Security.RoleCarryingUserIdentity { ApiKeyId: KeyId });
using var permissionContext = new Opc.Ua.Server.OperationContext(new RequestHeader(), null!,
Opc.Ua.Server.RequestType.Read, RequestLifetime.None, serverSession);
var typePermission = rig.Nm.ValidateRolePermissions(permissionContext, ObjectTypeIds.AuditWriteUpdateEventType, PermissionType.ReceiveEvents);
var sourcePermission = rig.Nm.ValidateRolePermissions(permissionContext, variable, PermissionType.ReceiveEvents);
typePermission.StatusCode.ShouldBe(StatusCodes.BadUserAccessDenied,
"ordinary SDK audit emission must stay denied; only the scoped application path may grant receive permission");
ServiceResult.IsGood(sourcePermission).ShouldBeTrue($"Audit source permission: {sourcePermission}");
var write = await granted.WriteAsync(
null,
[new WriteValue { NodeId = variable, AttributeId = Attributes.Value, Value = new DataValue(new Variant(42)) }],
@@ -156,8 +166,11 @@ public sealed class AclServerObjectModelChangeTests
// Reported after the audit event through the same delivery, so it queues behind it on the item.
rig.Sink.RaiseNodesAddedModelChange(EqA, AddressSpaceRealm.Uns);
await RecordedEvents.WaitUntilAsync(() => narrowEvents.Of(narrowItem).Any(NamesEqA));
await RecordedEvents.WaitUntilAsync(() => narrowEvents.Of(narrowItem).Any(NamesEqA)
&& grantedEvents.Of(grantedItem).Any(NamesEqA));
grantedEvents.Of(grantedItem).Count(f => f[RecordedEvents.EventTypeField].GetNodeId() == ObjectTypeIds.AuditWriteUpdateEventType)
.ShouldBe(1, "one application audit is delivered without an automatic SDK duplicate");
narrowEvents.HasType(narrowItem, ObjectTypeIds.AuditWriteUpdateEventType).ShouldBeFalse(
"a session that may not read the node must not receive its written values through an audit event");