diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Pages/Certificates.razor b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Pages/Certificates.razor
index 0ac30099..601aaaa9 100644
--- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Pages/Certificates.razor
+++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Pages/Certificates.razor
@@ -3,7 +3,11 @@
@rendermode RenderMode.InteractiveServer
@using System.Security.Cryptography.X509Certificates
@using Microsoft.Extensions.Configuration
+@using Microsoft.AspNetCore.Components.Authorization
+@using ZB.MOM.WW.OtOpcUa.AdminUI.Certificates
@inject IConfiguration Config
+@inject CertificateStoreManager CertManager
+@implements IDisposable
OPC UA certificates
@@ -22,6 +26,18 @@
}
else
{
+ @if (_statusMsg is not null)
+ {
+
+ }
+ @if (_pending is { } p)
+ {
+
+ Confirm @p.Verb of @p.Subject?
+
+
+
+ }
@foreach (var store in _rows)
{
@@ -51,6 +67,10 @@ else
Thumbprint |
Not before |
Not after |
+ @if (store.Kind is StoreKind.Trusted or StoreKind.Rejected)
+ {
+ Actions |
+ }
@@ -62,6 +82,24 @@ else
@c.Thumbprint[..16]… |
@c.NotBefore.ToString("u") |
@c.NotAfter.ToString("u") |
+ @if (store.Kind is StoreKind.Trusted or StoreKind.Rejected)
+ {
+
+
+
+ @if (store.Kind == StoreKind.Rejected)
+ {
+
+ }
+ else
+ {
+
+ }
+
+
+
+ |
+ }
}
@@ -75,21 +113,42 @@ else
@code {
private List? _rows;
- protected override void OnInitialized()
+ private enum StoreKind { Own, Trusted, Issuer, Rejected }
+ private sealed record StoreView(string Label, StoreKind Kind, string Path, List Certificates);
+
+ private (StoreKind Kind, string Thumbprint, string Subject, string Verb)? _pending;
+ private string? _statusMsg;
+ private bool _statusError;
+
+ protected override void OnInitialized() => LoadAll();
+
+ private void LoadAll()
{
+ DisposeRows();
var pkiRoot = Config.GetValue("OpcUa:PkiStoreRoot") ?? "pki";
_rows = new()
{
- LoadStore("Own", Path.Combine(pkiRoot, "own", "certs")),
- LoadStore("Trusted peers", Path.Combine(pkiRoot, "trusted", "certs")),
- LoadStore("Trusted issuers", Path.Combine(pkiRoot, "issuer", "certs")),
- LoadStore("Rejected", Path.Combine(pkiRoot, "rejected", "certs")),
+ LoadStore("Own", StoreKind.Own, Path.Combine(pkiRoot, "own", "certs")),
+ LoadStore("Trusted peers", StoreKind.Trusted, Path.Combine(pkiRoot, "trusted", "certs")),
+ LoadStore("Trusted issuers", StoreKind.Issuer, Path.Combine(pkiRoot, "issuer", "certs")),
+ LoadStore("Rejected", StoreKind.Rejected, Path.Combine(pkiRoot, "rejected", "certs")),
};
+ _pending = null;
}
- private static StoreView LoadStore(string label, string path)
+ private void DisposeRows()
{
- var view = new StoreView(label, path, new List());
+ if (_rows is null) return;
+ foreach (var store in _rows)
+ foreach (var c in store.Certificates)
+ c.Dispose();
+ }
+
+ public void Dispose() => DisposeRows();
+
+ private static StoreView LoadStore(string label, StoreKind kind, string path)
+ {
+ var view = new StoreView(label, kind, path, new List());
if (!Directory.Exists(path)) return view;
foreach (var file in Directory.EnumerateFiles(path).Where(IsCertFile))
{
@@ -107,5 +166,28 @@ else
|| ext.Equals(".crt", StringComparison.OrdinalIgnoreCase);
}
- private sealed record StoreView(string Label, string Path, List Certificates);
+ private void RequestAction(StoreKind kind, X509Certificate2 cert, string verb)
+ {
+ _pending = (kind, cert.Thumbprint!, cert.Subject, verb);
+ _statusMsg = null;
+ }
+
+ private void CancelAction() => _pending = null;
+
+ private void ConfirmAction()
+ {
+ if (_pending is not { } p) return;
+ var result = p.Verb switch
+ {
+ "trust" => CertManager.Trust(p.Thumbprint),
+ "untrust" => CertManager.Untrust(p.Thumbprint),
+ "delete" => CertManager.Delete(p.Kind == StoreKind.Trusted ? "trusted" : "rejected", p.Thumbprint),
+ _ => CertActionResult.Fail("unknown action"),
+ };
+ _statusError = !result.Success;
+ _statusMsg = result.Success
+ ? $"{char.ToUpper(p.Verb[0])}{p.Verb[1..]} of {p.Subject} succeeded."
+ : $"{p.Verb} failed: {result.Error}";
+ LoadAll(); // clears _pending
+ }
}
diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/EndpointRouteBuilderExtensions.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/EndpointRouteBuilderExtensions.cs
index ce7bef44..422a7dab 100644
--- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/EndpointRouteBuilderExtensions.cs
+++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/EndpointRouteBuilderExtensions.cs
@@ -51,6 +51,9 @@ public static class EndpointRouteBuilderExtensions
services.AddScoped();
services.AddScoped();
+ // Certificate-store actions (trust/untrust/delete) for the /certificates page.
+ services.AddSingleton();
+
return services;
}
}