diff --git a/ee/utilities/.gitignore b/ee/utilities/.gitignore index f54e439ba..7309a03ee 100644 --- a/ee/utilities/.gitignore +++ b/ee/utilities/.gitignore @@ -11,6 +11,7 @@ servers/peerjs-server.js servers/sourcemaps-handler.js servers/sourcemaps-server.js /Dockerfile +/utils/assistHelper.js /utils/geoIP.js /utils/HeapSnapshot.js /utils/helper.js diff --git a/ee/utilities/clean.sh b/ee/utilities/clean.sh index 3e8ec080b..24c54234f 100755 --- a/ee/utilities/clean.sh +++ b/ee/utilities/clean.sh @@ -1,3 +1,4 @@ +rm -rf ./utils/assistHelper.js rm -rf ./utils/geoIP.js rm -rf ./utils/HeapSnapshot.js rm -rf ./utils/helper.js diff --git a/ee/utilities/server.js b/ee/utilities/server.js index 93d6d2a2e..de9027545 100644 --- a/ee/utilities/server.js +++ b/ee/utilities/server.js @@ -8,24 +8,26 @@ if (process.env.redis === "true") { socket = require("./servers/websocket"); } -const HOST = '0.0.0.0'; -const PORT = 9001; +const HOST = process.env.LISTEN_HOST || '0.0.0.0'; +const PORT = process.env.LISTEN_PORT || 9001; +const P_KEY = process.env.ASSIST_KEY||process.env.S3_KEY; +const PREFIX = process.env.PREFIX || process.env.prefix || `/assist` -let debug = process.env.debug === "1" || false; -const PREFIX = process.env.prefix || `/assist` +let debug = process.env.debug === "1"; +const heapdump = process.env.heapdump === "1"; if (process.env.uws !== "true") { let wsapp = express(); wsapp.use(express.json()); wsapp.use(express.urlencoded({extended: true})); wsapp.use(request_logger("[wsapp]")); - wsapp.get([PREFIX, `${PREFIX}/`], (req, res) => { + wsapp.get(['/', PREFIX, `${PREFIX}/`, `${PREFIX}/${P_KEY}`, `${PREFIX}/${P_KEY}/`], (req, res) => { res.statusCode = 200; res.end("ok!"); } ); - wsapp.use(`/heapdump/${process.env.S3_KEY}`, dumps.router); - wsapp.use(`${PREFIX}/${process.env.S3_KEY}`, socket.wsRouter); + heapdump && wsapp.use(`${PREFIX}/${P_KEY}/heapdump`, dumps.router); + wsapp.use(`${PREFIX}/${P_KEY}`, socket.wsRouter); wsapp.enable('trust proxy'); const wsserver = wsapp.listen(PORT, HOST, () => { console.log(`WS App listening on http://${HOST}:${PORT}`); @@ -44,9 +46,11 @@ if (process.env.uws !== "true") { const healthFn = (res, req) => { res.writeStatus('200 OK').end('ok!'); } + uapp.get('/', healthFn); uapp.get(PREFIX, healthFn); uapp.get(`${PREFIX}/`, healthFn); - uapp.get(`${PREFIX}/${process.env.S3_KEY}`, healthFn); + uapp.get(`${PREFIX}/${P_KEY}`, healthFn); + uapp.get(`${PREFIX}/${P_KEY}/`, healthFn); /* Either onAborted or simply finished request */ @@ -73,19 +77,19 @@ if (process.env.uws !== "true") { return fn(req, res); } } - uapp.get(`${PREFIX}/${process.env.S3_KEY}/sockets-list`, uWrapper(socket.handlers.socketsList)); - uapp.post(`${PREFIX}/${process.env.S3_KEY}/sockets-list`, uWrapper(socket.handlers.socketsList)); - uapp.get(`${PREFIX}/${process.env.S3_KEY}/sockets-list/:projectKey/autocomplete`, uWrapper(socket.handlers.autocomplete)); - uapp.get(`${PREFIX}/${process.env.S3_KEY}/sockets-list/:projectKey`, uWrapper(socket.handlers.socketsListByProject)); - uapp.post(`${PREFIX}/${process.env.S3_KEY}/sockets-list/:projectKey`, uWrapper(socket.handlers.socketsListByProject)); - uapp.get(`${PREFIX}/${process.env.S3_KEY}/sockets-list/:projectKey/:sessionId`, uWrapper(socket.handlers.socketsListByProject)); + uapp.get(`${PREFIX}/${P_KEY}/sockets-list`, uWrapper(socket.handlers.socketsList)); + uapp.post(`${PREFIX}/${P_KEY}/sockets-list`, uWrapper(socket.handlers.socketsList)); + uapp.get(`${PREFIX}/${P_KEY}/sockets-list/:projectKey/autocomplete`, uWrapper(socket.handlers.autocomplete)); + uapp.get(`${PREFIX}/${P_KEY}/sockets-list/:projectKey`, uWrapper(socket.handlers.socketsListByProject)); + uapp.post(`${PREFIX}/${P_KEY}/sockets-list/:projectKey`, uWrapper(socket.handlers.socketsListByProject)); + uapp.get(`${PREFIX}/${P_KEY}/sockets-list/:projectKey/:sessionId`, uWrapper(socket.handlers.socketsListByProject)); - uapp.get(`${PREFIX}/${process.env.S3_KEY}/sockets-live`, uWrapper(socket.handlers.socketsLive)); - uapp.post(`${PREFIX}/${process.env.S3_KEY}/sockets-live`, uWrapper(socket.handlers.socketsLive)); - uapp.get(`${PREFIX}/${process.env.S3_KEY}/sockets-live/:projectKey/autocomplete`, uWrapper(socket.handlers.autocomplete)); - uapp.get(`${PREFIX}/${process.env.S3_KEY}/sockets-live/:projectKey`, uWrapper(socket.handlers.socketsLiveByProject)); - uapp.post(`${PREFIX}/${process.env.S3_KEY}/sockets-live/:projectKey`, uWrapper(socket.handlers.socketsLiveByProject)); - uapp.get(`${PREFIX}/${process.env.S3_KEY}/sockets-live/:projectKey/:sessionId`, uWrapper(socket.handlers.socketsLiveByProject)); + uapp.get(`${PREFIX}/${P_KEY}/sockets-live`, uWrapper(socket.handlers.socketsLive)); + uapp.post(`${PREFIX}/${P_KEY}/sockets-live`, uWrapper(socket.handlers.socketsLive)); + uapp.get(`${PREFIX}/${P_KEY}/sockets-live/:projectKey/autocomplete`, uWrapper(socket.handlers.autocomplete)); + uapp.get(`${PREFIX}/${P_KEY}/sockets-live/:projectKey`, uWrapper(socket.handlers.socketsLiveByProject)); + uapp.post(`${PREFIX}/${P_KEY}/sockets-live/:projectKey`, uWrapper(socket.handlers.socketsLiveByProject)); + uapp.get(`${PREFIX}/${P_KEY}/sockets-live/:projectKey/:sessionId`, uWrapper(socket.handlers.socketsLiveByProject)); socket.start(uapp); diff --git a/ee/utilities/servers/websocket-cluster.js b/ee/utilities/servers/websocket-cluster.js index b0649127c..e26912aa9 100644 --- a/ee/utilities/servers/websocket-cluster.js +++ b/ee/utilities/servers/websocket-cluster.js @@ -9,7 +9,8 @@ const { uniqueAutocomplete } = require('../utils/helper'); const { - extractSessionInfo + extractSessionInfo, + authorizer } = require('../utils/assistHelper'); const { extractProjectKeyFromRequest, @@ -33,7 +34,7 @@ const pubClient = createClient({url: REDIS_URL}); const subClient = pubClient.duplicate(); console.log(`Using Redis: ${REDIS_URL}`); let io; -const debug = process.env.debug === "1" || false; +const debug = process.env.debug === "1"; const createSocketIOServer = function (server, prefix) { if (process.env.uws !== "true") { @@ -287,6 +288,7 @@ module.exports = { wsRouter, start: (server, prefix) => { createSocketIOServer(server, prefix); + io.use(async (socket, next) => await authorizer.check(socket, next)); io.on('connection', async (socket) => { debug && console.log(`WS started:${socket.id}, Query:${JSON.stringify(socket.handshake.query)}`); socket.peerId = socket.handshake.query.peerId; @@ -371,7 +373,7 @@ module.exports = { }); }); - console.log("WS server started") + console.log("WS server started"); setInterval(async (io) => { try { let rooms = await io.of('/').adapter.allRooms(); @@ -395,7 +397,7 @@ module.exports = { } catch (e) { console.error(e); } - }, 20000, io); + }, 30000, io); Promise.all([pubClient.connect(), subClient.connect()]) .then(() => { io.adapter(createAdapter(pubClient, subClient)); diff --git a/ee/utilities/servers/websocket.js b/ee/utilities/servers/websocket.js index 4fa61aa42..521c4d798 100644 --- a/ee/utilities/servers/websocket.js +++ b/ee/utilities/servers/websocket.js @@ -9,7 +9,8 @@ const { uniqueAutocomplete } = require('../utils/helper'); const { - extractSessionInfo + extractSessionInfo, + authorizer } = require('../utils/assistHelper'); const { extractProjectKeyFromRequest, @@ -28,7 +29,7 @@ const SESSION_ALREADY_CONNECTED = "SESSION_ALREADY_CONNECTED"; const SESSION_RECONNECTED = "SESSION_RECONNECTED"; let io; -const debug = process.env.debug === "1" || false; +const debug = process.env.debug === "1"; const createSocketIOServer = function (server, prefix) { if (process.env.uws !== "true") { @@ -265,6 +266,7 @@ module.exports = { wsRouter, start: (server, prefix) => { createSocketIOServer(server, prefix); + io.use(async (socket, next) => await authorizer.check(socket, next)); io.on('connection', async (socket) => { debug && console.log(`WS started:${socket.id}, Query:${JSON.stringify(socket.handshake.query)}`); socket.peerId = socket.handshake.query.peerId; @@ -347,13 +349,13 @@ module.exports = { }); }); - console.log("WS server started") + console.log("WS server started"); setInterval(async (io) => { try { let count = 0; console.log(` ====== Rooms: ${io.sockets.adapter.rooms.size} ====== `); - const arr = Array.from(io.sockets.adapter.rooms) - const filtered = arr.filter(room => !room[1].has(room[0])) + const arr = Array.from(io.sockets.adapter.rooms); + const filtered = arr.filter(room => !room[1].has(room[0])); for (let i of filtered) { let {projectKey, sessionId} = extractPeerId(i[0]); if (projectKey !== null && sessionId !== null) { @@ -363,13 +365,13 @@ module.exports = { console.log(` ====== Valid Rooms: ${count} ====== `); if (debug) { for (let item of filtered) { - console.log(`Room: ${item[0]} connected: ${item[1].size}`) + console.log(`Room: ${item[0]} connected: ${item[1].size}`); } } } catch (e) { console.error(e); } - }, 20000, io); + }, 30000, io); }, handlers: { socketsList, diff --git a/utilities/package-lock.json b/utilities/package-lock.json index e8d8d3129..6f06ea9e9 100644 --- a/utilities/package-lock.json +++ b/utilities/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@maxmind/geoip2-node": "^3.4.0", "express": "^4.17.1", + "jsonwebtoken": "^8.5.1", "socket.io": "^4.5.1", "ua-parser-js": "^1.0.2" } @@ -99,6 +100,11 @@ "node": ">= 0.8" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -210,6 +216,14 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -419,6 +433,32 @@ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" }, + "node_modules/jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=4", + "npm": ">=1.4.28" + } + }, + "node_modules/jsonwebtoken/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, "node_modules/jsprim": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", @@ -433,6 +473,60 @@ "verror": "1.10.0" } }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "node_modules/lodash.set": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", @@ -647,6 +741,14 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, "node_modules/send": { "version": "0.17.2", "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", @@ -966,6 +1068,11 @@ "type-is": "~1.6.18" } }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -1047,6 +1154,14 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -1214,6 +1329,30 @@ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" }, + "jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "dependencies": { + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, "jsprim": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", @@ -1225,6 +1364,60 @@ "verror": "1.10.0" } }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "lodash.set": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", @@ -1360,6 +1553,11 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, "send": { "version": "0.17.2", "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", diff --git a/utilities/package.json b/utilities/package.json index cb6fb2b65..2d5de1d11 100644 --- a/utilities/package.json +++ b/utilities/package.json @@ -20,6 +20,7 @@ "dependencies": { "@maxmind/geoip2-node": "^3.4.0", "express": "^4.17.1", + "jsonwebtoken": "^8.5.1", "socket.io": "^4.5.1", "ua-parser-js": "^1.0.2" } diff --git a/utilities/server.js b/utilities/server.js index ad03aafab..5c64687a0 100644 --- a/utilities/server.js +++ b/utilities/server.js @@ -3,16 +3,25 @@ const express = require('express'); const socket = require("./servers/websocket"); const {request_logger} = require("./utils/helper"); -const HOST = '0.0.0.0'; -const PORT = 9001; +const debug = process.env.debug === "1"; +const heapdump = process.env.heapdump === "1"; +const HOST = process.env.LISTEN_HOST || '0.0.0.0'; +const PORT = process.env.LISTEN_PORT || 9001; +const P_KEY = process.env.ASSIST_KEY || process.env.S3_KEY; +const PREFIX = process.env.PREFIX || process.env.prefix || `/assist` const wsapp = express(); wsapp.use(express.json()); wsapp.use(express.urlencoded({extended: true})); wsapp.use(request_logger("[wsapp]")); -wsapp.use(`/assist/${process.env.S3_KEY}`, socket.wsRouter); -wsapp.use(`/heapdump/${process.env.S3_KEY}`, dumps.router); +wsapp.get(['/', PREFIX, `${PREFIX}/`, `${PREFIX}/${P_KEY}`, `${PREFIX}/${P_KEY}/`], (req, res) => { + res.statusCode = 200; + res.end("ok!"); + } +); +wsapp.use(`${PREFIX}/${P_KEY}`, socket.wsRouter); +heapdump && wsapp.use(`${PREFIX}/${P_KEY}/heapdump`, dumps.router); const wsserver = wsapp.listen(PORT, HOST, () => { console.log(`WS App listening on http://${HOST}:${PORT}`); diff --git a/utilities/servers/websocket.js b/utilities/servers/websocket.js index 5636eafcc..50c7aa23d 100644 --- a/utilities/servers/websocket.js +++ b/utilities/servers/websocket.js @@ -12,7 +12,8 @@ const { uniqueAutocomplete } = require('../utils/helper'); const { - extractSessionInfo + extractSessionInfo, + authorizer } = require('../utils/assistHelper'); const wsRouter = express.Router(); const UPDATE_EVENT = "UPDATE_SESSION"; @@ -26,7 +27,7 @@ const SESSION_ALREADY_CONNECTED = "SESSION_ALREADY_CONNECTED"; const SESSION_RECONNECTED = "SESSION_RECONNECTED"; let io; -const debug = process.env.debug === "1" || false; +const debug = process.env.debug === "1"; const createSocketIOServer = function (server, prefix) { io = _io(server, { @@ -246,6 +247,7 @@ module.exports = { wsRouter, start: (server, prefix) => { createSocketIOServer(server, prefix); + io.use(async (socket, next) => await authorizer.check(socket, next)); io.on('connection', async (socket) => { debug && console.log(`WS started:${socket.id}, Query:${JSON.stringify(socket.handshake.query)}`); socket.peerId = socket.handshake.query.peerId; @@ -328,13 +330,13 @@ module.exports = { }); }); - console.log("WS server started") + console.log("WS server started"); setInterval(async (io) => { try { let count = 0; console.log(` ====== Rooms: ${io.sockets.adapter.rooms.size} ====== `); - const arr = Array.from(io.sockets.adapter.rooms) - const filtered = arr.filter(room => !room[1].has(room[0])) + const arr = Array.from(io.sockets.adapter.rooms); + const filtered = arr.filter(room => !room[1].has(room[0])); for (let i of filtered) { let {projectKey, sessionId} = extractPeerId(i[0]); if (projectKey !== null && sessionId !== null) { @@ -344,13 +346,13 @@ module.exports = { console.log(` ====== Valid Rooms: ${count} ====== `); if (debug) { for (let item of filtered) { - console.log(`Room: ${item[0]} connected: ${item[1].size}`) + console.log(`Room: ${item[0]} connected: ${item[1].size}`); } } } catch (e) { console.error(e); } - }, 20000, io); + }, 30000, io); }, handlers: { socketsList, diff --git a/utilities/utils/assistHelper.js b/utilities/utils/assistHelper.js index dd97a1dd9..0fe19dfd9 100644 --- a/utilities/utils/assistHelper.js +++ b/utilities/utils/assistHelper.js @@ -1,7 +1,9 @@ +const jwt = require('jsonwebtoken'); const uaParser = require('ua-parser-js'); const {geoip} = require('./geoIP'); +const {extractPeerId} = require('./helper'); -let debug = process.env.debug === "1" || false; +let debug = process.env.debug === "1"; const BASE_sessionInfo = { "pageTitle": "Page", @@ -55,7 +57,50 @@ const extractSessionInfo = function (socket) { } } +function generateAccessToken(payload) { + return jwt.sign(payload, process.env.ASSIST_JWT_SECRET, {expiresIn: process.env.ASSIST_JWT_EXPIRATION || '30m'}); +} + +const JWT_TOKEN_PREFIX = "Bearer "; + +function check(socket, next) { + if (socket.handshake.query.identity === 'session') { + return next(); + } + if (socket.handshake.query.peerId && socket.handshake.auth && socket.handshake.auth.token) { + let token = socket.handshake.auth.token; + if (token.startsWith(JWT_TOKEN_PREFIX)) { + token = token.substring(JWT_TOKEN_PREFIX.length); + } + jwt.verify(token, process.env.ASSIST_JWT_SECRET, (err, decoded) => { + debug && console.log("JWT payload:"); + debug && console.log(decoded); + if (err) { + debug && console.error(err); + return next(new Error('Authentication error')); + } + const {projectKey, sessionId} = extractPeerId(socket.handshake.query.peerId); + if (!projectKey || !sessionId) { + debug && console.error("Missing attribute:"); + debug && console.error(`projectKey:${projectKey}, sessionId:${sessionId}`); + return next(new Error('Authentication error')); + } + if (String(projectKey) !== String(decoded.projectKey) || String(sessionId) !== String(decoded.sessionId)) { + debug && console.error(`Trying to access projectKey:${projectKey} instead of ${decoded.projectKey}\nor`); + debug && console.error(`Trying to access sessionId:${sessionId} instead of ${decoded.sessionId}`); + return next(new Error('Authorization error')); + } + socket.decoded = decoded; + return next(); + }); + } else { + debug && console.error("something missing in:"); + debug && console.error(socket.handshake); + return next(new Error('Authentication error')); + } +} module.exports = { - extractSessionInfo + extractSessionInfo, + authorizer: {generateAccessToken, check} }; \ No newline at end of file