converted to express, with sessions, no more login proc
This commit is contained in:
@@ -16,6 +16,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ajv": "^8.12.0",
|
"ajv": "^8.12.0",
|
||||||
"cookie-parser": "^1.4.6",
|
"cookie-parser": "^1.4.6",
|
||||||
|
"express": "^5.1.0",
|
||||||
"express-mysql-session": "^3.0.3",
|
"express-mysql-session": "^3.0.3",
|
||||||
"express-session": "^1.18.2",
|
"express-session": "^1.18.2",
|
||||||
"pm2": "^6.0.10",
|
"pm2": "^6.0.10",
|
||||||
@@ -23,5 +24,8 @@
|
|||||||
"urldecode": "^1.0.1",
|
"urldecode": "^1.0.1",
|
||||||
"ws": "^8.8.1",
|
"ws": "^8.8.1",
|
||||||
"yargs": "^17.5.1"
|
"yargs": "^17.5.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"wscat": "^6.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-38
@@ -25,33 +25,15 @@ export class WssConnexion {
|
|||||||
this.socket.on('message', this.receive.bind(this));
|
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}`)
|
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(){
|
welcome(){
|
||||||
clearTimeout(this.challengeTimeout)
|
clearTimeout(this.challengeTimeout)
|
||||||
this.challengeTimeout = null
|
this.challengeTimeout = null
|
||||||
this.cnxState = 'CONNECTED'
|
this.cnxState = 'CONNECTED'
|
||||||
if(this.debug) console.log(`Welcome to UUID ${this.uuid}`)
|
if(this.debug) console.log(`Welcome to UUID ${this.uuid}`)
|
||||||
this.resolveLogin()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async checkLogin(userInfo, otp){
|
async checkLogin(userInfo, otp){
|
||||||
@@ -98,31 +80,13 @@ export class WssConnexion {
|
|||||||
return;
|
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);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(typeof this['action_'+action] == "function") {
|
if(typeof this['action_'+action] == "function") {
|
||||||
if((this.debug) && (action != 'PONG')) console.warn(`${action} for uuid ${this.uuid}`);
|
if((this.debug) && (action != 'PONG')) console.warn(`${action} for uuid ${this.uuid}`);
|
||||||
this['action_'+action](action, payload, reqid);
|
this['action_'+action](action, payload, reqid);
|
||||||
} else {
|
} 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
|
send(data) { // Send to Websocket
|
||||||
|
|||||||
+84
-29
@@ -1,12 +1,14 @@
|
|||||||
import yargs from 'yargs/yargs'
|
import yargs from 'yargs/yargs'
|
||||||
import { hideBin } from 'yargs/helpers'
|
import { hideBin } from 'yargs/helpers'
|
||||||
|
import mysql from 'mysql2/promise'
|
||||||
import { WebSocketServer } from 'ws'
|
import { WebSocketServer } from 'ws'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import {RedisConnexion} from './redisConnexion.js'
|
import {RedisConnexion} from './redisConnexion.js'
|
||||||
import urlparser from 'url'
|
|
||||||
import {wssServer} from './wssServer.js'
|
import {wssServer} from './wssServer.js'
|
||||||
import {configHelper} from './configHelper.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', {})
|
const argv = yargs(hideBin(process.argv)).command('wssGateway', 'Redis <=> Websocket message bus gateway', {})
|
||||||
.options({
|
.options({
|
||||||
@@ -19,6 +21,56 @@ const argv = yargs(hideBin(process.argv)).command('wssGateway', 'Redis <=> Webso
|
|||||||
|
|
||||||
const debug = Boolean(process.env.DEBUG) || argv.debug
|
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({
|
const cfgh = new configHelper({
|
||||||
localfile: './wssGatewayConfig.json',
|
localfile: './wssGatewayConfig.json',
|
||||||
})
|
})
|
||||||
@@ -43,6 +95,7 @@ cfgh.fetchConfig().then( async wssGatewayConfig => {
|
|||||||
console.error('Cannot get a valid configuration ! Aaarrghhh...')
|
console.error('Cannot get a valid configuration ! Aaarrghhh...')
|
||||||
process.exit()
|
process.exit()
|
||||||
}
|
}
|
||||||
|
|
||||||
let httpLib
|
let httpLib
|
||||||
if(wssGatewayConfig.server.unsecure) httpLib = await import('http')
|
if(wssGatewayConfig.server.unsecure) httpLib = await import('http')
|
||||||
else httpLib = await import('https')
|
else httpLib = await import('https')
|
||||||
@@ -58,42 +111,44 @@ cfgh.fetchConfig().then( async wssGatewayConfig => {
|
|||||||
};
|
};
|
||||||
} else options = {}
|
} else options = {}
|
||||||
|
|
||||||
const httpRequestsHandler = function(request, res) {
|
const HTTPserver = httpLib.createServer(app)
|
||||||
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(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 = {
|
const wssServerOptions = {
|
||||||
server: HTTPserver,
|
// server: HTTPserver,
|
||||||
path: wssGatewayConfig.server.listenPath,
|
// path: wssGatewayConfig.server.listenPath,
|
||||||
|
noServer: true
|
||||||
}
|
}
|
||||||
if(!wssGatewayConfig.server.unsecure) {
|
if(!wssGatewayConfig.server.unsecure) {
|
||||||
wssServerOptions['key'] = fs.readFileSync(wssGatewayConfig.server.certKeyFile)
|
wssServerOptions['key'] = fs.readFileSync(wssGatewayConfig.server.certKeyFile)
|
||||||
wssServerOptions['cert'] = fs.readFileSync(wssGatewayConfig.server.certFile)
|
wssServerOptions['cert'] = fs.readFileSync(wssGatewayConfig.server.certFile)
|
||||||
}
|
}
|
||||||
const WSSServer = new WebSocketServer(wssServerOptions);
|
const WSSServer = new WebSocketServer(wssServerOptions);
|
||||||
|
|
||||||
|
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}`)
|
console.log(`WS${wssGatewayConfig.server.unsecure ? '': 'S'} server created for ${wssGatewayConfig.server.listenHost}:${wssGatewayConfig.server.listenPort}`)
|
||||||
|
})
|
||||||
|
|
||||||
startRedis(wssGatewayConfig).then((rediscnx) => {
|
startRedis(wssGatewayConfig).then((rediscnx) => {
|
||||||
if(debug) console.log('Redis started & logged in !');
|
if(debug) console.log('Redis started & logged in !');
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
{
|
{
|
||||||
"serviceName": "wssGateway",
|
"serviceName": "wssGateway",
|
||||||
"server": {
|
"server": {
|
||||||
"listenHost": "0.0.0.0",
|
"listenHost": "127.0.0.1",
|
||||||
"listenPort": 3999,
|
"listenPort": 3999,
|
||||||
"listenPath": "/msgbus",
|
"listenPath": "/msgbus",
|
||||||
"keepAliveInterval": "30",
|
"keepAliveInterval": "30",
|
||||||
"keepAliveTimeout": "5",
|
"keepAliveTimeout": "5",
|
||||||
"certFile": "/etc/letsencrypt/live/42.internike.com/fullchain.pem",
|
"XXcertFile": "/etc/letsencrypt/live/42.internike.com/fullchain.pem",
|
||||||
"certKeyFile": "/etc/letsencrypt/live/42.internike.com/privkey.pem",
|
"XXcertKeyFile": "/etc/letsencrypt/live/42.internike.com/privkey.pem",
|
||||||
"challengeExpiration": 20,
|
"challengeExpiration": 20,
|
||||||
"unsecure": false,
|
"unsecure": true,
|
||||||
"healthCheckPath": "/status",
|
"healthCheckPath": "/status",
|
||||||
"devotpToken": "qhsdfkjhqsgdfkqhs",
|
"devotpToken": "qhsdfkjhqsgdfkqhs",
|
||||||
"systemChannels": {
|
"systemChannels": {
|
||||||
|
|||||||
+16
-13
@@ -1,8 +1,6 @@
|
|||||||
import {AccesRights} from './accesRights.js'
|
import {AccesRights} from './accesRights.js'
|
||||||
import crypto from 'crypto'
|
import crypto from 'crypto'
|
||||||
import {WssConnexion} from './wssConnexion.js'
|
import {WssConnexion} from './wssConnexion.js'
|
||||||
import session from 'express-session'
|
|
||||||
import connectMySQL from 'express-mysql-session'
|
|
||||||
|
|
||||||
export class wssServer {
|
export class wssServer {
|
||||||
|
|
||||||
@@ -32,6 +30,7 @@ export class wssServer {
|
|||||||
|
|
||||||
newWSSConnexion(socket, req) {
|
newWSSConnexion(socket, req) {
|
||||||
var uuid = crypto.randomUUID();
|
var uuid = crypto.randomUUID();
|
||||||
|
if(socket.session && socket.session.authenticated && socket.session.userInfos && socket.session.userInfos.username){
|
||||||
var wssCnx = new WssConnexion({
|
var wssCnx = new WssConnexion({
|
||||||
socket: socket,
|
socket: socket,
|
||||||
req, req,
|
req, req,
|
||||||
@@ -43,18 +42,22 @@ export class wssServer {
|
|||||||
accessRights: this.accessRights,
|
accessRights: this.accessRights,
|
||||||
});
|
});
|
||||||
this.AllWssConnections[uuid] = wssCnx;
|
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
|
|
||||||
}))
|
|
||||||
this.postLoginActions(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) {
|
postLoginActions(wssCnx) {
|
||||||
|
|||||||
Reference in New Issue
Block a user