using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Configuration.Entities; using ZB.MOM.WW.OtOpcUa.Configuration.Enums; using ZB.MOM.WW.OtOpcUa.Host.OpcUa; using ZB.MOM.WW.OtOpcUa.OpcUaServer.Security; namespace ZB.MOM.WW.OtOpcUa.Host.Tests.OpcUa; /// /// The flag-off role (pluggable historian, spec §5). The /// broader mapper behaviour is covered by . /// public sealed class ApiKeyInterimRoleMapperTests { private const string Principal = "historian-editor"; [Fact] public void A_cluster_scoped_history_update_grant_confers_the_history_update_role() { var roles = ApiKeyInterimRoleMapper.Map( [Grant(NodeAclScopeKind.Cluster, NodePermissions.HistoryUpdate)], Principal); roles.ShouldBe([OpcUaDataPlaneRoles.HistoryUpdate]); } /// /// Writing a live value and rewriting recorded history are different authorities, so a key that can /// write must not thereby gain HistoryUpdate — neither from WriteOperate nor from any composite bundle /// (HistoryUpdate is deliberately in none of them). /// [Fact] public void Write_operate_and_the_bundles_do_not_confer_history_update() { foreach (var flags in new[] { NodePermissions.WriteOperate, NodePermissions.HistoryRead, NodePermissions.ReadOnly, NodePermissions.Operator, NodePermissions.Engineer, NodePermissions.Admin, }) { ApiKeyInterimRoleMapper.Map([Grant(NodeAclScopeKind.Cluster, flags)], Principal) .ShouldNotContain(OpcUaDataPlaneRoles.HistoryUpdate, $"{flags} must not confer HistoryUpdate"); } } [Fact] public void A_narrower_scoped_history_update_grant_confers_nothing() { foreach (var narrower in new[] { NodeAclScopeKind.Equipment, NodeAclScopeKind.UnsArea, NodeAclScopeKind.UnsLine, NodeAclScopeKind.Namespace, }) { ApiKeyInterimRoleMapper.Map([Grant(narrower, NodePermissions.HistoryUpdate)], Principal) .ShouldBeEmpty($"a {narrower}-scoped grant must not confer a server-wide role"); } } [Fact] public void History_update_unions_with_the_other_roles() { var roles = ApiKeyInterimRoleMapper.Map( [ Grant(NodeAclScopeKind.Cluster, NodePermissions.WriteOperate), Grant(NodeAclScopeKind.Cluster, NodePermissions.HistoryUpdate | NodePermissions.AlarmAcknowledge), ], Principal); roles.ShouldBe( [OpcUaDataPlaneRoles.WriteOperate, OpcUaDataPlaneRoles.AlarmAck, OpcUaDataPlaneRoles.HistoryUpdate], ignoreOrder: true); } private static NodeAcl Grant(NodeAclScopeKind scope, NodePermissions flags) => new() { NodeAclId = $"ACL-{scope}-{flags}", ClusterId = "MAIN", Principal = Principal, ScopeKind = scope, ScopeId = scope == NodeAclScopeKind.Cluster ? null : "SOME-SCOPE", PermissionFlags = flags, }; }