فهرست منبع

Update email in v1 when setting default in v2

James Allen 8 سال پیش
والد
کامیت
d0f77abc70

+ 8 - 0
services/web/app/coffee/Features/Errors/Errors.coffee

@@ -68,6 +68,13 @@ V1ConnectionError = (message) ->
 	return error
 V1ConnectionError.prototype.__proto___ = Error.prototype
 
+UnconfirmedEmailError = (message) ->
+	error = new Error(message)
+	error.name = "UnconfirmedEmailError"
+	error.__proto__ = UnconfirmedEmailError.prototype
+	return error
+UnconfirmedEmailError.prototype.__proto___ = Error.prototype
+
 module.exports = Errors =
 	NotFoundError: NotFoundError
 	ServiceNotConfiguredError: ServiceNotConfiguredError
@@ -79,3 +86,4 @@ module.exports = Errors =
 	V1HistoryNotSyncedError: V1HistoryNotSyncedError
 	ProjectHistoryDisabledError: ProjectHistoryDisabledError
 	V1ConnectionError: V1ConnectionError
+	UnconfirmedEmailError: UnconfirmedEmailError

+ 8 - 3
services/web/app/coffee/Features/User/UserEmailsController.coffee

@@ -47,9 +47,14 @@ module.exports = UserEmailsController =
 		email = EmailHelper.parseEmail(req.body.email)
 		return res.sendStatus 422 unless email?
 
-		UserUpdater.setDefaultEmailAddress userId, email, (error)->
-			return next(error) if error?
-			res.sendStatus 200
+		UserUpdater.updateV1AndSetDefaultEmailAddress userId, email, (error)->
+			if error?
+				if error instanceof Errors.UnconfirmedEmailError
+					return res.sendStatus 409
+				else
+					return next(error)
+			else
+				return res.sendStatus 200
 
 
 	endorse: (req, res, next) ->

+ 41 - 0
services/web/app/coffee/Features/User/UserUpdater.coffee

@@ -8,6 +8,8 @@ UserGetter = require("./UserGetter")
 { addAffiliation, removeAffiliation } = require("./UserAffiliationsManager")
 EmailHelper = require "../Helpers/EmailHelper"
 Errors = require "../Errors/Errors"
+Settings = require "settings-sharelatex"
+request = require 'request'
 
 module.exports = UserUpdater =
 	updateUser: (query, update, callback = (error) ->) ->
@@ -106,6 +108,45 @@ module.exports = UserUpdater =
 				return callback(new Error('Default email does not belong to user'))
 			callback()
 
+	updateV1AndSetDefaultEmailAddress: (userId, email, callback) ->
+		@updateEmailAddressInV1 userId, email, (error) =>
+			return callback(error) if error?
+			@setDefaultEmailAddress userId, email, callback
+
+	updateEmailAddressInV1: (userId, newEmail, callback) ->
+		if !Settings.apis?.v1?.url?
+			return callback()
+		UserGetter.getUser userId, { 'overleaf.id': 1, emails: 1 }, (error, user) ->
+			return callback(error) if error?
+			return callback(new Errors.NotFoundError('no user found')) if !user?
+			if !user.overleaf?.id?
+				return callback()
+			newEmailIsConfirmed = false
+			for email in user.emails
+				if email.email == newEmail and email.confirmedAt?
+					newEmailIsConfirmed = true
+					break
+			if !newEmailIsConfirmed
+				return callback(new Errors.UnconfirmedEmailError("can't update v1 with unconfirmed email"))
+			request {
+				baseUrl: Settings.apis.v1.url
+				url: "/api/v1/sharelatex/users/#{user.overleaf.id}/email"
+				method: 'PUT'
+				auth:
+					user: Settings.apis.v1.user
+					pass: Settings.apis.v1.pass
+					sendImmediately: true
+				json: { email: newEmail },
+				timeout: 5 * 1000
+			}, (error, response, body) ->
+				if error?
+					error = new Errors.V1ConnectionError('No V1 connection') if error.code == 'ECONNREFUSED'
+					return callback(error)
+				if 200 <= response.statusCode < 300
+					return callback()
+				else
+					return callback new Error("non-success code from v1: #{response.statusCode}")
+
 	confirmEmail: (userId, email, callback) ->
 		email = EmailHelper.parseEmail(email)
 		return callback(new Error('invalid email')) if !email?

+ 128 - 0
services/web/test/acceptance/coffee/UserEmailsTests.coffee

