feat(centralui): cert-management UI + Trust action + site relay (T17)
This commit is contained in:
@@ -3,7 +3,9 @@
|
||||
@using ZB.MOM.WW.ScadaBridge.Commons.Messages.Management
|
||||
@using ZB.MOM.WW.ScadaBridge.Commons.Types.DataConnections
|
||||
@using ZB.MOM.WW.ScadaBridge.Commons.Types.Flattening
|
||||
@using ZB.MOM.WW.ScadaBridge.Security
|
||||
@inject IEndpointVerificationService VerificationService
|
||||
@inject ICertManagementService CertManagementService
|
||||
|
||||
<div class="opcua-endpoint-editor">
|
||||
<h6 class="text-muted border-bottom pb-1">@Title</h6>
|
||||
@@ -91,9 +93,44 @@
|
||||
<dt class="col-sm-3">Not after</dt>
|
||||
<dd class="col-sm-9">@cert.NotAfterUtc.ToString("u")</dd>
|
||||
</dl>
|
||||
<div class="text-muted fst-italic">
|
||||
Use cert management to trust this certificate.
|
||||
</div>
|
||||
<AuthorizeView Policy="@AuthorizationPolicies.RequireAdmin">
|
||||
<Authorized>
|
||||
<button type="button" class="btn btn-outline-warning btn-sm mt-1"
|
||||
data-test="trust-cert-btn"
|
||||
disabled="@_trusting"
|
||||
@onclick="() => TrustCert(cert)">
|
||||
@if (_trusting)
|
||||
{
|
||||
<span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>
|
||||
<span>Trusting…</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>Trust certificate</span>
|
||||
}
|
||||
</button>
|
||||
<div class="text-muted fst-italic mt-1">
|
||||
Trusting adds this certificate to every node of the site's
|
||||
trusted-peer store (node-wide), then re-runs Verify.
|
||||
</div>
|
||||
</Authorized>
|
||||
<NotAuthorized>
|
||||
<div class="text-muted fst-italic">
|
||||
An Administrator must trust this certificate (cert management).
|
||||
</div>
|
||||
</NotAuthorized>
|
||||
</AuthorizeView>
|
||||
|
||||
@if (_trustError is { } trustError)
|
||||
{
|
||||
<div class="text-danger small mt-1" data-test="trust-cert-error">@trustError</div>
|
||||
}
|
||||
@if (_trustSucceeded)
|
||||
{
|
||||
<div class="text-success small mt-1" data-test="trust-cert-success">
|
||||
✓ Certificate trusted.
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@@ -322,11 +359,25 @@
|
||||
|
||||
private bool _verifying;
|
||||
private VerifyEndpointResult? _verifyResult;
|
||||
private bool _trusting;
|
||||
private bool _trustSucceeded;
|
||||
private string? _trustError;
|
||||
|
||||
private async Task VerifyEndpoint()
|
||||
private Task VerifyEndpoint() => VerifyEndpoint(clearTrustNotes: true);
|
||||
|
||||
private async Task VerifyEndpoint(bool clearTrustNotes)
|
||||
{
|
||||
_verifying = true;
|
||||
_verifyResult = null;
|
||||
// A fresh (user-initiated) verify supersedes any prior trust outcome — clear
|
||||
// the inline notes so a stale "trusted" / error banner doesn't linger across
|
||||
// probes. The trust-triggered re-verify keeps the success note so the operator
|
||||
// still sees confirmation even if the re-probe also surfaces a (different) cert.
|
||||
if (clearTrustNotes)
|
||||
{
|
||||
_trustSucceeded = false;
|
||||
_trustError = null;
|
||||
}
|
||||
try
|
||||
{
|
||||
_verifyResult = await VerificationService.VerifyAsync(
|
||||
@@ -343,6 +394,42 @@
|
||||
}
|
||||
}
|
||||
|
||||
// M7 T17: trust the captured untrusted server certificate at every node of the
|
||||
// owning site, then re-run Verify (which should now succeed and clear the cert
|
||||
// panel). Administrator-gated by the AuthorizeView wrapping the button; the
|
||||
// CertManagementService enforces the same role server-side-of-the-trust-boundary.
|
||||
private async Task TrustCert(ServerCertInfo cert)
|
||||
{
|
||||
_trusting = true;
|
||||
_trustError = null;
|
||||
_trustSucceeded = false;
|
||||
try
|
||||
{
|
||||
var result = await CertManagementService.TrustAsync(
|
||||
SiteIdentifier, ConnectionName, cert.DerBase64, cert.Thumbprint);
|
||||
if (result.Success)
|
||||
{
|
||||
_trustSucceeded = true;
|
||||
// Re-run Verify so the editor reflects the now-trusted state (and
|
||||
// clears the cert panel on the expected success). Preserve the
|
||||
// success note (clearTrustNotes:false) so confirmation is shown.
|
||||
await VerifyEndpoint(clearTrustNotes: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
_trustError = result.Error ?? "Failed to trust certificate.";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_trustError = ex.Message;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_trusting = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void EnableHeartbeat() =>
|
||||
Config.Heartbeat = new OpcUaHeartbeatConfig();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user