9cad9ed0fc
v2-ci / build (push) Failing after 41s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped
Adds <summary>/<param>/<returns>/<inheritdoc> where missing and removes project bookkeeping IDs (task/tracking refs) from shipped code comments, so the docs read cleanly and CommentChecker is quiet except for known false positives (PLC/protocol terms, event/IEqualityComparer inheritdoc). Doc/comment-only; no logic changed; solution builds clean.
90 lines
4.5 KiB
C#
90 lines
4.5 KiB
C#
using Microsoft.Extensions.Logging;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Config;
|
|
|
|
/// <summary>
|
|
/// Resolves <c>Gateway.ApiKeySecretRef</c> to the actual API-key string. Four
|
|
/// forms supported, evaluated in order:
|
|
/// <list type="number">
|
|
/// <item><c>env:NAME</c> — reads <c>Environment.GetEnvironmentVariable(NAME)</c>.
|
|
/// Throws when the variable is unset, so a misconfigured deployment fails
|
|
/// fast rather than silently sending an empty key.</item>
|
|
/// <item><c>file:PATH</c> — reads UTF-8 text from <c>PATH</c>, trimming
|
|
/// whitespace. Lets operators stash the key in an ACL'd file outside the
|
|
/// repo (the same pattern as the legacy <c>.local/galaxy-host-secret.txt</c>).</item>
|
|
/// <item><c>dev:KEY</c> — explicit cleartext literal. The <c>dev:</c> prefix
|
|
/// is a deliberate opt-in signal (dev box, parity rig) so the resolver
|
|
/// doesn't emit a warning; production should never use this arm.</item>
|
|
/// <item>Anything else — used as the literal API key for back-compat with
|
|
/// configs that pre-date this resolver. When a logger is supplied the
|
|
/// resolver emits a startup warning so an operator who accidentally
|
|
/// committed a cleartext key sees it.</item>
|
|
/// </list>
|
|
/// A future PR can swap any of these arms for a DPAPI-backed lookup without
|
|
/// changing the call site.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Lives in the Contracts project so both the runtime <c>GalaxyDriver</c> and the
|
|
/// AdminUI <c>GalaxyDriverBrowser</c> (which intentionally don't reference each
|
|
/// other) share a single resolver rather than each maintaining a copy.
|
|
/// </remarks>
|
|
public static class GalaxySecretRef
|
|
{
|
|
/// <summary>
|
|
/// Resolves the supplied secret reference. When the ref falls through to the
|
|
/// back-compat literal arm (an unprefixed cleartext API key in
|
|
/// <c>DriverConfig</c> JSON) and a <paramref name="logger"/> is supplied, emits
|
|
/// a <see cref="LogLevel.Warning"/>. The <c>dev:</c> prefix is the explicit
|
|
/// opt-in path that doesn't warn.
|
|
/// </summary>
|
|
/// <param name="secretRef">The secret reference string to resolve.</param>
|
|
/// <param name="logger">Optional logger for warning on cleartext keys.</param>
|
|
/// <returns>The resolved API-key string.</returns>
|
|
public static string ResolveApiKey(string secretRef, ILogger? logger = null)
|
|
{
|
|
ArgumentException.ThrowIfNullOrEmpty(secretRef);
|
|
|
|
if (secretRef.StartsWith("env:", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
var name = secretRef[4..];
|
|
var value = Environment.GetEnvironmentVariable(name);
|
|
return !string.IsNullOrEmpty(value)
|
|
? value
|
|
: throw new InvalidOperationException(
|
|
$"Galaxy.Gateway.ApiKeySecretRef='{secretRef}' resolves to env var '{name}', but it is unset.");
|
|
}
|
|
|
|
if (secretRef.StartsWith("file:", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
var path = secretRef[5..];
|
|
if (!File.Exists(path))
|
|
{
|
|
throw new InvalidOperationException(
|
|
$"Galaxy.Gateway.ApiKeySecretRef='{secretRef}' points at '{path}', which doesn't exist.");
|
|
}
|
|
var contents = File.ReadAllText(path).Trim();
|
|
return !string.IsNullOrEmpty(contents)
|
|
? contents
|
|
: throw new InvalidOperationException(
|
|
$"Galaxy.Gateway.ApiKeySecretRef='{secretRef}' file '{path}' is empty.");
|
|
}
|
|
|
|
if (secretRef.StartsWith("dev:", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
// Explicit dev opt-in — no warning, the operator deliberately chose a
|
|
// cleartext literal (dev box, parity rig).
|
|
return secretRef[4..];
|
|
}
|
|
|
|
// Back-compat literal arm. An unprefixed string is treated as the literal
|
|
// API key — but emit a warning so an operator who accidentally committed a
|
|
// cleartext key into DriverConfig sees it. Use the dev: prefix to suppress
|
|
// this warning when the literal is intentional.
|
|
logger?.LogWarning(
|
|
"Galaxy.Gateway.ApiKeySecretRef is being treated as a literal cleartext API key. " +
|
|
"Prefer env:NAME, file:PATH, or the explicit dev:KEY prefix for dev rigs — " +
|
|
"a literal key in DriverConfig JSON is stored in cleartext in the central config DB.");
|
|
return secretRef;
|
|
}
|
|
}
|