Athena, logging, etc

pull/4/head
AdamSBlack 2021-10-29 21:45:15 +01:00
parent 32325111c6
commit f4cb1ef142
7 changed files with 107 additions and 72 deletions

View File

@ -51,13 +51,17 @@ const authenticateDongle = async (ws, res, cookies) => {
if (verifiedJWT.identify === unsafeJwt.identify) {
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 )
_actionLogger(null, device.id, "ATHENA_DEVICE_AUTHENTICATE_SUCCESS", null, ws._socket.remoteAddress, null);
return true;
} else {
console.log("UNAUTHENTICATED DONGLE");
_actionLogger(null, device.id, "ATHENA_DEVICE_AUTHENTICATE_FAILURE", null, ws._socket.remoteAddress, JSON.stringify({jwt: cookies.jwt}), null);
console.log("UNAUTHENTICATED DONGLE");
return false;
}
}
@ -69,8 +73,14 @@ function commandBuilder(method, params) {
async function heartbeat() {
if (this.heartbeat - Date.now() > 300) {
deviceController.updateLastPing(this.device_id, this.dongleId);
}
this.isAlive = true;
this.heartbeat = Date.now();
}
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) {
ws.badMessages = 0;
ws.isAlive = true;
ws.heartbeat = Date.now();
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);
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) {
// TODO - This is dumb
function rebootDevice(dongleId, accountId) {
function invoke(command, params, dongleId, accountId) {
const websocket = _findSocketFromDongle(dongleId);
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);
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);
_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 {connected: false}
@ -176,7 +186,6 @@ function isDeviceConnected(dongleId, accountId) {
module.exports = {
rebootDevice,
isDeviceConnected,
getDetails
invoke,
isDeviceConnected
}

View File

@ -37,6 +37,16 @@ var config = {
flags: {
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
}
}
};

View File

@ -3,6 +3,7 @@ const config = require('./../config');
const authenticationController = require('./authentication');
const models_orm = require('./../models/index.model')
const sanitize = require('sanitize')();
const { Op } = require('sequelize')
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 = {
pairDevice: pairDevice,
unpairDevice: unpairDevice,
@ -125,4 +131,5 @@ module.exports = {
setIgnoredUploads,
getAllDevicesFiltered,
pairDeviceToAccountId,
updateLastPing
}

View File

@ -8,16 +8,12 @@ module.exports = (sequelize) => {
primaryKey: true,
type: DataTypes.INTEGER
},
account_id: {
device_id: {
allowNull: false,
type: DataTypes.INTEGER
},
device_id: {
allowNull: true,
type: DataTypes.INTEGER
},
type: {
allowNull: true,
allowNull: false,
type: DataTypes.TEXT
},
data: {

View File

@ -22,6 +22,7 @@
"esm": "^3.2.25",
"express": "^4.17.1",
"express-fileupload": "^1.2.1",
"express-rate-limit": "^5.5.0",
"express-ws": "^5.0.2",
"fast-folder-size": "^1.3.0",
"ffprobe": "^1.1.2",

View File

@ -3,26 +3,29 @@ const router = require('express').Router();
const authenticationController = require('./../../controllers/authentication');
const userController = require('./../../controllers/users');
const deviceController = require('./../../controllers/devices')
const models = require('./../../models/index.model')
const whitelistParams = {
getMessage: false,
getVersion: true,
setNavDestination: true,
listDataDirectory: false,
getmessage: false,
getversion: true,
setnavdestination: true,
listdatadirectory: false,
reboot: true,
uploadFileToUrl: false,
listUploadQueue: true,
cancelUpload: true,
primeActivated: false,
getPublicKey: true,
getSshAuthorizedKeys: true,
getSimInfo: true,
getNetworkType: true,
getNetworks: true,
takeSnapshot: true
uploadfiletourl: false,
listuploadqueue: true,
cancelupload: true,
primeactivated: true,
getpublickey: true,
getsshauthorizedkeys: true,
getsiminfo: true,
getnetworktype: true,
getnetworks: true,
takesnapshot: true
}
router.get('/dongle/:dongle_id/connected', async (req, res) => {
const account = await authenticationController.getAuthenticatedAccount(req, res);
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
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});
})
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);
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);
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
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);
// return res.status(200).json({success: true, dongle_id: device.dongle_id, data: deviceConnected});
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, method: req.params.method, data: data});
})
/*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]}})}
router.get('/dongle/:dongle_id/get', async (req, res) => {
const account = await authenticationController.getAuthenticatedAccount(req, res);
if (account == null) { return res.status(403).json({error: true, errorMsg: 'Unauthenticated', errorObject: {authenticated: false}})}
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}})}
// 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}})}
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

View File

@ -7,6 +7,7 @@ const https = require('https');
const express = require('express');
const cors = require('cors');
const cookieParser = require('cookie-parser');
const rateLimit = require("express-rate-limit");
log4js.configure({
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 controllers = require('./controllers');
let routers = require('./routes')
const athena = require('./Anetha/index');
@ -40,9 +42,13 @@ function runAsyncWrapper(callback) {
const app = express();
const athenaRateLimit = rateLimit({
windowMs: 30000,
max: config.athena.api.ratelimit
});
const athena = require('./Anetha/index');
const web = async () => {
@ -62,15 +68,21 @@ const web = async () => {
app.use(routers.api);
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);