Skip to content

Commit 4253d28

Browse files
AgentCopilot
andcommitted
Realign UaLens against upstream cert-manager + analyzer refactors
* AppConfig: replace legacy 'CertificateValidator.CertificateValidation += e => e.Accept = true' dev-default with SecurityConfiguration .AutoAcceptUntrustedCertificates = true (event hook removed in the cert-manager refactor). * ConnectionService.ConnectAsync: drop the CertificateValidator event wiring; while the new ICertificateValidatorEx surface stabilises, treat a non-null certPrompt as 'auto-accept untrusted for this connect call' by toggling SecurityConfiguration .AutoAcceptUntrustedCertificates and restoring it in finally. TODO to re-wire the interactive trust dialog on top of RejectedCertificateProcessor. Removes the now-unreachable HandleCertValidation + TryTrustPermanentlyAsync helpers. * CertificateStoreService + GdsPushPlugin + CertificateTrustDialog: switch X509Certificate2Collection-typed return / parameter usages to the new Opc.Ua.Security.Certificates.CertificateCollection / Certificate types — iterate via .AsX509Certificate2() on read and wrap inbound X509Certificate2 via Certificate.From(…) on write. * Tighten three new analyzer-flagged sites carried in by the merge: ValueFactory + PerformanceTargetDialog adopt the generic Enum.IsDefined<TEnum>; GdsManagementPlugin.Contains uses string.Contains instead of IndexOf. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 43e7c1a commit 4253d28

8 files changed

Lines changed: 49 additions & 73 deletions

File tree

Applications/Opc.Ua.Lens/Connection/AppConfig.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,13 @@ public static async Task<ApplicationConfiguration> BuildAsync(ITelemetryContext
3535
.CreateAsync()
3636
.ConfigureAwait(false);
3737

38-
cfg.CertificateValidator.CertificateValidation += (s, e) => e.Accept = true;
38+
// Dev-default trust: auto-accept untrusted peer certificates. Replaces
39+
// the legacy `CertificateValidator.CertificateValidation += e => e.Accept = true`
40+
// hook (gone in the upstream cert-manager refactor).
41+
if (cfg.SecurityConfiguration is { } sec)
42+
{
43+
sec.AutoAcceptUntrustedCertificates = true;
44+
}
3945
return cfg;
4046
}
4147
}

