unique tagging of athena requests
parent
2f9d45fabc
commit
59305f2fc9
182
Anetha/index.js
182
Anetha/index.js
|
@ -8,16 +8,19 @@ const authenticationController = require('./../controllers/authentication');
|
|||
const deviceController = require('./../controllers/devices');
|
||||
const { ws } = require('../routes/api/realtime');
|
||||
|
||||
let wss;
|
||||
var abnf = require('abnf');
|
||||
|
||||
let wss;
|
||||
async function __server() {
|
||||
wss = new WebSocket.WebSocketServer({ path: '/ws/v2/', port: 4040, handshakeTimeout: 500});
|
||||
|
||||
wss = new WebSocket.WebSocketServer({ path: '/ws/v2/', port: 4040, handshakeTimeout: 500 });
|
||||
|
||||
console.log("src")
|
||||
|
||||
const interval = setInterval(function ping() {
|
||||
wss.clients.forEach(function each(ws) {
|
||||
if (ws.isAlive === false) {
|
||||
_actionLogger(null, null, "ATHENA_DEVICE_TIMEOUT_FORCE_DISCONNECT", null, ws._socket.remoteAddress, null, ws.dongleId);
|
||||
wss.retropilotFunc.actionLogger(null, null, "ATHENA_DEVICE_TIMEOUT_FORCE_DISCONNECT", null, ws._socket.remoteAddress, null, ws.dongleId);
|
||||
console.log("TERMINATED", ws.dongleId)
|
||||
return ws.terminate();
|
||||
}
|
||||
|
||||
|
@ -26,166 +29,123 @@ async function __server() {
|
|||
});
|
||||
}, 5000);
|
||||
|
||||
wss.on('connection', manageConnection)
|
||||
wss.on('connection', manageConnection)
|
||||
wss.on('close', function close() {
|
||||
clearInterval(interval);
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
const authenticateDongle = async (ws, res, cookies) => {
|
||||
unsafeJwt = jsonwebtoken.decode(cookies.jwt);
|
||||
const device = await deviceController.getDeviceFromDongle(unsafeJwt.identity)
|
||||
|
||||
let verifiedJWT;
|
||||
|
||||
try {
|
||||
verifiedJWT = jsonwebtoken.verify(cookies.jwt, device.public_key, {ignoreNotBefore: true});
|
||||
} catch (err) {
|
||||
_actionLogger(null, null, "ATHENA_DEVICE_AUTHENTICATE_INVALID", null, ws._socket.remoteAddress, JSON.stringify({jwt: cookies.jwt}), null);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (verifiedJWT.identify === unsafeJwt.identify) {
|
||||
|
||||
ws.dongleId = device.dongle_id
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function commandBuilder(method, params) {
|
||||
return { method, params, "jsonrpc": "2.0", "id": 0 }
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
models.models.athena_action_log.create({
|
||||
account_id, device_id, action, user_ip, device_ip, meta, created_at: Date.now(), dongle_id
|
||||
})
|
||||
}
|
||||
|
||||
async function manageConnection(ws, res) {
|
||||
async function manageConnection(ws, res) {
|
||||
ws.badMessages = 0;
|
||||
ws.isAlive = true;
|
||||
|
||||
ws.heartbeat = Date.now();
|
||||
|
||||
ws.on('pong', heartbeat);
|
||||
|
||||
var cookies = cookie.parse(res.headers.cookie);
|
||||
|
||||
|
||||
ws.on('message', function incoming(message) {
|
||||
ws.on('message', async function incoming(message) {
|
||||
heartbeat.call(ws)
|
||||
if (!ws.dongleId) {
|
||||
_actionLogger(null, null, "ATHENA_DEVICE_UNATHENTICATED_MESSAGE", null, ws._socket.remoteAddress, JSON.stringify([message]), ws.dongleId);
|
||||
console.log("unauthenticated message, discarded");
|
||||
return null;
|
||||
if (!ws.dongleId) {
|
||||
// wss.retropilotFunc.actionLogger(null, null, "ATHENA_DEVICE_UNATHENTICATED_MESSAGE", null, ws._socket.remoteAddress, JSON.stringify([message]), ws.dongleId);
|
||||
console.log("unauthenticated message, discarded");
|
||||
return null;
|
||||
}
|
||||
|
||||
console.log(message);
|
||||
|
||||
const json = JSON.parse(message.toString('utf8'))
|
||||
console.log(json);
|
||||
|
||||
models.models.athena_returned_data.create({
|
||||
device_id: ws.device_id,
|
||||
type: "UNKNOWN",
|
||||
data: JSON.stringify(message),
|
||||
created_at: Date.now()
|
||||
console.log({device_id: ws.device_id, uuid: json.id});
|
||||
|
||||
})
|
||||
console.log( await models.models.athena_returned_data.update({
|
||||
data: JSON.stringify(json),
|
||||
resolved_at: Date.now()
|
||||
}, {where: {device_id: ws.device_id, uuid: json.id}}))
|
||||
|
||||
_actionLogger(null, null, "ATHENA_DEVICE_MESSAGE_UNKNOWN", null, ws._socket.remoteAddress, JSON.stringify([message]), ws.dongleId);
|
||||
const buff = Buffer.from(JSON.stringify(message), 'utf-8');
|
||||
|
||||
wss.retropilotFunc.actionLogger(null, null, "ATHENA_DEVICE_MESSAGE_UNKNOWN", null, ws._socket.remoteAddress, JSON.stringify([message]), ws.dongleId);
|
||||
|
||||
console.log(json)
|
||||
|
||||
// decode buffer as UTF-8
|
||||
console.log(buff.toString('base64'))
|
||||
|
||||
});
|
||||
|
||||
|
||||
if (await authenticateDongle(ws, res, cookies) === false) {
|
||||
|
||||
if (await wss.retropilotFunc.authenticateDongle(ws, res, cookies) === false) {
|
||||
ws.close();
|
||||
}
|
||||
|
||||
//ws.send(JSON.stringify(await commandBuilder('reboot')))
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
__server();
|
||||
|
||||
wss.retropilotFunc = {
|
||||
|
||||
findFromDongle: (dongleId) => {
|
||||
let websocket = null;
|
||||
wss.clients.forEach((value) => {
|
||||
if (value.dongleId === dongleId) {
|
||||
websocket = value;
|
||||
}
|
||||
})
|
||||
|
||||
return websocket;
|
||||
},
|
||||
|
||||
function _findSocketFromDongle(dongleId) {
|
||||
authenticateDongle: async (ws, res, cookies) => {
|
||||
unsafeJwt = jsonwebtoken.decode(cookies.jwt);
|
||||
const device = await deviceController.getDeviceFromDongle(unsafeJwt.identity)
|
||||
|
||||
let websocket = null;
|
||||
wss.clients.forEach((value) => {
|
||||
if (value.dongleId === dongleId) {
|
||||
websocket = value;
|
||||
let verifiedJWT;
|
||||
|
||||
try {
|
||||
verifiedJWT = jsonwebtoken.verify(cookies.jwt, device.public_key, { ignoreNotBefore: true });
|
||||
} catch (err) {
|
||||
wss.retropilotFunc.actionLogger(null, null, "ATHENA_DEVICE_AUTHENTICATE_INVALID", null, ws._socket.remoteAddress, JSON.stringify({ jwt: cookies.jwt }), null);
|
||||
return false;
|
||||
}
|
||||
})
|
||||
|
||||
return websocket;
|
||||
}
|
||||
if (verifiedJWT.identify === unsafeJwt.identify) {
|
||||
ws.dongleId = device.dongle_id
|
||||
ws.device_id = device.id
|
||||
console.log("AUTHENTICATED DONGLE", ws.dongleId)
|
||||
|
||||
wss.retropilotFunc.actionLogger(null, device.id, "ATHENA_DEVICE_AUTHENTICATE_SUCCESS", null, ws._socket.remoteAddress, null);
|
||||
return true;
|
||||
} else {
|
||||
console.log("UNAUTHENTICATED DONGLE");
|
||||
|
||||
wss.retropilotFunc.actionLogger(null, device.id, "ATHENA_DEVICE_AUTHENTICATE_FAILURE", null, ws._socket.remoteAddress, JSON.stringify({ jwt: cookies.jwt }), null);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
function invoke(command, params, dongleId, accountId) {
|
||||
const websocket = _findSocketFromDongle(dongleId);
|
||||
commandBuilder: (method, params, id) => {
|
||||
return { method, params: params, "jsonrpc": "2.0", "id": id }
|
||||
},
|
||||
|
||||
if (!websocket) {
|
||||
_actionLogger(accountId, null, "ATHENA_USER_INVOKE__FAILED_DISCONNECTED", null, null, null, dongleId);
|
||||
return {connected: false}
|
||||
}
|
||||
|
||||
_actionLogger(accountId, websocket.device_id, "ATHENA_USER_INVOKE__ISSUED", null, websocket._socket.remoteAddress, JSON.stringify({command, params}), websocket.dongleId);
|
||||
|
||||
websocket.send(JSON.stringify(commandBuilder(command)))
|
||||
|
||||
return {dispatched: true, heartbeat: websocket.heartbeat}
|
||||
actionLogger: async (account_id, device_id, action, user_ip, device_ip, meta, dongle_id) => {
|
||||
models.models.athena_action_log.create({
|
||||
account_id, device_id, action, user_ip, device_ip, meta, created_at: Date.now(), dongle_id
|
||||
})
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
|
||||
function isDeviceConnected(accountId, deviceId, 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 {connected: false}
|
||||
|
||||
return {connected: true, heartbeat: websocket.heartbeat};
|
||||
}
|
||||
const helpers = require('./helpers')(wss)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
module.exports = {
|
||||
invoke,
|
||||
isDeviceConnected
|
||||
}
|
||||
module.exports = helpers;
|
|
@ -9,21 +9,29 @@ module.exports = (sequelize) => {
|
|||
type: DataTypes.INTEGER
|
||||
},
|
||||
device_id: {
|
||||
allowNull: false,
|
||||
allowNull: true,
|
||||
type: DataTypes.INTEGER
|
||||
},
|
||||
type: {
|
||||
allowNull: false,
|
||||
allowNull: true,
|
||||
type: DataTypes.TEXT
|
||||
},
|
||||
data: {
|
||||
allowNull: true,
|
||||
type: DataTypes.BLOB
|
||||
type: DataTypes.TEXT
|
||||
},
|
||||
created_at: {
|
||||
allowNull: true,
|
||||
type: DataTypes.TEXT
|
||||
},
|
||||
uuid: {
|
||||
allowNull: false,
|
||||
type: DataTypes.TEXT
|
||||
},
|
||||
resolved_at: {
|
||||
allowNull: true,
|
||||
type: DataTypes.INTEGER
|
||||
}
|
||||
|
||||
}, {
|
||||
timestamps: false,
|
||||
|
|
|
@ -3,7 +3,8 @@ const sequelize = new Sequelize({
|
|||
dialect: 'sqlite',
|
||||
storage: 'database.sqlite',
|
||||
logQueryParameters: true,
|
||||
benchmark: true
|
||||
benchmark: true,
|
||||
logging: false
|
||||
});
|
||||
|
||||
const modelDefiners = [
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
"dependencies": {
|
||||
"@commaai/log_reader": "^0.8.0",
|
||||
"@sendgrid/client": "^7.4.3",
|
||||
"abnf": "0.0.5",
|
||||
"chai": "^4.3.4",
|
||||
"chai-http": "^4.3.0",
|
||||
"cookie": "^0.4.1",
|
||||
|
@ -40,6 +41,7 @@
|
|||
"sqlite": "^4.0.22",
|
||||
"sqlite3": "^5.0.2",
|
||||
"supertest": "^6.1.3",
|
||||
"uuid": "^8.3.2",
|
||||
"websocket": "^1.0.34",
|
||||
"ws": "^8.2.3"
|
||||
}
|
||||
|
|
|
@ -44,8 +44,7 @@ router.get('/dongle/:dongle_id/connected', 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}})}
|
||||
|
@ -57,6 +56,8 @@ router.get('/dongle/:dongle_id/send/:method', async (req, res) => {
|
|||
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.get('/dongle/:dongle_id/get', async (req, res) => {
|
||||
|
@ -71,5 +72,19 @@ router.get('/dongle/:dongle_id/get', async (req, res) => {
|
|||
res.json(await models.models.athena_returned_data.findAll({where: {device_id: device.id}}))
|
||||
})
|
||||
|
||||
router.get('/dongle/:dongle_id/temp/nav/:lat/:long', async (req, res) => {
|
||||
if (!req.params.lat || !req.params.long) { return res.status(403).json({error: true, errorMsg: 'Malformed_Request', errorObject: {malformed: true}})}
|
||||
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}})}
|
||||
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 data = await req.athenaWebsocketTemp.invoke("setNavDestination", {latitude: req.params.lat, longitude: req.params.long}, device.dongle_id, account.id);
|
||||
|
||||
return res.status(200).json({success: true, dongle_id: device.dongle_id, method: req.params.method, data: data});
|
||||
})
|
||||
|
||||
|
||||
module.exports = router
|
Loading…
Reference in New Issue