overleaf/services/web/test/acceptance/src/UserEmailsTests.js
Alasdair Smith 0ca81de78c Merge pull request #1717 from overleaf/as-decaffeinate-backend
Decaffeinate backend

GitOrigin-RevId: 4ca9f94fc809cab6f47cec8254cacaf1bb3806fa
2019-05-29 09:32:21 +00:00

824 lines
24 KiB
JavaScript

/* eslint-disable
handle-callback-err,
max-len,
no-unused-vars,
*/
// TODO: This file was created by bulk-decaffeinate.
// Fix any style issues and re-enable lint.
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
const { expect } = require('chai')
const async = require('async')
const User = require('./helpers/User')
const request = require('./helpers/request')
const settings = require('settings-sharelatex')
const { db, ObjectId } = require('../../../app/src/infrastructure/mongojs')
const MockV1Api = require('./helpers/MockV1Api')
describe('UserEmails', function() {
beforeEach(function(done) {
this.timeout(20000)
this.user = new User()
return this.user.login(done)
})
describe('confirming an email', function() {
it('should confirm the email', function(done) {
let token = null
return async.series(
[
cb => {
return this.user.request(
{
method: 'POST',
url: '/user/emails',
json: {
email: 'newly-added-email@example.com'
}
},
(error, response, body) => {
if (error != null) {
return done(error)
}
expect(response.statusCode).to.equal(204)
return cb()
}
)
},
cb => {
return this.user.request(
{ url: '/user/emails', json: true },
function(error, response, body) {
expect(response.statusCode).to.equal(200)
expect(body[0].confirmedAt).to.not.exist
expect(body[1].confirmedAt).to.not.exist
return cb()
}
)
},
cb => {
return db.tokens.find(
{
use: 'email_confirmation',
'data.user_id': this.user._id,
usedAt: { $exists: false }
},
(error, tokens) => {
// 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])
return cb()
}
)
},
cb => {
return this.user.request(
{
method: 'POST',
url: '/user/emails/confirm',
json: {
token
}
},
(error, response, body) => {
if (error != null) {
return done(error)
}
expect(response.statusCode).to.equal(200)
return cb()
}
)
},
cb => {
return this.user.request(
{ url: '/user/emails', json: true },
function(error, response, body) {
expect(response.statusCode).to.equal(200)
expect(body[0].confirmedAt).to.not.exist
expect(body[1].confirmedAt).to.exist
return cb()
}
)
},
cb => {
return db.tokens.find(
{
use: 'email_confirmation',
'data.user_id': this.user._id,
usedAt: { $exists: false }
},
(error, tokens) => {
// Token should be deleted after use
expect(tokens.length).to.equal(0)
return cb()
}
)
}
],
done
)
})
return 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'
return async.series(
[
cb => this.user2.login(cb),
cb => {
// Create email for first user
return this.user.request(
{
method: 'POST',
url: '/user/emails',
json: { email: this.email }
},
cb
)
},
cb => {
return db.tokens.find(
{
use: 'email_confirmation',
'data.user_id': this.user._id,
usedAt: { $exists: false }
},
(error, tokens) => {
// 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
return cb()
}
)
},
cb => {
// Delete the email from the first user
return this.user.request(
{
method: 'POST',
url: '/user/emails/delete',
json: { email: this.email }
},
cb
)
},
cb => {
// Create email for second user
return this.user2.request(
{
method: 'POST',
url: '/user/emails',
json: { email: this.email }
},
cb
)
},
cb => {
// Original confirmation token should no longer work
return this.user.request(
{
method: 'POST',
url: '/user/emails/confirm',
json: {
token: token1
}
},
(error, response, body) => {
if (error != null) {
return done(error)
}
expect(response.statusCode).to.equal(404)
return cb()
}
)
},
cb => {
return db.tokens.find(
{
use: 'email_confirmation',
'data.user_id': this.user2._id,
usedAt: { $exists: false }
},
(error, tokens) => {
// 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
return cb()
}
)
},
cb => {
// Second user should be able to confirm the email
return this.user2.request(
{
method: 'POST',
url: '/user/emails/confirm',
json: {
token: token2
}
},
(error, response, body) => {
if (error != null) {
return done(error)
}
expect(response.statusCode).to.equal(200)
return cb()
}
)
},
cb => {
return this.user2.request(
{ url: '/user/emails', json: true },
function(error, response, body) {
expect(response.statusCode).to.equal(200)
expect(body[0].confirmedAt).to.not.exist
expect(body[1].confirmedAt).to.exist
return cb()
}
)
}
],
done
)
})
})
describe('with an expired token', () =>
it('should not confirm the email', function(done) {
let token = null
return async.series(
[
cb => {
return this.user.request(
{
method: 'POST',
url: '/user/emails',
json: {
email: (this.email = 'expired-token-email@example.com')
}
},
(error, response, body) => {
if (error != null) {
return done(error)
}
expect(response.statusCode).to.equal(204)
return cb()
}
)
},
cb => {
return db.tokens.find(
{
use: 'email_confirmation',
'data.user_id': this.user._id,
usedAt: { $exists: false }
},
(error, tokens) => {
// 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])
return cb()
}
)
},
cb => {
return db.tokens.update(
{
token
},
{
$set: {
expiresAt: new Date(Date.now() - 1000000)
}
},
cb
)
},
cb => {
return this.user.request(
{
method: 'POST',
url: '/user/emails/confirm',
json: {
token
}
},
(error, response, body) => {
if (error != null) {
return done(error)
}
expect(response.statusCode).to.equal(404)
return cb()
}
)
}
],
done
)
}))
describe('resending the confirmation', function() {
it('should generate a new token', function(done) {
return async.series(
[
cb => {
return this.user.request(
{
method: 'POST',
url: '/user/emails',
json: {
email: 'reconfirmation-email@example.com'
}
},
(error, response, body) => {
if (error != null) {
return done(error)
}
expect(response.statusCode).to.equal(204)
return cb()
}
)
},
cb => {
return db.tokens.find(
{
use: 'email_confirmation',
'data.user_id': this.user._id,
usedAt: { $exists: false }
},
(error, tokens) => {
// 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)
return cb()
}
)
},
cb => {
return this.user.request(
{
method: 'POST',
url: '/user/emails/resend_confirmation',
json: {
email: 'reconfirmation-email@example.com'
}
},
(error, response, body) => {
if (error != null) {
return done(error)
}
expect(response.statusCode).to.equal(200)
return cb()
}
)
},
cb => {
return db.tokens.find(
{
use: 'email_confirmation',
'data.user_id': this.user._id,
usedAt: { $exists: false }
},
(error, tokens) => {
// 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)
return 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
return async.series(
[
cb => {
return db.tokens.remove(
{
use: 'email_confirmation',
'data.user_id': this.user._id,
usedAt: { $exists: false }
},
cb
)
},
cb => {
return this.user.request(
{
method: 'POST',
url: '/user/emails/resend_confirmation',
json: {
email: this.user.email
}
},
(error, response, body) => {
if (error != null) {
return done(error)
}
expect(response.statusCode).to.equal(200)
return cb()
}
)
},
cb => {
return db.tokens.find(
{
use: 'email_confirmation',
'data.user_id': this.user._id,
usedAt: { $exists: false }
},
(error, tokens) => {
// 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)
return cb()
}
)
}
],
done
)
})
return it("should not allow reconfirmation if the email doesn't match the user", function(done) {
return async.series(
[
cb => {
return this.user.request(
{
method: 'POST',
url: '/user/emails/resend_confirmation',
json: {
email: 'non-matching-email@example.com'
}
},
(error, response, body) => {
if (error != null) {
return done(error)
}
expect(response.statusCode).to.equal(422)
return cb()
}
)
},
cb => {
return db.tokens.find(
{
use: 'email_confirmation',
'data.user_id': this.user._id,
usedAt: { $exists: false }
},
(error, tokens) => {
expect(tokens.length).to.equal(0)
return cb()
}
)
}
],
done
)
})
})
return describe('setting a default email', function() {
it('should update confirmed emails for users not in v1', function(done) {
const token = null
return async.series(
[
cb => {
return this.user.request(
{
method: 'POST',
url: '/user/emails',
json: {
email: 'new-confirmed-default@example.com'
}
},
(error, response, body) => {
if (error != null) {
return done(error)
}
expect(response.statusCode).to.equal(204)
return cb()
}
)
},
cb => {
// Mark the email as confirmed
return db.users.update(
{
'emails.email': 'new-confirmed-default@example.com'
},
{
$set: {
'emails.$.confirmedAt': new Date()
}
},
cb
)
},
cb => {
return this.user.request(
{
method: 'POST',
url: '/user/emails/default',
json: {
email: 'new-confirmed-default@example.com'
}
},
(error, response, body) => {
if (error != null) {
return done(error)
}
expect(response.statusCode).to.equal(200)
return cb()
}
)
},
cb => {
return this.user.request(
{ url: '/user/emails', json: true },
function(error, response, body) {
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)
return cb()
}
)
}
],
done
)
})
it('should not allow changing unconfirmed emails in v1', function(done) {
const token = null
return async.series(
[
cb => {
return db.users.update(
{
_id: ObjectId(this.user._id)
},
{
$set: {
'overleaf.id': 42
}
},
cb
)
},
cb => {
return this.user.request(
{
method: 'POST',
url: '/user/emails',
json: {
email: 'new-unconfirmed-default@example.com'
}
},
(error, response, body) => {
if (error != null) {
return done(error)
}
expect(response.statusCode).to.equal(204)
return cb()
}
)
},
cb => {
return this.user.request(
{
method: 'POST',
url: '/user/emails/default',
json: {
email: 'new-unconfirmed-default@example.com'
}
},
(error, response, body) => {
if (error != null) {
return done(error)
}
expect(response.statusCode).to.equal(409)
return cb()
}
)
},
cb => {
return this.user.request(
{ url: '/user/emails', json: true },
function(error, response, body) {
expect(body[0].default).to.equal(true)
expect(body[1].default).to.equal(false)
return cb()
}
)
}
],
done
)
})
it('should update the email in v1 if confirmed', function(done) {
const token = null
return async.series(
[
cb => {
return db.users.update(
{
_id: ObjectId(this.user._id)
},
{
$set: {
'overleaf.id': 42
}
},
cb
)
},
cb => {
return this.user.request(
{
method: 'POST',
url: '/user/emails',
json: {
email: 'new-confirmed-default-in-v1@example.com'
}
},
(error, response, body) => {
if (error != null) {
return done(error)
}
expect(response.statusCode).to.equal(204)
return cb()
}
)
},
cb => {
// Mark the email as confirmed
return db.users.update(
{
'emails.email': 'new-confirmed-default-in-v1@example.com'
},
{
$set: {
'emails.$.confirmedAt': new Date()
}
},
cb
)
},
cb => {
return this.user.request(
{
method: 'POST',
url: '/user/emails/default',
json: {
email: 'new-confirmed-default-in-v1@example.com'
}
},
(error, response, body) => {
if (error != null) {
return done(error)
}
expect(response.statusCode).to.equal(200)
return cb()
}
)
}
],
error => {
if (error != null) {
return done(error)
}
expect(
MockV1Api.updateEmail.calledWith(
42,
'new-confirmed-default-in-v1@example.com'
)
).to.equal(true)
return done()
}
)
})
return it('should return an error if the email exists in v1', function(done) {
MockV1Api.existingEmails.push('exists-in-v1@example.com')
return async.series(
[
cb => {
return db.users.update(
{
_id: ObjectId(this.user._id)
},
{
$set: {
'overleaf.id': 42
}
},
cb
)
},
cb => {
return this.user.request(
{
method: 'POST',
url: '/user/emails',
json: {
email: 'exists-in-v1@example.com'
}
},
(error, response, body) => {
if (error != null) {
return done(error)
}
expect(response.statusCode).to.equal(204)
return cb()
}
)
},
cb => {
// Mark the email as confirmed
return db.users.update(
{
'emails.email': 'exists-in-v1@example.com'
},
{
$set: {
'emails.$.confirmedAt': new Date()
}
},
cb
)
},
cb => {
return this.user.request(
{
method: 'POST',
url: '/user/emails/default',
json: {
email: 'exists-in-v1@example.com'
}
},
(error, response, body) => {
if (error != null) {
return done(error)
}
expect(response.statusCode).to.equal(409)
expect(body).to.deep.equal({
message: 'This email is already registered'
})
return cb()
}
)
},
cb => {
return this.user.request(
{ url: '/user/emails', json: true },
function(error, response, body) {
expect(body[0].default).to.equal(true)
expect(body[1].default).to.equal(false)
return cb()
}
)
}
],
done
)
})
})
})