Remove static Users auth, use shared QualityMapper for historian, simplify LDAP permission checks

- Remove ConfigUserAuthenticationProvider and Users property — LDAP is the only auth mechanism
- Fix historian quality mapping to use existing QualityMapper (OPC DA quality bytes, not custom mapping)
- Add AppRoles constants, unify HasWritePermission/HasAlarmAckPermission into shared HasRole helper
- Hoist write permission check out of per-item loop, eliminate redundant _ldapRolesEnabled field
- Update docs (Configuration.md, Security.md, OpcUaServer.md, HistoricalDataAccess.md)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-03-28 19:23:20 -04:00
parent 74107ea95e
commit d9463d6998
19 changed files with 93 additions and 273 deletions

View File

@@ -1,54 +1,44 @@
using Opc.Ua;
using Shouldly;
using Xunit;
using ZB.MOM.WW.LmxOpcUa.Host.Historian;
using ZB.MOM.WW.LmxOpcUa.Host.Domain;
namespace ZB.MOM.WW.LmxOpcUa.Tests.Historian
{
public class HistorianQualityMappingTests
{
/// <summary>
/// Verifies that the Historian good-quality sentinel is surfaced to OPC UA clients as a good status code.
/// </summary>
[Fact]
public void Quality0_MapsToGood()
{
HistorianDataSource.MapQuality(0).ShouldBe(StatusCodes.Good);
}
private static StatusCode MapHistorianQuality(byte quality)
=> QualityMapper.MapToOpcUaStatusCode(QualityMapper.MapFromMxAccessQuality(quality));
/// <summary>
/// Verifies that the Historian bad-quality sentinel is surfaced to OPC UA clients as a bad status code.
/// </summary>
[Fact]
public void Quality1_MapsToBad()
{
HistorianDataSource.MapQuality(1).ShouldBe(StatusCodes.Bad);
}
/// <summary>
/// Verifies that Historian uncertainty quality bands are translated into OPC UA uncertain results.
/// </summary>
/// <param name="quality">A Wonderware Historian quality byte in the uncertain range.</param>
[Theory]
[InlineData(128)]
[InlineData(133)]
[InlineData(192)]
public void QualityAbove128_MapsToUncertain(byte quality)
[InlineData(192)] // Quality.Good
[InlineData(216)] // Quality.GoodLocalOverride
public void GoodQualityRange_MapsToGood(byte quality)
{
HistorianDataSource.MapQuality(quality).ShouldBe(StatusCodes.Uncertain);
StatusCode.IsGood(MapHistorianQuality(quality)).ShouldBeTrue();
}
/// <summary>
/// Verifies that nonzero non-uncertain Historian quality values are treated as bad historical samples.
/// </summary>
/// <param name="quality">A Wonderware Historian quality byte that should map to an OPC UA bad status.</param>
[Theory]
[InlineData(2)]
[InlineData(50)]
[InlineData(127)]
public void OtherBadQualities_MapToBad(byte quality)
[InlineData(64)] // Quality.Uncertain
[InlineData(68)] // Quality.UncertainLastUsable
[InlineData(80)] // Quality.UncertainSensorNotAccurate
[InlineData(88)] // Quality.UncertainSubNormal
[InlineData(128)] // Uncertain range (no exact enum match)
public void UncertainQualityRange_MapsToUncertain(byte quality)
{
HistorianDataSource.MapQuality(quality).ShouldBe(StatusCodes.Bad);
StatusCode.IsUncertain(MapHistorianQuality(quality)).ShouldBeTrue();
}
[Theory]
[InlineData(0)] // Quality.Bad
[InlineData(1)] // Bad range
[InlineData(4)] // Quality.BadConfigError
[InlineData(8)] // Quality.BadNotConnected
[InlineData(20)] // Quality.BadCommFailure
[InlineData(50)] // Bad range (no exact enum match)
public void BadQualityRange_MapsToBad(byte quality)
{
StatusCode.IsBad(MapHistorianQuality(quality)).ShouldBeTrue();
}
}
}