various
parent
8d435a3cbc
commit
dc9e96ca3d
|
@ -3,63 +3,87 @@ const fs = require('fs');
|
|||
const cookie = require('cookie')
|
||||
const jsonwebtoken = require('jsonwebtoken');
|
||||
const models = require('./../models/index.model')
|
||||
const config = require('./../config')
|
||||
const httpsServer = require('https');
|
||||
const httpServer = require('http');
|
||||
const { readFileSync } = require('fs');
|
||||
|
||||
const authenticationController = require('./../controllers/authentication');
|
||||
const deviceController = require('./../controllers/devices');
|
||||
const { ws } = require('../routes/api/realtime');
|
||||
const log4js = require('log4js');
|
||||
|
||||
const logger = log4js.getLogger('default');
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
let wss;
|
||||
async function __server() {
|
||||
wss = new WebSocket.WebSocketServer({ path: '/ws/v2/', port: 4040, handshakeTimeout: 500 });
|
||||
function __server() {
|
||||
let server;
|
||||
|
||||
console.log("src")
|
||||
if (config.athena.secure) {
|
||||
server = httpsServer.createServer({
|
||||
cert: readFileSync(config.sslCrt),
|
||||
key: readFileSync(config.sslKey)
|
||||
});
|
||||
} else {
|
||||
server = httpServer.createServer();
|
||||
}
|
||||
|
||||
const interval = setInterval(function ping() {
|
||||
wss = new WebSocket.WebSocketServer({ server }, { path: '/ws/v2/', handshakeTimeout: 500 });
|
||||
|
||||
const interval = setInterval(() => {
|
||||
wss.clients.forEach(function each(ws) {
|
||||
if (ws.isAlive === false) {
|
||||
logger.info(`Athena(Heartbeat) - Terminated ${ws.dongleId} - ${ws._socket.remoteAddress}`)
|
||||
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();
|
||||
}
|
||||
|
||||
ws.isAlive = false;
|
||||
ws.ping();
|
||||
});
|
||||
}, 5000);
|
||||
}, config.athena.socket.heartbeatFrequency ? config.athena.socket.heartbeatFrequency : 5000);
|
||||
|
||||
|
||||
server.listen(config.athena.socket.port, () => {
|
||||
logger.info(`Athena(Server) - UP @ ${config.athena.host}:${config.athena.port}`)
|
||||
})
|
||||
|
||||
|
||||
wss.on('connection', manageConnection)
|
||||
wss.on('close', function close() {
|
||||
logger.info(`Athena(Websocket) - DOWN`)
|
||||
clearInterval(interval);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
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 manageConnection(ws, res) {
|
||||
logger.info(`Athena(Websocket) - New Connection ${ws._socket.remoteAddress}`)
|
||||
ws.badMessages = 0;
|
||||
ws.isAlive = true;
|
||||
ws.heartbeat = Date.now();
|
||||
ws.on('pong', heartbeat);
|
||||
|
||||
|
||||
var cookies = cookie.parse(res.headers.cookie);
|
||||
|
||||
ws.on('message', async function incoming(message) {
|
||||
heartbeat.call(ws)
|
||||
if (!ws.dongleId) {
|
||||
// wss.retropilotFunc.actionLogger(null, null, "ATHENA_DEVICE_UNATHENTICATED_MESSAGE", null, ws._socket.remoteAddress, JSON.stringify([message]), 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);
|
||||
|
||||
|
@ -80,15 +104,16 @@ async function manageConnection(ws, res) {
|
|||
|
||||
|
||||
if (await wss.retropilotFunc.authenticateDongle(ws, res, cookies) === false) {
|
||||
ws.close();
|
||||
ws.terminate();
|
||||
}
|
||||
|
||||
|
||||
//ws.send(JSON.stringify(await commandBuilder('reboot')))
|
||||
}
|
||||
|
||||
|
||||
|
||||
__server();
|
||||
__server();
|
||||
|
||||
wss.retropilotFunc = {
|
||||
|
||||
|
@ -100,11 +125,23 @@ wss.retropilotFunc = {
|
|||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
return websocket;
|
||||
},
|
||||
|
||||
authenticateDongle: async (ws, res, cookies) => {
|
||||
unsafeJwt = jsonwebtoken.decode(cookies.jwt);
|
||||
|
||||
|
||||
try {
|
||||
unsafeJwt = jsonwebtoken.decode(cookies.jwt);
|
||||
} catch(e) {
|
||||
logger.info(`Athena(Websocket) - AUTHENTICATION FAILED (INVALID JWT) IP: ${ws._socket.remoteAddress}`)
|
||||
wss.retropilotFunc.actionLogger(null, null, "ATHENA_DEVICE_AUTHENTICATE_INVALID", null, ws._socket.remoteAddress, JSON.stringify({ jwt: cookies.jwt }), null);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
const device = await deviceController.getDeviceFromDongle(unsafeJwt.identity)
|
||||
|
||||
let verifiedJWT;
|
||||
|
@ -112,6 +149,7 @@ wss.retropilotFunc = {
|
|||
try {
|
||||
verifiedJWT = jsonwebtoken.verify(cookies.jwt, device.public_key, { ignoreNotBefore: true });
|
||||
} catch (err) {
|
||||
logger.info(`Athena(Websocket) - AUTHENTICATION FAILED (BAD JWT, CHECK SIGNATURE) IP: ${ws._socket.remoteAddress}`)
|
||||
wss.retropilotFunc.actionLogger(null, null, "ATHENA_DEVICE_AUTHENTICATE_INVALID", null, ws._socket.remoteAddress, JSON.stringify({ jwt: cookies.jwt }), null);
|
||||
return false;
|
||||
}
|
||||
|
@ -119,14 +157,13 @@ wss.retropilotFunc = {
|
|||
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);
|
||||
logger.info(`Athena(Websocket) - AUTHENTICATED IP: ${ws._socket.remoteAddress} DONGLE ID: ${ws.dongleId} DEVICE ID: ${ws.device_id}`)
|
||||
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);
|
||||
logger.info(`Athena(Websocket) - AUTHENTICATION FAILED (BAD CREDENTIALS) IP: ${ws._socket.remoteAddress}`);
|
||||
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
|
|
@ -41,10 +41,12 @@ var config = {
|
|||
|
||||
athena: {
|
||||
enabled: true, // Enables Athena service
|
||||
secure: true, // Disables crypto on Websocket server - use for testing on local network, change ATHENA_HOST in openpilot to ws:// instead of wss://
|
||||
api: {
|
||||
ratelimit: 100 // Maxmium hits to /realtime/* per 30s
|
||||
},
|
||||
socket: {
|
||||
port: 4040,
|
||||
heartbeatFrequency: 5000 // Higher the number = lower traffic, varies on how many devices are connected
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,12 +6,12 @@ const deviceController = require('./../../controllers/devices')
|
|||
const models = require('./../../models/index.model')
|
||||
|
||||
const whitelistParams = {
|
||||
getmessage: false,
|
||||
getmessage: true,
|
||||
getversion: true,
|
||||
setnavdestination: true,
|
||||
listdatadirectory: false,
|
||||
listdatadirectory: true,
|
||||
reboot: true,
|
||||
uploadfiletourl: false,
|
||||
uploadfiletourl: true,
|
||||
listuploadqueue: true,
|
||||
cancelupload: true,
|
||||
primeactivated: true,
|
||||
|
@ -41,7 +41,7 @@ router.get('/dongle/:dongle_id/connected', async (req, res) => {
|
|||
return res.status(200).json({success: true, dongle_id: device.dongle_id, data: deviceConnected});
|
||||
})
|
||||
|
||||
router.get('/dongle/:dongle_id/send/:method', 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}})}
|
||||
|
||||
|
@ -52,8 +52,11 @@ router.get('/dongle/:dongle_id/send/:method', async (req, res) => {
|
|||
// 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}})}
|
||||
let data;
|
||||
|
||||
data = await req.athenaWebsocketTemp.invoke(req.params.method, null, device.dongle_id, account.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, method: req.params.method, data: data});
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ log4js.configure({
|
|||
|
||||
});
|
||||
|
||||
|
||||
const logger = log4js.getLogger('default');
|
||||
// TODO evaluate if this is the best way to determine the root of project
|
||||
global.__basedir = __dirname;
|
||||
|
@ -57,6 +58,13 @@ const web = async () => {
|
|||
db = _models.models.__db;
|
||||
models = _models.models;
|
||||
|
||||
app.use(function(req, res, next) {
|
||||
res.header('Access-Control-Allow-Origin', "http://localhost:3000");
|
||||
res.header('Access-Control-Allow-Credentials', true);
|
||||
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
|
||||
next();
|
||||
});
|
||||
|
||||
const _controllers = await controllers(models, logger);
|
||||
controllers = _controllers;
|
||||
|
||||
|
|
Loading…
Reference in New Issue