Merge pull request #3061 from overleaf/jel-security-alert-clear-sessions

Send security alert email when sessions are cleared

GitOrigin-RevId: d1620214e84211edb69d7419ed64cf7618d1b460
This commit is contained in:
Jessica Lawshe 2020-07-30 08:51:28 -05:00 committed by Copybot
parent c3450e4414
commit 33365e56bc
2 changed files with 89 additions and 46 deletions

View file

@ -45,6 +45,23 @@ async function clearSessions(req, res, next) {
await UserSessionsManager.promises.revokeAllUserSessions(user, [
req.sessionID
])
const emailOptions = {
to: user.email,
actionDescribed: `active sessions were cleared on your account ${
user.email
}`,
action: 'active sessions cleared'
}
try {
await EmailHandler.promises.sendEmail('securityAlert', emailOptions)
} catch (error) {
logger.error(
{ userId: user._id },
'could not send security alert email when sessions cleared'
)
}
res.sendStatus(201)
}

View file

@ -111,19 +111,22 @@ describe('UserController', function() {
'../SudoMode/SudoModeHandler': this.SudoModeHandler,
'../Errors/HttpErrorHandler': this.HttpErrorHandler,
'settings-sharelatex': this.settings,
'logger-sharelatex': {
'logger-sharelatex': (this.logger = {
log() {},
warn() {},
err() {},
error() {}
},
error: sinon.stub()
}),
'metrics-sharelatex': {
inc() {}
},
'../Errors/Errors': Errors,
'@overleaf/o-error': OError,
'@overleaf/o-error/http': HttpErrors,
'../Email/EmailHandler': { sendEmail: sinon.stub() }
'../Email/EmailHandler': (this.EmailHandler = {
sendEmail: sinon.stub(),
promises: { sendEmail: sinon.stub().resolves() }
})
}
})
@ -565,57 +568,80 @@ describe('UserController', function() {
})
describe('clearSessions', function() {
it('should call revokeAllUserSessions', function(done) {
this.res.sendStatus.callsFake(() => {
this.UserSessionsManager.promises.revokeAllUserSessions.callCount.should.equal(
1
)
done()
})
this.UserController.clearSessions(this.req, this.res)
})
it('send a 201 response', function(done) {
this.res.sendStatus.callsFake(status => {
status.should.equal(201)
done()
})
this.UserController.clearSessions(this.req, this.res, () => {
done()
})
})
describe('when getAllUserSessions produces an error', function() {
it('should return an error', function(done) {
this.UserSessionsManager.promises.getAllUserSessions.rejects(
new Error('woops')
)
this.UserController.clearSessions(this.req, this.res, error => {
expect(error).to.be.instanceof(Error)
describe('success', function() {
it('should call revokeAllUserSessions', function(done) {
this.res.sendStatus.callsFake(() => {
this.UserSessionsManager.promises.revokeAllUserSessions.callCount.should.equal(
1
)
done()
})
this.UserController.clearSessions(this.req, this.res)
})
})
describe('when audit log addEntry produces an error', function() {
it('should call next with an error', function(done) {
this.UserAuditLogHandler.promises.addEntry.rejects(new Error('woops'))
this.UserController.clearSessions(this.req, this.res, error => {
expect(error).to.be.instanceof(Error)
it('send a 201 response', function(done) {
this.res.sendStatus.callsFake(status => {
status.should.equal(201)
done()
})
this.UserController.clearSessions(this.req, this.res)
})
it('sends a security alert email', function(done) {
this.res.sendStatus.callsFake(status => {
this.EmailHandler.promises.sendEmail.callCount.should.equal(1)
done()
})
this.UserController.clearSessions(this.req, this.res)
})
})
describe('when revokeAllUserSessions produces an error', function() {
it('should call next with an error', function(done) {
this.UserSessionsManager.promises.revokeAllUserSessions.rejects(
new Error('woops')
)
this.UserController.clearSessions(this.req, this.res, error => {
expect(error).to.be.instanceof(Error)
done()
describe('errors', function() {
describe('when getAllUserSessions produces an error', function() {
it('should return an error', function(done) {
this.UserSessionsManager.promises.getAllUserSessions.rejects(
new Error('woops')
)
this.UserController.clearSessions(this.req, this.res, error => {
expect(error).to.be.instanceof(Error)
done()
})
})
})
describe('when audit log addEntry produces an error', function() {
it('should call next with an error', function(done) {
this.UserAuditLogHandler.promises.addEntry.rejects(new Error('woops'))
this.UserController.clearSessions(this.req, this.res, error => {
expect(error).to.be.instanceof(Error)
done()
})
})
})
describe('when revokeAllUserSessions produces an error', function() {
it('should call next with an error', function(done) {
this.UserSessionsManager.promises.revokeAllUserSessions.rejects(
new Error('woops')
)
this.UserController.clearSessions(this.req, this.res, error => {
expect(error).to.be.instanceof(Error)
done()
})
})
})
describe('when EmailHandler produces an error', function() {
it('send a 201 response but log error', function(done) {
this.EmailHandler.promises.sendEmail.rejects(new Error('oops'))
this.res.sendStatus.callsFake(status => {
status.should.equal(201)
this.logger.error.callCount.should.equal(1)
done()
})
this.UserController.clearSessions(this.req, this.res)
})
})
})