feat(worker): route alarm acks through the operator-authenticated AckMsg writer
ci / nightly-windev (push) Has been skipped
ci / java (push) Failing after 2m5s
ci / windows-x86 (push) Successful in 2m10s
ci / portable (push) Failing after 2m17s

AlarmCommandHandler -> AlarmDispatcher -> IAlarmAckWriter: the dispatcher resolves the ack item through the consumer and the writer authenticates the operator and writes the comment. The executor builds AlarmAckOperator from operator_user/domain/password, maps AlarmAckOutcome to the reply (Applied/Unconfirmed Ok, AlarmNotFound/NotAckable InvalidRequest, AuthenticationFailed/WriteRejected MxaccessFailure) and reports escaping exceptions by type and HRESULT only, so operator_password cannot leak. Removes the inert managed/WNAL alarm-client ack path.

Claude-Session: https://claude.ai/code/session_016J6isfcgqy4SB2RK3ZvFvT
This commit is contained in:
Joseph Doherty
2026-10-19 19:35:55 -04:00
parent 0d4b2aea2d
commit d6db0ff40d
14 changed files with 695 additions and 1282 deletions
@@ -35,44 +35,21 @@ public interface IAlarmCommandHandler : IDisposable
/// <summary>Tear down the active subscription. No-op if not subscribed.</summary>
void Unsubscribe();
/// <summary>Acknowledge a single alarm by GUID. Returns AVEVA's native status (0 = success).</summary>
/// <summary>Acknowledges an alarm by GUID as <paramref name="ackOperator"/>.</summary>
/// <param name="alarmGuid">The alarm GUID.</param>
/// <param name="comment">The acknowledgment comment.</param>
/// <param name="operatorUser">The operator user name.</param>
/// <param name="operatorNode">The operator node name.</param>
/// <param name="operatorDomain">The operator domain name.</param>
/// <param name="operatorFullName">The operator full name.</param>
/// <returns>AVEVA's native alarm status code (0 = success).</returns>
int Acknowledge(
Guid alarmGuid,
string comment,
string operatorUser,
string operatorNode,
string operatorDomain,
string operatorFullName);
/// <param name="comment">The acknowledgement comment written to the alarm's ack item.</param>
/// <param name="ackOperator">The operator to authenticate as (credential-sensitive).</param>
/// <returns>The acknowledgement outcome.</returns>
AlarmAckResult Acknowledge(Guid alarmGuid, string comment, AlarmAckOperator ackOperator);
/// <summary>
/// Acknowledge a single alarm by (name, provider, group) — used when
/// the caller has the human-readable reference but not the GUID.
/// </summary>
/// <param name="alarmName">The alarm name.</param>
/// <summary>Acknowledges an alarm by name/provider/group as <paramref name="ackOperator"/>.</summary>
/// <param name="alarmName">The alarm (tag) name.</param>
/// <param name="providerName">The provider name.</param>
/// <param name="groupName">The group name.</param>
/// <param name="comment">The acknowledgment comment.</param>
/// <param name="operatorUser">The operator user name.</param>
/// <param name="operatorNode">The operator node name.</param>
/// <param name="operatorDomain">The operator domain name.</param>
/// <param name="operatorFullName">The operator full name.</param>
/// <returns>AVEVA's native alarm status code (0 = success).</returns>
int AcknowledgeByName(
string alarmName,
string providerName,
string groupName,
string comment,
string operatorUser,
string operatorNode,
string operatorDomain,
string operatorFullName);
/// <param name="comment">The acknowledgement comment written to the alarm's ack item.</param>
/// <param name="ackOperator">The operator to authenticate as (credential-sensitive).</param>
/// <returns>The acknowledgement outcome.</returns>
AlarmAckResult AcknowledgeByName(string alarmName, string providerName, string groupName, string comment, AlarmAckOperator ackOperator);
/// <summary>
/// Snapshot the currently-active alarm set, optionally scoped to a