diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Certificates/CertificateStoreManager.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Certificates/CertificateStoreManager.cs index fb0f7cc6..062b0584 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Certificates/CertificateStoreManager.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Certificates/CertificateStoreManager.cs @@ -29,6 +29,13 @@ public sealed class CertificateStoreManager private readonly string _pkiRoot; private readonly IAuditWriter _audit; + /// + /// The resolved PKI store root directory (e.g. pki). Derived once from + /// OpcUa:PkiStoreRoot in the production ctor; callers should read this property + /// rather than re-resolving the config key independently. + /// + public string PkiRoot => _pkiRoot; + /// Production ctor — reads OpcUa:PkiStoreRoot (default pki). /// App configuration. /// The audit writer that persists Trust/Untrust/Delete actions to ConfigAuditLog. 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 3f2b6c25..e32f3cae 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 @@ -127,7 +127,7 @@ else private void LoadAll() { DisposeRows(); - var pkiRoot = Config.GetValue("OpcUa:PkiStoreRoot") ?? "pki"; + var pkiRoot = CertManager.PkiRoot; // Single source: manager already resolved OpcUa:PkiStoreRoot (default "pki"). _rows = new() { LoadStore("Own", StoreKind.Own, Path.Combine(pkiRoot, "own", "certs")), @@ -200,8 +200,14 @@ else { StoreKind.Trusted => CertManager.Delete("trusted", p.Thumbprint, actor), StoreKind.Rejected => CertManager.Delete("rejected", p.Thumbprint, actor), + // Unreachable defensive guard — action buttons only render for Trusted/Rejected stores + // + the 3 literal verbs (Trust/Untrust/Delete); this arm never executes, so it + // intentionally does not route through CertificateStoreManager/audit. _ => CertActionResult.Fail($"cannot delete from {p.Kind}"), }, + // Unreachable defensive guard — action buttons only render for Trusted/Rejected stores + // + the 3 literal verbs (Trust/Untrust/Delete); this arm never executes, so it + // intentionally does not route through CertificateStoreManager/audit. _ => CertActionResult.Fail("unknown action"), }; _statusError = !result.Success;