fix(site-secrets): one shared secret: reference parser for scanner and MxGateway adapter
Final review M3: SecretReferenceScanner.TryParseSecretReference (trimmed, case-insensitive
prefix, trimmed name, empty = not a reference, ${secret:} never matches) is used by the scanner
and by MxGatewayDataConnection's ApiKey resolution, so a padded or case-variant reference
resolves the name central ships. A bare 'secret:' ApiKey now fails closed. M7: unit test pins
stamped hash == ComputeCurrentArtifactHashesAsync with a non-empty Secrets list.
This commit is contained in:
@@ -3,6 +3,7 @@ using Microsoft.Extensions.Logging;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Protocol;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Serialization;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Types.Secrets;
|
||||
using ZB.MOM.WW.Secrets.Abstractions;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Adapters;
|
||||
@@ -98,20 +99,27 @@ public class MxGatewayDataConnection
|
||||
// so a fail-closed throw here leaves adapter state untouched (no half-open client,
|
||||
// previous connection — if any — still intact for a later retry). A literal key
|
||||
// (no `secret:` prefix) passes through unchanged for back-compat.
|
||||
// The reference is parsed by the SAME parser central's SecretReferenceScanner uses to
|
||||
// decide which secret to ship (trimmed, case-insensitive prefix), so a padded or
|
||||
// case-variant reference resolves exactly the name that was deployed (final review M3).
|
||||
var apiKey = cfg.ApiKey;
|
||||
if (!string.IsNullOrEmpty(apiKey) &&
|
||||
apiKey.StartsWith("secret:", StringComparison.OrdinalIgnoreCase))
|
||||
if (SecretReferenceScanner.TryParseSecretReference(apiKey, out var secretName))
|
||||
{
|
||||
if (_secretResolver is null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"MxGateway ApiKey uses a 'secret:' reference but no ISecretResolver is available to resolve it.");
|
||||
}
|
||||
var secretName = apiKey["secret:".Length..];
|
||||
apiKey = await _secretResolver.GetAsync(new SecretName(secretName), cancellationToken)
|
||||
?? throw new InvalidOperationException(
|
||||
$"MxGateway ApiKey secret '{secretName}' could not be resolved (not found or tombstoned).");
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(apiKey) &&
|
||||
apiKey.TrimStart().StartsWith(SecretReferenceScanner.Prefix, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// `secret:` with no name: fail closed rather than sending the literal text as a key.
|
||||
throw new InvalidOperationException("MxGateway ApiKey is a 'secret:' reference with no secret name.");
|
||||
}
|
||||
|
||||
// Reconnect reuses this adapter: cancel the previous event loop and dispose
|
||||
// the previous client so a flapping gateway cannot accumulate orphaned
|
||||
|
||||
Reference in New Issue
Block a user