feat: execute full-repo remaining parity closure plan
This commit is contained in:
32
src/NATS.Server/Auth/AuthExtensionOptions.cs
Normal file
32
src/NATS.Server/Auth/AuthExtensionOptions.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
namespace NATS.Server.Auth;
|
||||
|
||||
public interface IExternalAuthClient
|
||||
{
|
||||
Task<ExternalAuthDecision> AuthorizeAsync(ExternalAuthRequest request, CancellationToken ct);
|
||||
}
|
||||
|
||||
public sealed record ExternalAuthRequest(
|
||||
string? Username,
|
||||
string? Password,
|
||||
string? Token,
|
||||
string? Jwt);
|
||||
|
||||
public sealed record ExternalAuthDecision(
|
||||
bool Allowed,
|
||||
string? Identity = null,
|
||||
string? Account = null,
|
||||
string? Reason = null);
|
||||
|
||||
public sealed class ExternalAuthOptions
|
||||
{
|
||||
public bool Enabled { get; set; }
|
||||
public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(2);
|
||||
public IExternalAuthClient? Client { get; set; }
|
||||
}
|
||||
|
||||
public sealed class ProxyAuthOptions
|
||||
{
|
||||
public bool Enabled { get; set; }
|
||||
public string UsernamePrefix { get; set; } = "proxy:";
|
||||
public string? Account { get; set; }
|
||||
}
|
||||
@@ -49,6 +49,18 @@ public sealed class AuthService
|
||||
nonceRequired = true;
|
||||
}
|
||||
|
||||
if (options.ExternalAuth is { Enabled: true, Client: not null } externalAuth)
|
||||
{
|
||||
authenticators.Add(new ExternalAuthCalloutAuthenticator(externalAuth.Client, externalAuth.Timeout));
|
||||
authRequired = true;
|
||||
}
|
||||
|
||||
if (options.ProxyAuth is { Enabled: true } proxyAuth)
|
||||
{
|
||||
authenticators.Add(new ProxyAuthenticator(proxyAuth));
|
||||
authRequired = true;
|
||||
}
|
||||
|
||||
// Priority order (matching Go): NKeys > Users > Token > SimpleUserPassword
|
||||
|
||||
if (options.NKeys is { Count: > 0 })
|
||||
|
||||
42
src/NATS.Server/Auth/ExternalAuthCalloutAuthenticator.cs
Normal file
42
src/NATS.Server/Auth/ExternalAuthCalloutAuthenticator.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
namespace NATS.Server.Auth;
|
||||
|
||||
public sealed class ExternalAuthCalloutAuthenticator : IAuthenticator
|
||||
{
|
||||
private readonly IExternalAuthClient _client;
|
||||
private readonly TimeSpan _timeout;
|
||||
|
||||
public ExternalAuthCalloutAuthenticator(IExternalAuthClient client, TimeSpan timeout)
|
||||
{
|
||||
_client = client;
|
||||
_timeout = timeout;
|
||||
}
|
||||
|
||||
public AuthResult? Authenticate(ClientAuthContext context)
|
||||
{
|
||||
using var cts = new CancellationTokenSource(_timeout);
|
||||
ExternalAuthDecision decision;
|
||||
try
|
||||
{
|
||||
decision = _client.AuthorizeAsync(
|
||||
new ExternalAuthRequest(
|
||||
context.Opts.Username,
|
||||
context.Opts.Password,
|
||||
context.Opts.Token,
|
||||
context.Opts.JWT),
|
||||
cts.Token).GetAwaiter().GetResult();
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!decision.Allowed)
|
||||
return null;
|
||||
|
||||
return new AuthResult
|
||||
{
|
||||
Identity = decision.Identity ?? context.Opts.Username ?? "external",
|
||||
AccountName = decision.Account,
|
||||
};
|
||||
}
|
||||
}
|
||||
27
src/NATS.Server/Auth/ProxyAuthenticator.cs
Normal file
27
src/NATS.Server/Auth/ProxyAuthenticator.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
namespace NATS.Server.Auth;
|
||||
|
||||
public sealed class ProxyAuthenticator(ProxyAuthOptions options) : IAuthenticator
|
||||
{
|
||||
public AuthResult? Authenticate(ClientAuthContext context)
|
||||
{
|
||||
if (!options.Enabled)
|
||||
return null;
|
||||
|
||||
var username = context.Opts.Username;
|
||||
if (string.IsNullOrEmpty(username))
|
||||
return null;
|
||||
|
||||
if (!username.StartsWith(options.UsernamePrefix, StringComparison.Ordinal))
|
||||
return null;
|
||||
|
||||
var identity = username[options.UsernamePrefix.Length..];
|
||||
if (identity.Length == 0)
|
||||
return null;
|
||||
|
||||
return new AuthResult
|
||||
{
|
||||
Identity = identity,
|
||||
AccountName = options.Account,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user