mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #2276 from overleaf/bg-reject-invalid-sessions
reject invalid sessions GitOrigin-RevId: 5dc59609d01d7ad9bc29f9bf18faee1165d10689
This commit is contained in:
parent
8ffaa5b0ca
commit
45ebc42bf6
5 changed files with 102 additions and 0 deletions
|
@ -8,6 +8,7 @@ const Settings = require('settings-sharelatex')
|
||||||
const basicAuth = require('basic-auth-connect')
|
const basicAuth = require('basic-auth-connect')
|
||||||
const UserHandler = require('../User/UserHandler')
|
const UserHandler = require('../User/UserHandler')
|
||||||
const UserSessionsManager = require('../User/UserSessionsManager')
|
const UserSessionsManager = require('../User/UserSessionsManager')
|
||||||
|
const SessionStoreManager = require('../../infrastructure/SessionStoreManager')
|
||||||
const Analytics = require('../Analytics/AnalyticsManager')
|
const Analytics = require('../Analytics/AnalyticsManager')
|
||||||
const passport = require('passport')
|
const passport = require('passport')
|
||||||
const NotificationsBuilder = require('../Notifications/NotificationsBuilder')
|
const NotificationsBuilder = require('../Notifications/NotificationsBuilder')
|
||||||
|
@ -309,6 +310,29 @@ const AuthenticationController = (module.exports = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
validateUserSession: function() {
|
||||||
|
// Middleware to check that the user's session is still good on key actions,
|
||||||
|
// such as opening a a project. Could be used to check that session has not
|
||||||
|
// exceeded a maximum lifetime (req.session.session_created), or for session
|
||||||
|
// hijacking checks (e.g. change of ip address, req.session.ip_address). For
|
||||||
|
// now, just check that the session has been loaded from the session store
|
||||||
|
// correctly.
|
||||||
|
return function(req, res, next) {
|
||||||
|
// check that the session store is returning valid results
|
||||||
|
if (req.session && !SessionStoreManager.hasValidationToken(req)) {
|
||||||
|
// force user to update session
|
||||||
|
req.session.regenerate(() => {
|
||||||
|
// need to destroy the existing session and generate a new one
|
||||||
|
// otherwise they will already be logged in when they are redirected
|
||||||
|
// to the login page
|
||||||
|
AuthenticationController._redirectToLoginOrRegisterPage(req, res)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
_globalLoginWhitelist: [],
|
_globalLoginWhitelist: [],
|
||||||
addEndpointToLoginWhitelist(endpoint) {
|
addEndpointToLoginWhitelist(endpoint) {
|
||||||
return AuthenticationController._globalLoginWhitelist.push(endpoint)
|
return AuthenticationController._globalLoginWhitelist.push(endpoint)
|
||||||
|
|
|
@ -61,5 +61,13 @@ module.exports = {
|
||||||
} else {
|
} else {
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
hasValidationToken(req) {
|
||||||
|
if (req && req.session && req.session.validationToken) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -288,6 +288,7 @@ function initialize(webRouter, privateApiRouter, publicApiRouter) {
|
||||||
maxRequests: 15,
|
maxRequests: 15,
|
||||||
timeInterval: 60
|
timeInterval: 60
|
||||||
}),
|
}),
|
||||||
|
AuthenticationController.validateUserSession(),
|
||||||
AuthorizationMiddleware.ensureUserCanReadProject,
|
AuthorizationMiddleware.ensureUserCanReadProject,
|
||||||
ProjectController.loadEditor
|
ProjectController.loadEditor
|
||||||
)
|
)
|
||||||
|
|
|
@ -33,6 +33,7 @@ describe('AuthenticationController', function() {
|
||||||
'../Analytics/AnalyticsManager': (this.AnalyticsManager = {
|
'../Analytics/AnalyticsManager': (this.AnalyticsManager = {
|
||||||
recordEvent: sinon.stub()
|
recordEvent: sinon.stub()
|
||||||
}),
|
}),
|
||||||
|
'../../infrastructure/SessionStoreManager': (this.SessionStoreManager = {}),
|
||||||
'logger-sharelatex': (this.logger = {
|
'logger-sharelatex': (this.logger = {
|
||||||
log: sinon.stub(),
|
log: sinon.stub(),
|
||||||
warn: sinon.stub(),
|
warn: sinon.stub(),
|
||||||
|
@ -582,6 +583,54 @@ describe('AuthenticationController', function() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('validateUserSession', function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
this.user = {
|
||||||
|
_id: 'user-id-123',
|
||||||
|
email: 'user@sharelatex.com'
|
||||||
|
}
|
||||||
|
this.middleware = this.AuthenticationController.validateUserSession()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when the user has a session token', function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
this.req.user = this.user
|
||||||
|
this.SessionStoreManager.hasValidationToken = sinon.stub().returns(true)
|
||||||
|
this.middleware(this.req, this.res, this.next)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should call the next method in the chain', function() {
|
||||||
|
this.next.called.should.equal(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when the user does not have a session token', function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
this.req.session = {
|
||||||
|
user: this.user,
|
||||||
|
regenerate: sinon.stub().yields()
|
||||||
|
}
|
||||||
|
this.req.user = this.user
|
||||||
|
this.AuthenticationController._redirectToLoginOrRegisterPage = sinon.stub()
|
||||||
|
this.req.query = {}
|
||||||
|
this.SessionStoreManager.hasValidationToken = sinon
|
||||||
|
.stub()
|
||||||
|
.returns(false)
|
||||||
|
this.middleware(this.req, this.res, this.next)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should destroy the current session', function() {
|
||||||
|
this.req.session.regenerate.called.should.equal(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should redirect to the register or login page', function() {
|
||||||
|
this.AuthenticationController._redirectToLoginOrRegisterPage
|
||||||
|
.calledWith(this.req, this.res)
|
||||||
|
.should.equal(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('requireOauth', function() {
|
describe('requireOauth', function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
this.res.send = sinon.stub()
|
this.res.send = sinon.stub()
|
||||||
|
|
|
@ -84,4 +84,24 @@ describe('SessionStoreManager', function() {
|
||||||
expect(this.next).to.be.calledWithExactly()
|
expect(this.next).to.be.calledWithExactly()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
describe('hasValidationToken', function() {
|
||||||
|
this.beforeEach(function() {
|
||||||
|
this.req = { sessionID: '123456789' }
|
||||||
|
})
|
||||||
|
it('should return true when the session is valid', function() {
|
||||||
|
this.req.session = { validationToken: 'v1:6789' }
|
||||||
|
const result = this.SessionStoreManager.hasValidationToken(this.req)
|
||||||
|
expect(result).to.equal(true)
|
||||||
|
})
|
||||||
|
it('should return false when the session is valid', function() {
|
||||||
|
this.req.session = { validationToken: 'v1:abcd' }
|
||||||
|
const result = this.SessionStoreManager.hasValidationToken(this.req)
|
||||||
|
expect(result).to.equal(true)
|
||||||
|
})
|
||||||
|
it('should return false when the validation token is missing', function() {
|
||||||
|
this.req.session = {}
|
||||||
|
const result = this.SessionStoreManager.hasValidationToken(this.req)
|
||||||
|
expect(result).to.equal(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue