From d8d71eab950a640cc90dc08336d63538f4bec683 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Sat, 28 Feb 2026 06:30:23 -0500 Subject: [PATCH] feat(batch1): implement jwt wipe and nonce-required internal logic --- .../ZB.MOM.NatsNet.Server/Auth/AuthHandler.cs | 2 +- .../Auth/JwtProcessor.cs | 9 ++ .../NatsServer.Listeners.cs | 23 ++++- .../Auth/JwtProcessorTests.cs | 4 +- .../Server/NonceRequiredTests.cs | 87 ++++++++++++++++++ porting.db | Bin 6352896 -> 6352896 bytes reports/current.md | 8 +- reports/report_f9b582d.md | 37 ++++++++ 8 files changed, 161 insertions(+), 9 deletions(-) create mode 100644 dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Server/NonceRequiredTests.cs create mode 100644 reports/report_f9b582d.md diff --git a/dotnet/src/ZB.MOM.NatsNet.Server/Auth/AuthHandler.cs b/dotnet/src/ZB.MOM.NatsNet.Server/Auth/AuthHandler.cs index 963057e..52b2a6f 100644 --- a/dotnet/src/ZB.MOM.NatsNet.Server/Auth/AuthHandler.cs +++ b/dotnet/src/ZB.MOM.NatsNet.Server/Auth/AuthHandler.cs @@ -269,7 +269,7 @@ public static partial class AuthHandler /// public static void WipeSlice(Span buf) { - buf.Fill((byte)'x'); + JwtProcessor.WipeSlice(buf); } /// diff --git a/dotnet/src/ZB.MOM.NatsNet.Server/Auth/JwtProcessor.cs b/dotnet/src/ZB.MOM.NatsNet.Server/Auth/JwtProcessor.cs index afb79e5..357c119 100644 --- a/dotnet/src/ZB.MOM.NatsNet.Server/Auth/JwtProcessor.cs +++ b/dotnet/src/ZB.MOM.NatsNet.Server/Auth/JwtProcessor.cs @@ -31,6 +31,15 @@ public static class JwtProcessor /// public const string JwtPrefix = "eyJ"; + /// + /// Wipes a byte slice by filling with 'x'. + /// Mirrors Go wipeSlice. + /// + public static void WipeSlice(Span buf) + { + buf.Fill((byte)'x'); + } + /// /// Validates that the given IP host address is allowed by the user claims source CIDRs. /// Returns true if the host is within any of the allowed CIDRs, or if no CIDRs are specified. diff --git a/dotnet/src/ZB.MOM.NatsNet.Server/NatsServer.Listeners.cs b/dotnet/src/ZB.MOM.NatsNet.Server/NatsServer.Listeners.cs index 136b731..6056e61 100644 --- a/dotnet/src/ZB.MOM.NatsNet.Server/NatsServer.Listeners.cs +++ b/dotnet/src/ZB.MOM.NatsNet.Server/NatsServer.Listeners.cs @@ -61,10 +61,29 @@ public sealed partial class NatsServer /// /// Returns true if this server requires clients to send a nonce for auth. - /// Stub — full implementation in session 11. + /// Mirrors Go Server.NonceRequired(). /// Mirrors Go Server.nonceRequired(). /// - private bool NonceRequired() => false; + private bool NonceRequired() + { + _mu.EnterReadLock(); + try + { + return NonceRequiredInternal(); + } + finally + { + _mu.ExitReadLock(); + } + } + + /// + /// Returns true if this server requires clients to send a nonce for auth. + /// Lock should be held by caller for strict Go parity. + /// Mirrors Go Server.nonceRequired(). + /// + internal bool NonceRequiredInternal() + => GetOpts().AlwaysEnableNonce || (_nkeys?.Count > 0) || _trustedKeys != null || _proxiesKeyPairs.Count > 0; /// /// Fills with random bytes. diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Auth/JwtProcessorTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Auth/JwtProcessorTests.cs index 5db8321..5ec132c 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Auth/JwtProcessorTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Auth/JwtProcessorTests.cs @@ -40,7 +40,7 @@ public class JwtProcessorTests public void WipeSlice_FillsWithX() { var buf = new byte[] { 0x01, 0x02, 0x03 }; - AuthHandler.WipeSlice(buf); + JwtProcessor.WipeSlice(buf); buf.ShouldAllBe(b => b == (byte)'x'); } @@ -48,7 +48,7 @@ public class JwtProcessorTests public void WipeSlice_EmptyBuffer_NoOp() { var buf = Array.Empty(); - AuthHandler.WipeSlice(buf); + JwtProcessor.WipeSlice(buf); } // ========================================================================= diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Server/NonceRequiredTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Server/NonceRequiredTests.cs new file mode 100644 index 0000000..fce2fe7 --- /dev/null +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Server/NonceRequiredTests.cs @@ -0,0 +1,87 @@ +// Copyright 2012-2026 The NATS Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Reflection; +using Shouldly; +using ZB.MOM.NatsNet.Server.Auth; + +namespace ZB.MOM.NatsNet.Server.Tests.Server; + +public sealed class NonceRequiredTests +{ + [Fact] + public void NonceRequiredInternal_NoConditions_ReturnsFalse() + { + var server = CreateServer(); + server.NonceRequiredInternal().ShouldBeFalse(); + } + + [Fact] + public void NonceRequiredInternal_AlwaysEnableNonceOptionSet_ReturnsTrue() + { + var server = CreateServer(); + var opts = server.GetOpts(); + opts.AlwaysEnableNonce = true; + server.SetOpts(opts); + + server.NonceRequiredInternal().ShouldBeTrue(); + } + + [Fact] + public void NonceRequiredInternal_NkeysConfigured_ReturnsTrue() + { + var server = CreateServer(); + SetPrivateField(server, "_nkeys", new Dictionary + { + ["UAEXAMPLE"] = new() { Nkey = "UAEXAMPLE" }, + }); + + server.NonceRequiredInternal().ShouldBeTrue(); + } + + [Fact] + public void NonceRequiredInternal_TrustedKeysPresent_ReturnsTrue() + { + var server = CreateServer(); + SetPrivateField(server, "_trustedKeys", new List { "OPKEY" }); + + server.NonceRequiredInternal().ShouldBeTrue(); + } + + [Fact] + public void NonceRequiredInternal_ProxiesKeyPairsPresent_ReturnsTrue() + { + var server = CreateServer(); + var proxiesField = typeof(NatsServer).GetField("_proxiesKeyPairs", BindingFlags.Instance | BindingFlags.NonPublic); + proxiesField.ShouldNotBeNull(); + var proxies = proxiesField!.GetValue(server).ShouldBeOfType>(); + proxies.Add(new object()); + + server.NonceRequiredInternal().ShouldBeTrue(); + } + + private static NatsServer CreateServer() + { + var (server, error) = NatsServer.NewServer(new ServerOptions()); + error.ShouldBeNull(); + server.ShouldNotBeNull(); + return server!; + } + + private static void SetPrivateField(NatsServer server, string fieldName, T value) + { + var field = typeof(NatsServer).GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic); + field.ShouldNotBeNull(); + field!.SetValue(server, value); + } +} diff --git a/porting.db b/porting.db index 7d41894cac4b1c73719af51bd140f39e9e9e74e2..5cda9e2a044d2cb37fd4f14c633ba681b47dcfac 100644 GIT binary patch delta 1082 zcmcK2ONdif7zgm&lb1J{B!6d|(RpO%j`dMBnM^W|X5u5($5d_AXsnO68Rwdu(OWXf zc<+s+(8UDh3&ViRo>ir~5(J$^+XGz{x@j#G+HQ)g0WFj+Y$-)3l;RnogSd0i#SiY{ z^8LT>&zWDo>YZ1XK22%2uHH<^n<;JU=PW4Y{YTngex@(TuYS`GaNoBq>JiKHY>${M zu!M9A?6fAoIl&4V^L{USitICA@PQ{Pe=M?0o3YGm#q|YXxrjm4Fgd)+o9XQo>4`5>cWua3l8bW#8kK3-%UZFmFLj7tk*z zfk(`{Pi$WcDSD^bpa`_%ytXVJ7x63fg@`xKu5jC$v3MmYTT>lS)++5vhq6xTRJxS) z$_Axd>5)?%y+{5Lw*tX_@kg{TEPjafZGLWbLLVVc=qC&i48kB`BjFXoCcIanxttampLEp7~Z;w;|Oo8sN?b)x1FJ~lgo~k>$7uJ?($i))-bEaoLzV8<$Bd{_~iz#m95&voceafon)!| zpkdCq+%{b9xTy;bt6DMm6|16ticyc(C%w&ypI=Avn(#`zhU}$BF51(HmC`J00 zbJ^rd`bhSFOvj?}Ed6+A)4y7QN^kzmwuyfC1b|4 zs_N?0oas2vE_GhGZ9JDAZT2n>{5QRKb-F5eb^7G7x~k=*YSFTJ<#0{)R#W{}>ldx^ TO9Zv-L`j)a&PzKnef{gF?4MIS delta 651 zcmWm9J4{ni9LMpV|9zEmABR>dU<-m&d{m0y179ujDx%=UB0ej{#D%B{S6!TLg7I#S zp29$hiz_TO`WM z${O3$i23r}I_tlqR>%&CWuLNQ(4{mfAEoEg1e2998f9dB5e)@;qgqOml((OTA`c!uy3PC` zK2M;?_`K@;5WTI*7P0ZON*5z9wAW(cu~i)yWes#NAPiLyf$e631-3NG&fgjJ+x9_^ z%d=vL^EJ_4!=-3qloEQBPqHofy}(r#OvgCXKonxI18N}-JE0Ehp+Qc^c4fE3%1pRX zY}kov@ySV~1g}>j&pQk~(0Kxq&!vG9I9){o?48wW203$F87r})wxC8|l zhs!VlSKumKgX?euZc3dO|2ej1dLgIQ4y->`m+8r7w^%X*FI|LNFe$xs>Dl}Lb1AoY diff --git a/reports/current.md b/reports/current.md index 3c033fb..04536ed 100644 --- a/reports/current.md +++ b/reports/current.md @@ -1,6 +1,6 @@ # NATS .NET Porting Status Report -Generated: 2026-02-28 11:05:07 UTC +Generated: 2026-02-28 11:30:24 UTC ## Modules (12 total) @@ -12,10 +12,10 @@ Generated: 2026-02-28 11:05:07 UTC | Status | Count | |--------|-------| -| deferred | 2375 | +| deferred | 2373 | | n_a | 24 | | stub | 1 | -| verified | 1273 | +| verified | 1275 | ## Unit Tests (3257 total) @@ -34,4 +34,4 @@ Generated: 2026-02-28 11:05:07 UTC ## Overall Progress -**2475/6942 items complete (35.7%)** +**2477/6942 items complete (35.7%)** diff --git a/reports/report_f9b582d.md b/reports/report_f9b582d.md new file mode 100644 index 0000000..04536ed --- /dev/null +++ b/reports/report_f9b582d.md @@ -0,0 +1,37 @@ +# NATS .NET Porting Status Report + +Generated: 2026-02-28 11:30:24 UTC + +## Modules (12 total) + +| Status | Count | +|--------|-------| +| verified | 12 | + +## Features (3673 total) + +| Status | Count | +|--------|-------| +| deferred | 2373 | +| n_a | 24 | +| stub | 1 | +| verified | 1275 | + +## Unit Tests (3257 total) + +| Status | Count | +|--------|-------| +| deferred | 2091 | +| n_a | 187 | +| verified | 979 | + +## Library Mappings (36 total) + +| Status | Count | +|--------|-------| +| mapped | 36 | + + +## Overall Progress + +**2477/6942 items complete (35.7%)**