feat(adminui): audit cert Trust/Untrust/Delete to ConfigAuditLog
This commit is contained in:
+105
-10
@@ -9,12 +9,27 @@ namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Certificates;
|
||||
public sealed class CertificateStoreManagerTests : IDisposable
|
||||
{
|
||||
private readonly string _root;
|
||||
private readonly RecordingAuditWriter _audit;
|
||||
private readonly CertificateStoreManager _sut;
|
||||
|
||||
public CertificateStoreManagerTests()
|
||||
{
|
||||
_root = Path.Combine(Path.GetTempPath(), $"pki-test-{Guid.NewGuid():N}");
|
||||
_sut = new CertificateStoreManager(_root);
|
||||
_audit = new RecordingAuditWriter();
|
||||
_sut = new CertificateStoreManager(_root, _audit);
|
||||
}
|
||||
|
||||
// ── Recording audit-writer fake ───────────────────────────────────────────
|
||||
|
||||
private sealed class RecordingAuditWriter : ZB.MOM.WW.Audit.IAuditWriter
|
||||
{
|
||||
public List<ZB.MOM.WW.Audit.AuditEvent> Events { get; } = new();
|
||||
|
||||
public Task WriteAsync(ZB.MOM.WW.Audit.AuditEvent evt, CancellationToken ct = default)
|
||||
{
|
||||
Events.Add(evt);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@@ -84,7 +99,7 @@ public sealed class CertificateStoreManagerTests : IDisposable
|
||||
var rejectedCerts = Path.Combine(_root, "rejected", "certs");
|
||||
var tp = SeedCert(rejectedCerts, "TestClient1");
|
||||
|
||||
var result = _sut.Trust(tp);
|
||||
var result = _sut.Trust(tp, "tester");
|
||||
|
||||
result.Success.ShouldBeTrue(result.Error);
|
||||
ThumbprintExistsInDir(rejectedCerts, tp).ShouldBeFalse("cert should have left rejected");
|
||||
@@ -97,7 +112,7 @@ public sealed class CertificateStoreManagerTests : IDisposable
|
||||
var trustedCerts = Path.Combine(_root, "trusted", "certs");
|
||||
var tp = SeedCert(trustedCerts, "TestClient2");
|
||||
|
||||
var result = _sut.Untrust(tp);
|
||||
var result = _sut.Untrust(tp, "tester");
|
||||
|
||||
result.Success.ShouldBeTrue(result.Error);
|
||||
ThumbprintExistsInDir(trustedCerts, tp).ShouldBeFalse("cert should have left trusted");
|
||||
@@ -110,7 +125,7 @@ public sealed class CertificateStoreManagerTests : IDisposable
|
||||
var rejectedCerts = Path.Combine(_root, "rejected", "certs");
|
||||
var tp = SeedCert(rejectedCerts, "TestClient3");
|
||||
|
||||
var result = _sut.Delete("rejected", tp);
|
||||
var result = _sut.Delete("rejected", tp, "tester");
|
||||
|
||||
result.Success.ShouldBeTrue(result.Error);
|
||||
ThumbprintExistsInDir(rejectedCerts, tp).ShouldBeFalse("cert should be gone from rejected");
|
||||
@@ -122,7 +137,7 @@ public sealed class CertificateStoreManagerTests : IDisposable
|
||||
var trustedCerts = Path.Combine(_root, "trusted", "certs");
|
||||
var tp = SeedCert(trustedCerts, "TestClient4");
|
||||
|
||||
var result = _sut.Delete("trusted", tp);
|
||||
var result = _sut.Delete("trusted", tp, "tester");
|
||||
|
||||
result.Success.ShouldBeTrue(result.Error);
|
||||
ThumbprintExistsInDir(trustedCerts, tp).ShouldBeFalse("cert should be gone from trusted");
|
||||
@@ -133,7 +148,7 @@ public sealed class CertificateStoreManagerTests : IDisposable
|
||||
{
|
||||
var tp = new string('A', 40);
|
||||
|
||||
var result = _sut.Trust(tp);
|
||||
var result = _sut.Trust(tp, "tester");
|
||||
|
||||
result.Success.ShouldBeFalse();
|
||||
CertCountInDir(Path.Combine(_root, "trusted", "certs")).ShouldBe(0);
|
||||
@@ -142,7 +157,7 @@ public sealed class CertificateStoreManagerTests : IDisposable
|
||||
[Fact]
|
||||
public void Trust_PathTraversalThumbprint_Rejected()
|
||||
{
|
||||
var result = _sut.Trust("../../x");
|
||||
var result = _sut.Trust("../../x", "tester");
|
||||
|
||||
result.Success.ShouldBeFalse();
|
||||
result.Error.ShouldNotBeNull();
|
||||
@@ -158,7 +173,7 @@ public sealed class CertificateStoreManagerTests : IDisposable
|
||||
var ownCerts = Path.Combine(_root, "own", "certs");
|
||||
var tp = SeedCert(ownCerts, "OwnCert");
|
||||
|
||||
var result = _sut.Delete("own", tp);
|
||||
var result = _sut.Delete("own", tp, "tester");
|
||||
|
||||
result.Success.ShouldBeFalse();
|
||||
result.Error.ShouldNotBeNull();
|
||||
@@ -178,7 +193,7 @@ public sealed class CertificateStoreManagerTests : IDisposable
|
||||
var srcFile = Directory.GetFiles(rejectedCerts).Single();
|
||||
File.Copy(srcFile, Path.Combine(trustedCerts, Path.GetFileName(srcFile)));
|
||||
|
||||
var result = _sut.Trust(tp);
|
||||
var result = _sut.Trust(tp, "tester");
|
||||
|
||||
result.Success.ShouldBeTrue(result.Error);
|
||||
ThumbprintExistsInDir(rejectedCerts, tp).ShouldBeFalse("source (rejected) should no longer have it");
|
||||
@@ -188,9 +203,89 @@ public sealed class CertificateStoreManagerTests : IDisposable
|
||||
[Fact]
|
||||
public void Trust_NullOrEmptyThumbprint_Fails()
|
||||
{
|
||||
var result = _sut.Trust("");
|
||||
var result = _sut.Trust("", "tester");
|
||||
|
||||
result.Success.ShouldBeFalse();
|
||||
result.Error.ShouldNotBeNull();
|
||||
}
|
||||
|
||||
// ── Audit tests ───────────────────────────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public void Trust_success_writes_one_success_audit_event()
|
||||
{
|
||||
var rejectedCerts = Path.Combine(_root, "rejected", "certs");
|
||||
var tp = SeedCert(rejectedCerts, "AuditTrust");
|
||||
|
||||
var result = _sut.Trust(tp, "alice");
|
||||
|
||||
result.Success.ShouldBeTrue(result.Error);
|
||||
var evt = _audit.Events.ShouldHaveSingleItem();
|
||||
evt.Action.ShouldBe("Trust");
|
||||
evt.Outcome.ShouldBe(ZB.MOM.WW.Audit.AuditOutcome.Success);
|
||||
evt.SourceNode.ShouldBe(tp);
|
||||
evt.Actor.ShouldBe("alice");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Untrust_success_writes_one_success_audit_event()
|
||||
{
|
||||
var trustedCerts = Path.Combine(_root, "trusted", "certs");
|
||||
var tp = SeedCert(trustedCerts, "AuditUntrust");
|
||||
|
||||
var result = _sut.Untrust(tp, "alice");
|
||||
|
||||
result.Success.ShouldBeTrue(result.Error);
|
||||
var evt = _audit.Events.ShouldHaveSingleItem();
|
||||
evt.Action.ShouldBe("Untrust");
|
||||
evt.Outcome.ShouldBe(ZB.MOM.WW.Audit.AuditOutcome.Success);
|
||||
evt.SourceNode.ShouldBe(tp);
|
||||
evt.Actor.ShouldBe("alice");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Delete_success_writes_one_success_audit_event()
|
||||
{
|
||||
var rejectedCerts = Path.Combine(_root, "rejected", "certs");
|
||||
var tp = SeedCert(rejectedCerts, "AuditDelete");
|
||||
|
||||
var result = _sut.Delete("rejected", tp, "alice");
|
||||
|
||||
result.Success.ShouldBeTrue(result.Error);
|
||||
var evt = _audit.Events.ShouldHaveSingleItem();
|
||||
evt.Action.ShouldBe("Delete");
|
||||
evt.Outcome.ShouldBe(ZB.MOM.WW.Audit.AuditOutcome.Success);
|
||||
evt.SourceNode.ShouldBe(tp);
|
||||
evt.Actor.ShouldBe("alice");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Delete_not_found_writes_one_failure_audit_event()
|
||||
{
|
||||
var absent = new string('0', 40);
|
||||
|
||||
var result = _sut.Delete("trusted", absent, "bob");
|
||||
|
||||
result.Success.ShouldBeFalse();
|
||||
var evt = _audit.Events.ShouldHaveSingleItem();
|
||||
evt.Action.ShouldBe("Delete");
|
||||
evt.Outcome.ShouldBe(ZB.MOM.WW.Audit.AuditOutcome.Failure);
|
||||
evt.SourceNode.ShouldBe(absent);
|
||||
evt.Actor.ShouldBe("bob");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Trust_not_found_writes_one_failure_audit_event()
|
||||
{
|
||||
var absent = new string('0', 40);
|
||||
|
||||
var result = _sut.Trust(absent, "bob");
|
||||
|
||||
result.Success.ShouldBeFalse();
|
||||
var evt = _audit.Events.ShouldHaveSingleItem();
|
||||
evt.Action.ShouldBe("Trust");
|
||||
evt.Outcome.ShouldBe(ZB.MOM.WW.Audit.AuditOutcome.Failure);
|
||||
evt.SourceNode.ShouldBe(absent);
|
||||
evt.Actor.ShouldBe("bob");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user