171 lines
5.4 KiB
JavaScript
Executable File
171 lines
5.4 KiB
JavaScript
Executable File
// Baseline
|
|
const product = 'Sinatra';
|
|
const version = '0.4.1';
|
|
|
|
// 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 = { ports: {} };
|
|
config.ports.radius_authentication = process.env['SINATRA_PORT_RADIUS_AUTH'] || 1812;
|
|
config.ports.radius_accounting = process.env['SINATRA_PORT_RADIUS_ACCT'] || 1813;
|
|
config.ports.api = process.env['SINATRA_PORT_API'] || 8088;
|
|
config.storage = process.env['SINATRA_STORAGE'] || "json:./data.json";
|
|
config.client_secret = process.env['SINATRA_CLIENT_SECRET'] || "password";
|
|
config.default_vlan = process.env['SINATRA_DEFAULT_VLAN'] || false;
|
|
config.mac_auth_only = process.env['SINATRA_MAC_AUTH_ONLY'] || false;
|
|
config.session_duration = process.env['SINATRA_SESSION_DURATION'] || 600;
|
|
config.time_rules = process.env['SINATRA_TIME_RULES'] || false;
|
|
|
|
// 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);
|