2020-09-23 04:49:26 -04:00
|
|
|
const { ObjectId } = require('mongodb')
|
2019-05-29 05:21:06 -04:00
|
|
|
const should = require('chai').should()
|
|
|
|
const SandboxedModule = require('sandboxed-module')
|
|
|
|
const assert = require('assert')
|
|
|
|
const path = require('path')
|
|
|
|
const sinon = require('sinon')
|
|
|
|
const modulePath = path.join(
|
|
|
|
__dirname,
|
|
|
|
'../../../../app/src/Features/User/UserGetter'
|
|
|
|
)
|
|
|
|
const { expect } = require('chai')
|
|
|
|
const Errors = require('../../../../app/src/Features/Errors/Errors')
|
|
|
|
|
|
|
|
describe('UserGetter', function() {
|
|
|
|
beforeEach(function() {
|
|
|
|
this.fakeUser = {
|
|
|
|
_id: '12390i',
|
|
|
|
email: 'email2@foo.bar',
|
|
|
|
emails: [
|
2020-05-22 07:16:52 -04:00
|
|
|
{
|
|
|
|
email: 'email1@foo.bar',
|
|
|
|
reversedHostname: 'rab.oof',
|
|
|
|
confirmedAt: new Date()
|
|
|
|
},
|
2019-05-29 05:21:06 -04:00
|
|
|
{ email: 'email2@foo.bar', reversedHostname: 'rab.oof' }
|
|
|
|
]
|
|
|
|
}
|
|
|
|
this.findOne = sinon.stub().callsArgWith(2, null, this.fakeUser)
|
2020-09-28 09:07:23 -04:00
|
|
|
this.findToArrayStub = sinon.stub().yields(null, [this.fakeUser])
|
|
|
|
this.find = sinon.stub().returns({ toArray: this.findToArrayStub })
|
2019-05-29 05:21:06 -04:00
|
|
|
this.Mongo = {
|
|
|
|
db: {
|
|
|
|
users: {
|
|
|
|
findOne: this.findOne,
|
|
|
|
find: this.find
|
|
|
|
}
|
|
|
|
},
|
|
|
|
ObjectId
|
|
|
|
}
|
|
|
|
const settings = { apis: { v1: { url: 'v1.url', user: '', pass: '' } } }
|
|
|
|
this.getUserAffiliations = sinon.stub().callsArgWith(1, null, [])
|
|
|
|
|
2020-02-27 07:45:53 -05:00
|
|
|
this.UserGetter = SandboxedModule.require(modulePath, {
|
2019-07-15 06:33:47 -04:00
|
|
|
globals: {
|
|
|
|
console: console
|
|
|
|
},
|
2019-05-29 05:21:06 -04:00
|
|
|
requires: {
|
|
|
|
'logger-sharelatex': {
|
|
|
|
log() {}
|
|
|
|
},
|
|
|
|
'../../infrastructure/mongojs': this.Mongo,
|
|
|
|
'metrics-sharelatex': {
|
|
|
|
timeAsyncMethod: sinon.stub()
|
|
|
|
},
|
|
|
|
'settings-sharelatex': settings,
|
|
|
|
'../Institutions/InstitutionsAPI': {
|
|
|
|
getUserAffiliations: this.getUserAffiliations
|
|
|
|
},
|
2019-08-19 11:06:36 -04:00
|
|
|
'../../infrastructure/Features': {
|
|
|
|
hasFeature: sinon.stub().returns(true)
|
|
|
|
},
|
2019-05-29 05:21:06 -04:00
|
|
|
'../Errors/Errors': Errors
|
|
|
|
}
|
2020-02-27 07:45:53 -05:00
|
|
|
})
|
2019-05-29 05:21:06 -04:00
|
|
|
})
|
|
|
|
|
|
|
|
describe('getUser', function() {
|
|
|
|
it('should get user', function(done) {
|
|
|
|
const query = { _id: 'foo' }
|
|
|
|
const projection = { email: 1 }
|
2020-02-27 07:45:53 -05:00
|
|
|
this.UserGetter.getUser(query, projection, (error, user) => {
|
|
|
|
expect(error).to.not.exist
|
2019-05-29 05:21:06 -04:00
|
|
|
this.findOne.called.should.equal(true)
|
|
|
|
this.findOne.calledWith(query, projection).should.equal(true)
|
|
|
|
user.should.deep.equal(this.fakeUser)
|
2020-02-27 07:45:53 -05:00
|
|
|
done()
|
2019-05-29 05:21:06 -04:00
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2019-06-21 09:46:09 -04:00
|
|
|
it('should not allow null query', function(done) {
|
2020-06-02 10:02:06 -04:00
|
|
|
this.UserGetter.getUser(null, {}, error => {
|
2019-05-29 05:21:06 -04:00
|
|
|
error.should.exist
|
2020-06-02 10:02:06 -04:00
|
|
|
error.message.should.equal('no query provided')
|
|
|
|
done()
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('getUsers', function() {
|
|
|
|
it('should get users with array of userIds', function(done) {
|
|
|
|
const query = [new ObjectId()]
|
|
|
|
const projection = { email: 1 }
|
|
|
|
this.UserGetter.getUsers(query, projection, (error, users) => {
|
|
|
|
expect(error).to.not.exist
|
|
|
|
this.find.should.have.been.calledWithMatch(
|
|
|
|
{ _id: { $in: query } },
|
|
|
|
projection
|
|
|
|
)
|
|
|
|
users.should.deep.equal([this.fakeUser])
|
|
|
|
done()
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should not allow null query', function(done) {
|
|
|
|
this.UserGetter.getUser(null, {}, error => {
|
|
|
|
error.should.exist
|
|
|
|
error.message.should.equal('no query provided')
|
2020-02-27 07:45:53 -05:00
|
|
|
done()
|
2019-05-29 05:21:06 -04:00
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('getUserFullEmails', function() {
|
|
|
|
it('should get user', function(done) {
|
|
|
|
this.UserGetter.getUser = sinon
|
|
|
|
.stub()
|
|
|
|
.callsArgWith(2, null, this.fakeUser)
|
2020-05-22 07:16:52 -04:00
|
|
|
const projection = { email: 1, emails: 1, samlIdentifiers: 1 }
|
2020-02-27 07:45:53 -05:00
|
|
|
this.UserGetter.getUserFullEmails(
|
2019-05-29 05:21:06 -04:00
|
|
|
this.fakeUser._id,
|
|
|
|
(error, fullEmails) => {
|
2020-02-27 07:45:53 -05:00
|
|
|
expect(error).to.not.exist
|
2019-05-29 05:21:06 -04:00
|
|
|
this.UserGetter.getUser.called.should.equal(true)
|
|
|
|
this.UserGetter.getUser
|
|
|
|
.calledWith(this.fakeUser._id, projection)
|
|
|
|
.should.equal(true)
|
2020-02-27 07:45:53 -05:00
|
|
|
done()
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should fetch emails data', function(done) {
|
|
|
|
this.UserGetter.getUser = sinon
|
|
|
|
.stub()
|
|
|
|
.callsArgWith(2, null, this.fakeUser)
|
2020-02-27 07:45:53 -05:00
|
|
|
this.UserGetter.getUserFullEmails(
|
2019-05-29 05:21:06 -04:00
|
|
|
this.fakeUser._id,
|
|
|
|
(error, fullEmails) => {
|
2020-02-27 07:45:53 -05:00
|
|
|
expect(error).to.not.exist
|
2019-05-29 05:21:06 -04:00
|
|
|
assert.deepEqual(fullEmails, [
|
|
|
|
{
|
|
|
|
email: 'email1@foo.bar',
|
|
|
|
reversedHostname: 'rab.oof',
|
2020-05-22 07:16:52 -04:00
|
|
|
confirmedAt: this.fakeUser.emails[0].confirmedAt,
|
|
|
|
emailHasInstitutionLicence: false,
|
2019-05-29 05:21:06 -04:00
|
|
|
default: false
|
|
|
|
},
|
|
|
|
{
|
|
|
|
email: 'email2@foo.bar',
|
|
|
|
reversedHostname: 'rab.oof',
|
2020-05-22 07:16:52 -04:00
|
|
|
emailHasInstitutionLicence: false,
|
2019-05-29 05:21:06 -04:00
|
|
|
default: true
|
|
|
|
}
|
|
|
|
])
|
2020-02-27 07:45:53 -05:00
|
|
|
done()
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should merge affiliation data', function(done) {
|
|
|
|
this.UserGetter.getUser = sinon
|
|
|
|
.stub()
|
|
|
|
.callsArgWith(2, null, this.fakeUser)
|
|
|
|
const affiliationsData = [
|
|
|
|
{
|
|
|
|
email: 'email1@foo.bar',
|
|
|
|
role: 'Prof',
|
|
|
|
department: 'Maths',
|
|
|
|
inferred: false,
|
2020-02-18 06:36:53 -05:00
|
|
|
licence: 'pro_plus',
|
2020-05-22 07:16:52 -04:00
|
|
|
institution: {
|
|
|
|
name: 'University Name',
|
|
|
|
isUniversity: true,
|
|
|
|
confirmed: true
|
|
|
|
}
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
|
|
|
]
|
|
|
|
this.getUserAffiliations.callsArgWith(1, null, affiliationsData)
|
2020-02-27 07:45:53 -05:00
|
|
|
this.UserGetter.getUserFullEmails(
|
2019-05-29 05:21:06 -04:00
|
|
|
this.fakeUser._id,
|
|
|
|
(error, fullEmails) => {
|
2020-02-27 07:45:53 -05:00
|
|
|
expect(error).to.not.exist
|
2019-05-29 05:21:06 -04:00
|
|
|
assert.deepEqual(fullEmails, [
|
|
|
|
{
|
|
|
|
email: 'email1@foo.bar',
|
|
|
|
reversedHostname: 'rab.oof',
|
2020-05-22 07:16:52 -04:00
|
|
|
confirmedAt: this.fakeUser.emails[0].confirmedAt,
|
2019-05-29 05:21:06 -04:00
|
|
|
default: false,
|
2020-05-22 07:16:52 -04:00
|
|
|
emailHasInstitutionLicence: true,
|
2019-05-29 05:21:06 -04:00
|
|
|
affiliation: {
|
|
|
|
institution: affiliationsData[0].institution,
|
|
|
|
inferred: affiliationsData[0].inferred,
|
|
|
|
department: affiliationsData[0].department,
|
2020-02-18 06:36:53 -05:00
|
|
|
role: affiliationsData[0].role,
|
|
|
|
licence: affiliationsData[0].licence
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
email: 'email2@foo.bar',
|
|
|
|
reversedHostname: 'rab.oof',
|
2020-05-22 07:16:52 -04:00
|
|
|
emailHasInstitutionLicence: false,
|
|
|
|
default: true
|
|
|
|
}
|
|
|
|
])
|
|
|
|
done()
|
|
|
|
}
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should merge SAML identifier', function(done) {
|
|
|
|
const fakeSamlIdentifiers = [
|
|
|
|
{ providerId: 'saml_id', exteranlUserId: 'whatever' }
|
|
|
|
]
|
|
|
|
const fakeUserWithSaml = this.fakeUser
|
|
|
|
fakeUserWithSaml.emails[0].samlProviderId = 'saml_id'
|
|
|
|
fakeUserWithSaml.samlIdentifiers = fakeSamlIdentifiers
|
|
|
|
this.UserGetter.getUser = sinon.stub().yields(null, this.fakeUser)
|
|
|
|
this.getUserAffiliations.callsArgWith(1, null, [])
|
|
|
|
this.UserGetter.getUserFullEmails(
|
|
|
|
this.fakeUser._id,
|
|
|
|
(error, fullEmails) => {
|
|
|
|
expect(error).to.not.exist
|
|
|
|
assert.deepEqual(fullEmails, [
|
|
|
|
{
|
|
|
|
email: 'email1@foo.bar',
|
|
|
|
reversedHostname: 'rab.oof',
|
|
|
|
confirmedAt: this.fakeUser.emails[0].confirmedAt,
|
|
|
|
default: false,
|
|
|
|
emailHasInstitutionLicence: false,
|
|
|
|
samlProviderId: 'saml_id',
|
|
|
|
samlIdentifier: fakeSamlIdentifiers[0]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
email: 'email2@foo.bar',
|
|
|
|
reversedHostname: 'rab.oof',
|
|
|
|
emailHasInstitutionLicence: false,
|
2019-05-29 05:21:06 -04:00
|
|
|
default: true
|
|
|
|
}
|
|
|
|
])
|
2020-02-27 07:45:53 -05:00
|
|
|
done()
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2019-06-21 09:46:09 -04:00
|
|
|
it('should get user when it has no emails field', function(done) {
|
2019-05-29 05:21:06 -04:00
|
|
|
this.fakeUser = {
|
|
|
|
_id: '12390i',
|
|
|
|
email: 'email2@foo.bar'
|
|
|
|
}
|
|
|
|
this.UserGetter.getUser = sinon
|
|
|
|
.stub()
|
|
|
|
.callsArgWith(2, null, this.fakeUser)
|
2020-05-22 07:16:52 -04:00
|
|
|
const projection = { email: 1, emails: 1, samlIdentifiers: 1 }
|
2020-02-27 07:45:53 -05:00
|
|
|
this.UserGetter.getUserFullEmails(
|
2019-05-29 05:21:06 -04:00
|
|
|
this.fakeUser._id,
|
|
|
|
(error, fullEmails) => {
|
2020-02-27 07:45:53 -05:00
|
|
|
expect(error).to.not.exist
|
2019-05-29 05:21:06 -04:00
|
|
|
this.UserGetter.getUser.called.should.equal(true)
|
|
|
|
this.UserGetter.getUser
|
|
|
|
.calledWith(this.fakeUser._id, projection)
|
|
|
|
.should.equal(true)
|
|
|
|
assert.deepEqual(fullEmails, [])
|
2020-02-27 07:45:53 -05:00
|
|
|
done()
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
|
|
|
)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('getUserbyMainEmail', function() {
|
|
|
|
it('query user by main email', function(done) {
|
|
|
|
const email = 'hello@world.com'
|
|
|
|
const projection = { emails: 1 }
|
2020-02-27 07:45:53 -05:00
|
|
|
this.UserGetter.getUserByMainEmail(email, projection, (error, user) => {
|
|
|
|
expect(error).to.not.exist
|
|
|
|
this.findOne.called.should.equal(true)
|
|
|
|
this.findOne.calledWith({ email }, projection).should.equal(true)
|
|
|
|
done()
|
|
|
|
})
|
2019-05-29 05:21:06 -04:00
|
|
|
})
|
|
|
|
|
|
|
|
it('return user if found', function(done) {
|
|
|
|
const email = 'hello@world.com'
|
2020-02-27 07:45:53 -05:00
|
|
|
this.UserGetter.getUserByMainEmail(email, (error, user) => {
|
|
|
|
expect(error).to.not.exist
|
2019-05-29 05:21:06 -04:00
|
|
|
user.should.deep.equal(this.fakeUser)
|
2020-02-27 07:45:53 -05:00
|
|
|
done()
|
2019-05-29 05:21:06 -04:00
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2019-06-21 09:46:09 -04:00
|
|
|
it('trim email', function(done) {
|
2019-05-29 05:21:06 -04:00
|
|
|
const email = 'hello@world.com'
|
2020-02-27 07:45:53 -05:00
|
|
|
this.UserGetter.getUserByMainEmail(` ${email} `, (error, user) => {
|
|
|
|
expect(error).to.not.exist
|
2019-05-29 05:21:06 -04:00
|
|
|
this.findOne.called.should.equal(true)
|
|
|
|
this.findOne.calledWith({ email }).should.equal(true)
|
2020-02-27 07:45:53 -05:00
|
|
|
done()
|
2019-05-29 05:21:06 -04:00
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('getUserByAnyEmail', function() {
|
|
|
|
it('query user for any email', function(done) {
|
|
|
|
const email = 'hello@world.com'
|
|
|
|
const expectedQuery = {
|
|
|
|
emails: { $exists: true },
|
|
|
|
'emails.email': email
|
|
|
|
}
|
|
|
|
const projection = { emails: 1 }
|
2020-02-27 07:45:53 -05:00
|
|
|
this.UserGetter.getUserByAnyEmail(
|
2019-05-29 05:21:06 -04:00
|
|
|
` ${email} `,
|
|
|
|
projection,
|
|
|
|
(error, user) => {
|
2020-02-27 07:45:53 -05:00
|
|
|
expect(error).to.not.exist
|
2019-05-29 05:21:06 -04:00
|
|
|
this.findOne.calledWith(expectedQuery, projection).should.equal(true)
|
|
|
|
user.should.deep.equal(this.fakeUser)
|
2020-02-27 07:45:53 -05:00
|
|
|
done()
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('query contains $exists:true so partial index is used', function(done) {
|
|
|
|
const expectedQuery = {
|
|
|
|
emails: { $exists: true },
|
|
|
|
'emails.email': ''
|
|
|
|
}
|
2020-02-27 07:45:53 -05:00
|
|
|
this.UserGetter.getUserByAnyEmail('', {}, (error, user) => {
|
|
|
|
expect(error).to.not.exist
|
2019-05-29 05:21:06 -04:00
|
|
|
this.findOne.calledWith(expectedQuery, {}).should.equal(true)
|
2020-02-27 07:45:53 -05:00
|
|
|
done()
|
2019-05-29 05:21:06 -04:00
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2019-06-21 09:46:09 -04:00
|
|
|
it('checks main email as well', function(done) {
|
2019-05-29 05:21:06 -04:00
|
|
|
this.findOne.callsArgWith(2, null, null)
|
|
|
|
const email = 'hello@world.com'
|
|
|
|
const projection = { emails: 1 }
|
2020-02-27 07:45:53 -05:00
|
|
|
this.UserGetter.getUserByAnyEmail(
|
2019-05-29 05:21:06 -04:00
|
|
|
` ${email} `,
|
|
|
|
projection,
|
|
|
|
(error, user) => {
|
2020-02-27 07:45:53 -05:00
|
|
|
expect(error).to.not.exist
|
2019-05-29 05:21:06 -04:00
|
|
|
this.findOne.calledTwice.should.equal(true)
|
|
|
|
this.findOne.calledWith({ email }, projection).should.equal(true)
|
2020-02-27 07:45:53 -05:00
|
|
|
done()
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
|
|
|
)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2019-08-07 10:04:04 -04:00
|
|
|
describe('getUsersByHostname', function() {
|
2019-05-29 05:21:06 -04:00
|
|
|
it('should find user by hostname', function(done) {
|
|
|
|
const hostname = 'bar.foo'
|
|
|
|
const expectedQuery = {
|
|
|
|
emails: { $exists: true },
|
|
|
|
'emails.reversedHostname': hostname
|
|
|
|
.split('')
|
|
|
|
.reverse()
|
|
|
|
.join('')
|
|
|
|
}
|
|
|
|
const projection = { emails: 1 }
|
2020-02-27 07:45:53 -05:00
|
|
|
this.UserGetter.getUsersByHostname(
|
2019-05-29 05:21:06 -04:00
|
|
|
hostname,
|
|
|
|
projection,
|
|
|
|
(error, users) => {
|
2020-02-27 07:45:53 -05:00
|
|
|
expect(error).to.not.exist
|
2019-07-29 05:47:44 -04:00
|
|
|
this.find.calledOnce.should.equal(true)
|
|
|
|
this.find.calledWith(expectedQuery, projection).should.equal(true)
|
2020-02-27 07:45:53 -05:00
|
|
|
done()
|
2019-07-29 05:47:44 -04:00
|
|
|
}
|
|
|
|
)
|
2019-08-07 10:04:04 -04:00
|
|
|
})
|
|
|
|
})
|
2019-07-29 05:47:44 -04:00
|
|
|
|
2019-08-07 10:04:04 -04:00
|
|
|
describe('getUsersByV1Id', function() {
|
2019-07-29 05:47:44 -04:00
|
|
|
it('should find users by list of v1 ids', function(done) {
|
|
|
|
const v1Ids = [501]
|
|
|
|
const expectedQuery = {
|
|
|
|
'overleaf.id': { $in: v1Ids }
|
|
|
|
}
|
|
|
|
const projection = { emails: 1 }
|
2020-02-27 07:45:53 -05:00
|
|
|
this.UserGetter.getUsersByV1Ids(v1Ids, projection, (error, users) => {
|
|
|
|
expect(error).to.not.exist
|
|
|
|
this.find.calledOnce.should.equal(true)
|
|
|
|
this.find.calledWith(expectedQuery, projection).should.equal(true)
|
|
|
|
done()
|
|
|
|
})
|
2019-08-07 10:04:04 -04:00
|
|
|
})
|
|
|
|
})
|
2019-05-29 05:21:06 -04:00
|
|
|
|
2019-06-21 09:46:09 -04:00
|
|
|
describe('ensureUniqueEmailAddress', function() {
|
2019-05-29 05:21:06 -04:00
|
|
|
beforeEach(function() {
|
2020-02-27 07:45:53 -05:00
|
|
|
this.UserGetter.getUserByAnyEmail = sinon.stub()
|
2019-05-29 05:21:06 -04:00
|
|
|
})
|
|
|
|
|
|
|
|
it('should return error if existing user is found', function(done) {
|
|
|
|
this.UserGetter.getUserByAnyEmail.callsArgWith(1, null, this.fakeUser)
|
2020-02-27 07:45:53 -05:00
|
|
|
this.UserGetter.ensureUniqueEmailAddress(this.newEmail, err => {
|
2019-05-29 05:21:06 -04:00
|
|
|
should.exist(err)
|
|
|
|
expect(err).to.be.an.instanceof(Errors.EmailExistsError)
|
2020-02-27 07:45:53 -05:00
|
|
|
done()
|
2019-05-29 05:21:06 -04:00
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2019-06-21 09:46:09 -04:00
|
|
|
it('should return null if no user is found', function(done) {
|
2019-05-29 05:21:06 -04:00
|
|
|
this.UserGetter.getUserByAnyEmail.callsArgWith(1)
|
2020-02-27 07:45:53 -05:00
|
|
|
this.UserGetter.ensureUniqueEmailAddress(this.newEmail, err => {
|
2019-05-29 05:21:06 -04:00
|
|
|
should.not.exist(err)
|
2020-02-27 07:45:53 -05:00
|
|
|
done()
|
2019-05-29 05:21:06 -04:00
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|