Applications/Opc.Ua.Lens/Connection/CertificateStoreService.cs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,13 @@ public async Task<IReadOnlyList<X509Certificate2>> ListAsync(CertStoreKind kind,
5454
}
5555
try
5656
{
57-
X509Certificate2Collection coll = await store.EnumerateAsync(ct).ConfigureAwait(false);
58-
return coll.Cast<X509Certificate2>().ToList();
57+
Opc.Ua.Security.Certificates.CertificateCollection coll = await store.EnumerateAsync(ct).ConfigureAwait(false);
58+
var list = new List<X509Certificate2>(coll.Count);
59+
foreach (Opc.Ua.Security.Certificates.Certificate cert in coll)
60+
{
61+
list.Add(cert.AsX509Certificate2());
62+
}
63+
return list;
5964
}
6065
finally
6166
{
@@ -98,7 +103,8 @@ public async Task<bool> AddAsync(CertStoreKind kind, X509Certificate2 cert, Canc
98103

99104
try
100105
{
101-
await store.AddAsync(cert, password: null, ct).ConfigureAwait(false);
106+
using Opc.Ua.Security.Certificates.Certificate wrapper = Opc.Ua.Security.Certificates.Certificate.From(cert);
107+
await store.AddAsync(wrapper, password: null, ct).ConfigureAwait(false);
102108
m_log.LogInformation("Added certificate {Thumbprint} ({Subject}) to {Kind}.",
103109
cert.Thumbprint, cert.Subject, kind);
104110
return true;
@@ -132,8 +138,8 @@ public async Task<bool> TrustRejectedAsync(string thumbprint, CancellationToken
132138
X509Certificate2? cert = null;
133139
try
134140
{
135-
X509Certificate2Collection found = await rej.FindByThumbprintAsync(thumbprint, ct).ConfigureAwait(false);
136-
cert = found.Count > 0 ? found[0] : null;
141+
Opc.Ua.Security.Certificates.CertificateCollection found = await rej.FindByThumbprintAsync(thumbprint, ct).ConfigureAwait(false);
142+
cert = found.Count > 0 ? found[0].AsX509Certificate2() : null;
137143
}
138144
finally
139145
{
@@ -153,7 +159,8 @@ public async Task<bool> TrustRejectedAsync(string thumbprint, CancellationToken
153159

154160
try
155161
{
156-
await trusted.AddAsync(cert, password: null, ct).ConfigureAwait(false);
162+
using Opc.Ua.Security.Certificates.Certificate wrapper = Opc.Ua.Security.Certificates.Certificate.From(cert);
163+
await trusted.AddAsync(wrapper, password: null, ct).ConfigureAwait(false);
157164
}
158165
finally
159166
{

Applications/Opc.Ua.Lens/Connection/ConnectionService.cs

Lines changed: 16 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -162,19 +162,29 @@ public async Task ConnectAsync(
162162
CancellationToken ct)
163163
{
164164
await m_lock.WaitAsync(ct).ConfigureAwait(false);
165-
CertificateValidationEventHandler? handler = null;
165+
bool? priorAutoAccept = null;
166166
try
167167
{
168168
await DisconnectInternalAsync().ConfigureAwait(false);
169169

170170
m_config ??= await AppConfig.BuildAsync(m_telemetry).ConfigureAwait(false);
171171
m_engine = options.Engine;
172172

173-
if (certPrompt is { } prompt)
173+
// The legacy `CertificateValidator.CertificateValidation`
174+
// event hook is gone in the upstream cert-manager refactor.
175+
// For now, when the caller supplied a trust-prompt callback,
176+
// assume the user wants to engage with the trust UX and
177+
// auto-accept untrusted peer certificates for the duration
178+
// of this connect call. The interactive trust dialog will
179+
// be re-wired on top of the new RejectedCertificateProcessor
180+
// once that contract is stable.
181+
// TODO: redesign cert-trust UX on top of ICertificateValidatorEx.
182+
if (certPrompt is not null && m_config.SecurityConfiguration is { } sec)
174183
{
175-
handler = (_, e) => HandleCertValidation(e, prompt);
176-
m_config.CertificateValidator.CertificateValidation += handler;
184+
priorAutoAccept = sec.AutoAcceptUntrustedCertificates;
185+
sec.AutoAcceptUntrustedCertificates = true;
177186
}
187+
_ = certPrompt; // intentionally unused while the new UX is pending
178188

179189
var configuredEndpoint = new ConfiguredEndpoint(null, endpoint, EndpointConfiguration.Create(m_config));
180190

@@ -205,66 +215,15 @@ public async Task ConnectAsync(
205215
}
206216
finally
207217
{
208-
if (handler is not null && m_config is not null)
218+
if (priorAutoAccept.HasValue && m_config?.SecurityConfiguration is { } sec)
209219
{
210-
m_config.CertificateValidator.CertificateValidation -= handler;
220+
sec.AutoAcceptUntrustedCertificates = priorAutoAccept.Value;
211221
}
212222
m_lock.Release();
213223
}
214224
StateChanged?.Invoke();
215225
}
216226

217-
/// <summary>
218-
/// CertificateValidator event bridge: blocks the validating thread while the
219-
/// UI thread runs the trust dialog, then sets <c>e.Accept</c> from the choice.
220-
/// "Trust permanently" also pushes the cert into the configured TrustedPeerCertificates store.
221-
/// </summary>
222-
private void HandleCertValidation(
223-
CertificateValidationEventArgs e,
224-
Func<X509Certificate2, ServiceResult, Task<TrustChoice>> prompt)
225-
{
226-
try
227-
{
228-
// Marshal to UI thread; block this (validator) thread until we have a decision.
229-
TrustChoice choice = prompt(e.Certificate, e.Error).GetAwaiter().GetResult();
230-
if (choice == TrustChoice.AcceptOnce || choice == TrustChoice.TrustPermanently)
231-
{
232-
e.Accept = true;
233-
}
234-
if (choice == TrustChoice.TrustPermanently && m_config is not null)
235-
{
236-
// Block the validator thread until the cert is added (or
237-
// fails); the validator already runs on a worker thread
238-
// and the prompt above is similarly synchronous.
239-
TryTrustPermanentlyAsync(e.Certificate).GetAwaiter().GetResult();
240-
}
241-
}
242-
catch (Exception ex)
243-
{
244-
m_log.LogWarning(ex, "Certificate trust prompt failed; rejecting.");
245-
}
246-
}
247-
248-
private async Task TryTrustPermanentlyAsync(X509Certificate2 cert)
249-
{
250-
try
251-
{
252-
if (m_config?.SecurityConfiguration?.TrustedPeerCertificates is { } trusted)
253-
{
254-
using ICertificateStore? store = trusted.OpenStore(m_telemetry);
255-
if (store is not null)
256-
{
257-
await store.AddAsync(cert).ConfigureAwait(false);
258-
m_log.LogInformation("Added certificate {Thumbprint} to trusted peer store.", cert.Thumbprint);
259-
}
260-
}
261-
}
262-
catch (Exception ex)
263-
{
264-
m_log.LogWarning(ex, "Failed to add certificate {Thumbprint} to trusted store.", cert.Thumbprint);
265-
}
266-
}
267-
268227
public async Task DisconnectAsync()
269228
{
270229
await m_lock.WaitAsync().ConfigureAwait(false);

Applications/Opc.Ua.Lens/Plugins/GdsManagement/GdsManagementPlugin.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -833,7 +833,7 @@ private static bool MatchesFilter(RegisteredApp app, string needle)
833833

834834
static bool Contains(string hay, string n) =>
835835
!string.IsNullOrEmpty(hay) &&
836-
hay.IndexOf(n, StringComparison.OrdinalIgnoreCase) >= 0;
836+
hay.Contains(n, StringComparison.OrdinalIgnoreCase);
837837
}
838838

839839
/// <summary>

Applications/Opc.Ua.Lens/Plugins/GdsPush/GdsPushPlugin.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,7 @@ private async Task RefreshAsync()
561561
TrustListMasks.All,
562562
0,
563563
CancellationToken.None).ConfigureAwait(true);
564-
X509Certificate2Collection rejected = await client.GetRejectedListAsync(
564+
Opc.Ua.Security.Certificates.CertificateCollection rejected = await client.GetRejectedListAsync(
565565
CancellationToken.None).ConfigureAwait(true);
566566
PopulateTrustList(list, rejected);
567567
SetResult($"Refreshed: {Trusted.Count} trusted · {Issuers.Count} issuers · {Rejected.Count} rejected.");
@@ -618,7 +618,8 @@ private async Task AddCertificateAsync()
618618
return;
619619
}
620620
bool isTrusted = result.Bucket == TrustListBucket.Trusted;
621-
await client.AddCertificateAsync(result.Certificate, isTrusted, CancellationToken.None)
621+
using Opc.Ua.Security.Certificates.Certificate wrapper = Opc.Ua.Security.Certificates.Certificate.From(result.Certificate);
622+
await client.AddCertificateAsync(wrapper, isTrusted, CancellationToken.None)
622623
.ConfigureAwait(true);
623624
SetResult($"Added {ShortName(result.Certificate.Subject)} to " +
624625
$"{(isTrusted ? "Trusted Peers" : "Issuers")}.");
@@ -828,7 +829,7 @@ private async Task PopulateServerInfoAsync(CancellationToken ct)
828829
await Task.CompletedTask.ConfigureAwait(false);
829830
}
830831

831-
private void PopulateTrustList(TrustListDataType list, X509Certificate2Collection rejected)
832+
private void PopulateTrustList(TrustListDataType list, Opc.Ua.Security.Certificates.CertificateCollection rejected)
832833
{
833834
Trusted.Clear();
834835
Issuers.Clear();
@@ -855,9 +856,9 @@ private void PopulateTrustList(TrustListDataType list, X509Certificate2Collectio
855856
}
856857
if (rejected is not null)
857858
{
858-
foreach (X509Certificate2 cert in rejected)
859+
foreach (Opc.Ua.Security.Certificates.Certificate cert in rejected)
859860
{
860-
Rejected.Add(ToItem(cert));
861+
Rejected.Add(ToItem(cert.AsX509Certificate2()));
861862
}
862863
}
863864
}

Applications/Opc.Ua.Lens/Plugins/Performance/PerformanceTargetDialog.axaml.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,6 @@ private static BuiltInType BuiltInForDataType(NodeId dataType)
427427

428428
uint id = (uint)dataType.Identifier;
429429
BuiltInType bi = (BuiltInType)id;
430-
return Enum.IsDefined(typeof(BuiltInType), bi) ? bi : BuiltInType.Int32;
430+
return Enum.IsDefined(bi) ? bi : BuiltInType.Int32;
431431
}
432432
}

Applications/Opc.Ua.Lens/Plugins/Performance/ValueFactory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public static BuiltInType BuiltInForArgument(Argument arg)
5656

5757
uint id = (uint)arg.DataType.Identifier;
5858
BuiltInType bi = (BuiltInType)id;
59-
return Enum.IsDefined(typeof(BuiltInType), bi) ? bi : BuiltInType.Int32;
59+
return Enum.IsDefined(bi) ? bi : BuiltInType.Int32;
6060
}
6161

6262
/// <summary>

Applications/Opc.Ua.Lens/Views/CertificateTrustDialog.axaml.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ public CertificateTrustDialog(X509Certificate2 cert, ServiceResult error)
2525
$"The server certificate failed validation: {error.StatusCode}{error.LocalizedText}";
2626
this.RequiredControl<TextBlock>("SubjectLabel").Text = cert.Subject;
2727
this.RequiredControl<TextBlock>("IssuerLabel").Text = cert.Issuer;
28-
this.RequiredControl<TextBlock>("AppUriLabel").Text = X509Utils.GetApplicationUriFromCertificate(cert) ?? "(none)";
28+
using (var certWrapper = Opc.Ua.Security.Certificates.Certificate.From(cert))
29+
{
30+
this.RequiredControl<TextBlock>("AppUriLabel").Text = X509Utils.GetApplicationUriFromCertificate(certWrapper) ?? "(none)";
31+
}
2932
DateTime nowUtc = DateTime.UtcNow;
3033
var notBefore = this.RequiredControl<TextBlock>("NotBeforeLabel");
3134
var notAfter = this.RequiredControl<TextBlock>("NotAfterLabel");

0 commit comments

Comments
 (0)