feat(contracts): carry operator credentials on alarm acknowledgement
Claude-Session: https://claude.ai/code/session_016J6isfcgqy4SB2RK3ZvFvT
This commit is contained in:
Binary file not shown.
@@ -391,10 +391,13 @@ message AlarmFailoverConfig {
|
|||||||
int32 failback_stable_probes = 3; // clean probes before switching back (>=1)
|
int32 failback_stable_probes = 3; // clean probes before switching back (>=1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Acknowledge a single alarm by its GUID. Operator identity fields are
|
// Acknowledge a single alarm by its GUID. The worker resolves the GUID to the
|
||||||
// recorded atomically with the ack transition in the alarm-history log.
|
// alarm's Galaxy reference from its active-alarm snapshot and acknowledges it
|
||||||
// The reply's hresult / native_status surfaces AVEVA's
|
// by writing `comment` to `<TAGNAME>.AckMsg` over MXAccess after
|
||||||
// AlarmAckByGUID return code.
|
// AuthenticateUser as the operator, so the alarm history records the real
|
||||||
|
// operator. The reply's hresult / native_status is 0 when the write was
|
||||||
|
// applied (or sent but unconfirmed), otherwise the AuthenticateUser HRESULT or
|
||||||
|
// the OnWriteComplete status detail.
|
||||||
message AcknowledgeAlarmCommand {
|
message AcknowledgeAlarmCommand {
|
||||||
// Canonical 8-4-4-4-12 GUID string (e.g. "BCC47053-9542-4D65-BDAA-BCDEA6A32A73").
|
// Canonical 8-4-4-4-12 GUID string (e.g. "BCC47053-9542-4D65-BDAA-BCDEA6A32A73").
|
||||||
string alarm_guid = 1;
|
string alarm_guid = 1;
|
||||||
@@ -403,6 +406,10 @@ message AcknowledgeAlarmCommand {
|
|||||||
string operator_node = 4;
|
string operator_node = 4;
|
||||||
string operator_domain = 5;
|
string operator_domain = 5;
|
||||||
string operator_full_name = 6;
|
string operator_full_name = 6;
|
||||||
|
// Operator credential used for AuthenticateUser before the AckMsg write.
|
||||||
|
// Implementations must keep this field out of logs, metrics labels, command
|
||||||
|
// lines, and diagnostics.
|
||||||
|
string operator_password = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Snapshot the currently-active alarm set. Optional filter prefix scopes
|
// Snapshot the currently-active alarm set. Optional filter prefix scopes
|
||||||
@@ -414,9 +421,7 @@ message QueryActiveAlarmsCommand {
|
|||||||
|
|
||||||
// Acknowledge a single alarm by its (name, provider, group) tuple. Used
|
// Acknowledge a single alarm by its (name, provider, group) tuple. Used
|
||||||
// when the public RPC's AlarmFullReference (Provider!Group.Tag) cannot
|
// when the public RPC's AlarmFullReference (Provider!Group.Tag) cannot
|
||||||
// be resolved to a GUID directly. The worker invokes
|
// be resolved to a GUID directly. The worker acknowledges by writing `comment` to `<alarm_name>.AckMsg` (Galaxy provider only) after AuthenticateUser as the operator.
|
||||||
// wwAlarmConsumerClass.AlarmAckByName which reaches the same alarm
|
|
||||||
// history path as AlarmAckByGUID.
|
|
||||||
message AcknowledgeAlarmByNameCommand {
|
message AcknowledgeAlarmByNameCommand {
|
||||||
// Tag/alarm name (e.g. "TestMachine_001.TestAlarm001"). Tag itself
|
// Tag/alarm name (e.g. "TestMachine_001.TestAlarm001"). Tag itself
|
||||||
// may contain dots; the gateway-side parser splits on the first dot
|
// may contain dots; the gateway-side parser splits on the first dot
|
||||||
@@ -431,6 +436,10 @@ message AcknowledgeAlarmByNameCommand {
|
|||||||
string operator_node = 6;
|
string operator_node = 6;
|
||||||
string operator_domain = 7;
|
string operator_domain = 7;
|
||||||
string operator_full_name = 8;
|
string operator_full_name = 8;
|
||||||
|
// Operator credential used for AuthenticateUser before the AckMsg write.
|
||||||
|
// Implementations must keep this field out of logs, metrics labels, command
|
||||||
|
// lines, and diagnostics.
|
||||||
|
string operator_password = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
message UnsubscribeBulkCommand {
|
message UnsubscribeBulkCommand {
|
||||||
@@ -725,11 +734,11 @@ message DrainEventsReply {
|
|||||||
// Reply payload for AcknowledgeAlarmCommand AND
|
// Reply payload for AcknowledgeAlarmCommand AND
|
||||||
// AcknowledgeAlarmByNameCommand — both ack command kinds reuse this
|
// AcknowledgeAlarmByNameCommand — both ack command kinds reuse this
|
||||||
// payload case (`MxCommandReply.acknowledge_alarm`); there is no
|
// payload case (`MxCommandReply.acknowledge_alarm`); there is no
|
||||||
// dedicated by-name reply case. Surfaces AVEVA's native ack return
|
// dedicated by-name reply case. Surfaces the ack outcome: 0 when the
|
||||||
// code (AlarmAckByGUID for the GUID arm, AlarmAckByName for the
|
// AckMsg write was applied or sent but unconfirmed; otherwise the
|
||||||
// by-name arm); 0 means success. The MxCommandReply's hresult field
|
// AuthenticateUser HRESULT or the OnWriteComplete status detail. The MxCommandReply's hresult field
|
||||||
// carries the same value and is preferred for protocol consumers —
|
// carries the same value and is preferred for protocol consumers —
|
||||||
// this payload exists so the gateway-side WorkerAlarmRpcDispatcher
|
// this payload exists so GatewayAlarmMonitor
|
||||||
// can echo native_status into AcknowledgeAlarmReply.hresult without
|
// can echo native_status into AcknowledgeAlarmReply.hresult without
|
||||||
// unpacking the outer envelope.
|
// unpacking the outer envelope.
|
||||||
message AcknowledgeAlarmReplyPayload {
|
message AcknowledgeAlarmReplyPayload {
|
||||||
@@ -987,6 +996,15 @@ message AcknowledgeAlarmRequest {
|
|||||||
// Operator principal performing the acknowledgement. The lmxopcua side
|
// Operator principal performing the acknowledgement. The lmxopcua side
|
||||||
// resolves this from the OPC UA session prior to invoking the RPC.
|
// resolves this from the OPC UA session prior to invoking the RPC.
|
||||||
string operator_user = 5;
|
string operator_user = 5;
|
||||||
|
// Operator's Galaxy (or OS) password. The gateway authenticates as
|
||||||
|
// operator_user with it before writing the acknowledgement so the alarm
|
||||||
|
// history records the real operator. May be empty for accounts without a
|
||||||
|
// password. Credential-sensitive: implementations must keep this field out
|
||||||
|
// of logs, metrics labels, command lines, and diagnostics.
|
||||||
|
string operator_password = 6;
|
||||||
|
// Optional domain for OS-authenticated galaxies. When set the gateway
|
||||||
|
// authenticates as "<operator_domain>\<operator_user>".
|
||||||
|
string operator_domain = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
message AcknowledgeAlarmReply {
|
message AcknowledgeAlarmReply {
|
||||||
@@ -997,8 +1015,7 @@ message AcknowledgeAlarmReply {
|
|||||||
ProtocolStatus protocol_status = 3;
|
ProtocolStatus protocol_status = 3;
|
||||||
// Native ack return code echoed from the worker. The worker carries the
|
// Native ack return code echoed from the worker. The worker carries the
|
||||||
// ack outcome as a single int32 (AcknowledgeAlarmReplyPayload.native_status,
|
// ack outcome as a single int32 (AcknowledgeAlarmReplyPayload.native_status,
|
||||||
// = AlarmAckByName / AlarmAckByGUID return code; 0 = success); the gateway's
|
// 0 = applied or sent; otherwise the AuthenticateUser HRESULT or OnWriteComplete detail); GatewayAlarmMonitor copies that value here. This is the authoritative
|
||||||
// WorkerAlarmRpcDispatcher copies that value here. This is the authoritative
|
|
||||||
// ack-outcome field for the public RPC. Absent only when the worker reply
|
// ack-outcome field for the public RPC. Absent only when the worker reply
|
||||||
// omitted the value entirely (a protocol violation).
|
// omitted the value entirely (a protocol violation).
|
||||||
optional int32 hresult = 4;
|
optional int32 hresult = 4;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -391,10 +391,13 @@ message AlarmFailoverConfig {
|
|||||||
int32 failback_stable_probes = 3; // clean probes before switching back (>=1)
|
int32 failback_stable_probes = 3; // clean probes before switching back (>=1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Acknowledge a single alarm by its GUID. Operator identity fields are
|
// Acknowledge a single alarm by its GUID. The worker resolves the GUID to the
|
||||||
// recorded atomically with the ack transition in the alarm-history log.
|
// alarm's Galaxy reference from its active-alarm snapshot and acknowledges it
|
||||||
// The reply's hresult / native_status surfaces AVEVA's
|
// by writing `comment` to `<TAGNAME>.AckMsg` over MXAccess after
|
||||||
// AlarmAckByGUID return code.
|
// AuthenticateUser as the operator, so the alarm history records the real
|
||||||
|
// operator. The reply's hresult / native_status is 0 when the write was
|
||||||
|
// applied (or sent but unconfirmed), otherwise the AuthenticateUser HRESULT or
|
||||||
|
// the OnWriteComplete status detail.
|
||||||
message AcknowledgeAlarmCommand {
|
message AcknowledgeAlarmCommand {
|
||||||
// Canonical 8-4-4-4-12 GUID string (e.g. "BCC47053-9542-4D65-BDAA-BCDEA6A32A73").
|
// Canonical 8-4-4-4-12 GUID string (e.g. "BCC47053-9542-4D65-BDAA-BCDEA6A32A73").
|
||||||
string alarm_guid = 1;
|
string alarm_guid = 1;
|
||||||
@@ -403,6 +406,10 @@ message AcknowledgeAlarmCommand {
|
|||||||
string operator_node = 4;
|
string operator_node = 4;
|
||||||
string operator_domain = 5;
|
string operator_domain = 5;
|
||||||
string operator_full_name = 6;
|
string operator_full_name = 6;
|
||||||
|
// Operator credential used for AuthenticateUser before the AckMsg write.
|
||||||
|
// Implementations must keep this field out of logs, metrics labels, command
|
||||||
|
// lines, and diagnostics.
|
||||||
|
string operator_password = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Snapshot the currently-active alarm set. Optional filter prefix scopes
|
// Snapshot the currently-active alarm set. Optional filter prefix scopes
|
||||||
@@ -414,9 +421,7 @@ message QueryActiveAlarmsCommand {
|
|||||||
|
|
||||||
// Acknowledge a single alarm by its (name, provider, group) tuple. Used
|
// Acknowledge a single alarm by its (name, provider, group) tuple. Used
|
||||||
// when the public RPC's AlarmFullReference (Provider!Group.Tag) cannot
|
// when the public RPC's AlarmFullReference (Provider!Group.Tag) cannot
|
||||||
// be resolved to a GUID directly. The worker invokes
|
// be resolved to a GUID directly. The worker acknowledges by writing `comment` to `<alarm_name>.AckMsg` (Galaxy provider only) after AuthenticateUser as the operator.
|
||||||
// wwAlarmConsumerClass.AlarmAckByName which reaches the same alarm
|
|
||||||
// history path as AlarmAckByGUID.
|
|
||||||
message AcknowledgeAlarmByNameCommand {
|
message AcknowledgeAlarmByNameCommand {
|
||||||
// Tag/alarm name (e.g. "TestMachine_001.TestAlarm001"). Tag itself
|
// Tag/alarm name (e.g. "TestMachine_001.TestAlarm001"). Tag itself
|
||||||
// may contain dots; the gateway-side parser splits on the first dot
|
// may contain dots; the gateway-side parser splits on the first dot
|
||||||
@@ -431,6 +436,10 @@ message AcknowledgeAlarmByNameCommand {
|
|||||||
string operator_node = 6;
|
string operator_node = 6;
|
||||||
string operator_domain = 7;
|
string operator_domain = 7;
|
||||||
string operator_full_name = 8;
|
string operator_full_name = 8;
|
||||||
|
// Operator credential used for AuthenticateUser before the AckMsg write.
|
||||||
|
// Implementations must keep this field out of logs, metrics labels, command
|
||||||
|
// lines, and diagnostics.
|
||||||
|
string operator_password = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
message UnsubscribeBulkCommand {
|
message UnsubscribeBulkCommand {
|
||||||
@@ -725,11 +734,11 @@ message DrainEventsReply {
|
|||||||
// Reply payload for AcknowledgeAlarmCommand AND
|
// Reply payload for AcknowledgeAlarmCommand AND
|
||||||
// AcknowledgeAlarmByNameCommand — both ack command kinds reuse this
|
// AcknowledgeAlarmByNameCommand — both ack command kinds reuse this
|
||||||
// payload case (`MxCommandReply.acknowledge_alarm`); there is no
|
// payload case (`MxCommandReply.acknowledge_alarm`); there is no
|
||||||
// dedicated by-name reply case. Surfaces AVEVA's native ack return
|
// dedicated by-name reply case. Surfaces the ack outcome: 0 when the
|
||||||
// code (AlarmAckByGUID for the GUID arm, AlarmAckByName for the
|
// AckMsg write was applied or sent but unconfirmed; otherwise the
|
||||||
// by-name arm); 0 means success. The MxCommandReply's hresult field
|
// AuthenticateUser HRESULT or the OnWriteComplete status detail. The MxCommandReply's hresult field
|
||||||
// carries the same value and is preferred for protocol consumers —
|
// carries the same value and is preferred for protocol consumers —
|
||||||
// this payload exists so the gateway-side WorkerAlarmRpcDispatcher
|
// this payload exists so GatewayAlarmMonitor
|
||||||
// can echo native_status into AcknowledgeAlarmReply.hresult without
|
// can echo native_status into AcknowledgeAlarmReply.hresult without
|
||||||
// unpacking the outer envelope.
|
// unpacking the outer envelope.
|
||||||
message AcknowledgeAlarmReplyPayload {
|
message AcknowledgeAlarmReplyPayload {
|
||||||
@@ -987,6 +996,15 @@ message AcknowledgeAlarmRequest {
|
|||||||
// Operator principal performing the acknowledgement. The lmxopcua side
|
// Operator principal performing the acknowledgement. The lmxopcua side
|
||||||
// resolves this from the OPC UA session prior to invoking the RPC.
|
// resolves this from the OPC UA session prior to invoking the RPC.
|
||||||
string operator_user = 5;
|
string operator_user = 5;
|
||||||
|
// Operator's Galaxy (or OS) password. The gateway authenticates as
|
||||||
|
// operator_user with it before writing the acknowledgement so the alarm
|
||||||
|
// history records the real operator. May be empty for accounts without a
|
||||||
|
// password. Credential-sensitive: implementations must keep this field out
|
||||||
|
// of logs, metrics labels, command lines, and diagnostics.
|
||||||
|
string operator_password = 6;
|
||||||
|
// Optional domain for OS-authenticated galaxies. When set the gateway
|
||||||
|
// authenticates as "<operator_domain>\<operator_user>".
|
||||||
|
string operator_domain = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
message AcknowledgeAlarmReply {
|
message AcknowledgeAlarmReply {
|
||||||
@@ -997,8 +1015,7 @@ message AcknowledgeAlarmReply {
|
|||||||
ProtocolStatus protocol_status = 3;
|
ProtocolStatus protocol_status = 3;
|
||||||
// Native ack return code echoed from the worker. The worker carries the
|
// Native ack return code echoed from the worker. The worker carries the
|
||||||
// ack outcome as a single int32 (AcknowledgeAlarmReplyPayload.native_status,
|
// ack outcome as a single int32 (AcknowledgeAlarmReplyPayload.native_status,
|
||||||
// = AlarmAckByName / AlarmAckByGUID return code; 0 = success); the gateway's
|
// 0 = applied or sent; otherwise the AuthenticateUser HRESULT or OnWriteComplete detail); GatewayAlarmMonitor copies that value here. This is the authoritative
|
||||||
// WorkerAlarmRpcDispatcher copies that value here. This is the authoritative
|
|
||||||
// ack-outcome field for the public RPC. Absent only when the worker reply
|
// ack-outcome field for the public RPC. Absent only when the worker reply
|
||||||
// omitted the value entirely (a protocol violation).
|
// omitted the value entirely (a protocol violation).
|
||||||
optional int32 hresult = 4;
|
optional int32 hresult = 4;
|
||||||
|
|||||||
@@ -22,4 +22,10 @@ public static class WorkerLaunchEnvironment
|
|||||||
|
|
||||||
/// <summary>Maximum alarm records requested in one current-alarm fetch.</summary>
|
/// <summary>Maximum alarm records requested in one current-alarm fetch.</summary>
|
||||||
public const string MaxAlarmsPerFetch = "MXGATEWAY_ALARM_MAX_ALARMS_PER_FETCH";
|
public const string MaxAlarmsPerFetch = "MXGATEWAY_ALARM_MAX_ALARMS_PER_FETCH";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// MXAccess client name the worker's alarm-ack writer registers with. The
|
||||||
|
/// Historian records it as the acknowledgement's OperatorNode.
|
||||||
|
/// </summary>
|
||||||
|
public const string AlarmAckClientName = "MXGATEWAY_ALARM_ACK_CLIENT_NAME";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -310,11 +310,47 @@ public sealed class ProtobufContractRoundTripTests
|
|||||||
AlarmFullReference = "Tank01.Level.HiHi",
|
AlarmFullReference = "Tank01.Level.HiHi",
|
||||||
Comment = "shift handover",
|
Comment = "shift handover",
|
||||||
OperatorUser = "operator2",
|
OperatorUser = "operator2",
|
||||||
|
OperatorPassword = "s3cret",
|
||||||
|
OperatorDomain = "CORP",
|
||||||
};
|
};
|
||||||
|
|
||||||
var parsed = AcknowledgeAlarmRequest.Parser.ParseFrom(original.ToByteArray());
|
var parsed = AcknowledgeAlarmRequest.Parser.ParseFrom(original.ToByteArray());
|
||||||
|
|
||||||
Assert.Equal(original, parsed);
|
Assert.Equal(original, parsed);
|
||||||
|
Assert.Equal("s3cret", parsed.OperatorPassword);
|
||||||
|
Assert.Equal("CORP", parsed.OperatorDomain);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Verifies that the worker ack commands round-trip the operator password.</summary>
|
||||||
|
[Fact]
|
||||||
|
public void AcknowledgeAlarmCommands_RoundTripOperatorPassword()
|
||||||
|
{
|
||||||
|
MxCommand byGuid = new()
|
||||||
|
{
|
||||||
|
Kind = MxCommandKind.AcknowledgeAlarm,
|
||||||
|
AcknowledgeAlarmCommand = new AcknowledgeAlarmCommand
|
||||||
|
{
|
||||||
|
AlarmGuid = "BCC47053-9542-4D65-BDAA-BCDEA6A32A73",
|
||||||
|
OperatorUser = "alice",
|
||||||
|
OperatorDomain = "CORP",
|
||||||
|
OperatorPassword = "s3cret",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
MxCommand byName = new()
|
||||||
|
{
|
||||||
|
Kind = MxCommandKind.AcknowledgeAlarmByName,
|
||||||
|
AcknowledgeAlarmByNameCommand = new AcknowledgeAlarmByNameCommand
|
||||||
|
{
|
||||||
|
AlarmName = "TestMachine_001.TestAlarm001",
|
||||||
|
ProviderName = "Galaxy",
|
||||||
|
GroupName = "TestArea",
|
||||||
|
OperatorUser = "alice",
|
||||||
|
OperatorPassword = "s3cret",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Assert.Equal("s3cret", MxCommand.Parser.ParseFrom(byGuid.ToByteArray()).AcknowledgeAlarmCommand.OperatorPassword);
|
||||||
|
Assert.Equal("s3cret", MxCommand.Parser.ParseFrom(byName.ToByteArray()).AcknowledgeAlarmByNameCommand.OperatorPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Verifies that AcknowledgeAlarmReply round-trips with status, hresult, and diagnostics.</summary>
|
/// <summary>Verifies that AcknowledgeAlarmReply round-trips with status, hresult, and diagnostics.</summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user