HistorianWcfTagClient: respect options.Transport for cert variant

Browse/metadata previously hardcoded the Windows transport binding +
/Hist-Integrated endpoint regardless of options.Transport. That meant
RemoteTcpCertificate clients (notably anything from Linux, where the
Windows transport security isn't available in WCF) couldn't use these
ops even when other reads worked.

Made the binding+endpoint selection conditional:
- LocalPipe / RemoteTcpIntegrated: keep existing /Hist-Integrated +
  Windows transport binding (the legacy Open2-V1 buffer carries its
  own auth blob and only validates against this transport).
- RemoteTcpCertificate: switch to /HistCert + cert binding, propagating
  options.ServerDnsIdentity. Cert-binding callers also opt out of
  setting Windows credentials on the factory.

Cert-validation override (HistorianWcfClientCredentialsHelper.Configure)
now applies on both the history and retrieval factories.

Confirmed locally: 178/178 tests pass on Windows. Linux verification
still pending — gated tests don't exercise the path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-05-05 03:07:13 -04:00
parent 8a553423ed
commit 5efa767721
@@ -1,6 +1,7 @@
using System.Net;
using System.Runtime.CompilerServices;
using System.ServiceModel;
using System.ServiceModel.Channels;
using AVEVA.Historian.Client.Models;
using AVEVA.Historian.Client.Wcf.Contracts;
@@ -281,17 +282,38 @@ internal static class HistorianWcfTagClient
{
ValidateSupportedAuth(options);
// The browse/metadata code uses the legacy Open2-V1 buffer, which carries
// its own auth blob. That buffer is only valid against the WCF transport that
// negotiates Windows security at the channel level (`/Hist-Integrated`) or
// against the cert binding (which trusts the channel-level cert identity).
// For LocalPipe and RemoteTcpIntegrated the original behaviour stays —
// hit the Integrated endpoint with the Windows transport binding. Only
// RemoteTcpCertificate gets the cert binding here, so browse/metadata
// works from a Linux client over the cert transport.
(Binding historyBinding, EndpointAddress historyEndpoint) = options.Transport switch
{
HistorianTransport.RemoteTcpCertificate => (
HistorianWcfBindingFactory.CreateMdasNetTcpCertificateBinding(options.RequestTimeout),
HistorianWcfBindingFactory.CreateEndpointAddress(options.Host, options.Port, HistorianWcfServiceNames.HistoryCertificate, options.ServerDnsIdentity)),
_ => (
HistorianWcfBindingFactory.CreateMdasNetTcpWindowsBinding(options.RequestTimeout),
HistorianWcfBindingFactory.CreateEndpointAddress(options.Host, options.Port, HistorianWcfServiceNames.HistoryIntegrated)),
};
ChannelFactory<IHistoryServiceContract2>? historyFactory = null;
IHistoryServiceContract2? historyChannel = null;
ChannelFactory<IRetrievalServiceContract2>? retrievalFactory = null;
IRetrievalServiceContract2? retrievalChannel = null;
ChannelFactory<IRetrievalServiceContract2>? retrievalFactory = null;
IRetrievalServiceContract2? retrievalChannel = null;
try
{
historyFactory = new ChannelFactory<IHistoryServiceContract2>(
HistorianWcfBindingFactory.CreateMdasNetTcpWindowsBinding(options.RequestTimeout),
HistorianWcfBindingFactory.CreateEndpointAddress(options.Host, options.Port, HistorianWcfServiceNames.HistoryIntegrated));
historyFactory.Credentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
ApplyWindowsCredential(historyFactory, options);
historyFactory = new ChannelFactory<IHistoryServiceContract2>(historyBinding, historyEndpoint);
HistorianWcfClientCredentialsHelper.Configure(historyFactory, options);
if (options.Transport != HistorianTransport.RemoteTcpCertificate)
{
// Windows transport-security only applies to the integrated-auth binding.
historyFactory.Credentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
ApplyWindowsCredential(historyFactory, options);
}
historyFactory.Open();
historyChannel = historyFactory.CreateChannel();
@@ -310,6 +332,7 @@ internal static class HistorianWcfTagClient
retrievalFactory = new ChannelFactory<IRetrievalServiceContract2>(
HistorianWcfBindingFactory.CreateMdasNetTcpBinding(options.RequestTimeout),
HistorianWcfBindingFactory.CreateEndpointAddress(options.Host, options.Port, HistorianWcfServiceNames.Retrieval));
HistorianWcfClientCredentialsHelper.Configure(retrievalFactory, options);
retrievalFactory.Open();
retrievalChannel = retrievalFactory.CreateChannel();