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 7d41894..5cda9e2 100644 Binary files a/porting.db and b/porting.db differ 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%)**