@@ -302,3 +302,131 @@ describe "UserEmails", ->
 						expect(tokens.length).to.equal 0
 						cb()
 			], done
+
+	describe 'setting a default email', ->
+		it 'should update confirmed emails', (done) ->
+			token = null
+			async.series [
+				(cb) =>
+					@user.request {
+						method: 'POST',
+						url: '/user/emails',
+						json:
+							email: 'new-confirmed-default@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': 'new-confirmed-default@example.com'
+					}, { 
+						$set: {
+							'emails.$.confirmedAt': new Date()
+						}
+					}, cb
+				(cb) =>
+					@user.request {
+						method: 'POST',
+						url: '/user/emails/default',
+						json:
+							email: 'new-confirmed-default@example.com'
+					}, (error, response, body) =>
+						return done(error) if error?
+						expect(response.statusCode).to.equal 200
+						cb()
+				(cb) =>
+					@user.request { url: '/user/emails', json: true }, (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
+						cb()
+			], done
+
+		it 'should not allow changing unconfirmed emails in v1', (done) ->
+			token = null
+			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: 'new-unconfirmed-default@example.com'
+					}, (error, response, body) =>
+						return done(error) if error?
+						expect(response.statusCode).to.equal 204
+						cb()
+				(cb) =>
+					@user.request {
+						method: 'POST',
+						url: '/user/emails/default',
+						json:
+							email: 'new-unconfirmed-default@example.com'
+					}, (error, response, body) =>
+						return done(error) if error?
+						expect(response.statusCode).to.equal 409
+						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
+
+		it 'should update the email in v2', (done) ->
+			token = null
+			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: 'new-confirmed-default-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': 'new-confirmed-default-in-v1@example.com'
+					}, { 
+						$set: {
+							'emails.$.confirmedAt': new Date()
+						}
+					}, cb
+				(cb) =>
+					@user.request {
+						method: 'POST',
+						url: '/user/emails/default',
+						json:
+							email: 'new-confirmed-default-in-v1@example.com'
+					}, (error, response, body) =>
+						return done(error) if error?
+						expect(response.statusCode).to.equal 200
+						cb()
+			], (error) =>
+				return done(error) if error?
+				expect(
+					MockV1Api.updateEmail.calledWith(42, 'new-confirmed-default-in-v1@example.com')
+				).to.equal true
+				done()

+ 6 - 0
services/web/test/acceptance/coffee/helpers/MockV1Api.coffee

@@ -28,6 +28,8 @@ module.exports = MockV1Api =
 
 	affiliations: []
 
+	updateEmail: sinon.stub()
+
 	setAffiliations: (affiliations) -> @affiliations = affiliations
 
 	run: () ->
@@ -64,6 +66,10 @@ module.exports = MockV1Api =
 		app.get '/university/domains', (req, res, next) ->
 			res.json []
 
+		app.put '/api/v1/sharelatex/users/:id/email', (req, res, next) =>
+			@updateEmail parseInt(req.params.id), req.body.email
+			res.sendStatus 200
+
 		app.listen 5000, (error) ->
 			throw error if error?
 		.on "error", (error) ->

+ 3 - 2
services/web/test/unit/coffee/User/UserEmailsControllerTests.coffee

@@ -24,6 +24,7 @@ describe "UserEmailsController", ->
 			addEmailAddress: sinon.stub()
 			removeEmailAddress: sinon.stub()
 			setDefaultEmailAddress: sinon.stub()
+			updateV1AndSetDefaultEmailAddress: sinon.stub()
 		@EmailHelper =
 			parseEmail: sinon.stub()
 		@endorseAffiliation = sinon.stub().yields()
@@ -126,13 +127,13 @@ describe "UserEmailsController", ->
 			@EmailHelper.parseEmail.returns @email
 
 		it 'sets default email', (done) ->
-			@UserUpdater.setDefaultEmailAddress.callsArgWith 2, null
+			@UserUpdater.updateV1AndSetDefaultEmailAddress.callsArgWith 2, null
 
 			@UserEmailsController.setDefault @req, 
 				sendStatus: (code) =>
 					code.should.equal 200
 					assertCalledWith @EmailHelper.parseEmail, @email
-					assertCalledWith @UserUpdater.setDefaultEmailAddress, @user._id, @email
+					assertCalledWith @UserUpdater.updateV1AndSetDefaultEmailAddress, @user._id, @email
 					done()
 
 		it 'handles email parse error', (done) ->