dc0a24f224
v2-ci / unit-tests (push) Failing after 1h8m43s
mxaccessgw 488649c+ acknowledges by AuthenticateUser as the operator and
writing <alarm>.AckMsg, so AcknowledgeAlarmRequest now needs a real Galaxy
operator_user + operator_domain + operator_password. The OPC UA data plane is
API-key/anonymous and carries no Galaxy password, so the driver resolves the
account per ack:
1. the session's API key's Galaxy ack identity, set on AdminUI /apikeys and
held ONLY in the encrypted Secrets store
(otopcua/apikeys/<keyId>/galaxy-ack-{user,password}); a key with a user but
no password secret is refused, never silently downgraded;
2. else the driver instance's AlarmAck fallback account (DriverConfig
FallbackUser/FallbackDomain/FallbackPasswordSecretRef; secret:/${secret:}/
env:/file:/dev: only — a literal is refused);
3. else DriverOperationRefusedException: nothing is sent, never an empty
operator_user. Refusals are excluded from the resilience retry/breaker.
- Pins ZB.MOM.WW.MxGateway.Client/Contracts 0.2.1 -> 0.3.0 (vendored into
nuget-packages/ from the Gitea feed, per the local-mxgw source mapping).
- The session ApiKeyId (not a secret) rides NativeAlarmAck ->
RouteNativeAlarmAck -> RouteAlarmAck -> AlarmAcknowledgeRequest; the password
is resolved in the driver and never travels through actor messages.
- The OPC UA principal + key id are appended to the AckMsg comment
("[OPC UA: <principal>, key <id>]") and logged, so a shared fallback
account does not lose who acknowledged.
- Password never logged: GalaxyAckCredential.ToString omits it, the
acknowledger scrubs it from every gateway message it echoes and withholds
any exception that echoes it; audit rows carry only the key id.
- AdminUI: /apikeys "Ack identity" editor (set/remove; delete-key clears it),
Galaxy driver form gains the fallback section.
- Synchronous OPC UA status for a refusal is follow-up #760 (the native ack
path is fire-and-forget after the Part 9 method returns).
Tests: request mapping (password populated), password absent from logs and
exception text (mutation-checked), key-mapped / fallback / refused paths at
resolver and driver level, breaker non-trip, key-id propagation through node
manager and DriverHostActor, AdminUI service + form round-trip.
Closes #749
34 lines
1.5 KiB
C#
34 lines
1.5 KiB
C#
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Runtime;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests.Runtime;
|
|
|
|
/// <summary>
|
|
/// <see cref="IGalaxyAckCredentialResolver"/> test double that resolves every acknowledge to one fixed
|
|
/// credential (or refuses all), recording the key ids it was asked about.
|
|
/// </summary>
|
|
internal sealed class FixedAckCredentialResolver : IGalaxyAckCredentialResolver
|
|
{
|
|
private readonly GalaxyAckCredential? _credential;
|
|
|
|
private FixedAckCredentialResolver(GalaxyAckCredential? credential) => _credential = credential;
|
|
|
|
/// <summary>Gets the key ids the driver resolved for, in order.</summary>
|
|
public List<string?> RequestedKeyIds { get; } = [];
|
|
|
|
/// <summary>A resolver that always returns a fallback account <c>ACKDOM\ack-op</c> / <c>pw</c>.</summary>
|
|
public static FixedAckCredentialResolver Fallback() =>
|
|
new(new GalaxyAckCredential("ack-op", "ACKDOM", "pw", GalaxyAckCredentialSource.Fallback));
|
|
|
|
/// <summary>A resolver that refuses every acknowledge.</summary>
|
|
public static FixedAckCredentialResolver RefuseAll() => new(null);
|
|
|
|
/// <inheritdoc />
|
|
public Task<GalaxyAckCredentialResolution> ResolveAsync(string? apiKeyId, CancellationToken cancellationToken)
|
|
{
|
|
RequestedKeyIds.Add(apiKeyId);
|
|
return Task.FromResult(_credential is null
|
|
? GalaxyAckCredentialResolution.Refused("test refusal")
|
|
: GalaxyAckCredentialResolution.Resolved(_credential));
|
|
}
|
|
}
|