Sinatra/code/index.js

195 lines
6.5 KiB
JavaScript

// Baseline
const product = 'NetRadius';
const version = '0.2.0';
// Load dependencies
const dgram = require ('dgram');
const fs = require ('fs');
const http = require ('http');
// Load modules
log = require ('./lib/logger.js');
const handlers = require ('./lib/handlers.js');
// Load configuration
log.write (product + ' v' + version);
config = {};
try {
config = JSON.parse (fs.readFileSync ('./config.json').toString ());
}
catch (error) {
log.write ('Cannot open or read configuration file.');
log.write ('Using defaults');
config = {
ports: {
radius_authentication: 1812,
radius_accounting: 1813,
api: 8088
},
storage: "json:./data.json",
client_secret: "password",
default_vlan_enabled: false,
mac_auth_only: false,
session_duration: 60
}
}
if (process.env['NETRADIUS_PORT_RADIUS_AUTH']) config.ports.radius_authentication = process.env['NETRADIUS_PORT_RADIUS_AUTH'];
if (process.env['NETRADIUS_PORT_RADIUS_ACCT']) config.ports.radius_accounting = process.env['NETRADIUS_PORT_RADIUS_ACCT'];
if (process.env['NETRADIUS_PORT_API']) config.ports.api = process.env['NETRADIUS_PORT_API'];
if (process.env['NETRADIUS_STORAGE']) config.storage = process.env['NETRADIUS_STORAGE'];
if (process.env['NETRADIUS_DEFAULT_VLAN']) config.default_vlan_enabled = process.env['NETRADIUS_DEFAULT_VLAN'];
if (process.env['NETRADIUS_DEFAULT_VLAN_ID']) config.default_vlan_id = process.env['NETRADIUS_DEFAULT_VLAN_ID'];
if (process.env['NETRADIUS_CLIENT_SECRET']) config.client_secret = process.env['NETRADIUS_CLIENT_SECRET'];
if (process.env['NETRADIUS_MAC_AUTH_ONLY']) config.mac_auth_only = process.env['NETRADIUS_MAC_AUTH_ONLY'];
if (process.env['NETRADIUS_SESSION_DURATION']) config.session_duration = process.env['NETRADIUS_SESSION_DURATION'];
// Set defaults
if (!config.ports.radius_authentication) config.ports.radius_authentication = 1812;
if (!config.ports.radius_accounting) config.ports.radius_accounting = 1813;
if (!config.ports.api) config.ports.api = 8088;
// Display active configuration
log.write ('Using configuration: ' + JSON.stringify (config));
// Listeners
var listeners = {
authentication: {
socket: dgram.createSocket ({ type: 'udp4', reuseAddr: true }),
handler: handlers.radius.authentication
},
accounting: {
socket: dgram.createSocket ({ type: 'udp4', reuseAddr: true }),
handler: handlers.radius.accounting
}
};
// RADIUS authentication
listeners.authentication.socket.on ('message', (msg, rinfo) => {
handlers.radius.authentication (msg, rinfo, (response, err) => {
if (!err) {
listeners.authentication.socket.send (response, 0, response.length, rinfo.port, rinfo.address, function (err, bytes) {
if (err) {
console.log ('Error sending response to ', rinfo);
}
});
}
});
});
listeners.authentication.socket.on ('listening', () => {
var address = listeners.authentication.socket.address ();
log.write ("Authentication listening on " + address.address + ":" + address.port);
});
listeners.authentication.socket.bind (config.ports.radius_authentication);
// RADIUS accounting
listeners.accounting.socket.on ('message', (msg, rinfo) => {
handlers.radius.accounting (msg, rinfo, (response, err) => {
if (!err) {
listeners.accounting.socket.send (response, 0, response.length, rinfo.port, rinfo.address, function (err, bytes) {
if (err) {
console.log ('Error sending response to ', rinfo);
}
});
}
});
});
listeners.accounting.socket.on ('listening', () => {
var address = listeners.accounting.socket.address ();
log.write ("Accounting listening on " + address.address + ":" + address.port);
});
listeners.accounting.socket.bind (config.ports.radius_accounting);
// HTTP listener
const respond = (res, content, status) => {
var type = "text/plain";
if (typeof (content) != "string") {
content = JSON.stringify (content);
type = "application/json";
}
res.writeHead (status, {
"Content-Type": type,
"Content-Length": content.length
}).end (content);
};
http.createServer (function (req, res) {
var url = req.url.substring (0, req.url.lastIndexOf ("/")) || req.url;
var endpoint = req.method + " " + url;
switch (endpoint) {
// Used for docker healthcheck
case "GET /health":
respond (res, "OK", 200);
break;
case "GET /users":
handlers.user.getall ((users, err) => {
if (err) {
respond (res, err, 404);
}
else {
respond (res, users, 200);
}
});
break;
case "GET /user":
handlers.user.getone (req.url.substring (req.url.lastIndexOf ("/") + 1), (user, err) => {
if (err) {
respond (res, err, 404);
}
else {
respond (res, user, 200);
}
});
break;
case "POST /user":
var payload = '';
req.on ('data', chunk => {
payload += chunk.toString ();
});
req.on ('end', () => {
handlers.user.create (payload, (status, err) => {
if (err) {
respond (res, err, 500);
}
else {
respond (res, status, 200);
}
});
});
break;
case "DELETE /user":
handlers.user.delete (req.url.substring (req.url.lastIndexOf ("/") + 1), (status, err) => {
if (err) {
respond (res, err, 404);
}
else {
respond (res, status, 200);
}
});
break;
default:
respond (res, "Not found", 404);
}
}).listen (config.ports.api);
log.write ("API listening on port " + config.ports.api);
// Exit handles
const exitHandler = () => {
log.write ('Shutting down');
listeners.authentication.socket.close ();
listeners.accounting.socket.close ();
log.write ('Exiting');
process.exit ();
}
process.on ('SIGTERM', exitHandler);
process.on ('SIGINT', exitHandler);
process.on ('SIGUSR1', exitHandler);
process.on ('SIGUSR2', exitHandler);