From dc553c4150d465fb42b7cce281d0a78cd717b895 Mon Sep 17 00:00:00 2001 From: Jakob Ackermann Date: Sat, 6 Jun 2020 13:37:40 +0100 Subject: [PATCH] [misc] vendor a patched session.socket.io middleware --- services/real-time/app.coffee | 2 +- .../app/coffee/SessionSockets.coffee | 23 ++++ services/real-time/package-lock.json | 5 - services/real-time/package.json | 1 - .../coffee/SessionSocketsTests.coffee | 67 ++++++++++ .../unit/coffee/SessionSocketsTests.coffee | 126 ++++++++++++++++++ 6 files changed, 217 insertions(+), 7 deletions(-) create mode 100644 services/real-time/app/coffee/SessionSockets.coffee create mode 100644 services/real-time/test/acceptance/coffee/SessionSocketsTests.coffee create mode 100644 services/real-time/test/unit/coffee/SessionSocketsTests.coffee diff --git a/services/real-time/app.coffee b/services/real-time/app.coffee index 06fd3555f6..9f4c9cc44f 100644 --- a/services/real-time/app.coffee +++ b/services/real-time/app.coffee @@ -17,7 +17,7 @@ if Settings.sentry?.dsn? sessionRedisClient = redis.createClient(Settings.redis.websessions) RedisStore = require('connect-redis')(session) -SessionSockets = require('session.socket.io') +SessionSockets = require('./app/js/SessionSockets') CookieParser = require("cookie-parser") DrainManager = require("./app/js/DrainManager") diff --git a/services/real-time/app/coffee/SessionSockets.coffee b/services/real-time/app/coffee/SessionSockets.coffee new file mode 100644 index 0000000000..229e07b3bb --- /dev/null +++ b/services/real-time/app/coffee/SessionSockets.coffee @@ -0,0 +1,23 @@ +{EventEmitter} = require('events') + +module.exports = (io, sessionStore, cookieParser, cookieName) -> + missingSessionError = new Error('could not look up session by key') + + sessionSockets = new EventEmitter() + next = (error, socket, session) -> + sessionSockets.emit 'connection', error, socket, session + + io.on 'connection', (socket) -> + req = socket.handshake + cookieParser req, {}, () -> + sessionId = req.signedCookies and req.signedCookies[cookieName] + if not sessionId + return next(missingSessionError, socket) + sessionStore.get sessionId, (error, session) -> + if error + return next(error, socket) + if not session + return next(missingSessionError, socket) + next(null, socket, session) + + return sessionSockets diff --git a/services/real-time/package-lock.json b/services/real-time/package-lock.json index 1541d23171..3256a58aca 100644 --- a/services/real-time/package-lock.json +++ b/services/real-time/package-lock.json @@ -2165,11 +2165,6 @@ "send": "0.16.2" } }, - "session.socket.io": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/session.socket.io/-/session.socket.io-0.1.6.tgz", - "integrity": "sha1-vh7sJAYJWgP4dw6ozN5s8SYBxdA=" - }, "setprototypeof": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", diff --git a/services/real-time/package.json b/services/real-time/package.json index 356a78e712..b50fb9c6ce 100644 --- a/services/real-time/package.json +++ b/services/real-time/package.json @@ -33,7 +33,6 @@ "metrics-sharelatex": "^2.6.2", "redis-sharelatex": "^1.0.12", "request": "^2.88.0", - "session.socket.io": "^0.1.6", "settings-sharelatex": "^1.1.0", "socket.io": "0.9.19", "socket.io-client": "^0.9.16" diff --git a/services/real-time/test/acceptance/coffee/SessionSocketsTests.coffee b/services/real-time/test/acceptance/coffee/SessionSocketsTests.coffee new file mode 100644 index 0000000000..3009da682f --- /dev/null +++ b/services/real-time/test/acceptance/coffee/SessionSocketsTests.coffee @@ -0,0 +1,67 @@ +RealTimeClient = require("./helpers/RealTimeClient") +Settings = require("settings-sharelatex") +{expect} = require('chai') + +describe 'SessionSockets', -> + before -> + @checkSocket = (fn) -> + client = RealTimeClient.connect() + client.on 'connectionAccepted', fn + client.on 'connectionRejected', fn + return null + + describe 'without cookies', -> + before -> + RealTimeClient.cookie = null + + it 'should return a lookup error', (done) -> + @checkSocket (error) -> + expect(error).to.exist + expect(error.message).to.equal('invalid session') + done() + + describe 'with a different cookie', -> + before -> + RealTimeClient.cookie = "some.key=someValue" + + it 'should return a lookup error', (done) -> + @checkSocket (error) -> + expect(error).to.exist + expect(error.message).to.equal('invalid session') + done() + + describe 'with an invalid cookie', -> + before (done) -> + RealTimeClient.setSession {}, (error) -> + return done(error) if error + RealTimeClient.cookie = "#{Settings.cookieName}=#{ + RealTimeClient.cookie.slice(17, 49) + }" + done() + return null + + it 'should return a lookup error', (done) -> + @checkSocket (error) -> + expect(error).to.exist + expect(error.message).to.equal('invalid session') + done() + + describe 'with a valid cookie and no matching session', -> + before -> + RealTimeClient.cookie = "#{Settings.cookieName}=unknownId" + + it 'should return a lookup error', (done) -> + @checkSocket (error) -> + expect(error).to.exist + expect(error.message).to.equal('invalid session') + done() + + describe 'with a valid cookie and a matching session', -> + before (done) -> + RealTimeClient.setSession({}, done) + return null + + it 'should not return an error', (done) -> + @checkSocket (error) -> + expect(error).to.not.exist + done() diff --git a/services/real-time/test/unit/coffee/SessionSocketsTests.coffee b/services/real-time/test/unit/coffee/SessionSocketsTests.coffee new file mode 100644 index 0000000000..2f81699309 --- /dev/null +++ b/services/real-time/test/unit/coffee/SessionSocketsTests.coffee @@ -0,0 +1,126 @@ +{EventEmitter} = require('events') +{expect} = require('chai') +SandboxedModule = require('sandboxed-module') +modulePath = '../../../app/js/SessionSockets' +sinon = require('sinon') + +describe 'SessionSockets', -> + before -> + @SessionSocketsModule = SandboxedModule.require modulePath + @io = new EventEmitter() + @id1 = Math.random().toString() + @id2 = Math.random().toString() + redisResponses = + error: [new Error('Redis: something went wrong'), null] + unknownId: [null, null] + redisResponses[@id1] = [null, {user: {_id: '123'}}] + redisResponses[@id2] = [null, {user: {_id: 'abc'}}] + + @sessionStore = + get: sinon.stub().callsFake (id, fn) -> + fn.apply(null, redisResponses[id]) + @cookieParser = (req, res, next) -> + req.signedCookies = req._signedCookies + next() + @SessionSockets = @SessionSocketsModule(@io, @sessionStore, @cookieParser, 'ol.sid') + @checkSocket = (socket, fn) => + @SessionSockets.once('connection', fn) + @io.emit('connection', socket) + + describe 'without cookies', -> + before -> + @socket = {handshake: {}} + + it 'should return a lookup error', (done) -> + @checkSocket @socket, (error) -> + expect(error).to.exist + expect(error.message).to.equal('could not look up session by key') + done() + + it 'should not query redis', (done) -> + @checkSocket @socket, () => + expect(@sessionStore.get.called).to.equal(false) + done() + + describe 'with a different cookie', -> + before -> + @socket = {handshake: {_signedCookies: {other: 1}}} + + it 'should return a lookup error', (done) -> + @checkSocket @socket, (error) -> + expect(error).to.exist + expect(error.message).to.equal('could not look up session by key') + done() + + it 'should not query redis', (done) -> + @checkSocket @socket, () => + expect(@sessionStore.get.called).to.equal(false) + done() + + describe 'with a valid cookie and a failing session lookup', -> + before -> + @socket = {handshake: {_signedCookies: {'ol.sid': 'error'}}} + + it 'should query redis', (done) -> + @checkSocket @socket, () => + expect(@sessionStore.get.called).to.equal(true) + done() + + it 'should return a redis error', (done) -> + @checkSocket @socket, (error) -> + expect(error).to.exist + expect(error.message).to.equal('Redis: something went wrong') + done() + + describe 'with a valid cookie and no matching session', -> + before -> + @socket = {handshake: {_signedCookies: {'ol.sid': 'unknownId'}}} + + it 'should query redis', (done) -> + @checkSocket @socket, () => + expect(@sessionStore.get.called).to.equal(true) + done() + + it 'should return a lookup error', (done) -> + @checkSocket @socket, (error) -> + expect(error).to.exist + expect(error.message).to.equal('could not look up session by key') + done() + + describe 'with a valid cookie and a matching session', -> + before -> + @socket = {handshake: {_signedCookies: {'ol.sid': @id1}}} + + it 'should query redis', (done) -> + @checkSocket @socket, () => + expect(@sessionStore.get.called).to.equal(true) + done() + + it 'should not return an error', (done) -> + @checkSocket @socket, (error) -> + expect(error).to.not.exist + done() + + it 'should return the session', (done) -> + @checkSocket @socket, (error, s, session) -> + expect(session).to.deep.equal({user: {_id: '123'}}) + done() + + describe 'with a different valid cookie and matching session', -> + before -> + @socket = {handshake: {_signedCookies: {'ol.sid': @id2}}} + + it 'should query redis', (done) -> + @checkSocket @socket, () => + expect(@sessionStore.get.called).to.equal(true) + done() + + it 'should not return an error', (done) -> + @checkSocket @socket, (error) -> + expect(error).to.not.exist + done() + + it 'should return the other session', (done) -> + @checkSocket @socket, (error, s, session) -> + expect(session).to.deep.equal({user: {_id: 'abc'}}) + done()