Merge pull request #3227 from overleaf/jel-security-email-alerts

Move security alert handling to private function

GitOrigin-RevId: a59b6b0802986b2caa9e9715d80225eb11b163a9
This commit is contained in:
Jessica Lawshe
2020-09-30 02:04:23 +00:00
committed by Copybot
parent 12c18f9381
commit 9d3f2eb7dc
6 changed files with 212 additions and 78 deletions
@@ -26,6 +26,9 @@ describe('ThirdPartyIdentityManager', function() {
ThirdPartyIdentityExistsError: sinon.stub(),
ThirdPartyUserNotFoundError: sinon.stub()
}),
'logger-sharelatex': (this.logger = {
error: sinon.stub()
}),
'../../../../app/src/models/User': {
User: (this.User = {
findOneAndUpdate: sinon.stub().yields(undefined, this.user),
@@ -59,6 +62,34 @@ describe('ThirdPartyIdentityManager', function() {
'a Google account was linked'
)
})
describe('errors', function() {
const anError = new Error('oops')
describe('EmailHandler', function() {
beforeEach(function() {
this.EmailHandler.sendEmail.yields(anError)
})
it('should log but not return the error', function(done) {
this.ThirdPartyIdentityManager.link(
this.userId,
'google',
this.externalUserId,
this.externalData,
error => {
expect(error).to.not.exist
const loggerCall = this.logger.error.getCall(0)
expect(loggerCall.args[0]).to.deep.equal({
error: anError,
userId: this.userId
})
expect(loggerCall.args[1]).to.contain(
'could not send security alert email when Google linked'
)
done()
}
)
})
})
})
})
describe('unlink', function() {
it('should send email alert', async function() {
@@ -69,5 +100,31 @@ describe('ThirdPartyIdentityManager', function() {
'an Orcid account is no longer linked'
)
})
describe('errors', function() {
const anError = new Error('oops')
describe('EmailHandler', function() {
beforeEach(function() {
this.EmailHandler.sendEmail.yields(anError)
})
it('should log but not return the error', function(done) {
this.ThirdPartyIdentityManager.unlink(
this.userId,
'google',
error => {
expect(error).to.not.exist
const loggerCall = this.logger.error.getCall(0)
expect(loggerCall.args[0]).to.deep.equal({
error: anError,
userId: this.userId
})
expect(loggerCall.args[1]).to.contain(
'could not send security alert email when Google no longer linked'
)
done()
}
)
})
})
})
})
})
@@ -596,6 +596,16 @@ describe('UserController', function() {
it('sends a security alert email', function(done) {
this.res.sendStatus.callsFake(status => {
this.EmailHandler.promises.sendEmail.callCount.should.equal(1)
const expectedArg = {
to: this.user.email,
actionDescribed: `active sessions were cleared on your account ${
this.user.email
}`,
action: 'active sessions cleared'
}
const emailCall = this.EmailHandler.promises.sendEmail.lastCall
expect(emailCall.args[0]).to.equal('securityAlert')
expect(emailCall.args[1]).to.deep.equal(expectedArg)
done()
})
@@ -639,11 +649,20 @@ describe('UserController', function() {
})
describe('when EmailHandler produces an error', function() {
const anError = new Error('oops')
it('send a 201 response but log error', function(done) {
this.EmailHandler.promises.sendEmail.rejects(new Error('oops'))
this.EmailHandler.promises.sendEmail.rejects(anError)
this.res.sendStatus.callsFake(status => {
status.should.equal(201)
this.logger.error.callCount.should.equal(1)
const loggerCall = this.logger.error.getCall(0)
expect(loggerCall.args[0]).to.deep.equal({
error: anError,
userId: this.user_id
})
expect(loggerCall.args[1]).to.contain(
'could not send security alert email when sessions cleared'
)
done()
})
this.UserController.clearSessions(this.req, this.res)
@@ -793,6 +812,7 @@ describe('UserController', function() {
})
describe('EmailHandler error', function() {
const anError = new Error('oops')
beforeEach(function() {
this.AuthenticationManager.promises.authenticate.resolves(this.user)
this.AuthenticationManager.promises.setUserPassword.resolves()
@@ -800,12 +820,19 @@ describe('UserController', function() {
newPassword1: 'newpass',
newPassword2: 'newpass'
}
this.EmailHandler.sendEmail.yields(new Error('oops'))
this.EmailHandler.sendEmail.yields(anError)
})
it('should not return error but should log it', function(done) {
this.res.json.callsFake(result => {
expect(result.message.type).to.equal('success')
this.logger.error.callCount.should.equal(1)
expect(this.logger.error).to.have.been.calledWithExactly(
{
error: anError,
userId: this.user_id
},
'could not send security alert email when password changed'
)
done()
})
this.UserController.changePassword(this.req, this.res)
@@ -669,6 +669,38 @@ describe('UserUpdater', function() {
}
)
})
describe('errors', function() {
const anError = new Error('oops')
describe('EmailHandler', function() {
beforeEach(function() {
this.EmailHandler.promises.sendEmail.rejects(anError)
this.UserUpdater.promises.updateUser = sinon
.stub()
.resolves({ n: 1 })
})
it('should log but not pass back the error', function(done) {
this.UserUpdater.setDefaultEmailAddress(
this.stubbedUser._id,
this.newEmail,
false,
this.auditLog,
true,
error => {
expect(error).to.not.exist
const loggerCall = this.logger.error.firstCall
expect(loggerCall.args[0]).to.deep.equal({
error: anError,
userId: this.stubbedUser._id
})
expect(loggerCall.args[1]).to.contain(
'could not send security alert email when primary email changed'
)
done()
}
)
})
})
})
})
})