UserEmailsTests.coffee 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. expect = require("chai").expect
  2. async = require("async")
  3. User = require "./helpers/User"
  4. request = require "./helpers/request"
  5. settings = require "settings-sharelatex"
  6. {db, ObjectId} = require("../../../app/js/infrastructure/mongojs")
  7. MockV1Api = require "./helpers/MockV1Api"
  8. describe "UserEmails", ->
  9. beforeEach (done) ->
  10. @timeout(20000)
  11. @user = new User()
  12. @user.login done
  13. describe 'confirming an email', ->
  14. it 'should confirm the email', (done) ->
  15. token = null
  16. async.series [
  17. (cb) =>
  18. @user.request {
  19. method: 'POST',
  20. url: '/user/emails',
  21. json:
  22. email: 'newly-added-email@example.com'
  23. }, (error, response, body) =>
  24. return done(error) if error?
  25. expect(response.statusCode).to.equal 204
  26. cb()
  27. (cb) =>
  28. @user.request { url: '/user/emails', json: true }, (error, response, body) ->
  29. expect(response.statusCode).to.equal 200
  30. expect(body[0].confirmedAt).to.not.exist
  31. expect(body[1].confirmedAt).to.not.exist
  32. cb()
  33. (cb) =>
  34. db.tokens.find {
  35. use: 'email_confirmation',
  36. 'data.user_id': @user._id,
  37. usedAt: { $exists: false }
  38. }, (error, tokens) =>
  39. # There should only be one confirmation token at the moment
  40. expect(tokens.length).to.equal 1
  41. expect(tokens[0].data.email).to.equal 'newly-added-email@example.com'
  42. expect(tokens[0].data.user_id).to.equal @user._id
  43. token = tokens[0].token
  44. cb()
  45. (cb) =>
  46. @user.request {
  47. method: 'POST',
  48. url: '/user/emails/confirm',
  49. json:
  50. token: token
  51. }, (error, response, body) =>
  52. return done(error) if error?
  53. expect(response.statusCode).to.equal 200
  54. cb()
  55. (cb) =>
  56. @user.request { url: '/user/emails', json: true }, (error, response, body) ->
  57. expect(response.statusCode).to.equal 200
  58. expect(body[0].confirmedAt).to.not.exist
  59. expect(body[1].confirmedAt).to.exist
  60. cb()
  61. (cb) =>
  62. db.tokens.find {
  63. use: 'email_confirmation',
  64. 'data.user_id': @user._id,
  65. usedAt: { $exists: false }
  66. }, (error, tokens) =>
  67. # Token should be deleted after use
  68. expect(tokens.length).to.equal 0
  69. cb()
  70. ], done
  71. it 'should not allow confirmation of the email if the user has changed', (done) ->
  72. token1 = null
  73. token2 = null
  74. @user2 = new User()
  75. @email = 'duplicate-email@example.com'
  76. async.series [
  77. (cb) => @user2.login cb
  78. (cb) =>
  79. # Create email for first user
  80. @user.request {
  81. method: 'POST',
  82. url: '/user/emails',
  83. json: {@email}
  84. }, cb
  85. (cb) =>
  86. db.tokens.find {
  87. use: 'email_confirmation',
  88. 'data.user_id': @user._id,
  89. usedAt: { $exists: false }
  90. }, (error, tokens) =>
  91. # There should only be one confirmation token at the moment
  92. expect(tokens.length).to.equal 1
  93. expect(tokens[0].data.email).to.equal @email
  94. expect(tokens[0].data.user_id).to.equal @user._id
  95. token1 = tokens[0].token
  96. cb()
  97. (cb) =>
  98. # Delete the email from the first user
  99. @user.request {
  100. method: 'POST',
  101. url: '/user/emails/delete',
  102. json: {@email}
  103. }, cb
  104. (cb) =>
  105. # Create email for second user
  106. @user2.request {
  107. method: 'POST',
  108. url: '/user/emails',
  109. json: {@email}
  110. }, cb
  111. (cb) =>
  112. # Original confirmation token should no longer work
  113. @user.request {
  114. method: 'POST',
  115. url: '/user/emails/confirm',
  116. json:
  117. token: token1
  118. }, (error, response, body) =>
  119. return done(error) if error?
  120. expect(response.statusCode).to.equal 404
  121. cb()
  122. (cb) =>
  123. db.tokens.find {
  124. use: 'email_confirmation',
  125. 'data.user_id': @user2._id,
  126. usedAt: { $exists: false }
  127. }, (error, tokens) =>
  128. # The first token has been used, so this should be token2 now
  129. expect(tokens.length).to.equal 1
  130. expect(tokens[0].data.email).to.equal @email
  131. expect(tokens[0].data.user_id).to.equal @user2._id
  132. token2 = tokens[0].token
  133. cb()
  134. (cb) =>
  135. # Second user should be able to confirm the email
  136. @user2.request {
  137. method: 'POST',
  138. url: '/user/emails/confirm',
  139. json:
  140. token: token2
  141. }, (error, response, body) =>
  142. return done(error) if error?
  143. expect(response.statusCode).to.equal 200
  144. cb()
  145. (cb) =>
  146. @user2.request { url: '/user/emails', json: true }, (error, response, body) ->
  147. expect(response.statusCode).to.equal 200
  148. expect(body[0].confirmedAt).to.not.exist
  149. expect(body[1].confirmedAt).to.exist
  150. cb()
  151. ], done
  152. describe "with an expired token", ->
  153. it 'should not confirm the email', (done) ->
  154. token = null
  155. async.series [
  156. (cb) =>
  157. @user.request {
  158. method: 'POST',
  159. url: '/user/emails',
  160. json:
  161. email: @email = 'expired-token-email@example.com'
  162. }, (error, response, body) =>
  163. return done(error) if error?
  164. expect(response.statusCode).to.equal 204
  165. cb()
  166. (cb) =>
  167. db.tokens.find {
  168. use: 'email_confirmation',
  169. 'data.user_id': @user._id,
  170. usedAt: { $exists: false }
  171. }, (error, tokens) =>
  172. # There should only be one confirmation token at the moment
  173. expect(tokens.length).to.equal 1
  174. expect(tokens[0].data.email).to.equal @email
  175. expect(tokens[0].data.user_id).to.equal @user._id
  176. token = tokens[0].token
  177. cb()
  178. (cb) =>
  179. db.tokens.update {
  180. token: token
  181. }, {
  182. $set: {
  183. expiresAt: new Date(Date.now() - 1000000)
  184. }
  185. }, cb
  186. (cb) =>
  187. @user.request {
  188. method: 'POST',
  189. url: '/user/emails/confirm',
  190. json:
  191. token: token
  192. }, (error, response, body) =>
  193. return done(error) if error?
  194. expect(response.statusCode).to.equal 404
  195. cb()
  196. ], done
  197. describe 'resending the confirmation', ->
  198. it 'should generate a new token', (done) ->
  199. async.series [
  200. (cb) =>
  201. @user.request {
  202. method: 'POST',
  203. url: '/user/emails',
  204. json:
  205. email: 'reconfirmation-email@example.com'
  206. }, (error, response, body) =>
  207. return done(error) if error?
  208. expect(response.statusCode).to.equal 204
  209. cb()
  210. (cb) =>
  211. db.tokens.find {
  212. use: 'email_confirmation',
  213. 'data.user_id': @user._id,
  214. usedAt: { $exists: false }
  215. }, (error, tokens) =>
  216. # There should only be one confirmation token at the moment
  217. expect(tokens.length).to.equal 1
  218. expect(tokens[0].data.email).to.equal 'reconfirmation-email@example.com'
  219. expect(tokens[0].data.user_id).to.equal @user._id
  220. cb()
  221. (cb) =>
  222. @user.request {
  223. method: 'POST',
  224. url: '/user/emails/resend_confirmation',
  225. json:
  226. email: 'reconfirmation-email@example.com'
  227. }, (error, response, body) =>
  228. return done(error) if error?
  229. expect(response.statusCode).to.equal 200
  230. cb()
  231. (cb) =>
  232. db.tokens.find {
  233. use: 'email_confirmation',
  234. 'data.user_id': @user._id,
  235. usedAt: { $exists: false }
  236. }, (error, tokens) =>
  237. # There should be two tokens now
  238. expect(tokens.length).to.equal 2
  239. expect(tokens[0].data.email).to.equal 'reconfirmation-email@example.com'
  240. expect(tokens[0].data.user_id).to.equal @user._id
  241. expect(tokens[1].data.email).to.equal 'reconfirmation-email@example.com'
  242. expect(tokens[1].data.user_id).to.equal @user._id
  243. cb()
  244. ], done
  245. it 'should create a new token if none exists', (done) ->
  246. # This should only be for users that have sign up with their main
  247. # emails before the confirmation system existed
  248. async.series [
  249. (cb) =>
  250. db.tokens.remove {
  251. use: 'email_confirmation',
  252. 'data.user_id': @user._id,
  253. usedAt: { $exists: false }
  254. }, cb
  255. (cb) =>
  256. @user.request {
  257. method: 'POST',
  258. url: '/user/emails/resend_confirmation',
  259. json:
  260. email: @user.email
  261. }, (error, response, body) =>
  262. return done(error) if error?
  263. expect(response.statusCode).to.equal 200
  264. cb()
  265. (cb) =>
  266. db.tokens.find {
  267. use: 'email_confirmation',
  268. 'data.user_id': @user._id,
  269. usedAt: { $exists: false }
  270. }, (error, tokens) =>
  271. # There should still only be one confirmation token
  272. expect(tokens.length).to.equal 1
  273. expect(tokens[0].data.email).to.equal @user.email
  274. expect(tokens[0].data.user_id).to.equal @user._id
  275. cb()
  276. ], done
  277. it "should not allow reconfirmation if the email doesn't match the user", (done) ->
  278. async.series [
  279. (cb) =>
  280. @user.request {
  281. method: 'POST',
  282. url: '/user/emails/resend_confirmation',
  283. json:
  284. email: 'non-matching-email@example.com'
  285. }, (error, response, body) =>
  286. return done(error) if error?
  287. expect(response.statusCode).to.equal 422
  288. cb()
  289. (cb) =>
  290. db.tokens.find {
  291. use: 'email_confirmation',
  292. 'data.user_id': @user._id,
  293. usedAt: { $exists: false }
  294. }, (error, tokens) =>
  295. expect(tokens.length).to.equal 0
  296. cb()
  297. ], done
  298. describe 'setting a default email', ->
  299. it 'should update confirmed emails for users not in v1', (done) ->
  300. token = null
  301. async.series [
  302. (cb) =>
  303. @user.request {
  304. method: 'POST',
  305. url: '/user/emails',
  306. json:
  307. email: 'new-confirmed-default@example.com'
  308. }, (error, response, body) =>
  309. return done(error) if error?
  310. expect(response.statusCode).to.equal 204
  311. cb()
  312. (cb) =>
  313. # Mark the email as confirmed
  314. db.users.update {
  315. 'emails.email': 'new-confirmed-default@example.com'
  316. }, {
  317. $set: {
  318. 'emails.$.confirmedAt': new Date()
  319. }
  320. }, cb
  321. (cb) =>
  322. @user.request {
  323. method: 'POST',
  324. url: '/user/emails/default',
  325. json:
  326. email: 'new-confirmed-default@example.com'
  327. }, (error, response, body) =>
  328. return done(error) if error?
  329. expect(response.statusCode).to.equal 200
  330. cb()
  331. (cb) =>
  332. @user.request { url: '/user/emails', json: true }, (error, response, body) ->
  333. expect(response.statusCode).to.equal 200
  334. expect(body[0].confirmedAt).to.not.exist
  335. expect(body[0].default).to.equal false
  336. expect(body[1].confirmedAt).to.exist
  337. expect(body[1].default).to.equal true
  338. cb()
  339. ], done
  340. it 'should not allow changing unconfirmed emails in v1', (done) ->
  341. token = null
  342. async.series [
  343. (cb) =>
  344. db.users.update {
  345. _id: ObjectId(@user._id)
  346. }, {
  347. $set: {
  348. 'overleaf.id': 42
  349. }
  350. }, cb
  351. (cb) =>
  352. @user.request {
  353. method: 'POST',
  354. url: '/user/emails',
  355. json:
  356. email: 'new-unconfirmed-default@example.com'
  357. }, (error, response, body) =>
  358. return done(error) if error?
  359. expect(response.statusCode).to.equal 204
  360. cb()
  361. (cb) =>
  362. @user.request {
  363. method: 'POST',
  364. url: '/user/emails/default',
  365. json:
  366. email: 'new-unconfirmed-default@example.com'
  367. }, (error, response, body) =>
  368. return done(error) if error?
  369. expect(response.statusCode).to.equal 409
  370. cb()
  371. (cb) =>
  372. @user.request { url: '/user/emails', json: true }, (error, response, body) ->
  373. expect(body[0].default).to.equal true
  374. expect(body[1].default).to.equal false
  375. cb()
  376. ], done
  377. it 'should update the email in v1 if confirmed', (done) ->
  378. token = null
  379. async.series [
  380. (cb) =>
  381. db.users.update {
  382. _id: ObjectId(@user._id)
  383. }, {
  384. $set: {
  385. 'overleaf.id': 42
  386. }
  387. }, cb
  388. (cb) =>
  389. @user.request {
  390. method: 'POST',
  391. url: '/user/emails',
  392. json:
  393. email: 'new-confirmed-default-in-v1@example.com'
  394. }, (error, response, body) =>
  395. return done(error) if error?
  396. expect(response.statusCode).to.equal 204
  397. cb()
  398. (cb) =>
  399. # Mark the email as confirmed
  400. db.users.update {
  401. 'emails.email': 'new-confirmed-default-in-v1@example.com'
  402. }, {
  403. $set: {
  404. 'emails.$.confirmedAt': new Date()
  405. }
  406. }, cb
  407. (cb) =>
  408. @user.request {
  409. method: 'POST',
  410. url: '/user/emails/default',
  411. json:
  412. email: 'new-confirmed-default-in-v1@example.com'
  413. }, (error, response, body) =>
  414. return done(error) if error?
  415. expect(response.statusCode).to.equal 200
  416. cb()
  417. ], (error) =>
  418. return done(error) if error?
  419. expect(
  420. MockV1Api.updateEmail.calledWith(42, 'new-confirmed-default-in-v1@example.com')
  421. ).to.equal true
  422. done()
  423. it 'should return an error if the email exists in v1', (done) ->
  424. MockV1Api.existingEmails.push 'exists-in-v1@example.com'
  425. async.series [
  426. (cb) =>
  427. db.users.update {
  428. _id: ObjectId(@user._id)
  429. }, {
  430. $set: {
  431. 'overleaf.id': 42
  432. }
  433. }, cb
  434. (cb) =>
  435. @user.request {
  436. method: 'POST',
  437. url: '/user/emails',
  438. json:
  439. email: 'exists-in-v1@example.com'
  440. }, (error, response, body) =>
  441. return done(error) if error?
  442. expect(response.statusCode).to.equal 204
  443. cb()
  444. (cb) =>
  445. # Mark the email as confirmed
  446. db.users.update {
  447. 'emails.email': 'exists-in-v1@example.com'
  448. }, {
  449. $set: {
  450. 'emails.$.confirmedAt': new Date()
  451. }
  452. }, cb
  453. (cb) =>
  454. @user.request {
  455. method: 'POST',
  456. url: '/user/emails/default',
  457. json:
  458. email: 'exists-in-v1@example.com'
  459. }, (error, response, body) =>
  460. return done(error) if error?
  461. expect(response.statusCode).to.equal 409
  462. expect(body).to.deep.equal {
  463. message: "This email is already registered"
  464. }
  465. cb()
  466. (cb) =>
  467. @user.request { url: '/user/emails', json: true }, (error, response, body) ->
  468. expect(body[0].default).to.equal true
  469. expect(body[1].default).to.equal false
  470. cb()
  471. ], done