| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188 |
- const { expect } = require('chai')
- const async = require('async')
- const moment = require('moment')
- const Features = require('../../../app/src/infrastructure/Features')
- const User = require('./helpers/User')
- const UserHelper = require('./helpers/UserHelper')
- const UserUpdater = require('../../../app/src/Features/User/UserUpdater')
- const { db, ObjectId } = require('../../../app/src/infrastructure/mongodb')
- const MockV1ApiClass = require('./mocks/MockV1Api')
- const expectErrorResponse = require('./helpers/expectErrorResponse')
- let MockV1Api
- before(function () {
- MockV1Api = MockV1ApiClass.instance()
- })
- describe('UserEmails', function () {
- beforeEach(function (done) {
- this.timeout(20000)
- this.user = new User()
- this.user.login(done)
- })
- describe('confirming an email', function () {
- it('should confirm the email', function (done) {
- let token = null
- async.series(
- [
- cb => {
- this.user.request(
- {
- method: 'POST',
- url: '/user/emails',
- json: {
- email: 'newly-added-email@example.com',
- },
- },
- (error, response, body) => {
- expect(error).to.not.exist
- expect(response.statusCode).to.equal(204)
- cb()
- }
- )
- },
- cb => {
- this.user.request(
- { url: '/user/emails', json: true },
- (error, response, body) => {
- expect(error).to.not.exist
- expect(response.statusCode).to.equal(200)
- expect(body[0].confirmedAt).to.not.exist
- expect(body[0].reconfirmedAt).to.not.exist
- expect(body[1].confirmedAt).to.not.exist
- expect(body[1].reconfirmedAt).to.not.exist
- cb()
- }
- )
- },
- cb => {
- db.tokens
- .find({
- use: 'email_confirmation',
- 'data.user_id': this.user._id,
- usedAt: { $exists: false },
- })
- .toArray((error, tokens) => {
- expect(error).to.not.exist
- // There should only be one confirmation token at the moment
- expect(tokens.length).to.equal(1)
- expect(tokens[0].data.email).to.equal(
- 'newly-added-email@example.com'
- )
- expect(tokens[0].data.user_id).to.equal(this.user._id)
- ;({ token } = tokens[0])
- cb()
- })
- },
- cb => {
- this.user.request(
- {
- method: 'POST',
- url: '/user/emails/confirm',
- json: {
- token,
- },
- },
- (error, response, body) => {
- expect(error).to.not.exist
- expect(response.statusCode).to.equal(200)
- cb()
- }
- )
- },
- cb => {
- this.user.request(
- { url: '/user/emails', json: true },
- (error, response, body) => {
- expect(error).to.not.exist
- expect(response.statusCode).to.equal(200)
- expect(body[0].confirmedAt).to.not.exist
- expect(body[0].reconfirmedAt).to.not.exist
- expect(body[1].confirmedAt).to.exist
- expect(body[1].reconfirmedAt).to.exist
- expect(body[1].reconfirmedAt).to.deep.equal(body[1].confirmedAt)
- cb()
- }
- )
- },
- cb => {
- db.tokens
- .find({
- use: 'email_confirmation',
- 'data.user_id': this.user._id,
- usedAt: { $exists: false },
- })
- .toArray((error, tokens) => {
- expect(error).to.not.exist
- // Token should be deleted after use
- expect(tokens.length).to.equal(0)
- cb()
- })
- },
- ],
- done
- )
- })
- it('should not allow confirmation of the email if the user has changed', function (done) {
- let token1 = null
- let token2 = null
- this.user2 = new User()
- this.email = 'duplicate-email@example.com'
- async.series(
- [
- cb => this.user2.login(cb),
- cb => {
- // Create email for first user
- this.user.request(
- {
- method: 'POST',
- url: '/user/emails',
- json: { email: this.email },
- },
- cb
- )
- },
- cb => {
- db.tokens
- .find({
- use: 'email_confirmation',
- 'data.user_id': this.user._id,
- usedAt: { $exists: false },
- })
- .toArray((error, tokens) => {
- expect(error).to.not.exist
- // There should only be one confirmation token at the moment
- expect(tokens.length).to.equal(1)
- expect(tokens[0].data.email).to.equal(this.email)
- expect(tokens[0].data.user_id).to.equal(this.user._id)
- token1 = tokens[0].token
- cb()
- })
- },
- cb => {
- // Delete the email from the first user
- this.user.request(
- {
- method: 'POST',
- url: '/user/emails/delete',
- json: { email: this.email },
- },
- cb
- )
- },
- cb => {
- // Create email for second user
- this.user2.request(
- {
- method: 'POST',
- url: '/user/emails',
- json: { email: this.email },
- },
- cb
- )
- },
- cb => {
- // Original confirmation token should no longer work
- this.user.request(
- {
- method: 'POST',
- url: '/user/emails/confirm',
- json: {
- token: token1,
- },
- },
- (error, response, body) => {
- expect(error).to.not.exist
- expect(response.statusCode).to.equal(404)
- cb()
- }
- )
- },
- cb => {
- db.tokens
- .find({
- use: 'email_confirmation',
- 'data.user_id': this.user2._id,
- usedAt: { $exists: false },
- })
- .toArray((error, tokens) => {
- expect(error).to.not.exist
- // The first token has been used, so this should be token2 now
- expect(tokens.length).to.equal(1)
- expect(tokens[0].data.email).to.equal(this.email)
- expect(tokens[0].data.user_id).to.equal(this.user2._id)
- token2 = tokens[0].token
- cb()
- })
- },
- cb => {
- // Second user should be able to confirm the email
- this.user2.request(
- {
- method: 'POST',
- url: '/user/emails/confirm',
- json: {
- token: token2,
- },
- },
- (error, response, body) => {
- expect(error).to.not.exist
- expect(response.statusCode).to.equal(200)
- cb()
- }
- )
- },
- cb => {
- this.user2.request(
- { url: '/user/emails', json: true },
- (error, response, body) => {
- expect(error).to.not.exist
- expect(response.statusCode).to.equal(200)
- expect(body[0].confirmedAt).to.not.exist
- expect(body[1].confirmedAt).to.exist
- cb()
- }
- )
- },
- ],
- done
- )
- })
- })
- describe('reconfirm an email', function () {
- let email, userHelper, confirmedAtDate
- beforeEach(async function () {
- userHelper = new UserHelper()
- email = userHelper.getDefaultEmail()
- userHelper = await UserHelper.createUser({ email })
- userHelper = await UserHelper.loginUser({
- email,
- password: userHelper.getDefaultPassword(),
- })
- // original confirmation
- await userHelper.confirmEmail(userHelper.user._id, email)
- const user = (await UserHelper.getUser({ email })).user
- confirmedAtDate = user.emails[0].confirmedAt
- expect(user.emails[0].confirmedAt).to.exist
- expect(user.emails[0].reconfirmedAt).to.exist
- })
- it('should set reconfirmedAt and not reset confirmedAt', async function () {
- await userHelper.confirmEmail(userHelper.user._id, email)
- const user = (await UserHelper.getUser({ email })).user
- expect(user.emails[0].confirmedAt).to.exist
- expect(user.emails[0].reconfirmedAt).to.exist
- expect(user.emails[0].confirmedAt).to.deep.equal(confirmedAtDate)
- expect(user.emails[0].reconfirmedAt).to.not.deep.equal(
- user.emails[0].confirmedAt
- )
- expect(user.emails[0].reconfirmedAt > user.emails[0].confirmedAt).to.be
- .true
- })
- })
- describe('with an expired token', function () {
- it('should not confirm the email', function (done) {
- let token = null
- async.series(
- [
- cb => {
- this.user.request(
- {
- method: 'POST',
- url: '/user/emails',
- json: {
- email: (this.email = 'expired-token-email@example.com'),
- },
- },
- (error, response, body) => {
- expect(error).to.not.exist
- expect(response.statusCode).to.equal(204)
- cb()
- }
- )
- },
- cb => {
- db.tokens
- .find({
- use: 'email_confirmation',
- 'data.user_id': this.user._id,
- usedAt: { $exists: false },
- })
- .toArray((error, tokens) => {
- expect(error).to.not.exist
- // There should only be one confirmation token at the moment
- expect(tokens.length).to.equal(1)
- expect(tokens[0].data.email).to.equal(this.email)
- expect(tokens[0].data.user_id).to.equal(this.user._id)
- ;({ token } = tokens[0])
- cb()
- })
- },
- cb => {
- db.tokens.update(
- {
- token,
- },
- {
- $set: {
- expiresAt: new Date(Date.now() - 1000000),
- },
- },
- cb
- )
- },
- cb => {
- this.user.request(
- {
- method: 'POST',
- url: '/user/emails/confirm',
- json: {
- token,
- },
- },
- (error, response, body) => {
- expect(error).to.not.exist
- expect(response.statusCode).to.equal(404)
- cb()
- }
- )
- },
- ],
- done
- )
- })
- })
- describe('resending the confirmation', function () {
- it('should generate a new token', function (done) {
- async.series(
- [
- cb => {
- this.user.request(
- {
- method: 'POST',
- url: '/user/emails',
- json: {
- email: 'reconfirmation-email@example.com',
- },
- },
- (error, response, body) => {
- expect(error).to.not.exist
- expect(response.statusCode).to.equal(204)
- cb()
- }
- )
- },
- cb => {
- db.tokens
- .find({
- use: 'email_confirmation',
- 'data.user_id': this.user._id,
- usedAt: { $exists: false },
- })
- .toArray((error, tokens) => {
- expect(error).to.not.exist
- // There should only be one confirmation token at the moment
- expect(tokens.length).to.equal(1)
- expect(tokens[0].data.email).to.equal(
- 'reconfirmation-email@example.com'
- )
- expect(tokens[0].data.user_id).to.equal(this.user._id)
- cb()
- })
- },
- cb => {
- this.user.request(
- {
- method: 'POST',
- url: '/user/emails/resend_confirmation',
- json: {
- email: 'reconfirmation-email@example.com',
- },
- },
- (error, response, body) => {
- expect(error).to.not.exist
- expect(response.statusCode).to.equal(200)
- cb()
- }
- )
- },
- cb => {
- db.tokens
- .find({
- use: 'email_confirmation',
- 'data.user_id': this.user._id,
- usedAt: { $exists: false },
- })
- .toArray((error, tokens) => {
- expect(error).to.not.exist
- // There should be two tokens now
- expect(tokens.length).to.equal(2)
- expect(tokens[0].data.email).to.equal(
- 'reconfirmation-email@example.com'
- )
- expect(tokens[0].data.user_id).to.equal(this.user._id)
- expect(tokens[1].data.email).to.equal(
- 'reconfirmation-email@example.com'
- )
- expect(tokens[1].data.user_id).to.equal(this.user._id)
- cb()
- })
- },
- ],
- done
- )
- })
- it('should create a new token if none exists', function (done) {
- // This should only be for users that have sign up with their main
- // emails before the confirmation system existed
- async.series(
- [
- cb => {
- db.tokens.remove(
- {
- use: 'email_confirmation',
- 'data.user_id': this.user._id,
- usedAt: { $exists: false },
- },
- cb
- )
- },
- cb => {
- this.user.request(
- {
- method: 'POST',
- url: '/user/emails/resend_confirmation',
- json: {
- email: this.user.email,
- },
- },
- (error, response, body) => {
- expect(error).to.not.exist
- expect(response.statusCode).to.equal(200)
- cb()
- }
- )
- },
- cb => {
- db.tokens
- .find({
- use: 'email_confirmation',
- 'data.user_id': this.user._id,
- usedAt: { $exists: false },
- })
- .toArray((error, tokens) => {
- expect(error).to.not.exist
- // There should still only be one confirmation token
- expect(tokens.length).to.equal(1)
- expect(tokens[0].data.email).to.equal(this.user.email)
- expect(tokens[0].data.user_id).to.equal(this.user._id)
- cb()
- })
- },
- ],
- done
- )
- })
- it("should not allow reconfirmation if the email doesn't match the user", function (done) {
- async.series(
- [
- cb => {
- this.user.request(
- {
- method: 'POST',
- url: '/user/emails/resend_confirmation',
- json: {
- email: 'non-matching-email@example.com',
- },
- },
- (error, response, body) => {
- expect(error).to.not.exist
- expect(response.statusCode).to.equal(422)
- cb()
- }
- )
- },
- cb => {
- db.tokens
- .find({
- use: 'email_confirmation',
- 'data.user_id': this.user._id,
- usedAt: { $exists: false },
- })
- .toArray((error, tokens) => {
- expect(error).to.not.exist
- expect(tokens.length).to.equal(0)
- cb()
- })
- },
- ],
- done
- )
- })
- })
- describe('setting a default email', function () {
- it('should update confirmed emails for users not in v1', function (done) {
- async.series(
- [
- cb => {
- this.user.request(
- {
- method: 'POST',
- url: '/user/emails',
- json: {
- email: 'new-confirmed-default@example.com',
- },
- },
- (error, response, body) => {
- expect(error).to.not.exist
- expect(response.statusCode).to.equal(204)
- cb()
- }
- )
- },
- cb => {
- // Mark the email as confirmed
- db.users.updateOne(
- {
- 'emails.email': 'new-confirmed-default@example.com',
- },
- {
- $set: {
- 'emails.$.confirmedAt': new Date(),
- },
- },
- cb
- )
- },
- cb => {
- this.user.request(
- {
- method: 'POST',
- url: '/user/emails/default',
- json: {
- email: 'new-confirmed-default@example.com',
- },
- },
- (error, response, body) => {
- expect(error).to.not.exist
- expect(response.statusCode).to.equal(200)
- cb()
- }
- )
- },
- cb => {
- this.user.request(
- { url: '/user/emails', json: true },
- (error, response, body) => {
- expect(error).to.not.exist
- expect(response.statusCode).to.equal(200)
- expect(body[0].confirmedAt).to.not.exist
- expect(body[0].default).to.equal(false)
- expect(body[1].confirmedAt).to.exist
- expect(body[1].default).to.equal(true)
- cb()
- }
- )
- },
- ],
- done
- )
- })
- it('should not allow changing unconfirmed emails in v1', function (done) {
- async.series(
- [
- cb => {
- db.users.updateOne(
- {
- _id: ObjectId(this.user._id),
- },
- {
- $set: {
- 'overleaf.id': 42,
- },
- },
- cb
- )
- },
- cb => {
- this.user.request(
- {
- method: 'POST',
- url: '/user/emails',
- json: {
- email: 'new-unconfirmed-default@example.com',
- },
- },
- (error, response, body) => {
- expect(error).to.not.exist
- expect(response.statusCode).to.equal(204)
- cb()
- }
- )
- },
- cb => {
- this.user.request(
- {
- method: 'POST',
- url: '/user/emails/default',
- json: {
- email: 'new-unconfirmed-default@example.com',
- },
- },
- (error, response, body) => {
- expect(error).to.not.exist
- expect(response.statusCode).to.equal(409)
- cb()
- }
- )
- },
- cb => {
- this.user.request(
- { url: '/user/emails', json: true },
- (error, response, body) => {
- expect(error).to.not.exist
- expect(body[0].default).to.equal(true)
- expect(body[1].default).to.equal(false)
- cb()
- }
- )
- },
- ],
- done
- )
- })
- it('should not update the email in v1', function (done) {
- async.series(
- [
- cb => {
- db.users.updateOne(
- {
- _id: ObjectId(this.user._id),
- },
- {
- $set: {
- 'overleaf.id': 42,
- },
- },
- cb
- )
- },
- cb => {
- this.user.request(
- {
- method: 'POST',
- url: '/user/emails',
- json: {
- email: 'new-confirmed-default-in-v1@example.com',
- },
- },
- (error, response, body) => {
- expect(error).to.not.exist
- expect(response.statusCode).to.equal(204)
- cb()
- }
- )
- },
- cb => {
- // Mark the email as confirmed
- db.users.updateOne(
- {
- 'emails.email': 'new-confirmed-default-in-v1@example.com',
- },
- {
- $set: {
- 'emails.$.confirmedAt': new Date(),
- },
- },
- cb
- )
- },
- cb => {
- this.user.request(
- {
- method: 'POST',
- url: '/user/emails/default',
- json: {
- email: 'new-confirmed-default-in-v1@example.com',
- },
- },
- (error, response, body) => {
- expect(error).to.not.exist
- expect(response.statusCode).to.equal(200)
- cb()
- }
- )
- },
- ],
- error => {
- expect(error).to.not.exist
- expect(MockV1Api.updateEmail.callCount).to.equal(0)
- done()
- }
- )
- })
- it('should not return an error if the email exists in v1', function (done) {
- MockV1Api.existingEmails.push('exists-in-v1@example.com')
- async.series(
- [
- cb => {
- db.users.updateOne(
- {
- _id: ObjectId(this.user._id),
- },
- {
- $set: {
- 'overleaf.id': 42,
- },
- },
- cb
- )
- },
- cb => {
- this.user.request(
- {
- method: 'POST',
- url: '/user/emails',
- json: {
- email: 'exists-in-v1@example.com',
- },
- },
- (error, response, body) => {
- expect(error).to.not.exist
- expect(response.statusCode).to.equal(204)
- cb()
- }
- )
- },
- cb => {
- // Mark the email as confirmed
- db.users.updateOne(
- {
- 'emails.email': 'exists-in-v1@example.com',
- },
- {
- $set: {
- 'emails.$.confirmedAt': new Date(),
- },
- },
- cb
- )
- },
- cb => {
- this.user.request(
- {
- method: 'POST',
- url: '/user/emails/default',
- json: {
- email: 'exists-in-v1@example.com',
- },
- },
- (error, response, body) => {
- expect(error).to.not.exist
- expect(response.statusCode).to.equal(200)
- cb()
- }
- )
- },
- cb => {
- this.user.request(
- { url: '/user/emails', json: true },
- (error, response, body) => {
- expect(error).to.not.exist
- expect(body[0].default).to.equal(false)
- expect(body[1].default).to.equal(true)
- cb()
- }
- )
- },
- ],
- done
- )
- })
- describe('audit log', function () {
- const originalEmail = 'original@overleaf.com'
- let otherEmail, response, userHelper, user, userId
- beforeEach(async function () {
- otherEmail = 'other@overleaf.com'
- userHelper = new UserHelper()
- userHelper = await UserHelper.createUser({
- email: originalEmail,
- })
- userHelper = await UserHelper.loginUser({
- email: originalEmail,
- password: userHelper.getDefaultPassword(),
- })
- userId = userHelper.user._id
- response = await userHelper.request.post({
- form: {
- email: otherEmail,
- },
- simple: false,
- uri: '/user/emails',
- })
- expect(response.statusCode).to.equal(204)
- const token = (
- await db.tokens.findOne({
- 'data.user_id': userId.toString(),
- 'data.email': otherEmail,
- })
- ).token
- response = await userHelper.request.post(`/user/emails/confirm`, {
- form: {
- token,
- },
- simple: false,
- })
- expect(response.statusCode).to.equal(200)
- response = await userHelper.request.post('/user/emails/default', {
- form: {
- email: otherEmail,
- },
- simple: false,
- })
- expect(response.statusCode).to.equal(200)
- userHelper = await UserHelper.getUser(userId)
- user = userHelper.user
- })
- it('should be updated', function () {
- const auditLog = userHelper.getAuditLogWithoutNoise()
- const entry = auditLog[auditLog.length - 1]
- expect(typeof entry.initiatorId).to.equal('object')
- expect(entry.initiatorId).to.deep.equal(user._id)
- expect(entry.ipAddress).to.equal('127.0.0.1')
- expect(entry.info).to.deep.equal({
- newPrimaryEmail: otherEmail,
- oldPrimaryEmail: originalEmail,
- })
- })
- })
- describe('session cleanup', function () {
- beforeEach(function setupSecondSession(done) {
- this.userSession2 = new User()
- this.userSession2.email = this.user.email
- this.userSession2.emails = this.user.emails
- this.userSession2.password = this.user.password
- // login before adding the new email address
- // User.login() performs a mongo update and resets the .emails field.
- this.userSession2.login(done)
- })
- beforeEach(function checkSecondSessionLiveness(done) {
- this.userSession2.request(
- { method: 'GET', url: '/project', followRedirect: false },
- (error, response) => {
- expect(error).to.not.exist
- expect(response.statusCode).to.equal(200)
- done()
- }
- )
- })
- beforeEach(function addSecondaryEmail(done) {
- this.user.request(
- {
- method: 'POST',
- url: '/user/emails',
- json: { email: 'new-confirmed-default@example.com' },
- },
- (error, response) => {
- expect(error).to.not.exist
- expect(response.statusCode).to.equal(204)
- done()
- }
- )
- })
- beforeEach(function confirmSecondaryEmail(done) {
- db.users.updateOne(
- { 'emails.email': 'new-confirmed-default@example.com' },
- { $set: { 'emails.$.confirmedAt': new Date() } },
- done
- )
- })
- beforeEach(function setDefault(done) {
- this.user.request(
- {
- method: 'POST',
- url: '/user/emails/default',
- json: { email: 'new-confirmed-default@example.com' },
- },
- (error, response) => {
- expect(error).to.not.exist
- expect(response.statusCode).to.equal(200)
- done()
- }
- )
- })
- it('should logout the other sessions', function (done) {
- this.userSession2.request(
- { method: 'GET', url: '/project', followRedirect: false },
- (error, response) => {
- expect(error).to.not.exist
- expect(response.statusCode).to.equal(302)
- expect(response.headers)
- .to.have.property('location')
- .to.match(/^\/login/)
- done()
- }
- )
- })
- })
- })
- describe('when not logged in', function () {
- beforeEach(function (done) {
- this.anonymous = new User()
- this.anonymous.getCsrfToken(done)
- })
- it('should return a plain 403 when setting the email', function (done) {
- this.anonymous.request(
- {
- method: 'POST',
- url: '/user/emails',
- json: {
- email: 'newly-added-email@example.com',
- },
- },
- (error, response, body) => {
- if (error) {
- return done(error)
- }
- expectErrorResponse.requireLogin.json(response, body)
- done()
- }
- )
- })
- })
- describe('secondary email', function () {
- let newEmail, userHelper, userId, user
- beforeEach(async function () {
- newEmail = 'a-new-email@overleaf.com'
- userHelper = new UserHelper()
- userHelper = await UserHelper.createUser()
- userHelper = await UserHelper.loginUser({
- email: userHelper.getDefaultEmail(),
- password: userHelper.getDefaultPassword(),
- })
- userId = userHelper.user._id
- await userHelper.request.post({
- form: {
- email: newEmail,
- },
- simple: false,
- uri: '/user/emails',
- })
- userHelper = await UserHelper.getUser(userId)
- user = userHelper.user
- })
- it('should add the email', async function () {
- expect(user.emails[1].email).to.equal(newEmail)
- })
- it('should add to the user audit log', async function () {
- const auditLog = userHelper.getAuditLogWithoutNoise()
- expect(typeof auditLog[0].initiatorId).to.equal('object')
- expect(auditLog[0].initiatorId).to.deep.equal(user._id)
- expect(auditLog[0].info.newSecondaryEmail).to.equal(newEmail)
- expect(auditLog[0].ip).to.equal(this.user.request.ip)
- })
- })
- describe('notification period', function () {
- let defaultEmail, userHelper, email1, email2, email3
- const maxConfirmationMonths = 12
- beforeEach(async function () {
- if (!Features.hasFeature('affiliations')) {
- this.skip()
- }
- userHelper = new UserHelper()
- defaultEmail = userHelper.getDefaultEmail()
- userHelper = await UserHelper.createUser({ email: defaultEmail })
- userHelper = await UserHelper.loginUser({
- email: defaultEmail,
- password: userHelper.getDefaultPassword(),
- })
- const institutionId = MockV1Api.createInstitution({
- commonsAccount: true,
- ssoEnabled: false,
- maxConfirmationMonths,
- })
- const domain = 'example-affiliation.com'
- MockV1Api.addInstitutionDomain(institutionId, domain, { confirmed: true })
- email1 = `leonard@${domain}`
- email2 = `mccoy@${domain}`
- email3 = `bones@${domain}`
- })
- describe('non SSO affiliations', function () {
- beforeEach(async function () {
- // create a user with 3 affiliations at the institution.
- // all are within in the notification period
- const userId = userHelper.user._id
- await userHelper.addEmailAndConfirm(userId, email1)
- await userHelper.addEmailAndConfirm(userId, email2)
- await userHelper.addEmailAndConfirm(userId, email3)
- await userHelper.changeConfirmedToNotificationPeriod(
- userId,
- email1,
- maxConfirmationMonths
- )
- await userHelper.changeConfirmedToNotificationPeriod(
- userId,
- email2,
- maxConfirmationMonths
- )
- await userHelper.changeConfirmedToPastReconfirmation(
- userId,
- email3,
- maxConfirmationMonths
- )
- })
- describe('when all affiliations in notification period or past reconfirm date', function () {
- it('should flag inReconfirmNotificationPeriod for all affiliations in period', async function () {
- const response = await userHelper.request.get('/user/emails')
- expect(response.statusCode).to.equal(200)
- const fullEmails = JSON.parse(response.body)
- expect(fullEmails.length).to.equal(4)
- expect(fullEmails[0].affiliation).to.not.exist
- expect(
- fullEmails[1].affiliation.inReconfirmNotificationPeriod
- ).to.equal(true)
- expect(
- fullEmails[2].affiliation.inReconfirmNotificationPeriod
- ).to.equal(true)
- expect(
- fullEmails[3].affiliation.inReconfirmNotificationPeriod
- ).to.equal(true)
- })
- it('should set pastReconfirmDate and emailHasInstitutionLicence:false for lapsed confirmations', async function () {
- const response = await userHelper.request.get('/user/emails')
- expect(response.statusCode).to.equal(200)
- const fullEmails = JSON.parse(response.body)
- expect(fullEmails.length).to.equal(4)
- expect(fullEmails[0].affiliation).to.not.exist
- expect(fullEmails[1].affiliation.pastReconfirmDate).to.equal(false)
- expect(fullEmails[1].emailHasInstitutionLicence).to.equal(true)
- expect(fullEmails[2].affiliation.pastReconfirmDate).to.equal(false)
- expect(fullEmails[2].emailHasInstitutionLicence).to.equal(true)
- expect(fullEmails[3].affiliation.pastReconfirmDate).to.equal(true)
- expect(fullEmails[3].emailHasInstitutionLicence).to.equal(false)
- })
- })
- describe('should flag emails before their confirmation expires, but within the notification period', function () {
- beforeEach(async function () {
- const dateInPeriodButNotExpired = moment()
- .subtract(maxConfirmationMonths, 'months')
- .add(14, 'days')
- .toDate()
- const backdatedDays = moment().diff(dateInPeriodButNotExpired, 'days')
- await userHelper.changeConfirmationDate(
- userHelper.user._id,
- email1,
- backdatedDays
- )
- await userHelper.changeConfirmationDate(
- userHelper.user._id,
- email2,
- backdatedDays
- )
- await userHelper.changeConfirmationDate(
- userHelper.user._id,
- email3,
- backdatedDays
- )
- })
- it('should flag the emails', async function () {
- const response = await userHelper.request.get('/user/emails')
- expect(response.statusCode).to.equal(200)
- const fullEmails = JSON.parse(response.body)
- expect(fullEmails.length).to.equal(4)
- expect(fullEmails[0].affiliation).to.not.exist
- expect(
- fullEmails[1].affiliation.inReconfirmNotificationPeriod
- ).to.equal(true)
- expect(
- fullEmails[2].affiliation.inReconfirmNotificationPeriod
- ).to.equal(true)
- expect(
- fullEmails[3].affiliation.inReconfirmNotificationPeriod
- ).to.equal(true)
- // ensure dates are not past reconfirmation period
- function _getLastDayToReconfirm(date) {
- return moment(date).add(maxConfirmationMonths, 'months')
- }
- expect(
- moment(fullEmails[1].reconfirmedAt).isAfter(
- _getLastDayToReconfirm(fullEmails[1].reconfirmedAt)
- )
- ).to.equal(false)
- expect(
- moment(fullEmails[2].reconfirmedAt).isAfter(
- _getLastDayToReconfirm(fullEmails[2].reconfirmedAt)
- )
- ).to.equal(false)
- expect(
- moment(fullEmails[3].reconfirmedAt).isAfter(
- _getLastDayToReconfirm(fullEmails[3].reconfirmedAt)
- )
- ).to.equal(false)
- })
- })
- describe('missing reconfirmedAt', function () {
- beforeEach(async function () {
- const userId = userHelper.user._id
- const query = {
- _id: userId,
- 'emails.email': email2,
- }
- const update = {
- $unset: { 'emails.$.reconfirmedAt': true },
- }
- await UserUpdater.promises.updateUser(query, update)
- })
- it('should fallback to confirmedAt for date check', async function () {
- const response = await userHelper.request.get('/user/emails')
- expect(response.statusCode).to.equal(200)
- const fullEmails = JSON.parse(response.body)
- expect(fullEmails.length).to.equal(4)
- expect(fullEmails[0].affiliation).to.not.exist
- expect(fullEmails[2].reconfirmedAt).to.not.exist
- expect(
- fullEmails[1].affiliation.inReconfirmNotificationPeriod
- ).to.equal(true)
- expect(
- fullEmails[2].affiliation.inReconfirmNotificationPeriod
- ).to.equal(true)
- expect(
- fullEmails[3].affiliation.inReconfirmNotificationPeriod
- ).to.equal(true)
- })
- })
- })
- })
- })
|