feat(gateway): forward operator credentials on alarm acknowledgement
Claude-Session: https://claude.ai/code/session_016J6isfcgqy4SB2RK3ZvFvT
This commit is contained in:
@@ -1169,7 +1169,7 @@ public sealed class GatewayAlarmMonitor : BackgroundService, IGatewayAlarmServic
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private static MxCommand? BuildAcknowledgeCommand(AcknowledgeAlarmRequest request, out string? parseError)
|
||||
internal static MxCommand? BuildAcknowledgeCommand(AcknowledgeAlarmRequest request, out string? parseError)
|
||||
{
|
||||
parseError = null;
|
||||
if (string.IsNullOrWhiteSpace(request.AlarmFullReference))
|
||||
@@ -1180,6 +1180,8 @@ public sealed class GatewayAlarmMonitor : BackgroundService, IGatewayAlarmServic
|
||||
|
||||
string comment = request.Comment ?? string.Empty;
|
||||
string operatorUser = request.OperatorUser ?? string.Empty;
|
||||
string operatorPassword = request.OperatorPassword ?? string.Empty;
|
||||
string operatorDomain = request.OperatorDomain ?? string.Empty;
|
||||
|
||||
if (Guid.TryParse(request.AlarmFullReference, out Guid guid))
|
||||
{
|
||||
@@ -1192,7 +1194,8 @@ public sealed class GatewayAlarmMonitor : BackgroundService, IGatewayAlarmServic
|
||||
Comment = comment,
|
||||
OperatorUser = operatorUser,
|
||||
OperatorNode = string.Empty,
|
||||
OperatorDomain = string.Empty,
|
||||
OperatorDomain = operatorDomain,
|
||||
OperatorPassword = operatorPassword,
|
||||
OperatorFullName = string.Empty,
|
||||
},
|
||||
};
|
||||
@@ -1211,7 +1214,8 @@ public sealed class GatewayAlarmMonitor : BackgroundService, IGatewayAlarmServic
|
||||
Comment = comment,
|
||||
OperatorUser = operatorUser,
|
||||
OperatorNode = string.Empty,
|
||||
OperatorDomain = string.Empty,
|
||||
OperatorDomain = operatorDomain,
|
||||
OperatorPassword = operatorPassword,
|
||||
OperatorFullName = string.Empty,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -73,6 +73,13 @@ public sealed class AlarmsOptions
|
||||
/// </summary>
|
||||
public int MaxAlarmsPerFetch { get; init; } = 1024;
|
||||
|
||||
/// <summary>
|
||||
/// MXAccess client name the alarm worker's ack writer registers with. The
|
||||
/// AVEVA Historian records it as each acknowledgement's OperatorNode.
|
||||
/// Conveyed to workers via MXGATEWAY_ALARM_ACK_CLIENT_NAME.
|
||||
/// </summary>
|
||||
public string AckClientName { get; init; } = "MxGateway";
|
||||
|
||||
/// <summary>
|
||||
/// Configuration for the alarm-manager ↔ subtag fallback mechanism:
|
||||
/// operating mode, failure-detection thresholds, discovery, and subtag
|
||||
|
||||
@@ -488,6 +488,13 @@ public sealed class GatewayOptionsValidator : OptionsValidatorBase<GatewayOption
|
||||
$"MxGateway:Alarms:MaxAlarmsPerFetch must be between {MinimumMaxAlarmsPerFetch} and {MaximumMaxAlarmsPerFetch}.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(options.AckClientName)
|
||||
|| options.AckClientName.Length > 64
|
||||
|| options.AckClientName.Any(char.IsControl))
|
||||
{
|
||||
builder.Add("MxGateway:Alarms:AckClientName must be 1-64 printable characters.");
|
||||
}
|
||||
|
||||
if (!options.Enabled)
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -12,7 +12,10 @@ public static class GatewayLogRedactor
|
||||
{
|
||||
"AuthenticateUser",
|
||||
"WriteSecured",
|
||||
"WriteSecured2"
|
||||
"WriteSecured2",
|
||||
// Carry operator_password.
|
||||
"AcknowledgeAlarm",
|
||||
"AcknowledgeAlarmByName"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -33,6 +33,14 @@ public sealed class GatewayLogRedactorSeam : ILogRedactor
|
||||
}
|
||||
|
||||
RedactCommandValue(properties);
|
||||
|
||||
foreach (string key in properties.Keys.ToList())
|
||||
{
|
||||
if (key.Contains("password", StringComparison.OrdinalIgnoreCase) && properties[key] is not null)
|
||||
{
|
||||
properties[key] = GatewayLogRedactor.RedactedValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -197,6 +197,11 @@ public sealed class MxAccessGatewayService(
|
||||
throw new RpcException(new Status(StatusCode.InvalidArgument, "alarm_full_reference is required."));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(request.OperatorUser))
|
||||
{
|
||||
throw new RpcException(new Status(StatusCode.InvalidArgument, "operator_user is required."));
|
||||
}
|
||||
|
||||
return await alarmService.AcknowledgeAsync(request, context.CancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -223,6 +223,8 @@ public sealed class WorkerProcessLauncher : IWorkerProcessLauncher
|
||||
startInfo.Environment[WorkerLaunchEnvironment.MaxAlarmsPerFetch] =
|
||||
_alarmsOptions.MaxAlarmsPerFetch.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||
|
||||
startInfo.Environment[WorkerLaunchEnvironment.AlarmAckClientName] = _alarmsOptions.AckClientName;
|
||||
|
||||
commandLine = new WorkerProcessCommandLine(executablePath, arguments);
|
||||
|
||||
return startInfo;
|
||||
|
||||
Reference in New Issue
Block a user