68 lines
2.0 KiB
C#
68 lines
2.0 KiB
C#
using System.Security.Cryptography.X509Certificates;
|
|
|
|
namespace NATS.Server.Auth;
|
|
|
|
/// <summary>
|
|
/// Authenticates clients by mapping TLS certificate subject DN to configured users.
|
|
/// Corresponds to Go server/auth.go checkClientTLSCertSubject.
|
|
/// </summary>
|
|
public sealed class TlsMapAuthenticator : IAuthenticator
|
|
{
|
|
private readonly Dictionary<string, User> _usersByDn;
|
|
private readonly Dictionary<string, User> _usersByCn;
|
|
|
|
public TlsMapAuthenticator(IReadOnlyList<User> users)
|
|
{
|
|
_usersByDn = new Dictionary<string, User>(StringComparer.OrdinalIgnoreCase);
|
|
_usersByCn = new Dictionary<string, User>(StringComparer.OrdinalIgnoreCase);
|
|
foreach (var user in users)
|
|
{
|
|
_usersByDn[user.Username] = user;
|
|
_usersByCn[user.Username] = user;
|
|
}
|
|
}
|
|
|
|
public AuthResult? Authenticate(ClientAuthContext context)
|
|
{
|
|
var cert = context.ClientCertificate;
|
|
if (cert == null)
|
|
return null;
|
|
|
|
var dn = cert.SubjectName;
|
|
var dnString = dn.Name; // RFC 2253 format
|
|
|
|
// Try exact DN match first
|
|
if (_usersByDn.TryGetValue(dnString, out var user))
|
|
return BuildResult(user);
|
|
|
|
// Try CN extraction
|
|
var cn = ExtractCn(dn);
|
|
if (cn != null && _usersByCn.TryGetValue(cn, out user))
|
|
return BuildResult(user);
|
|
|
|
return null;
|
|
}
|
|
|
|
private static string? ExtractCn(X500DistinguishedName dn)
|
|
{
|
|
var dnString = dn.Name;
|
|
foreach (var rdn in dnString.Split(',', StringSplitOptions.TrimEntries))
|
|
{
|
|
if (rdn.StartsWith("CN=", StringComparison.OrdinalIgnoreCase))
|
|
return rdn[3..];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private static AuthResult BuildResult(User user)
|
|
{
|
|
return new AuthResult
|
|
{
|
|
Identity = user.Username,
|
|
AccountName = user.Account,
|
|
Permissions = user.Permissions,
|
|
Expiry = user.ConnectionDeadline,
|
|
};
|
|
}
|
|
}
|