From e7dcb7290ed88bd2e9a3c544f4f4b110fbbfa932 Mon Sep 17 00:00:00 2001 From: STEINNI Date: Mon, 15 Sep 2025 19:13:05 +0000 Subject: [PATCH] converted to express, with sessions, no more login proc --- package.json | 4 ++ wssConnexion.js | 48 +++--------------- wssGateway.js | 115 +++++++++++++++++++++++++++++++----------- wssGatewayConfig.json | 8 +-- wssServer.js | 51 ++++++++++--------- 5 files changed, 126 insertions(+), 100 deletions(-) diff --git a/package.json b/package.json index f555da8..b89beac 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "dependencies": { "ajv": "^8.12.0", "cookie-parser": "^1.4.6", + "express": "^5.1.0", "express-mysql-session": "^3.0.3", "express-session": "^1.18.2", "pm2": "^6.0.10", @@ -23,5 +24,8 @@ "urldecode": "^1.0.1", "ws": "^8.8.1", "yargs": "^17.5.1" + }, + "devDependencies": { + "wscat": "^6.1.0" } } diff --git a/wssConnexion.js b/wssConnexion.js index 3fbb249..5feecbb 100644 --- a/wssConnexion.js +++ b/wssConnexion.js @@ -25,33 +25,15 @@ export class WssConnexion { this.socket.on('message', this.receive.bind(this)); if(this.debug) console.log(`Spawned new WSS connection to client: ${this.req.socket.remoteAddress}:${this.req.socket.remotePort}`) + console.log('Session infos:',this.socket.session.authenticated, this.socket.session.userInfos) } - - doLogin(){ - this.cnxState = 'LOGIN' // then CONNECTED - this.challenge = crypto.randomUUID() - this.challengeTimeout = setTimeout(() => { - if(this.debug) console.warn(`Timeout waiting for login response for UUID ${this.uuid}, closing connection !`); - this.close() - }, this.config.server.challengeExpiration*1000) - this.send(JSON.stringify({ - 'action': 'LOGIN', - 'challenge': this.challenge - })); - if(this.debug) console.log(`Sent LOGIN for UUID ${this.uuid} ==> challenge=${this.challenge}`) - return(new Promise((resolve, reject) => { - this.resolveLogin = resolve - })) - } - welcome(){ clearTimeout(this.challengeTimeout) this.challengeTimeout = null this.cnxState = 'CONNECTED' if(this.debug) console.log(`Welcome to UUID ${this.uuid}`) - this.resolveLogin() } async checkLogin(userInfo, otp){ @@ -98,31 +80,13 @@ export class WssConnexion { return; } - if(this.cnxState == 'LOGIN'){ - if((action=='LOGIN') && pdata.userInfo && pdata.otp) { - if(this.debug) console.log(`received login response : user=${pdata.userInfo} otp=${pdata.otp}`) - if(await this.checkLogin(pdata.userInfo, pdata.otp)) { - this.userId = pdata.userInfo - this.welcome() - } else { - if(this.debug) console.warn(`Bad OTP response to login request for uuid ${this.uuid}`); - this.send(JSON.stringify({ - 'action': 'LOGIN', - 'logged': false - })); - this.close() - } - } else { - if(this.debug) console.warn(`Invalid response to login request for uuid ${this.uuid}`,pdata); - } + if(typeof this['action_'+action] == "function") { + if((this.debug) && (action != 'PONG')) console.warn(`${action} for uuid ${this.uuid}`); + this['action_'+action](action, payload, reqid); } else { - if(typeof this['action_'+action] == "function") { - if((this.debug) && (action != 'PONG')) console.warn(`${action} for uuid ${this.uuid}`); - this['action_'+action](action, payload, reqid); - } else { - if(this.debug) console.warn(`Unknown action ${action} for UUID ${this.uuid}`); - } + if(this.debug) console.warn(`Unknown action ${action} for UUID ${this.uuid}`); } + } send(data) { // Send to Websocket diff --git a/wssGateway.js b/wssGateway.js index cfa2230..bc196ff 100644 --- a/wssGateway.js +++ b/wssGateway.js @@ -1,12 +1,14 @@ import yargs from 'yargs/yargs' import { hideBin } from 'yargs/helpers' - +import mysql from 'mysql2/promise' import { WebSocketServer } from 'ws' import fs from 'fs' import {RedisConnexion} from './redisConnexion.js' -import urlparser from 'url' import {wssServer} from './wssServer.js' import {configHelper} from './configHelper.js' +import express from 'express' +import session from 'express-session' +import connectMySQL from 'express-mysql-session' const argv = yargs(hideBin(process.argv)).command('wssGateway', 'Redis <=> Websocket message bus gateway', {}) .options({ @@ -19,6 +21,56 @@ const argv = yargs(hideBin(process.argv)).command('wssGateway', 'Redis <=> Webso const debug = Boolean(process.env.DEBUG) || argv.debug +const MySQLStore = connectMySQL(session) + +const app = express(); +app.set('trust proxy', 1) // trust first proxy (nginx), so we serve http to nginx, but we still behave as if we're in https +app.use(express.json()) + +const mysqlCreds = { + // host: '127.0.0.1', + // port: 3306, + socketPath: '/var/run/mysqld/mysqld.sock', + user: 'p42', + password: 'C3h=V9!r>Mvc>skxPf9?W2P3duJTk', + database: 'p42', + waitForConnections: true, + connectionLimit: 10, + queueLimit: 0 +} + +const db = await mysql.createConnection(mysqlCreds) + +const sessionStore = new MySQLStore({ + createDatabaseTable: false, + clearExpired: true, + schema: { + tableName: 'p42_sessions', + columnNames: { + session_id: 'session_id', + expires: 'expires', + data: 'data' + } + } +}, db) + + +const sessionParser = session({ + name: 'p42.api.sid', + secret: 'qNhy555Y9vyxj?!3yaYA=aKfgk+Wy5eymNtP*?4i', + store: sessionStore, + resave: false, + saveUninitialized: false, + cookie: { + maxAge: 1000 * 60 * 60 * 4, + secure: true, //See trust proxy above + sameSite: 'lax', + }, + } + ) + +app.use(sessionParser) + const cfgh = new configHelper({ localfile: './wssGatewayConfig.json', }) @@ -43,6 +95,7 @@ cfgh.fetchConfig().then( async wssGatewayConfig => { console.error('Cannot get a valid configuration ! Aaarrghhh...') process.exit() } + let httpLib if(wssGatewayConfig.server.unsecure) httpLib = await import('http') else httpLib = await import('https') @@ -58,42 +111,44 @@ cfgh.fetchConfig().then( async wssGatewayConfig => { }; } else options = {} - const httpRequestsHandler = function(request, res) { - let parsedUrl - try{ - parsedUrl = new urlparser.URL(request.url, `http${wssGatewayConfig.server.unsecure ? '': 's'}://${request.headers.host}`); - } catch(e) { - res.end() - return - } - if(parsedUrl.pathname === wssGatewayConfig.server.healthCheckPath) { - //if(debug) console.log('Got a Health-Check Request') - res.end(JSON.stringify({ - status: 'Healthy!', - })) - } - } + const HTTPserver = httpLib.createServer(app) - const HTTPserver = httpLib.createServer(options, httpRequestsHandler) - .listen(Number(wssGatewayConfig.server.listenPort), - wssGatewayConfig.server.listenHost ? wssGatewayConfig.server.listenHost : undefined, - function (req, res) { - console.log(`HTTP${wssGatewayConfig.server.unsecure ? '': 'S'} now listening on ${wssGatewayConfig.server.listenHost}:${wssGatewayConfig.server.listenPort}\n`+ - `Websocket served at ${wssGatewayConfig.server.listenPath}\n`+ - `Healthcheck served at ${wssGatewayConfig.server.healthCheckPath}`) - }); - - // Start serving WSS const wssServerOptions = { - server: HTTPserver, - path: wssGatewayConfig.server.listenPath, + // server: HTTPserver, + // path: wssGatewayConfig.server.listenPath, + noServer: true } if(!wssGatewayConfig.server.unsecure) { wssServerOptions['key'] = fs.readFileSync(wssGatewayConfig.server.certKeyFile) wssServerOptions['cert'] = fs.readFileSync(wssGatewayConfig.server.certFile) } const WSSServer = new WebSocketServer(wssServerOptions); - console.log(`WS${wssGatewayConfig.server.unsecure ? '': 'S'} server created for ${wssGatewayConfig.server.listenHost}:${wssGatewayConfig.server.listenPort}`) + + HTTPserver.on('upgrade', (req, socket, head) => { + sessionParser(req, {}, () => { + if (!req.session) { + console.warn('No session, bye bye!') + socket.destroy() + return + } + WSSServer.handleUpgrade(req, socket, head, (ws) => { + ws.session = req.session // direct access to Express session + WSSServer.emit('connection', ws, req) + }) + }) + }) + + // WSSServer.on('connection', (ws, req) => { + // console.log('WS connected with session:', ws.session, ws.session.authenticated, ws.session.userInfos) + // // modify session if you want + // ws.session.views = (ws.session.views || 0) + 1 + // ws.session.save() + // ws.send(`You visited ${ws.session.views} times`) + // }) + + HTTPserver.listen(wssGatewayConfig.server.listenPort, () => { + console.log(`WS${wssGatewayConfig.server.unsecure ? '': 'S'} server created for ${wssGatewayConfig.server.listenHost}:${wssGatewayConfig.server.listenPort}`) + }) startRedis(wssGatewayConfig).then((rediscnx) => { if(debug) console.log('Redis started & logged in !'); diff --git a/wssGatewayConfig.json b/wssGatewayConfig.json index c17b144..4fd7f41 100644 --- a/wssGatewayConfig.json +++ b/wssGatewayConfig.json @@ -1,15 +1,15 @@ { "serviceName": "wssGateway", "server": { - "listenHost": "0.0.0.0", + "listenHost": "127.0.0.1", "listenPort": 3999, "listenPath": "/msgbus", "keepAliveInterval": "30", "keepAliveTimeout": "5", - "certFile": "/etc/letsencrypt/live/42.internike.com/fullchain.pem", - "certKeyFile": "/etc/letsencrypt/live/42.internike.com/privkey.pem", + "XXcertFile": "/etc/letsencrypt/live/42.internike.com/fullchain.pem", + "XXcertKeyFile": "/etc/letsencrypt/live/42.internike.com/privkey.pem", "challengeExpiration": 20, - "unsecure": false, + "unsecure": true, "healthCheckPath": "/status", "devotpToken": "qhsdfkjhqsgdfkqhs", "systemChannels": { diff --git a/wssServer.js b/wssServer.js index ac0e1a6..d3aff56 100644 --- a/wssServer.js +++ b/wssServer.js @@ -1,8 +1,6 @@ import {AccesRights} from './accesRights.js' import crypto from 'crypto' import {WssConnexion} from './wssConnexion.js' -import session from 'express-session' -import connectMySQL from 'express-mysql-session' export class wssServer { @@ -32,29 +30,34 @@ export class wssServer { newWSSConnexion(socket, req) { var uuid = crypto.randomUUID(); - var wssCnx = new WssConnexion({ - socket: socket, - req, req, - uuid: uuid, - wssSrv: this, - debug: this.debug, - config: this.wssGatewayConfig, - rediscnx: this.REDIScnx, - accessRights: this.accessRights, - }); - this.AllWssConnections[uuid] = wssCnx; - wssCnx.doLogin().then(() => { // Things to execute only when successfuly logged-in - if(!(wssCnx.userId in this.Users2uuids)) this.Users2uuids[wssCnx.userId] = new Set(); - this.Users2uuids[wssCnx.userId].add(uuid); - this.OnlineUsers.add(wssCnx.userId); - this.REDIScnx.wssConnections[uuid] = wssCnx; - //}).then(() => { - wssCnx.send(JSON.stringify({ - 'action': 'LOGIN', - 'logged': true - })) + if(socket.session && socket.session.authenticated && socket.session.userInfos && socket.session.userInfos.username){ + var wssCnx = new WssConnexion({ + socket: socket, + req, req, + uuid: uuid, + wssSrv: this, + debug: this.debug, + config: this.wssGatewayConfig, + rediscnx: this.REDIScnx, + accessRights: this.accessRights, + }); + this.AllWssConnections[uuid] = wssCnx; this.postLoginActions(wssCnx) - }) + } else socket.close() + + + // wssCnx.doLogin().then(() => { // Things to execute only when successfuly logged-in + // if(!(wssCnx.userId in this.Users2uuids)) this.Users2uuids[wssCnx.userId] = new Set(); + // this.Users2uuids[wssCnx.userId].add(uuid); + // this.OnlineUsers.add(wssCnx.userId); + // this.REDIScnx.wssConnections[uuid] = wssCnx; + // //}).then(() => { + // wssCnx.send(JSON.stringify({ + // 'action': 'LOGIN', + // 'logged': true + // })) + // this.postLoginActions(wssCnx) + // }) } postLoginActions(wssCnx) {