feat(smartradius): Implement full RADIUS server and client with RFC 2865/2866 compliance, including packet handling, authenticators, attributes, secrets manager, client APIs, and comprehensive tests and documentation

This commit is contained in:
2026-02-01 17:40:36 +00:00
parent 5a6a3cf66e
commit be9f49fff9
45 changed files with 11694 additions and 70 deletions

View File

@@ -0,0 +1,116 @@
import type { TSecretResolver } from './interfaces.js';
/**
* RADIUS Shared Secrets Manager
* Manages per-client shared secrets for RADIUS authentication
*/
export class RadiusSecrets {
private readonly secrets: Map<string, string> = new Map();
private defaultSecret?: string;
private customResolver?: TSecretResolver;
/**
* Create a new secrets manager
*/
constructor(options?: {
defaultSecret?: string;
secrets?: Record<string, string>;
resolver?: TSecretResolver;
}) {
if (options?.defaultSecret) {
this.defaultSecret = options.defaultSecret;
}
if (options?.secrets) {
for (const [ip, secret] of Object.entries(options.secrets)) {
this.secrets.set(ip, secret);
}
}
if (options?.resolver) {
this.customResolver = options.resolver;
}
}
/**
* Set secret for a specific client IP
*/
public setClientSecret(clientIp: string, secret: string): void {
this.secrets.set(clientIp, secret);
}
/**
* Remove secret for a specific client IP
*/
public removeClientSecret(clientIp: string): boolean {
return this.secrets.delete(clientIp);
}
/**
* Set the default secret
*/
public setDefaultSecret(secret: string): void {
this.defaultSecret = secret;
}
/**
* Set a custom resolver
*/
public setResolver(resolver: TSecretResolver): void {
this.customResolver = resolver;
}
/**
* Get secret for a client IP
* Priority: 1. Custom resolver, 2. Per-client secret, 3. Default secret
*/
public getSecret(clientIp: string): string | undefined {
// Try custom resolver first
if (this.customResolver) {
const resolved = this.customResolver(clientIp);
if (resolved !== undefined) {
return resolved;
}
}
// Try per-client secret
const clientSecret = this.secrets.get(clientIp);
if (clientSecret !== undefined) {
return clientSecret;
}
// Fall back to default secret
return this.defaultSecret;
}
/**
* Check if a client is known (has a secret)
*/
public isKnownClient(clientIp: string): boolean {
return this.getSecret(clientIp) !== undefined;
}
/**
* Get all registered client IPs
*/
public getClientIps(): string[] {
return Array.from(this.secrets.keys());
}
/**
* Clear all per-client secrets
*/
public clearClientSecrets(): void {
this.secrets.clear();
}
/**
* Set secrets from a CIDR range (simplified - just IP addresses)
* For actual CIDR support, use a custom resolver
*/
public setSecretsForNetwork(network: string, secret: string): void {
// For simplicity, this just sets a single IP
// Real CIDR support would require a resolver
this.secrets.set(network, secret);
}
}
export default RadiusSecrets;