a224fd6391
A Designer could point a Galaxy / OpcUaClient endpoint at a host they control, set a credential field to any reference the node can resolve (env:ConnectionStrings__ConfigDb, env:ServerHistorian__ApiKey, secret:<any>, Galaxy file:<any path>) and click Test Connect / live browse; the node resolved the value and sent it to that host. One shared rule in Core.Abstractions (CredentialReferences + DriverCredentialPolicy) generalises the #803 Heidenhain namespace: - Galaxy: secret:galaxy/... | secret:otopcua/galaxy/... | env:OTOPCUA_GALAXY_... | legacy env:GALAXY_MXGW_...; file: refused; dev:/literal API key unchanged. - OpcUaClient: secret:opcuaclient/... | env:OTOPCUA_OPCUACLIENT_...; literals unchanged. - Heidenhain adopts the shared parser/resolver (namespace unchanged). Enforced at the AdminUI save path (DriverConfigSaveGate), the deploy gate (CredentialReferenceDeployChecks, code CredentialReferenceOutOfScope), Test Connect (AdminOperationsActor, before any probe), live browse (BrowserSessionService, before any browser) and again at resolve time in every driver/browser (fail closed, before any store/env read). Co-Authored-By: Claude <noreply@anthropic.com>
47 lines
2.5 KiB
C#
47 lines
2.5 KiB
C#
using ZB.MOM.WW.OtOpcUa.Commons.Types;
|
|
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Configuration.Validation;
|
|
|
|
/// <summary>
|
|
/// Deploy gate for credential references (lmxopcua#815): every DriverConfig and DeviceConfig of a driver type
|
|
/// listed in <see cref="DriverCredentialPolicy"/> must carry its credential fields as blank, an acceptable
|
|
/// literal, or a reference inside that driver's namespace (e.g. Galaxy <c>secret:galaxy/…</c> /
|
|
/// <c>env:OTOPCUA_GALAXY_…</c>). The same table the AdminUI save path, Test Connect, live browse and every
|
|
/// driver's resolve-time check read — so the gate and the runtime cannot disagree.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Heidenhain is excluded here only because <see cref="HeidenhainDeployChecks"/> already reports the same rule
|
|
/// (as <see cref="HeidenhainDeployChecks.CredentialCode"/>) through the shared parser; reporting it twice would
|
|
/// duplicate every error. The message names the field and the allowed forms, never the value.
|
|
/// </remarks>
|
|
public static class CredentialReferenceDeployChecks
|
|
{
|
|
/// <summary>A credential field holding a reference outside the driver's namespace (or a refused literal).</summary>
|
|
public const string Code = "CredentialReferenceOutOfScope";
|
|
|
|
/// <summary>Runs the gate.</summary>
|
|
/// <param name="draft">The draft.</param>
|
|
/// <returns>The errors.</returns>
|
|
public static IReadOnlyList<ValidationError> Errors(DraftSnapshot draft)
|
|
{
|
|
var errors = new List<ValidationError>();
|
|
foreach (var driver in draft.DriverInstances)
|
|
{
|
|
if (string.Equals(driver.DriverType, DriverTypeNames.Heidenhain, StringComparison.OrdinalIgnoreCase)) continue;
|
|
if (!DriverCredentialPolicy.TryGet(driver.DriverType, out _)) continue;
|
|
|
|
foreach (var problem in DriverCredentialPolicy.DescribeProblems(driver.DriverType, driver.DriverConfig))
|
|
errors.Add(new(Code, $"{driver.DriverType} driver '{driver.Name}': {problem}", driver.DriverInstanceId));
|
|
|
|
foreach (var device in draft.Devices.Where(d => string.Equals(d.DriverInstanceId, driver.DriverInstanceId, StringComparison.Ordinal)))
|
|
{
|
|
foreach (var problem in DriverCredentialPolicy.DescribeProblems(driver.DriverType, device.DeviceConfig))
|
|
errors.Add(new(Code, $"{driver.DriverType} device '{device.Name}': {problem}", device.DeviceId));
|
|
}
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
}
|