diff --git a/services/web/app/coffee/Features/Errors/Errors.coffee b/services/web/app/coffee/Features/Errors/Errors.coffee index adf6fcaf12..f33f3e523e 100644 --- a/services/web/app/coffee/Features/Errors/Errors.coffee +++ b/services/web/app/coffee/Features/Errors/Errors.coffee @@ -75,6 +75,13 @@ UnconfirmedEmailError = (message) -> return error UnconfirmedEmailError.prototype.__proto___ = Error.prototype +EmailExistsError = (message) -> + error = new Error(message) + error.name = "EmailExistsError" + error.__proto__ = EmailExistsError.prototype + return error +EmailExistsError.prototype.__proto___ = Error.prototype + module.exports = Errors = NotFoundError: NotFoundError ServiceNotConfiguredError: ServiceNotConfiguredError @@ -87,3 +94,4 @@ module.exports = Errors = ProjectHistoryDisabledError: ProjectHistoryDisabledError V1ConnectionError: V1ConnectionError UnconfirmedEmailError: UnconfirmedEmailError + EmailExistsError: EmailExistsError diff --git a/services/web/app/coffee/Features/User/UserEmailsController.coffee b/services/web/app/coffee/Features/User/UserEmailsController.coffee index 32fabe4c6d..b3bac95ca7 100644 --- a/services/web/app/coffee/Features/User/UserEmailsController.coffee +++ b/services/web/app/coffee/Features/User/UserEmailsController.coffee @@ -51,6 +51,11 @@ module.exports = UserEmailsController = if error? if error instanceof Errors.UnconfirmedEmailError return res.sendStatus 409 + else if error instanceof Errors.EmailExistsError + return res.status(409).json { + error: + message: "The email '#{email}' is already in use by another account" + } else return next(error) else diff --git a/services/web/app/coffee/Features/User/UserUpdater.coffee b/services/web/app/coffee/Features/User/UserUpdater.coffee index 22ec235f91..2c27f5178d 100644 --- a/services/web/app/coffee/Features/User/UserUpdater.coffee +++ b/services/web/app/coffee/Features/User/UserUpdater.coffee @@ -142,7 +142,9 @@ module.exports = UserUpdater = if error? error = new Errors.V1ConnectionError('No V1 connection') if error.code == 'ECONNREFUSED' return callback(error) - if 200 <= response.statusCode < 300 + if response.statusCode == 409 # Conflict + return callback(new Errors.EmailExistsError('email exists in v1')) + else if 200 <= response.statusCode < 300 return callback() else return callback new Error("non-success code from v1: #{response.statusCode}") diff --git a/services/web/test/acceptance/coffee/UserEmailsTests.coffee b/services/web/test/acceptance/coffee/UserEmailsTests.coffee index 2ae078ff6c..2d4c437fc1 100644 --- a/services/web/test/acceptance/coffee/UserEmailsTests.coffee +++ b/services/web/test/acceptance/coffee/UserEmailsTests.coffee @@ -304,7 +304,7 @@ describe "UserEmails", -> ], done describe 'setting a default email', -> - it 'should update confirmed emails', (done) -> + it 'should update confirmed emails for users not in v1', (done) -> token = null async.series [ (cb) => @@ -384,7 +384,7 @@ describe "UserEmails", -> cb() ], done - it 'should update the email in v2', (done) -> + it 'should update the email in v1 if confirmed', (done) -> token = null async.series [ (cb) => @@ -430,3 +430,53 @@ describe "UserEmails", -> MockV1Api.updateEmail.calledWith(42, 'new-confirmed-default-in-v1@example.com') ).to.equal true done() + + it 'should return an error if the email exists in v1', (done) -> + MockV1Api.existingEmails.push 'exists-in-v1@example.com' + async.series [ + (cb) => + db.users.update { + _id: ObjectId(@user._id) + }, { + $set: { + 'overleaf.id': 42 + } + }, cb + (cb) => + @user.request { + method: 'POST', + url: '/user/emails', + json: + email: 'exists-in-v1@example.com' + }, (error, response, body) => + return done(error) if error? + expect(response.statusCode).to.equal 204 + cb() + (cb) => + # Mark the email as confirmed + db.users.update { + 'emails.email': 'exists-in-v1@example.com' + }, { + $set: { + 'emails.$.confirmedAt': new Date() + } + }, cb + (cb) => + @user.request { + method: 'POST', + url: '/user/emails/default', + json: + email: 'exists-in-v1@example.com' + }, (error, response, body) => + return done(error) if error? + expect(response.statusCode).to.equal 409 + expect(body.error).to.deep.equal { + message: "The email 'exists-in-v1@example.com' is already in use by another account" + } + cb() + (cb) => + @user.request { url: '/user/emails', json: true }, (error, response, body) -> + expect(body[0].default).to.equal true + expect(body[1].default).to.equal false + cb() + ], done \ No newline at end of file diff --git a/services/web/test/acceptance/coffee/helpers/MockV1Api.coffee b/services/web/test/acceptance/coffee/helpers/MockV1Api.coffee index a54d556935..ef80ac8b23 100644 --- a/services/web/test/acceptance/coffee/helpers/MockV1Api.coffee +++ b/services/web/test/acceptance/coffee/helpers/MockV1Api.coffee @@ -30,6 +30,8 @@ module.exports = MockV1Api = updateEmail: sinon.stub() + existingEmails: [] + setAffiliations: (affiliations) -> @affiliations = affiliations run: () -> @@ -67,8 +69,12 @@ module.exports = MockV1Api = res.json [] app.put '/api/v1/sharelatex/users/:id/email', (req, res, next) => - @updateEmail parseInt(req.params.id), req.body.email - res.sendStatus 200 + { email } = req.body + if email in @existingEmails + return res.sendStatus 409 + else + @updateEmail parseInt(req.params.id), email + return res.sendStatus 200 app.listen 5000, (error) -> throw error if error?