AddSecondaryEmailTests.mjs 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. import { expect } from 'chai'
  2. import UserHelper from './helpers/User.mjs'
  3. import logger from '@overleaf/logger'
  4. import sinon from 'sinon'
  5. import { db } from '../../../app/src/infrastructure/mongodb.js'
  6. import Features from '../../../app/src/infrastructure/Features.js'
  7. const User = UserHelper.promises
  8. describe('Add secondary email address confirmation code email', function () {
  9. let spy
  10. let user, user2, res, confirmCode
  11. const extractConfirmCode = () => {
  12. const emailDebugLog = spy.args.find(
  13. ([, msg]) => msg === 'Would send email if enabled.'
  14. )
  15. const emailConfirmSubject = emailDebugLog[0].options.subject
  16. return emailConfirmSubject.match(/\((\d{6})\)/)[1]
  17. }
  18. beforeEach(async function () {
  19. if (!Features.hasFeature('saas')) {
  20. this.skip()
  21. }
  22. spy = sinon.spy(logger, 'info')
  23. user = new User()
  24. await user.register()
  25. await user.login()
  26. spy.resetHistory()
  27. res = await user.doRequest('POST', {
  28. json: {
  29. email: 'secondary@overleaf.com',
  30. },
  31. uri: `/user/emails/secondary`,
  32. })
  33. confirmCode = extractConfirmCode()
  34. })
  35. afterEach(function () {
  36. if (!Features.hasFeature('saas')) {
  37. this.skip()
  38. }
  39. spy.restore()
  40. })
  41. it('should send email with confirmation code', function () {
  42. expect(res.response.statusCode).to.equal(200)
  43. expect(confirmCode.length).to.equal(6)
  44. })
  45. describe('with a valid confirmation code', function () {
  46. beforeEach(async function () {
  47. this.result = await user.doRequest('POST', {
  48. json: {
  49. code: confirmCode,
  50. },
  51. uri: '/user/emails/confirm-secondary',
  52. })
  53. })
  54. it('should redirect to /project', async function () {
  55. expect(this.result.response.statusCode).to.equal(200)
  56. expect(this.result.body.redir).to.equal('/project')
  57. })
  58. it('the new email should be saved in mongo', async function () {
  59. const userInDb = await db.users.findOne(
  60. { email: user.email },
  61. { projection: { emails: 1 } }
  62. )
  63. expect(userInDb).to.exist
  64. const newSecondaryEmail = userInDb.emails.find(
  65. email => email.email === 'secondary@overleaf.com'
  66. )
  67. expect(newSecondaryEmail).to.exist
  68. expect(newSecondaryEmail.confirmedAt).to.exist
  69. expect(newSecondaryEmail.reconfirmedAt).to.exist
  70. expect(newSecondaryEmail.reconfirmedAt).to.deep.equal(
  71. newSecondaryEmail.confirmedAt
  72. )
  73. })
  74. })
  75. describe('with an invalid confirmation code', function () {
  76. beforeEach(async function () {
  77. this.result = await user.doRequest('POST', {
  78. json: {
  79. code: '123',
  80. },
  81. uri: '/user/emails/confirm-secondary',
  82. })
  83. })
  84. it('should respond with invalid confirmation code error', async function () {
  85. expect(this.result.response.statusCode).to.equal(403)
  86. expect(this.result.body.message.key).to.equal('invalid_confirmation_code')
  87. })
  88. })
  89. describe('with a duplicate email', async function () {
  90. beforeEach(async function () {
  91. await user.doRequest('POST', {
  92. json: {
  93. code: confirmCode,
  94. },
  95. uri: '/user/emails/confirm-secondary',
  96. })
  97. user2 = new User()
  98. await user2.register()
  99. await user2.login()
  100. })
  101. it('should respond with a email already registered error', async function () {
  102. res = await user2.doRequest('POST', {
  103. json: {
  104. email: 'secondary@overleaf.com',
  105. },
  106. uri: `/user/emails/secondary`,
  107. })
  108. expect(res.response.statusCode).to.equal(409)
  109. expect(res.body.message.text).to.equal('This email is already registered')
  110. })
  111. })
  112. it('should hit rate limit on code check', async function () {
  113. let confirmEmailReq
  114. for (let i = 0; i < 20; i++) {
  115. confirmEmailReq = await user.doRequest('POST', {
  116. json: {
  117. code: '123',
  118. },
  119. uri: '/user/emails/confirm-secondary',
  120. })
  121. }
  122. expect(confirmEmailReq.response.statusCode).to.equal(429)
  123. })
  124. it('should resend confirm code', async function () {
  125. const oldConfirmCode = extractConfirmCode()
  126. spy.resetHistory()
  127. const resendCodeRes = await user.doRequest('POST', {
  128. uri: '/user/emails/resend-secondary-confirmation',
  129. })
  130. const newConfirmCode = extractConfirmCode()
  131. expect(resendCodeRes.response.statusCode).to.equal(200)
  132. expect(JSON.parse(resendCodeRes.body).message.key).to.equal(
  133. 'we_sent_new_code'
  134. )
  135. const oldConfirmRes = await user.doRequest('POST', {
  136. json: {
  137. code: oldConfirmCode,
  138. },
  139. uri: '/user/emails/confirm-secondary',
  140. })
  141. expect(oldConfirmRes.response.statusCode).to.equal(403)
  142. expect(oldConfirmRes.body.message.key).to.equal('invalid_confirmation_code')
  143. const newCodeRes = await user.doRequest('POST', {
  144. json: {
  145. code: newConfirmCode,
  146. },
  147. uri: '/user/emails/confirm-secondary',
  148. })
  149. expect(newCodeRes.response.statusCode).to.equal(200)
  150. expect(newCodeRes.body.redir).to.equal('/project')
  151. })
  152. it('should hit rate limit on code resend', async function () {
  153. let resendCodeReq
  154. for (let i = 0; i < 5; i++) {
  155. resendCodeReq = await user.doRequest('POST', {
  156. json: true,
  157. uri: '/user/emails/resend-secondary-confirmation',
  158. })
  159. }
  160. expect(resendCodeReq.response.statusCode).to.equal(429)
  161. })
  162. })