feat(radius): add RADIUS server with MAC authentication (MAB), VLAN assignment, accounting and OpsServer API handlers

This commit is contained in:
2026-02-01 19:21:37 +00:00
parent fcea194cf6
commit c2d3ace0dd
31 changed files with 2498 additions and 531 deletions

View File

@@ -16,6 +16,7 @@ export class OpsServer {
private logsHandler: handlers.LogsHandler;
private securityHandler: handlers.SecurityHandler;
private statsHandler: handlers.StatsHandler;
private radiusHandler: handlers.RadiusHandler;
constructor(dcRouterRefArg: DcRouter) {
this.dcRouterRef = dcRouterRefArg;
@@ -53,7 +54,8 @@ export class OpsServer {
this.logsHandler = new handlers.LogsHandler(this);
this.securityHandler = new handlers.SecurityHandler(this);
this.statsHandler = new handlers.StatsHandler(this);
this.radiusHandler = new handlers.RadiusHandler(this);
console.log('✅ OpsServer TypedRequest handlers initialized');
}

View File

@@ -2,4 +2,5 @@ export * from './admin.handler.js';
export * from './config.handler.js';
export * from './logs.handler.js';
export * from './security.handler.js';
export * from './stats.handler.js';
export * from './stats.handler.js';
export * from './radius.handler.js';

View File

@@ -0,0 +1,405 @@
import * as plugins from '../../plugins.js';
import type { OpsServer } from '../classes.opsserver.js';
import * as interfaces from '../../../ts_interfaces/index.js';
export class RadiusHandler {
public typedrouter = new plugins.typedrequest.TypedRouter();
constructor(private opsServerRef: OpsServer) {
// Add this handler's router to the parent
this.opsServerRef.typedrouter.addTypedRouter(this.typedrouter);
this.registerHandlers();
}
private registerHandlers(): void {
// ========================================================================
// RADIUS Client Management
// ========================================================================
// Get all RADIUS clients
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRadiusClients>(
'getRadiusClients',
async (dataArg, toolsArg) => {
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
if (!radiusServer) {
return { clients: [] };
}
const clients = radiusServer.getClients();
return {
clients: clients.map(c => ({
name: c.name,
ipRange: c.ipRange,
description: c.description,
enabled: c.enabled,
})),
};
}
)
);
// Add or update a RADIUS client
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_SetRadiusClient>(
'setRadiusClient',
async (dataArg, toolsArg) => {
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
if (!radiusServer) {
return { success: false, message: 'RADIUS server not configured' };
}
try {
await radiusServer.addClient(dataArg.client);
return { success: true };
} catch (error) {
return { success: false, message: error.message };
}
}
)
);
// Remove a RADIUS client
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_RemoveRadiusClient>(
'removeRadiusClient',
async (dataArg, toolsArg) => {
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
if (!radiusServer) {
return { success: false, message: 'RADIUS server not configured' };
}
const removed = radiusServer.removeClient(dataArg.name);
return {
success: removed,
message: removed ? undefined : 'Client not found',
};
}
)
);
// ========================================================================
// VLAN Mapping Management
// ========================================================================
// Get all VLAN mappings
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetVlanMappings>(
'getVlanMappings',
async (dataArg, toolsArg) => {
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
if (!radiusServer) {
return {
mappings: [],
config: { defaultVlan: 1, allowUnknownMacs: true },
};
}
const vlanManager = radiusServer.getVlanManager();
const mappings = vlanManager.getAllMappings();
const config = vlanManager.getConfig();
return {
mappings: mappings.map(m => ({
mac: m.mac,
vlan: m.vlan,
description: m.description,
enabled: m.enabled,
createdAt: m.createdAt,
updatedAt: m.updatedAt,
})),
config: {
defaultVlan: config.defaultVlan,
allowUnknownMacs: config.allowUnknownMacs,
},
};
}
)
);
// Add or update a VLAN mapping
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_SetVlanMapping>(
'setVlanMapping',
async (dataArg, toolsArg) => {
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
if (!radiusServer) {
return { success: false, message: 'RADIUS server not configured' };
}
try {
const vlanManager = radiusServer.getVlanManager();
const mapping = await vlanManager.addMapping(dataArg.mapping);
return {
success: true,
mapping: {
mac: mapping.mac,
vlan: mapping.vlan,
description: mapping.description,
enabled: mapping.enabled,
createdAt: mapping.createdAt,
updatedAt: mapping.updatedAt,
},
};
} catch (error) {
return { success: false, message: error.message };
}
}
)
);
// Remove a VLAN mapping
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_RemoveVlanMapping>(
'removeVlanMapping',
async (dataArg, toolsArg) => {
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
if (!radiusServer) {
return { success: false, message: 'RADIUS server not configured' };
}
const vlanManager = radiusServer.getVlanManager();
const removed = await vlanManager.removeMapping(dataArg.mac);
return {
success: removed,
message: removed ? undefined : 'Mapping not found',
};
}
)
);
// Update VLAN configuration
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_UpdateVlanConfig>(
'updateVlanConfig',
async (dataArg, toolsArg) => {
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
if (!radiusServer) {
return {
success: false,
config: { defaultVlan: 1, allowUnknownMacs: true },
};
}
const vlanManager = radiusServer.getVlanManager();
vlanManager.updateConfig({
defaultVlan: dataArg.defaultVlan,
allowUnknownMacs: dataArg.allowUnknownMacs,
});
const config = vlanManager.getConfig();
return {
success: true,
config: {
defaultVlan: config.defaultVlan,
allowUnknownMacs: config.allowUnknownMacs,
},
};
}
)
);
// Test VLAN assignment
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_TestVlanAssignment>(
'testVlanAssignment',
async (dataArg, toolsArg) => {
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
if (!radiusServer) {
return { assigned: false, vlan: 0, isDefault: false };
}
const vlanManager = radiusServer.getVlanManager();
const result = vlanManager.assignVlan(dataArg.mac);
return {
assigned: result.assigned,
vlan: result.vlan,
isDefault: result.isDefault,
matchedRule: result.matchedRule
? {
mac: result.matchedRule.mac,
vlan: result.matchedRule.vlan,
description: result.matchedRule.description,
}
: undefined,
};
}
)
);
// ========================================================================
// Accounting / Session Management
// ========================================================================
// Get active sessions
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRadiusSessions>(
'getRadiusSessions',
async (dataArg, toolsArg) => {
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
if (!radiusServer) {
return { sessions: [], totalCount: 0 };
}
const accountingManager = radiusServer.getAccountingManager();
let sessions = accountingManager.getActiveSessions();
// Apply filters
if (dataArg.filter) {
if (dataArg.filter.username) {
sessions = sessions.filter(s => s.username === dataArg.filter!.username);
}
if (dataArg.filter.nasIpAddress) {
sessions = sessions.filter(s => s.nasIpAddress === dataArg.filter!.nasIpAddress);
}
if (dataArg.filter.vlanId !== undefined) {
sessions = sessions.filter(s => s.vlanId === dataArg.filter!.vlanId);
}
}
return {
sessions: sessions.map(s => ({
sessionId: s.sessionId,
username: s.username,
macAddress: s.macAddress,
nasIpAddress: s.nasIpAddress,
nasIdentifier: s.nasIdentifier,
vlanId: s.vlanId,
framedIpAddress: s.framedIpAddress,
startTime: s.startTime,
lastUpdateTime: s.lastUpdateTime,
status: s.status,
inputOctets: s.inputOctets,
outputOctets: s.outputOctets,
sessionTime: s.sessionTime,
})),
totalCount: sessions.length,
};
}
)
);
// Disconnect a session
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_DisconnectRadiusSession>(
'disconnectRadiusSession',
async (dataArg, toolsArg) => {
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
if (!radiusServer) {
return { success: false, message: 'RADIUS server not configured' };
}
const accountingManager = radiusServer.getAccountingManager();
const disconnected = await accountingManager.disconnectSession(
dataArg.sessionId,
dataArg.reason || 'AdminReset'
);
return {
success: disconnected,
message: disconnected ? undefined : 'Session not found',
};
}
)
);
// Get accounting summary
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRadiusAccountingSummary>(
'getRadiusAccountingSummary',
async (dataArg, toolsArg) => {
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
if (!radiusServer) {
return {
summary: {
periodStart: dataArg.startTime,
periodEnd: dataArg.endTime,
totalSessions: 0,
activeSessions: 0,
totalInputBytes: 0,
totalOutputBytes: 0,
totalSessionTime: 0,
averageSessionDuration: 0,
uniqueUsers: 0,
sessionsByVlan: {},
topUsersByTraffic: [],
},
};
}
const accountingManager = radiusServer.getAccountingManager();
const summary = await accountingManager.getSummary(dataArg.startTime, dataArg.endTime);
return { summary };
}
)
);
// ========================================================================
// Statistics
// ========================================================================
// Get RADIUS statistics
this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRadiusStatistics>(
'getRadiusStatistics',
async (dataArg, toolsArg) => {
const radiusServer = this.opsServerRef.dcRouterRef.radiusServer;
if (!radiusServer) {
return {
stats: {
running: false,
uptime: 0,
authRequests: 0,
authAccepts: 0,
authRejects: 0,
accountingRequests: 0,
activeSessions: 0,
vlanMappings: 0,
clients: 0,
},
vlanStats: {
totalMappings: 0,
enabledMappings: 0,
exactMatches: 0,
ouiPatterns: 0,
wildcardPatterns: 0,
},
accountingStats: {
activeSessions: 0,
totalSessionsStarted: 0,
totalSessionsStopped: 0,
totalInputBytes: 0,
totalOutputBytes: 0,
interimUpdatesReceived: 0,
},
};
}
const stats = radiusServer.getStats();
const vlanStats = radiusServer.getVlanManager().getStats();
const accountingStats = radiusServer.getAccountingManager().getStats();
return {
stats,
vlanStats,
accountingStats,
};
}
)
);
}
}