using System.Collections.Generic; using System.Threading.Tasks; using Opc.Ua; using Shouldly; using Xunit; using ZB.MOM.WW.LmxOpcUa.Host.Domain; using ZB.MOM.WW.LmxOpcUa.Tests.Helpers; namespace ZB.MOM.WW.LmxOpcUa.Tests.Integration { public class AccessLevelTests { private static FakeGalaxyRepository CreateRepoWithSecurityLevels() { return new FakeGalaxyRepository { Hierarchy = new List { new GalaxyObjectInfo { GobjectId = 1, TagName = "TestObj", BrowseName = "TestObj", ParentGobjectId = 0, IsArea = false } }, Attributes = new List { new GalaxyAttributeInfo { GobjectId = 1, TagName = "TestObj", AttributeName = "FreeAttr", FullTagReference = "TestObj.FreeAttr", MxDataType = 5, SecurityClassification = 0 }, new GalaxyAttributeInfo { GobjectId = 1, TagName = "TestObj", AttributeName = "OperateAttr", FullTagReference = "TestObj.OperateAttr", MxDataType = 5, SecurityClassification = 1 }, new GalaxyAttributeInfo { GobjectId = 1, TagName = "TestObj", AttributeName = "SecuredAttr", FullTagReference = "TestObj.SecuredAttr", MxDataType = 5, SecurityClassification = 2 }, new GalaxyAttributeInfo { GobjectId = 1, TagName = "TestObj", AttributeName = "VerifiedAttr", FullTagReference = "TestObj.VerifiedAttr", MxDataType = 5, SecurityClassification = 3 }, new GalaxyAttributeInfo { GobjectId = 1, TagName = "TestObj", AttributeName = "TuneAttr", FullTagReference = "TestObj.TuneAttr", MxDataType = 5, SecurityClassification = 4 }, new GalaxyAttributeInfo { GobjectId = 1, TagName = "TestObj", AttributeName = "ConfigAttr", FullTagReference = "TestObj.ConfigAttr", MxDataType = 5, SecurityClassification = 5 }, new GalaxyAttributeInfo { GobjectId = 1, TagName = "TestObj", AttributeName = "ViewOnlyAttr", FullTagReference = "TestObj.ViewOnlyAttr", MxDataType = 5, SecurityClassification = 6 }, } }; } /// /// Verifies that writable Galaxy security classifications publish OPC UA variables with read-write access. /// [Fact] public async Task ReadWriteAttribute_HasCurrentReadOrWrite_AccessLevel() { var fixture = OpcUaServerFixture.WithFakeMxAccessClient(repo: CreateRepoWithSecurityLevels()); await fixture.InitializeAsync(); try { using var client = new OpcUaTestClient(); await client.ConnectAsync(fixture.EndpointUrl); foreach (var attrName in new[] { "FreeAttr", "OperateAttr", "TuneAttr", "ConfigAttr" }) { var nodeId = client.MakeNodeId($"TestObj.{attrName}"); var accessLevel = client.ReadAttribute(nodeId, Attributes.AccessLevel); ((byte)accessLevel.Value).ShouldBe(AccessLevels.CurrentReadOrWrite, $"{attrName} should be ReadWrite"); } } finally { await fixture.DisposeAsync(); } } /// /// Verifies that secured and view-only Galaxy classifications publish OPC UA variables with read-only access. /// [Fact] public async Task ReadOnlyAttribute_HasCurrentRead_AccessLevel() { var fixture = OpcUaServerFixture.WithFakeMxAccessClient(repo: CreateRepoWithSecurityLevels()); await fixture.InitializeAsync(); try { using var client = new OpcUaTestClient(); await client.ConnectAsync(fixture.EndpointUrl); foreach (var attrName in new[] { "SecuredAttr", "VerifiedAttr", "ViewOnlyAttr" }) { var nodeId = client.MakeNodeId($"TestObj.{attrName}"); var accessLevel = client.ReadAttribute(nodeId, Attributes.AccessLevel); ((byte)accessLevel.Value).ShouldBe(AccessLevels.CurrentRead, $"{attrName} should be ReadOnly"); } } finally { await fixture.DisposeAsync(); } } /// /// Verifies that the bridge rejects writes against Galaxy attributes whose security classification is read-only. /// [Fact] public async Task Write_ToReadOnlyAttribute_IsRejected() { var fixture = OpcUaServerFixture.WithFakeMxAccessClient(repo: CreateRepoWithSecurityLevels()); await fixture.InitializeAsync(); try { using var client = new OpcUaTestClient(); await client.ConnectAsync(fixture.EndpointUrl); var nodeId = client.MakeNodeId("TestObj.ViewOnlyAttr"); var result = client.Write(nodeId, "test"); StatusCode.IsBad(result).ShouldBeTrue("Write to ReadOnly attribute should be rejected"); } finally { await fixture.DisposeAsync(); } } /// /// Verifies that writes succeed for Galaxy attributes whose security classification permits operator updates. /// [Fact] public async Task Write_ToReadWriteAttribute_Succeeds() { var mxClient = new FakeMxAccessClient(); var fixture = OpcUaServerFixture.WithFakeMxAccessClient(mxClient: mxClient, repo: CreateRepoWithSecurityLevels()); await fixture.InitializeAsync(); try { using var client = new OpcUaTestClient(); await client.ConnectAsync(fixture.EndpointUrl); var nodeId = client.MakeNodeId("TestObj.OperateAttr"); var result = client.Write(nodeId, "test"); StatusCode.IsGood(result).ShouldBeTrue("Write to ReadWrite attribute should succeed"); } finally { await fixture.DisposeAsync(); } } } }