diff --git a/docs/plans/2026-02-26-stub-features-design.md b/docs/plans/2026-02-26-stub-features-design.md new file mode 100644 index 0000000..1c13cfb --- /dev/null +++ b/docs/plans/2026-02-26-stub-features-design.md @@ -0,0 +1,185 @@ +# Stub Features Implementation Design + +**Date:** 2026-02-26 +**Scope:** Complete the 93 remaining `stub` features in Phase 6 +**Approach:** Two parallel sessions (Config + Auth) + +## Overview + +After Phase 6's 23 porting sessions, 93 features remain at `stub` status. They fall into two +independent concerns that can be implemented in parallel: + +| Group | Go File | Stubs | Go LOC | Concern | +|-------|---------|-------|--------|---------| +| Config | `server/opts.go` | 67 | ~4,876 | Configuration file parsing / binding | +| Auth | `server/auth.go` | 19 | ~1,296 | Authentication dispatch | +| Auth | `server/auth_callout.go` | 3 | ~456 | External auth callout | +| Auth | `server/jwt.go` | 3 | ~137 | Operator JWT validation | +| Signals | `server/signal.go` | 1 | ~46 | OS signal handling | + +--- + +## Session A: Configuration Binding (67 stubs, opts.go) + +### Decision + +Map all NATS server configuration to **`appsettings.json`** via +`Microsoft.Extensions.Configuration`. The Go `conf` package tokenizer and the 765-line +`processConfigFileLine` dispatch loop are **not ported** — JSON deserialization replaces them. + +### New Files + +**`Config/ServerOptionsConfiguration.cs`** + +```csharp +public static class ServerOptionsConfiguration +{ + public static ServerOptions ProcessConfigFile(string path); + public static ServerOptions ProcessConfigString(string json); + public static void BindConfiguration(IConfiguration config, ServerOptions target); +} +``` + +- `ProcessConfigFile` uses `new ConfigurationBuilder().AddJsonFile(path).Build()` +- `ProcessConfigString` uses `AddJsonStream(new MemoryStream(Encoding.UTF8.GetBytes(json)))` +- `BindConfiguration` calls `config.Bind(target)` with custom converters registered + +**`Config/NatsJsonConverters.cs`** + +Custom `JsonConverter` for non-trivial types: + +| Converter | Input | Output | Mirrors | +|-----------|-------|--------|---------| +| `DurationJsonConverter` | `"2s"`, `"100ms"`, `"1h30m"` | `TimeSpan` | `parseDuration` | +| `TlsVersionJsonConverter` | `"1.2"`, `"TLS12"` | `SslProtocols` | `parseTLSVersion` | +| `NatsUrlJsonConverter` | `"nats://host:port"` | validated `string` | `parseURL` | +| `StorageSizeJsonConverter` | `"1GB"`, `"512mb"` | `long` (bytes) | `getStorageSize` | + +### ServerOptions.cs Changes + +Add `[JsonPropertyName("...")]` attributes for fields whose JSON key names differ from C# names. +JSON key names follow NATS server conventions (lowercase, underscore-separated): + +```json +{ + "port": 4222, + "host": "0.0.0.0", + "tls": { "cert_file": "...", "key_file": "...", "ca_file": "..." }, + "cluster": { "port": 6222, "name": "my-cluster" }, + "gateway": { "port": 7222, "name": "my-gateway" }, + "jetstream": { "store_dir": "/data/jetstream", "max_memory": "1GB" }, + "leafnodes": { "port": 7422 }, + "mqtt": { "port": 1883 }, + "websocket": { "port": 8080 }, + "accounts": [ { "name": "A", "users": [ { "user": "u1", "password": "p1" } ] } ] +} +``` + +### DB Outcome + +All 67 opts.go stubs → `complete`: +- Feature IDs 2505–2574, 2580, 2584 (+ `configureSystemAccount` 2509, `setupUsersAndNKeysDuplicateCheckMap` 2515) +- `parse*` functions have no C# equivalent — their logic is subsumed by converters and JSON binding + +--- + +## Session B: Auth Implementation (26 stubs) + +### New Files + +**`NatsServer.Auth.cs`** — `partial class NatsServer` with: + +| Method | Go Equivalent | Notes | +|--------|--------------|-------| +| `ConfigureAuthorization()` | `configureAuthorization` | Builds `_nkeys`/`_users` dicts from `_opts` | +| `BuildNkeysAndUsersFromOptions()` | `buildNkeysAndUsersFromOptions` | Creates typed lookup maps | +| `CheckAuthforWarnings()` | `checkAuthforWarnings` | Validates auth config consistency | +| `AssignGlobalAccountToOrphanUsers()` | `assignGlobalAccountToOrphanUsers` | — | +| `CheckAuthentication(ClientConnection)` | `checkAuthentication` | Entry point | +| `IsClientAuthorized(ClientConnection)` | `isClientAuthorized` | Check user credentials | +| `ProcessClientOrLeafAuthentication(ClientConnection, ServerOptions)` | `processClientOrLeafAuthentication` | Main 554-line auth dispatch | +| `IsRouterAuthorized(ClientConnection)` | `isRouterAuthorized` | Route-specific auth | +| `IsGatewayAuthorized(ClientConnection)` | `isGatewayAuthorized` | Gateway-specific auth | +| `RegisterLeafWithAccount(ClientConnection, string)` | `registerLeafWithAccount` | — | +| `IsLeafNodeAuthorized(ClientConnection)` | `isLeafNodeAuthorized` | Leaf-specific auth | +| `ProcessProxiesTrustedKeys()` | `processProxiesTrustedKeys` | Proxy key setup | +| `ProxyCheck(ClientConnection, ServerOptions)` | `proxyCheck` | Validate proxy headers | + +**Auth dispatch flow in `ProcessClientOrLeafAuthentication`:** +``` +if callout configured → ProcessClientOrLeafCallout() +else if JWT bearer → JwtProcessor.ValidateAndRegisterUser() +else if NKey → verify NKey signature (NATS.NKeys NuGet) +else if user+password → BCrypt.Net.BCrypt.Verify() (BCrypt.Net-Next NuGet) +else if TLS cert map → CheckClientTlsCertSubject() +else if no-auth mode → allow (if opts.NoAuth) +→ set client account, permissions, labels +``` + +**`Auth/AuthCallout.cs`** — `partial class NatsServer` with: +- `ProcessClientOrLeafCallout(ClientConnection, ServerOptions)` — publishes to `$SYS.REQ.USER.AUTH`, waits for signed JWT response, validates it +- `FillClientInfo(AuthorizationRequestClaims, ClientConnection)` — populate auth request payload +- `FillConnectOpts(AuthorizationRequestClaims, ClientConnection)` — populate connect opts in payload + +**`Auth/JwtProcessor.cs` additions:** +- `ReadOperatorJwt(string path)` — read operator JWT from file, decode `OperatorClaims` +- `ReadOperatorJwtInternal(string jwtString)` — decode from string +- `ValidateTrustedOperators(ServerOptions opts)` — walk operator → account → user signing key chain + +**`Auth/AuthHandler.cs` additions:** +- `ProcessUserPermissionsTemplate(UserPermissionLimits, UserClaims, Account)` — expand `{{account}}`, `{{tag.*}}` template variables in JWT user permissions +- `GetTlsAuthDcs(X509DistinguishedName)` — extract DC= components from TLS cert RDN +- `CheckClientTlsCertSubject(ClientConnection, Func)` — TLS cert subject matching +- `ValidateProxies(ServerOptions)` — validate proxy configuration +- `GetAuthErrClosedState(ClientConnection)` — map auth failure to client closed state enum + +### New NuGet Packages + +| Package | Version | Purpose | +|---------|---------|---------| +| `BCrypt.Net-Next` | ≥4.0 | bcrypt password hashing and comparison | +| `NATS.NKeys` | ≥2.0 | NKey keypair creation, signature verify | + +### `NatsServer.Signals.cs` + +New partial class file: +```csharp +// Registers OS signal handlers via PosixSignalRegistration (cross-platform). +// SIGHUP → server.Reload() +// SIGTERM → server.Shutdown() +// SIGINT → server.Shutdown() +// Windows fallback: Console.CancelKeyPress → Shutdown() +``` + +### DB Outcome + +All 26 auth/jwt/callout/signal stubs → `complete`: +- Feature IDs 354–383, 1973–1976, 2584, 3156 + +--- + +## File Summary + +| File | Action | +|------|--------| +| `Config/ServerOptionsConfiguration.cs` | CREATE | +| `Config/NatsJsonConverters.cs` | CREATE | +| `NatsServer.Auth.cs` | CREATE (partial) | +| `NatsServer.Signals.cs` | CREATE (partial) | +| `Auth/AuthCallout.cs` | CREATE (partial) | +| `Auth/JwtProcessor.cs` | MODIFY (add 3 methods) | +| `Auth/AuthHandler.cs` | MODIFY (add 5 methods) | +| `ServerOptions.cs` | MODIFY (add JsonPropertyName attrs) | +| `ZB.MOM.NatsNet.Server.csproj` | MODIFY (add 2 NuGet packages) | + +--- + +## Testing + +Unit tests in `ZB.MOM.NatsNet.Server.Tests/`: +- `Config/ServerOptionsConfigurationTests.cs` — round-trip JSON bind tests for each major option group +- `Auth/AuthHandlerTests.cs` additions — bcrypt comparison, NKey verify, TLS cert subject matching +- `Auth/JwtProcessorTests.cs` additions — operator JWT read/validate + +No new test IDs needed — these are implementations of already-tracked Phase 6 features. +After implementation, relevant test IDs in Phase 7 will be marked complete. diff --git a/reports/current.md b/reports/current.md index 27003c3..52513eb 100644 --- a/reports/current.md +++ b/reports/current.md @@ -1,6 +1,6 @@ # NATS .NET Porting Status Report -Generated: 2026-02-26 21:31:42 UTC +Generated: 2026-02-26 21:54:06 UTC ## Modules (12 total) diff --git a/reports/report_a58e8e2.md b/reports/report_a58e8e2.md new file mode 100644 index 0000000..52513eb --- /dev/null +++ b/reports/report_a58e8e2.md @@ -0,0 +1,38 @@ +# NATS .NET Porting Status Report + +Generated: 2026-02-26 21:54:06 UTC + +## Modules (12 total) + +| Status | Count | +|--------|-------| +| complete | 11 | +| not_started | 1 | + +## Features (3673 total) + +| Status | Count | +|--------|-------| +| complete | 3503 | +| n_a | 77 | +| stub | 93 | + +## Unit Tests (3257 total) + +| Status | Count | +|--------|-------| +| complete | 319 | +| n_a | 181 | +| not_started | 2533 | +| stub | 224 | + +## Library Mappings (36 total) + +| Status | Count | +|--------|-------| +| mapped | 36 | + + +## Overall Progress + +**4091/6942 items complete (58.9%)**