mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-07 20:31:06 -05:00
760e6719c1
Improve SSO error log GitOrigin-RevId: a7d3899c662283e49b7505d24b3191213de3968e
243 lines
7.4 KiB
JavaScript
243 lines
7.4 KiB
JavaScript
const sinon = require('sinon')
|
|
const chai = require('chai')
|
|
const { expect } = chai
|
|
const SandboxedModule = require('sandboxed-module')
|
|
const modulePath =
|
|
'../../../../app/src/Features/User/ThirdPartyIdentityManager.js'
|
|
|
|
describe('ThirdPartyIdentityManager', function() {
|
|
beforeEach(function() {
|
|
this.userId = 'a1b2c3'
|
|
this.user = {
|
|
_id: this.userId,
|
|
email: 'example@overleaf.com'
|
|
}
|
|
this.externalUserId = 'id789'
|
|
this.externalData = {}
|
|
this.auditLog = { initiatorId: this.userId, ipAddress: '0:0:0:0' }
|
|
this.ThirdPartyIdentityManager = SandboxedModule.require(modulePath, {
|
|
globals: {
|
|
console: console
|
|
},
|
|
requires: {
|
|
'../../../../app/src/Features/User/UserAuditLogHandler': (this.UserAuditLogHandler = {
|
|
addEntry: sinon.stub().yields()
|
|
}),
|
|
'../../../../app/src/Features/Email/EmailHandler': (this.EmailHandler = {
|
|
sendEmail: sinon.stub().yields()
|
|
}),
|
|
'logger-sharelatex': (this.logger = {
|
|
error: sinon.stub()
|
|
}),
|
|
'../../../../app/src/models/User': {
|
|
User: (this.User = {
|
|
findOneAndUpdate: sinon.stub().yields(undefined, this.user),
|
|
findOne: sinon.stub().yields(undefined, undefined)
|
|
})
|
|
},
|
|
'settings-sharelatex': {
|
|
oauthProviders: {
|
|
google: {
|
|
name: 'Google'
|
|
},
|
|
orcid: {
|
|
name: 'Orcid'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
})
|
|
})
|
|
describe('getUser', function() {
|
|
it('should an error when missing providerId or externalUserId', function(done) {
|
|
this.ThirdPartyIdentityManager.getUser(
|
|
undefined,
|
|
undefined,
|
|
(error, user) => {
|
|
expect(error).to.exist
|
|
expect(error.message).to.equal('invalid SSO arguments')
|
|
expect(error.info).to.deep.equal({
|
|
externalUserId: undefined,
|
|
providerId: undefined
|
|
})
|
|
done()
|
|
}
|
|
)
|
|
})
|
|
describe('when user linked', function() {
|
|
beforeEach(function() {
|
|
this.User.findOne.yields(undefined, this.user)
|
|
})
|
|
|
|
it('should return the user', async function() {
|
|
this.User.findOne.returns(undefined, this.user)
|
|
const user = await this.ThirdPartyIdentityManager.promises.getUser(
|
|
'google',
|
|
'an-id-linked'
|
|
)
|
|
expect(user).to.deep.equal(this.user)
|
|
})
|
|
})
|
|
it('should return ThirdPartyUserNotFoundError when no user linked', function(done) {
|
|
this.ThirdPartyIdentityManager.getUser(
|
|
'google',
|
|
'an-id-not-linked',
|
|
(error, user) => {
|
|
expect(error).to.exist
|
|
expect(error.name).to.equal('ThirdPartyUserNotFoundError')
|
|
done()
|
|
}
|
|
)
|
|
})
|
|
})
|
|
describe('link', function() {
|
|
it('should send email alert', async function() {
|
|
await this.ThirdPartyIdentityManager.promises.link(
|
|
this.userId,
|
|
'google',
|
|
this.externalUserId,
|
|
this.externalData,
|
|
this.auditLog
|
|
)
|
|
const emailCall = this.EmailHandler.sendEmail.getCall(0)
|
|
expect(emailCall.args[0]).to.equal('securityAlert')
|
|
expect(emailCall.args[1].actionDescribed).to.contain(
|
|
'a Google account was linked'
|
|
)
|
|
})
|
|
|
|
it('should update user audit log', async function() {
|
|
await this.ThirdPartyIdentityManager.promises.link(
|
|
this.userId,
|
|
'google',
|
|
this.externalUserId,
|
|
this.externalData,
|
|
this.auditLog
|
|
)
|
|
expect(this.UserAuditLogHandler.addEntry).to.have.been.calledOnceWith(
|
|
this.userId,
|
|
'link-sso',
|
|
this.auditLog.initiatorId,
|
|
this.auditLog.ipAddress,
|
|
{
|
|
providerId: 'google'
|
|
}
|
|
)
|
|
})
|
|
describe('errors', function() {
|
|
const anError = new Error('oops')
|
|
it('should not unlink if the UserAuditLogHandler throws an error', function(done) {
|
|
this.UserAuditLogHandler.addEntry.yields(anError)
|
|
this.ThirdPartyIdentityManager.link(
|
|
this.userId,
|
|
'google',
|
|
this.externalUserId,
|
|
this.externalData,
|
|
this.auditLog,
|
|
error => {
|
|
expect(error).to.exist
|
|
expect(error).to.equal(anError)
|
|
expect(this.User.findOneAndUpdate).to.not.have.been.called
|
|
done()
|
|
}
|
|
)
|
|
})
|
|
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,
|
|
this.auditLog,
|
|
error => {
|
|
expect(error).to.not.exist
|
|
expect(this.logger.error.lastCall).to.be.calledWithExactly(
|
|
{
|
|
err: anError,
|
|
userId: this.userId
|
|
},
|
|
'could not send security alert email when Google account linked'
|
|
)
|
|
done()
|
|
}
|
|
)
|
|
})
|
|
})
|
|
})
|
|
})
|
|
describe('unlink', function() {
|
|
it('should send email alert', async function() {
|
|
await this.ThirdPartyIdentityManager.promises.unlink(
|
|
this.userId,
|
|
'orcid',
|
|
this.auditLog
|
|
)
|
|
const emailCall = this.EmailHandler.sendEmail.getCall(0)
|
|
expect(emailCall.args[0]).to.equal('securityAlert')
|
|
expect(emailCall.args[1].actionDescribed).to.contain(
|
|
'an Orcid account was unlinked from'
|
|
)
|
|
})
|
|
it('should update user audit log', async function() {
|
|
await this.ThirdPartyIdentityManager.promises.unlink(
|
|
this.userId,
|
|
'orcid',
|
|
this.auditLog
|
|
)
|
|
expect(this.UserAuditLogHandler.addEntry).to.have.been.calledOnceWith(
|
|
this.userId,
|
|
'unlink-sso',
|
|
this.auditLog.initiatorId,
|
|
this.auditLog.ipAddress,
|
|
{
|
|
providerId: 'orcid'
|
|
}
|
|
)
|
|
})
|
|
describe('errors', function() {
|
|
const anError = new Error('oops')
|
|
it('should not unlink if the UserAuditLogHandler throws an error', function(done) {
|
|
this.UserAuditLogHandler.addEntry.yields(anError)
|
|
this.ThirdPartyIdentityManager.unlink(
|
|
this.userId,
|
|
'orcid',
|
|
this.auditLog,
|
|
error => {
|
|
expect(error).to.exist
|
|
expect(error).to.equal(anError)
|
|
expect(this.User.findOneAndUpdate).to.not.have.been.called
|
|
done()
|
|
}
|
|
)
|
|
expect(this.User.findOneAndUpdate).to.not.have.been.called
|
|
})
|
|
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',
|
|
this.auditLog,
|
|
error => {
|
|
expect(error).to.not.exist
|
|
expect(this.logger.error.lastCall).to.be.calledWithExactly(
|
|
{
|
|
err: anError,
|
|
userId: this.userId
|
|
},
|
|
'could not send security alert email when Google account no longer linked'
|
|
)
|
|
done()
|
|
}
|
|
)
|
|
})
|
|
})
|
|
})
|
|
})
|
|
})
|