186 lines
7.9 KiB
Markdown
186 lines
7.9 KiB
Markdown
# 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<T>` 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<string, bool>)` — 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.
|