Athena, logging, etc
parent
32325111c6
commit
f4cb1ef142
|
@ -51,13 +51,17 @@ const authenticateDongle = async (ws, res, cookies) => {
|
||||||
|
|
||||||
|
|
||||||
if (verifiedJWT.identify === unsafeJwt.identify) {
|
if (verifiedJWT.identify === unsafeJwt.identify) {
|
||||||
|
|
||||||
ws.dongleId = device.dongle_id
|
ws.dongleId = device.dongle_id
|
||||||
_actionLogger(null, device.id, "ATHENA_DEVICE_AUTHENTICATE_SUCCESS", null, ws._socket.remoteAddress, null);
|
ws.device_id = device.id
|
||||||
console.log("AUTHENTICATED DONGLE", ws.dongleId )
|
console.log("AUTHENTICATED DONGLE", ws.dongleId )
|
||||||
|
|
||||||
|
_actionLogger(null, device.id, "ATHENA_DEVICE_AUTHENTICATE_SUCCESS", null, ws._socket.remoteAddress, null);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
console.log("UNAUTHENTICATED DONGLE");
|
||||||
|
|
||||||
_actionLogger(null, device.id, "ATHENA_DEVICE_AUTHENTICATE_FAILURE", null, ws._socket.remoteAddress, JSON.stringify({jwt: cookies.jwt}), null);
|
_actionLogger(null, device.id, "ATHENA_DEVICE_AUTHENTICATE_FAILURE", null, ws._socket.remoteAddress, JSON.stringify({jwt: cookies.jwt}), null);
|
||||||
console.log("UNAUTHENTICATED DONGLE");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,8 +73,14 @@ function commandBuilder(method, params) {
|
||||||
|
|
||||||
|
|
||||||
async function heartbeat() {
|
async function heartbeat() {
|
||||||
|
|
||||||
|
if (this.heartbeat - Date.now() > 300) {
|
||||||
|
deviceController.updateLastPing(this.device_id, this.dongleId);
|
||||||
|
}
|
||||||
|
|
||||||
this.isAlive = true;
|
this.isAlive = true;
|
||||||
this.heartbeat = Date.now();
|
this.heartbeat = Date.now();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function _actionLogger(account_id, device_id, action, user_ip, device_ip, meta, dongle_id) {
|
async function _actionLogger(account_id, device_id, action, user_ip, device_ip, meta, dongle_id) {
|
||||||
|
@ -82,6 +92,7 @@ async function _actionLogger(account_id, device_id, action, user_ip, device_ip,
|
||||||
async function manageConnection(ws, res) {
|
async function manageConnection(ws, res) {
|
||||||
ws.badMessages = 0;
|
ws.badMessages = 0;
|
||||||
ws.isAlive = true;
|
ws.isAlive = true;
|
||||||
|
|
||||||
ws.heartbeat = Date.now();
|
ws.heartbeat = Date.now();
|
||||||
|
|
||||||
ws.on('pong', heartbeat);
|
ws.on('pong', heartbeat);
|
||||||
|
@ -98,8 +109,21 @@ async function manageConnection(ws, res) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
models.models.athena_returned_data.create({
|
||||||
|
device_id: ws.device_id,
|
||||||
|
type: "UNKNOWN",
|
||||||
|
data: JSON.stringify(message),
|
||||||
|
created_at: Date.now()
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
_actionLogger(null, null, "ATHENA_DEVICE_MESSAGE_UNKNOWN", null, ws._socket.remoteAddress, JSON.stringify([message]), ws.dongleId);
|
_actionLogger(null, null, "ATHENA_DEVICE_MESSAGE_UNKNOWN", null, ws._socket.remoteAddress, JSON.stringify([message]), ws.dongleId);
|
||||||
console.log("unknown message", JSON.stringify(message))
|
const buff = Buffer.from(JSON.stringify(message), 'utf-8');
|
||||||
|
|
||||||
|
// decode buffer as UTF-8
|
||||||
|
console.log(buff.toString('base64'))
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -131,40 +155,26 @@ function _findSocketFromDongle(dongleId) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function invoke(command, params, dongleId, accountId) {
|
||||||
// TODO - This is dumb
|
|
||||||
function rebootDevice(dongleId, accountId) {
|
|
||||||
const websocket = _findSocketFromDongle(dongleId);
|
const websocket = _findSocketFromDongle(dongleId);
|
||||||
|
|
||||||
|
|
||||||
if (!websocket) {
|
if (!websocket) {
|
||||||
return _actionLogger(accountId, null, "ATHENA_USER_INVOKE__REBOOT_FAILED_DISCONNECTED", null, null, null, ws.dongleId);
|
_actionLogger(accountId, null, "ATHENA_USER_INVOKE__FAILED_DISCONNECTED", null, null, null, dongleId);
|
||||||
|
return {connected: false}
|
||||||
}
|
}
|
||||||
|
|
||||||
_actionLogger(accountId, null, "ATHENA_USER_INVOKE__REBOOT_ISSUED", null, ws._socket.remoteAddress, null, ws.dongleId);
|
_actionLogger(accountId, websocket.device_id, "ATHENA_USER_INVOKE__ISSUED", null, websocket._socket.remoteAddress, JSON.stringify({command, params}), websocket.dongleId);
|
||||||
|
|
||||||
websocket.send(JSON.stringify(commandBuilder('reboot')))
|
websocket.send(JSON.stringify(commandBuilder(command)))
|
||||||
|
|
||||||
|
return {dispatched: true, heartbeat: websocket.heartbeat}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function getDetails(dongleId, accountId) {
|
function isDeviceConnected(accountId, deviceId, dongleId ) {
|
||||||
const websocket = _findSocketFromDongle(dongleId);
|
const websocket = _findSocketFromDongle(dongleId);
|
||||||
|
_actionLogger(accountId, deviceId, "ATHENA_USER_STATUS__IS_CONNECTED", null, websocket ? websocket._socket.remoteAddress : null, JSON.stringify({connected: websocket ? true : false, heartbeat: websocket ? websocket.heartbeat : null}), dongleId);
|
||||||
if (!websocket) {
|
|
||||||
return _actionLogger(accountId, null, "ATHENA_USER_INVOKE__GETVERSION_FAILED_DISCONNECTED", null, null, null, ws.dongleId);
|
|
||||||
}
|
|
||||||
|
|
||||||
_actionLogger(accountId, null, "ATHENA_USER_INVOKE__GETVERSION_ISSUED", null, ws._socket.remoteAddress, null, ws.dongleId);
|
|
||||||
|
|
||||||
websocket.send(JSON.stringify(commandBuilder('getVersion')))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function isDeviceConnected(dongleId, accountId) {
|
|
||||||
const websocket = _findSocketFromDongle(dongleId);
|
|
||||||
_actionLogger(null, null, "ATHENA_USER_STATUS__IS_CONNECTED", null, websocket ? websocket._socket.remoteAddress : null, JSON.stringify({connected: websocket ? true : false, heartbeat: websocket ? websocket.heartbeat : null}), dongleId);
|
|
||||||
|
|
||||||
if (!websocket) return {connected: false}
|
if (!websocket) return {connected: false}
|
||||||
|
|
||||||
|
@ -176,7 +186,6 @@ function isDeviceConnected(dongleId, accountId) {
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
rebootDevice,
|
invoke,
|
||||||
isDeviceConnected,
|
isDeviceConnected
|
||||||
getDetails
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,16 @@ var config = {
|
||||||
|
|
||||||
flags: {
|
flags: {
|
||||||
useUserAdminApi: false,
|
useUserAdminApi: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
athena: {
|
||||||
|
enabled: true, // Enables Athena service
|
||||||
|
api: {
|
||||||
|
ratelimit: 100 // Maxmium hits to /realtime/* per 30s
|
||||||
|
},
|
||||||
|
socket: {
|
||||||
|
heartbeatFrequency: 5000 // Higher the number = lower traffic, varies on how many devices are connected
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ const config = require('./../config');
|
||||||
const authenticationController = require('./authentication');
|
const authenticationController = require('./authentication');
|
||||||
const models_orm = require('./../models/index.model')
|
const models_orm = require('./../models/index.model')
|
||||||
const sanitize = require('sanitize')();
|
const sanitize = require('sanitize')();
|
||||||
|
const { Op } = require('sequelize')
|
||||||
|
|
||||||
|
|
||||||
async function pairDevice(account, qr_string) {
|
async function pairDevice(account, qr_string) {
|
||||||
|
@ -116,6 +117,11 @@ async function getAllDevicesFiltered() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function updateLastPing(device_id, dongle_id) {
|
||||||
|
models_orm.models.device.update({ last_ping: Date.now() }, {where: {[Op.or] : [{id: device_id}, {dongle_id: dongle_id}]}})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
pairDevice: pairDevice,
|
pairDevice: pairDevice,
|
||||||
unpairDevice: unpairDevice,
|
unpairDevice: unpairDevice,
|
||||||
|
@ -125,4 +131,5 @@ module.exports = {
|
||||||
setIgnoredUploads,
|
setIgnoredUploads,
|
||||||
getAllDevicesFiltered,
|
getAllDevicesFiltered,
|
||||||
pairDeviceToAccountId,
|
pairDeviceToAccountId,
|
||||||
|
updateLastPing
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,16 +8,12 @@ module.exports = (sequelize) => {
|
||||||
primaryKey: true,
|
primaryKey: true,
|
||||||
type: DataTypes.INTEGER
|
type: DataTypes.INTEGER
|
||||||
},
|
},
|
||||||
account_id: {
|
device_id: {
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
type: DataTypes.INTEGER
|
type: DataTypes.INTEGER
|
||||||
},
|
},
|
||||||
device_id: {
|
|
||||||
allowNull: true,
|
|
||||||
type: DataTypes.INTEGER
|
|
||||||
},
|
|
||||||
type: {
|
type: {
|
||||||
allowNull: true,
|
allowNull: false,
|
||||||
type: DataTypes.TEXT
|
type: DataTypes.TEXT
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
"esm": "^3.2.25",
|
"esm": "^3.2.25",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"express-fileupload": "^1.2.1",
|
"express-fileupload": "^1.2.1",
|
||||||
|
"express-rate-limit": "^5.5.0",
|
||||||
"express-ws": "^5.0.2",
|
"express-ws": "^5.0.2",
|
||||||
"fast-folder-size": "^1.3.0",
|
"fast-folder-size": "^1.3.0",
|
||||||
"ffprobe": "^1.1.2",
|
"ffprobe": "^1.1.2",
|
||||||
|
|
|
@ -3,26 +3,29 @@ const router = require('express').Router();
|
||||||
const authenticationController = require('./../../controllers/authentication');
|
const authenticationController = require('./../../controllers/authentication');
|
||||||
const userController = require('./../../controllers/users');
|
const userController = require('./../../controllers/users');
|
||||||
const deviceController = require('./../../controllers/devices')
|
const deviceController = require('./../../controllers/devices')
|
||||||
|
const models = require('./../../models/index.model')
|
||||||
|
|
||||||
const whitelistParams = {
|
const whitelistParams = {
|
||||||
getMessage: false,
|
getmessage: false,
|
||||||
getVersion: true,
|
getversion: true,
|
||||||
setNavDestination: true,
|
setnavdestination: true,
|
||||||
listDataDirectory: false,
|
listdatadirectory: false,
|
||||||
reboot: true,
|
reboot: true,
|
||||||
uploadFileToUrl: false,
|
uploadfiletourl: false,
|
||||||
listUploadQueue: true,
|
listuploadqueue: true,
|
||||||
cancelUpload: true,
|
cancelupload: true,
|
||||||
primeActivated: false,
|
primeactivated: true,
|
||||||
getPublicKey: true,
|
getpublickey: true,
|
||||||
getSshAuthorizedKeys: true,
|
getsshauthorizedkeys: true,
|
||||||
getSimInfo: true,
|
getsiminfo: true,
|
||||||
getNetworkType: true,
|
getnetworktype: true,
|
||||||
getNetworks: true,
|
getnetworks: true,
|
||||||
takeSnapshot: true
|
takesnapshot: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
router.get('/dongle/:dongle_id/connected', async (req, res) => {
|
router.get('/dongle/:dongle_id/connected', async (req, res) => {
|
||||||
const account = await authenticationController.getAuthenticatedAccount(req, res);
|
const account = await authenticationController.getAuthenticatedAccount(req, res);
|
||||||
if (account == null) { return res.status(403).json({error: true, errorMsg: 'Unauthenticated', errorObject: {authenticated: false}})}
|
if (account == null) { return res.status(403).json({error: true, errorMsg: 'Unauthenticated', errorObject: {authenticated: false}})}
|
||||||
|
@ -33,14 +36,17 @@ router.get('/dongle/:dongle_id/connected', async (req, res) => {
|
||||||
// TODO remove indication of dongle existing
|
// TODO remove indication of dongle existing
|
||||||
if (device.account_id !== account.id) {return res.status(403).json({error: true, errorMsg: 'unauthorised', errorObject: {authenticated: true, dongle_exists: true, authorised_user: false}})}
|
if (device.account_id !== account.id) {return res.status(403).json({error: true, errorMsg: 'unauthorised', errorObject: {authenticated: true, dongle_exists: true, authorised_user: false}})}
|
||||||
|
|
||||||
const deviceConnected = await req.athenaWebsocketTemp.isDeviceConnected(device.dongle_id);
|
const deviceConnected = await req.athenaWebsocketTemp.isDeviceConnected(device.id, account.id, device.dongle_id);
|
||||||
|
|
||||||
return res.status(200).json({success: true, dongle_id: device.dongle_id, data: deviceConnected});
|
return res.status(200).json({success: true, dongle_id: device.dongle_id, data: deviceConnected});
|
||||||
})
|
})
|
||||||
|
|
||||||
router.get('/dongle/:dongle_id/getDetails', async (req, res) => {
|
router.get('/dongle/:dongle_id/send/:method', async (req, res) => {
|
||||||
const account = await authenticationController.getAuthenticatedAccount(req, res);
|
const account = await authenticationController.getAuthenticatedAccount(req, res);
|
||||||
if (account == null) { return res.status(403).json({error: true, errorMsg: 'Unauthenticated', errorObject: {authenticated: false}})}
|
if (account == null) { return res.status(403).json({error: true, errorMsg: 'Unauthenticated', errorObject: {authenticated: false}})}
|
||||||
|
console.log(req.params.method.toLowerCase());
|
||||||
|
console.log(whitelistParams[req.params.method.toLowerCase()])
|
||||||
|
if (!whitelistParams[req.params.method.toLowerCase()]) { return res.status(409).json({error: true, errorMsg: 'invalid_method'}) }
|
||||||
const device = await deviceController.getDeviceFromDongle(req.params.dongle_id);
|
const device = await deviceController.getDeviceFromDongle(req.params.dongle_id);
|
||||||
if (!device) {return res.status(400).json({error: true, errorMsg: 'no_dongle', errorObject: {authenticated: true, dongle_exists: false}})}
|
if (!device) {return res.status(400).json({error: true, errorMsg: 'no_dongle', errorObject: {authenticated: true, dongle_exists: false}})}
|
||||||
|
|
||||||
|
@ -48,28 +54,22 @@ router.get('/dongle/:dongle_id/getDetails', async (req, res) => {
|
||||||
// TODO remove indication of dongle existing
|
// TODO remove indication of dongle existing
|
||||||
if (device.account_id !== account.id) {return res.status(403).json({error: true, errorMsg: 'unauthorised', errorObject: {authenticated: true, dongle_exists: true, authorised_user: false}})}
|
if (device.account_id !== account.id) {return res.status(403).json({error: true, errorMsg: 'unauthorised', errorObject: {authenticated: true, dongle_exists: true, authorised_user: false}})}
|
||||||
|
|
||||||
const deviceConnected = await req.athenaWebsocketTemp.getDetails(device.dongle_id);
|
const data = await req.athenaWebsocketTemp.invoke(req.params.method, null, device.dongle_id, account.id);
|
||||||
|
|
||||||
// return res.status(200).json({success: true, dongle_id: device.dongle_id, data: deviceConnected});
|
return res.status(200).json({success: true, dongle_id: device.dongle_id, method: req.params.method, data: data});
|
||||||
})
|
})
|
||||||
|
|
||||||
|
router.get('/dongle/:dongle_id/get', async (req, res) => {
|
||||||
/*router.post('/dongle/:dongle_id/raw', bodyParser.urlencoded({extended: true}), async (req, res) => {
|
|
||||||
if (!req.body.hasOwnProperty('method') { return res.status(403).json({error: true, errorMsg: 'missing_data', errorObject: {missing: [method]}})}
|
|
||||||
const account = await authenticationController.getAuthenticatedAccount(req, res);
|
const account = await authenticationController.getAuthenticatedAccount(req, res);
|
||||||
if (account == null) { return res.status(403).json({error: true, errorMsg: 'Unauthenticated', errorObject: {authenticated: false}})}
|
if (account == null) { return res.status(403).json({error: true, errorMsg: 'Unauthenticated', errorObject: {authenticated: false}})}
|
||||||
const device = await deviceController.getDeviceFromDongle(req.params.dongle_id);
|
const device = await deviceController.getDeviceFromDongle(req.params.dongle_id);
|
||||||
if (!device) {return res.status(400).json({error: true, errorMsg: 'no_dongle', errorObject: {authenticated: true, dongle_exists: false}})}
|
if (!device) {return res.status(400).json({error: true, errorMsg: 'no_dongle', errorObject: {authenticated: true, dongle_exists: false}})}
|
||||||
|
|
||||||
// TODO support delgation of access
|
|
||||||
// TODO remove indication of dongle existing
|
|
||||||
if (device.account_id !== account.id) {return res.status(403).json({error: true, errorMsg: 'unauthorised', errorObject: {authenticated: true, dongle_exists: true, authorised_user: false}})}
|
if (device.account_id !== account.id) {return res.status(403).json({error: true, errorMsg: 'unauthorised', errorObject: {authenticated: true, dongle_exists: true, authorised_user: false}})}
|
||||||
|
|
||||||
const deviceConnected = await req.athenaWebsocketTemp.raw(req.body.method, req.body.params);
|
|
||||||
|
|
||||||
return res.status(200).json({success: true, dongle_id: device.dongle_id, data: deviceConnected});
|
|
||||||
})*/
|
|
||||||
|
|
||||||
|
res.json(await models.models.athena_returned_data.findAll({where: {device_id: device.id}}))
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
module.exports = router
|
module.exports = router
|
30
server.js
30
server.js
|
@ -7,6 +7,7 @@ const https = require('https');
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const cors = require('cors');
|
const cors = require('cors');
|
||||||
const cookieParser = require('cookie-parser');
|
const cookieParser = require('cookie-parser');
|
||||||
|
const rateLimit = require("express-rate-limit");
|
||||||
|
|
||||||
log4js.configure({
|
log4js.configure({
|
||||||
appenders: {logfile: {type: "file", filename: "server.log"}, out: {type: 'console'} /*{type: "file", filename: "server1.log"}*/},
|
appenders: {logfile: {type: "file", filename: "server.log"}, out: {type: 'console'} /*{type: "file", filename: "server1.log"}*/},
|
||||||
|
@ -22,6 +23,7 @@ let models = require('./models/index');
|
||||||
let models_sqli = require('./models/index.model');
|
let models_sqli = require('./models/index.model');
|
||||||
let controllers = require('./controllers');
|
let controllers = require('./controllers');
|
||||||
let routers = require('./routes')
|
let routers = require('./routes')
|
||||||
|
const athena = require('./Anetha/index');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -40,9 +42,13 @@ function runAsyncWrapper(callback) {
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
|
|
||||||
|
const athenaRateLimit = rateLimit({
|
||||||
|
windowMs: 30000,
|
||||||
|
max: config.athena.api.ratelimit
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const athena = require('./Anetha/index');
|
|
||||||
|
|
||||||
|
|
||||||
const web = async () => {
|
const web = async () => {
|
||||||
|
@ -62,15 +68,21 @@ const web = async () => {
|
||||||
app.use(routers.api);
|
app.use(routers.api);
|
||||||
app.use(routers.useradmin);
|
app.use(routers.useradmin);
|
||||||
|
|
||||||
app.use((req, res, next) => {
|
if (config.athena.enabled) {
|
||||||
|
app.use((req, res, next) => {
|
||||||
|
req.athenaWebsocketTemp = athena;
|
||||||
|
return next();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
app.use('/admin', routers.admin);
|
||||||
|
app.use('/realtime', athenaRateLimit);
|
||||||
|
app.use('/realtime', routers.realtime);
|
||||||
|
} else {
|
||||||
|
logger.log("Athena disabled");
|
||||||
|
}
|
||||||
|
|
||||||
req.athenaWebsocketTemp = athena;
|
|
||||||
return next();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
app.use('/admin', routers.admin);
|
|
||||||
app.use('/realtime', routers.realtime);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue