feat: add OCSP peer reject and chain validation events (Gap 10.10)
Add OcspChainValidationEvent DTO, OcspStatus enum, and OcspEventBuilder helper with BuildPeerReject, BuildChainValidation, and ParseStatus methods. Register OcspChainValidationEvent in EventJsonContext source-gen context. Add OcspPeerReject and OcspChainValidation subject constants to EventSubjects. 10 new tests in OcspEventTests cover all DTOs, builder methods, status parsing, and JSON round-trip.
This commit is contained in:
@@ -11,4 +11,5 @@ namespace NATS.Server.Events;
|
||||
[JsonSerializable(typeof(LameDuckEventMsg))]
|
||||
[JsonSerializable(typeof(AuthErrorEventMsg))]
|
||||
[JsonSerializable(typeof(OcspPeerRejectEventMsg))]
|
||||
[JsonSerializable(typeof(OcspChainValidationEvent))]
|
||||
internal partial class EventJsonContext : JsonSerializerContext;
|
||||
|
||||
@@ -40,6 +40,11 @@ public static class EventSubjects
|
||||
// Inbox for responses
|
||||
public const string InboxResponse = "$SYS._INBOX_.{0}";
|
||||
|
||||
// OCSP advisory events
|
||||
// Go reference: ocsp.go — OCSP peer reject and chain validation subjects.
|
||||
public const string OcspPeerReject = "$SYS.SERVER.{0}.OCSP.PEER.REJECT";
|
||||
public const string OcspChainValidation = "$SYS.SERVER.{0}.OCSP.CHAIN.VALIDATION";
|
||||
|
||||
// JetStream advisory events
|
||||
// Go reference: jetstream_api.go advisory subjects
|
||||
public const string JsAdvisoryStreamCreated = "$JS.EVENT.ADVISORY.STREAM.CREATED.{0}";
|
||||
|
||||
@@ -540,6 +540,119 @@ public sealed class OcspPeerRejectEventMsg
|
||||
public string Reason { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OCSP chain validation advisory, published when a certificate's OCSP status
|
||||
/// is checked during TLS handshake.
|
||||
/// Go reference: ocsp.go — OCSP peer verification advisory.
|
||||
/// </summary>
|
||||
public sealed class OcspChainValidationEvent
|
||||
{
|
||||
public const string EventType = "io.nats.server.advisory.v1.ocsp_chain_validation";
|
||||
|
||||
[JsonPropertyName("type")]
|
||||
public string Type { get; init; } = EventType;
|
||||
|
||||
[JsonPropertyName("id")]
|
||||
public string Id { get; init; } = Guid.NewGuid().ToString("N");
|
||||
|
||||
[JsonPropertyName("time")]
|
||||
public string Time { get; init; } = DateTime.UtcNow.ToString("O");
|
||||
|
||||
[JsonPropertyName("server")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public EventServerInfo? Server { get; set; }
|
||||
|
||||
[JsonPropertyName("cert_subject")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? CertSubject { get; set; }
|
||||
|
||||
[JsonPropertyName("cert_issuer")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? CertIssuer { get; set; }
|
||||
|
||||
[JsonPropertyName("serial_number")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? SerialNumber { get; set; }
|
||||
|
||||
/// <summary>OCSP status string: "good", "revoked", or "unknown".</summary>
|
||||
[JsonPropertyName("ocsp_status")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? OcspStatus { get; set; }
|
||||
|
||||
[JsonPropertyName("checked_at")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public DateTime? CheckedAt { get; set; }
|
||||
|
||||
[JsonPropertyName("error")]
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? Error { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OCSP status values matching the Go OCSP response status strings.
|
||||
/// Go reference: ocsp.go — ocspStatusGood, ocspStatusRevoked, ocspStatusUnknown.
|
||||
/// </summary>
|
||||
public enum OcspStatus
|
||||
{
|
||||
Unknown,
|
||||
Good,
|
||||
Revoked,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Factory helpers for constructing OCSP advisory event messages.
|
||||
/// Go reference: ocsp.go — OCSP peer reject and chain validation advisory publishing.
|
||||
/// </summary>
|
||||
public static class OcspEventBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Build an OcspPeerRejectEventMsg for a rejected peer certificate.
|
||||
/// Go reference: ocsp.go — postOCSPPeerRejectEvent.
|
||||
/// </summary>
|
||||
public static OcspPeerRejectEventMsg BuildPeerReject(
|
||||
string serverId, string serverName,
|
||||
string kind, string reason) =>
|
||||
new()
|
||||
{
|
||||
Id = EventBuilder.GenerateEventId(),
|
||||
Time = DateTime.UtcNow,
|
||||
Server = new EventServerInfo { Id = serverId, Name = serverName },
|
||||
Kind = kind,
|
||||
Reason = reason,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Build an OcspChainValidationEvent for a certificate OCSP check.
|
||||
/// Go reference: ocsp.go — OCSP chain validation advisory.
|
||||
/// </summary>
|
||||
public static OcspChainValidationEvent BuildChainValidation(
|
||||
string serverId, string serverName,
|
||||
string certSubject, string certIssuer, string serialNumber,
|
||||
string ocspStatus, string? error = null) =>
|
||||
new()
|
||||
{
|
||||
Server = new EventServerInfo { Id = serverId, Name = serverName },
|
||||
CertSubject = certSubject,
|
||||
CertIssuer = certIssuer,
|
||||
SerialNumber = serialNumber,
|
||||
OcspStatus = ocspStatus,
|
||||
CheckedAt = DateTime.UtcNow,
|
||||
Error = error,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Parse an OCSP status string into the <see cref="OcspStatus"/> enum.
|
||||
/// Go reference: ocsp.go — ocspStatusGood / ocspStatusRevoked string constants.
|
||||
/// </summary>
|
||||
public static OcspStatus ParseStatus(string? status) =>
|
||||
status?.ToLowerInvariant() switch
|
||||
{
|
||||
"good" => OcspStatus.Good,
|
||||
"revoked" => OcspStatus.Revoked,
|
||||
_ => OcspStatus.Unknown,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remote server shutdown advisory.
|
||||
/// Go reference: events.go — remote server lifecycle.
|
||||
|
||||
Reference in New Issue
Block a user