feat(alarms): AlarmAcknowledgeRequest carries OperatorUser; Galaxy/ScriptedAlarmSource honor it [H6b]

This commit is contained in:
Joseph Doherty
2026-06-15 14:11:40 -04:00
parent c236263e8d
commit ed941c51da
5 changed files with 65 additions and 7 deletions
@@ -41,10 +41,15 @@ public interface IAlarmSubscriptionHandle
}
/// <summary>One alarm acknowledgement in a batch.</summary>
/// <param name="SourceNodeId">Driver-side identifier for the alarm source.</param>
/// <param name="ConditionId">Stable id correlating raise / ack / clear of the same condition.</param>
/// <param name="Comment">Operator-supplied comment forwarded to the upstream alarm system.</param>
/// <param name="OperatorUser">Authenticated principal performing the ack; null on the raw/non-OPC-UA path.</param>
public sealed record AlarmAcknowledgeRequest(
string SourceNodeId,
string ConditionId,
string? Comment);
string? Comment,
string? OperatorUser = null);
/// <summary>Event payload for <see cref="IAlarmSource.OnAlarmEvent"/>.</summary>
/// <param name="SubscriptionHandle">Subscription this event belongs to.</param>
@@ -68,11 +68,11 @@ public sealed class ScriptedAlarmSource : IAlarmSource, IDisposable
if (acknowledgements is null) throw new ArgumentNullException(nameof(acknowledgements));
foreach (var a in acknowledgements)
{
// The base interface doesn't carry a user identity — Stream G provides the
// authenticated principal at the OPC UA dispatch layer + proxies through
// the engine's richer AcknowledgeAsync. Here we default to "opcua-client"
// so callers using the raw IAlarmSource still produce an audit entry.
await _engine.AcknowledgeAsync(a.ConditionId, "opcua-client", a.Comment, cancellationToken)
// Honor the authenticated principal carried on the request when present
// (the OPC UA dispatch layer / Stream G threads it through). When absent —
// the raw/non-OPC-UA path — default to "opcua-client" so the ack still
// produces an audit entry.
await _engine.AcknowledgeAsync(a.ConditionId, a.OperatorUser ?? "opcua-client", a.Comment, cancellationToken)
.ConfigureAwait(false);
}
}
@@ -1122,7 +1122,9 @@ public sealed class GalaxyDriver
await _alarmAcknowledger.AcknowledgeAsync(
alarmFullReference,
ack.Comment ?? string.Empty,
operatorUser: string.Empty, // server-side ACL fills this from the OPC UA session
// Honor the authenticated principal carried on the request when present;
// fall back to empty (server-side ACL fills this from the OPC UA session).
operatorUser: ack.OperatorUser ?? string.Empty,
cancellationToken).ConfigureAwait(false);
}
}