From b522ba42d61abc5ff4cc6613de32172e3083b845 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Wed, 14 Feb 2018 19:05:34 -0200 Subject: [PATCH 1/6] fixing bug, when calling saveUser method without the field verified, it was setting always to false. --- .../server/functions/saveUser.js | 4 +- tests/end-to-end/api/01-users.js | 72 ++++++++++++++++--- 2 files changed, 64 insertions(+), 12 deletions(-) diff --git a/packages/rocketchat-lib/server/functions/saveUser.js b/packages/rocketchat-lib/server/functions/saveUser.js index ed727b385fa50..e70e0f17d522a 100644 --- a/packages/rocketchat-lib/server/functions/saveUser.js +++ b/packages/rocketchat-lib/server/functions/saveUser.js @@ -167,7 +167,9 @@ RocketChat.saveUser = function(userId, userData) { updateUser.$set.requirePasswordChange = userData.requirePasswordChange; } - updateUser.$set['emails.0.verified'] = !!userData.verified; + if (userData.verified) { + updateUser.$set['emails.0.verified'] = true; + } Meteor.users.update({ _id: userData._id }, updateUser); diff --git a/tests/end-to-end/api/01-users.js b/tests/end-to-end/api/01-users.js index b92fa23b7e873..c0fff814a7033 100644 --- a/tests/end-to-end/api/01-users.js +++ b/tests/end-to-end/api/01-users.js @@ -2,10 +2,20 @@ /* globals expect */ /* eslint no-unused-vars: 0 */ -import {getCredentials, api, login, request, credentials, apiEmail, apiUsername, targetUser, log} from '../../data/api-data.js'; -import {adminEmail, password} from '../../data/user.js'; -import {imgURL} from '../../data/interactions.js'; -import {customFieldText, clearCustomFields, setCustomFields} from '../../data/custom-fields.js'; +import { + getCredentials, + api, + login, + request, + credentials, + apiEmail, + apiUsername, + targetUser, + log +} from '../../data/api-data.js'; +import { adminEmail, password } from '../../data/user.js'; +import { imgURL } from '../../data/interactions.js'; +import { customFieldText, clearCustomFields, setCustomFields } from '../../data/custom-fields.js'; describe('[Users]', function() { this.retries(0); @@ -46,7 +56,7 @@ describe('[Users]', function() { }); it('should create a new user with custom fields', (done) => { - setCustomFields({customFieldText}, (error) => { + setCustomFields({ customFieldText }, (error) => { if (error) { return done(error); } @@ -117,10 +127,12 @@ describe('[Users]', function() { } [ - {name: 'customFieldText', value: '', reason: 'is required and missing'}, - {name: 'customFieldText', value: '0', reason: 'length is less than minLength'}, - {name: 'customFieldText', value: '0123456789-0', reason: 'length is more than maxLength'} - ].forEach((field) => { failUserWithCustomField(field); }); + { name: 'customFieldText', value: '', reason: 'is required and missing' }, + { name: 'customFieldText', value: '0', reason: 'length is less than minLength' }, + { name: 'customFieldText', value: '0123456789-0', reason: 'length is more than maxLength' } + ].forEach((field) => { + failUserWithCustomField(field); + }); }); describe('[/users.info]', () => { @@ -210,12 +222,13 @@ describe('[Users]', function() { }); describe('[/users.update]', () => { + it('should update a user\'s info by userId', (done) => { request.post(api('users.update')) .set(credentials) .send({ userId: targetUser._id, - data :{ + data: { email: apiEmail, name: `edited${ apiUsername }`, username: `edited${ apiUsername }`, @@ -235,6 +248,43 @@ describe('[Users]', function() { }) .end(done); }); + + it('should update a user\'s email by userId', (done) => { + request.post(api('users.update')) + .set(credentials) + .send({ + userId: targetUser._id, + data: { + email: `edited${ apiEmail }` + } + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.nested.property('user.emails[0].address', `edited${ apiEmail }`); + expect(res.body).to.have.nested.property('user.emails[0].verified', false); + }) + .end(done); + }); + + it('should verify user\'s email by userId', (done) => { + request.post(api('users.update')) + .set(credentials) + .send({ + userId: targetUser._id, + data: { + verified: true + } + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.nested.property('user.emails[0].verified', true); + }) + .end(done); + }); }); describe('[/users.createToken]', () => { @@ -349,7 +399,7 @@ describe('[Users]', function() { .send({ username: user.username }) .expect('Content-Type', 'application/json') .end((err, res) => { - return err ? done () : request.get(api('me')) + return err ? done() : request.get(api('me')) .set({ 'X-Auth-Token': `${ res.body.data.authToken }`, 'X-User-Id': res.body.data.userId }) .expect(200) .expect((res) => { From 8374c975fe4e848b5637f3280dda470c51e0db93 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Wed, 7 Mar 2018 17:16:43 -0300 Subject: [PATCH 2/6] added endpoint to update a user's basic informations. --- packages/rocketchat-api/package.js | 3 +- packages/rocketchat-api/server/v1/users.js | 37 +++++++- tests/end-to-end/api/01-users.js | 98 ++++++++++++++++++++++ 3 files changed, 136 insertions(+), 2 deletions(-) diff --git a/packages/rocketchat-api/package.js b/packages/rocketchat-api/package.js index 633765ab24d28..98b7079eded86 100644 --- a/packages/rocketchat-api/package.js +++ b/packages/rocketchat-api/package.js @@ -9,7 +9,8 @@ Package.onUse(function(api) { api.use([ 'ecmascript', 'rocketchat:lib', - 'nimble:restivus' + 'nimble:restivus', + 'sha' ]); api.addFiles('server/api.js', 'server'); diff --git a/packages/rocketchat-api/server/v1/users.js b/packages/rocketchat-api/server/v1/users.js index 234e73e83ac6c..9371aff141b39 100644 --- a/packages/rocketchat-api/server/v1/users.js +++ b/packages/rocketchat-api/server/v1/users.js @@ -260,6 +260,41 @@ RocketChat.API.v1.addRoute('users.update', { authRequired: true }, { } }); +RocketChat.API.v1.addRoute('users.updateOwnBasicInfo', { authRequired: true }, { + post() { + check(this.bodyParams, { + userId: String, + data: Match.ObjectIncluding({ + email: Match.Maybe(String), + name: Match.Maybe(String), + username: Match.Maybe(String), + actualPassword: Match.Maybe(String), + newPassword: Match.Maybe(String) + }), + customFields: Match.Maybe(Object) + }); + + const userData = { + _id: this.bodyParams.userId, + email: this.bodyParams.data.email, + realname: this.bodyParams.data.name, + username: this.bodyParams.data.username, + newPassword: this.bodyParams.data.newPassword + }; + + if (this.bodyParams.data.actualPassword) { + userData.typedPassword = SHA256(this.bodyParams.data.actualPassword); + } + + Meteor.runAsUser(this.bodyParams.userId, () => Meteor.call('saveUserProfile', userData, this.bodyParams.customFields)); + if (userData.email) { + Meteor.call('sendConfirmationEmail', userData.email); + } + + return RocketChat.API.v1.success({ user: RocketChat.models.Users.findOneById(this.bodyParams.userId, { fields: RocketChat.API.v1.defaultFieldsToExclude }) }); + } +}); + RocketChat.API.v1.addRoute('users.createToken', { authRequired: true }, { post() { const user = this.getUserFromParams(); @@ -267,6 +302,6 @@ RocketChat.API.v1.addRoute('users.createToken', { authRequired: true }, { Meteor.runAsUser(this.userId, () => { data = Meteor.call('createToken', user._id); }); - return data ? RocketChat.API.v1.success({data}) : RocketChat.API.v1.unauthorized(); + return data ? RocketChat.API.v1.success({ data }) : RocketChat.API.v1.unauthorized(); } }); diff --git a/tests/end-to-end/api/01-users.js b/tests/end-to-end/api/01-users.js index c0fff814a7033..49178c85e0e8f 100644 --- a/tests/end-to-end/api/01-users.js +++ b/tests/end-to-end/api/01-users.js @@ -287,6 +287,104 @@ describe('[Users]', function() { }); }); + describe('[/users.updateOwnBasicInfo]', () => { + const newPassword = `${ password }test`; + const editedUsername = `basicInfo.name${ +new Date() }`; + const editedName = `basic-info-test-name${ +new Date() }`; + const editedEmail = `test${ +new Date() }@mail.com`; + + it('should update the user own basic information', (done) => { + request.post(api('users.updateOwnBasicInfo')) + .set(credentials) + .send({ + userId: targetUser._id, + data: { + name: editedName, + username: editedUsername, + actualPassword: password, + newPassword + } + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + const user = res.body.user; + expect(res.body).to.have.property('success', true); + expect(user.username).to.be.equal(editedUsername); + expect(user.name).to.be.equal(editedName); + }) + .end(done); + }); + + it('should update the user name only', (done) => { + request.post(api('users.updateOwnBasicInfo')) + .set(credentials) + .send({ + userId: targetUser._id, + data: { + username: editedUsername + } + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + const user = res.body.user; + expect(res.body).to.have.property('success', true); + expect(user.username).to.be.equal(editedUsername); + }) + .end(done); + }); + + it('should throw an error when user try change email without the password', (done) => { + request.post(api('users.updateOwnBasicInfo')) + .set(credentials) + .send({ + userId: targetUser._id, + data: { + email: editedEmail + } + }) + .expect('Content-Type', 'application/json') + .expect(400) + .end(done); + }); + + it('should throw an error when user try change password without the actual password', (done) => { + request.post(api('users.updateOwnBasicInfo')) + .set(credentials) + .send({ + userId: targetUser._id, + data: { + newPassword: 'the new pass' + } + }) + .expect('Content-Type', 'application/json') + .expect(400) + .end(done); + }); + + it('should set new email as \'unverified\'', (done) => { + request.post(api('users.updateOwnBasicInfo')) + .set(credentials) + .send({ + userId: targetUser._id, + data: { + email: editedEmail, + actualPassword: newPassword + } + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + const user = res.body.user; + expect(res.body).to.have.property('success', true); + expect(user.emails[0].address).to.be.equal(editedEmail); + expect(user.emails[0].verified).to.be.false; + }) + .end(done); + }); + }); + describe('[/users.createToken]', () => { let user; beforeEach((done) => { From c2430681a8a5565f14265f061efd3c8052e60bae Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Fri, 9 Mar 2018 10:23:37 -0300 Subject: [PATCH 3/6] fix some issues requested in the last review about users.updateOwnBasicInfo endpoint. --- packages/rocketchat-api/server/v1/users.js | 12 ++-- .../server/functions/saveUser.js | 68 ++++++++++++++----- tests/end-to-end/api/01-users.js | 55 +++++++++++---- 3 files changed, 99 insertions(+), 36 deletions(-) diff --git a/packages/rocketchat-api/server/v1/users.js b/packages/rocketchat-api/server/v1/users.js index ae264262e0d94..891d4063dfc31 100644 --- a/packages/rocketchat-api/server/v1/users.js +++ b/packages/rocketchat-api/server/v1/users.js @@ -263,35 +263,33 @@ RocketChat.API.v1.addRoute('users.update', { authRequired: true }, { RocketChat.API.v1.addRoute('users.updateOwnBasicInfo', { authRequired: true }, { post() { check(this.bodyParams, { - userId: String, data: Match.ObjectIncluding({ email: Match.Maybe(String), name: Match.Maybe(String), username: Match.Maybe(String), - actualPassword: Match.Maybe(String), + currentPassword: Match.Maybe(String), newPassword: Match.Maybe(String) }), customFields: Match.Maybe(Object) }); const userData = { - _id: this.bodyParams.userId, email: this.bodyParams.data.email, realname: this.bodyParams.data.name, username: this.bodyParams.data.username, newPassword: this.bodyParams.data.newPassword }; - if (this.bodyParams.data.actualPassword) { - userData.typedPassword = SHA256(this.bodyParams.data.actualPassword); + if (this.bodyParams.data.currentPassword) { + userData.typedPassword = SHA256(this.bodyParams.data.currentPassword); } - Meteor.runAsUser(this.bodyParams.userId, () => Meteor.call('saveUserProfile', userData, this.bodyParams.customFields)); + Meteor.runAsUser(this.userId, () => Meteor.call('saveUserProfile', userData, this.bodyParams.customFields)); if (userData.email) { Meteor.call('sendConfirmationEmail', userData.email); } - return RocketChat.API.v1.success({ user: RocketChat.models.Users.findOneById(this.bodyParams.userId, { fields: RocketChat.API.v1.defaultFieldsToExclude }) }); + return RocketChat.API.v1.success({ user: RocketChat.models.Users.findOneById(this.userId, { fields: RocketChat.API.v1.defaultFieldsToExclude }) }); } }); diff --git a/packages/rocketchat-lib/server/functions/saveUser.js b/packages/rocketchat-lib/server/functions/saveUser.js index 32ca74a99915c..cf804f6234bc6 100644 --- a/packages/rocketchat-lib/server/functions/saveUser.js +++ b/packages/rocketchat-lib/server/functions/saveUser.js @@ -7,27 +7,45 @@ RocketChat.saveUser = function(userId, userData) { const existingRoles = _.pluck(RocketChat.authz.getRoles(), '_id'); if (userData._id && userId !== userData._id && !RocketChat.authz.hasPermission(userId, 'edit-other-user-info')) { - throw new Meteor.Error('error-action-not-allowed', 'Editing user is not allowed', { method: 'insertOrUpdateUser', action: 'Editing_user' }); + throw new Meteor.Error('error-action-not-allowed', 'Editing user is not allowed', { + method: 'insertOrUpdateUser', + action: 'Editing_user' + }); } if (!userData._id && !RocketChat.authz.hasPermission(userId, 'create-user')) { - throw new Meteor.Error('error-action-not-allowed', 'Adding user is not allowed', { method: 'insertOrUpdateUser', action: 'Adding_user' }); + throw new Meteor.Error('error-action-not-allowed', 'Adding user is not allowed', { + method: 'insertOrUpdateUser', + action: 'Adding_user' + }); } if (userData.roles && _.difference(userData.roles, existingRoles).length > 0) { - throw new Meteor.Error('error-action-not-allowed', 'The field Roles consist invalid role name', { method: 'insertOrUpdateUser', action: 'Assign_role' }); + throw new Meteor.Error('error-action-not-allowed', 'The field Roles consist invalid role name', { + method: 'insertOrUpdateUser', + action: 'Assign_role' + }); } if (userData.roles && _.indexOf(userData.roles, 'admin') >= 0 && !RocketChat.authz.hasPermission(userId, 'assign-admin-role')) { - throw new Meteor.Error('error-action-not-allowed', 'Assigning admin is not allowed', { method: 'insertOrUpdateUser', action: 'Assign_admin' }); + throw new Meteor.Error('error-action-not-allowed', 'Assigning admin is not allowed', { + method: 'insertOrUpdateUser', + action: 'Assign_admin' + }); } if (!userData._id && !s.trim(userData.name)) { - throw new Meteor.Error('error-the-field-is-required', 'The field Name is required', { method: 'insertOrUpdateUser', field: 'Name' }); + throw new Meteor.Error('error-the-field-is-required', 'The field Name is required', { + method: 'insertOrUpdateUser', + field: 'Name' + }); } if (!userData._id && !s.trim(userData.username)) { - throw new Meteor.Error('error-the-field-is-required', 'The field Username is required', { method: 'insertOrUpdateUser', field: 'Username' }); + throw new Meteor.Error('error-the-field-is-required', 'The field Username is required', { + method: 'insertOrUpdateUser', + field: 'Username' + }); } let nameValidation; @@ -39,20 +57,33 @@ RocketChat.saveUser = function(userId, userData) { } if (userData.username && !nameValidation.test(userData.username)) { - throw new Meteor.Error('error-input-is-not-a-valid-field', `${ _.escape(userData.username) } is not a valid username`, { method: 'insertOrUpdateUser', input: userData.username, field: 'Username' }); + throw new Meteor.Error('error-input-is-not-a-valid-field', `${ _.escape(userData.username) } is not a valid username`, { + method: 'insertOrUpdateUser', + input: userData.username, + field: 'Username' + }); } if (!userData._id && !userData.password) { - throw new Meteor.Error('error-the-field-is-required', 'The field Password is required', { method: 'insertOrUpdateUser', field: 'Password' }); + throw new Meteor.Error('error-the-field-is-required', 'The field Password is required', { + method: 'insertOrUpdateUser', + field: 'Password' + }); } if (!userData._id) { if (!RocketChat.checkUsernameAvailability(userData.username)) { - throw new Meteor.Error('error-field-unavailable', `${ _.escape(userData.username) } is already in use :(`, { method: 'insertOrUpdateUser', field: userData.username }); + throw new Meteor.Error('error-field-unavailable', `${ _.escape(userData.username) } is already in use :(`, { + method: 'insertOrUpdateUser', + field: userData.username + }); } if (userData.email && !RocketChat.checkEmailAvailability(userData.email)) { - throw new Meteor.Error('error-field-unavailable', `${ _.escape(userData.email) } is already in use :(`, { method: 'insertOrUpdateUser', field: userData.email }); + throw new Meteor.Error('error-field-unavailable', `${ _.escape(userData.email) } is already in use :(`, { + method: 'insertOrUpdateUser', + field: userData.email + }); } RocketChat.validateEmailDomain(userData.email); @@ -73,7 +104,7 @@ RocketChat.saveUser = function(userId, userData) { $set: { name: userData.name, roles: userData.roles || ['user'], - settings: userData.settings + settings: userData.settings || {} } }; @@ -81,8 +112,8 @@ RocketChat.saveUser = function(userId, userData) { updateUser.$set.requirePasswordChange = userData.requirePasswordChange; } - if (userData.verified) { - updateUser.$set['emails.0.verified'] = true; + if (typeof userData.verified === 'boolean') { + updateUser.$set['emails.0.verified'] = userData.verified; } Meteor.users.update({ _id }, updateUser); @@ -120,7 +151,10 @@ RocketChat.saveUser = function(userId, userData) { try { Email.send(email); } catch (error) { - throw new Meteor.Error('error-email-send-failed', `Error trying to send email: ${ error.message }`, { function: 'RocketChat.saveUser', message: error.message }); + throw new Meteor.Error('error-email-send-failed', `Error trying to send email: ${ error.message }`, { + function: 'RocketChat.saveUser', + message: error.message + }); } }); } @@ -128,7 +162,7 @@ RocketChat.saveUser = function(userId, userData) { userData._id = _id; if (RocketChat.settings.get('Accounts_SetDefaultAvatar') === true && userData.email) { - const gravatarUrl = Gravatar.imageUrl(userData.email, {default: '404', size: 200, secure: true}); + const gravatarUrl = Gravatar.imageUrl(userData.email, { default: '404', size: 200, secure: true }); try { RocketChat.setUserAvatar(userData, gravatarUrl, '', 'url'); @@ -172,8 +206,8 @@ RocketChat.saveUser = function(userId, userData) { updateUser.$set.requirePasswordChange = userData.requirePasswordChange; } - if (userData.verified) { - updateUser.$set['emails.0.verified'] = true; + if (typeof userData.verified === 'boolean') { + updateUser.$set['emails.0.verified'] = userData.verified; } Meteor.users.update({ _id: userData._id }, updateUser); diff --git a/tests/end-to-end/api/01-users.js b/tests/end-to-end/api/01-users.js index a7bf63b908359..56851bd9cf1e1 100644 --- a/tests/end-to-end/api/01-users.js +++ b/tests/end-to-end/api/01-users.js @@ -13,7 +13,7 @@ import { targetUser, log } from '../../data/api-data.js'; -import { adminEmail, password } from '../../data/user.js'; +import { adminEmail, preferences, password } from '../../data/user.js'; import { imgURL } from '../../data/interactions.js'; import { customFieldText, clearCustomFields, setCustomFields } from '../../data/custom-fields.js'; @@ -288,6 +288,42 @@ describe('[Users]', function() { }); describe('[/users.updateOwnBasicInfo]', () => { + let user; + before((done) => { + const username = `user.test.${ Date.now() }`; + const email = `${ username }@rocket.chat`; + request.post(api('users.create')) + .set(credentials) + .send({ email, name: username, username, password}) + .end((err, res) => { + user = res.body.user; + done(); + }); + }); + + let userCredentials; + before((done) => { + request.post(api('login')) + .send({ + user: user.username, + password + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + userCredentials = {}; + userCredentials['X-Auth-Token'] = res.body.data.authToken; + userCredentials['X-User-Id'] = res.body.data.userId; + }) + .end(done); + }); + after(done => { + request.post(api('users.delete')).set(credentials).send({ + userId: user._id + }).end(done); + user = undefined; + }); + const newPassword = `${ password }test`; const editedUsername = `basicInfo.name${ +new Date() }`; const editedName = `basic-info-test-name${ +new Date() }`; @@ -295,13 +331,12 @@ describe('[Users]', function() { it('should update the user own basic information', (done) => { request.post(api('users.updateOwnBasicInfo')) - .set(credentials) + .set(userCredentials) .send({ - userId: targetUser._id, data: { name: editedName, username: editedUsername, - actualPassword: password, + currentPassword: password, newPassword } }) @@ -318,9 +353,8 @@ describe('[Users]', function() { it('should update the user name only', (done) => { request.post(api('users.updateOwnBasicInfo')) - .set(credentials) + .set(userCredentials) .send({ - userId: targetUser._id, data: { username: editedUsername } @@ -337,9 +371,8 @@ describe('[Users]', function() { it('should throw an error when user try change email without the password', (done) => { request.post(api('users.updateOwnBasicInfo')) - .set(credentials) + .set(userCredentials) .send({ - userId: targetUser._id, data: { email: editedEmail } @@ -353,7 +386,6 @@ describe('[Users]', function() { request.post(api('users.updateOwnBasicInfo')) .set(credentials) .send({ - userId: targetUser._id, data: { newPassword: 'the new pass' } @@ -365,12 +397,11 @@ describe('[Users]', function() { it('should set new email as \'unverified\'', (done) => { request.post(api('users.updateOwnBasicInfo')) - .set(credentials) + .set(userCredentials) .send({ - userId: targetUser._id, data: { email: editedEmail, - actualPassword: newPassword + currentPassword: newPassword } }) .expect('Content-Type', 'application/json') From 028f08aec483cab758587da08ac76e961e0c08b5 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Fri, 9 Mar 2018 11:57:39 -0300 Subject: [PATCH 4/6] Change the current password, to be send in sha256, and change sendVerificationEmail method --- packages/rocketchat-api/package.js | 3 +-- packages/rocketchat-api/server/v1/users.js | 10 ++-------- packages/rocketchat-lib/server/functions/saveUser.js | 3 ++- packages/rocketchat-lib/server/functions/setEmail.js | 3 ++- server/methods/sendConfirmationEmail.js | 4 ++-- tests/end-to-end/api/01-users.js | 5 +++-- 6 files changed, 12 insertions(+), 16 deletions(-) diff --git a/packages/rocketchat-api/package.js b/packages/rocketchat-api/package.js index 9c9934fc55a97..df6d1b9b3ab4a 100644 --- a/packages/rocketchat-api/package.js +++ b/packages/rocketchat-api/package.js @@ -9,8 +9,7 @@ Package.onUse(function(api) { api.use([ 'ecmascript', 'rocketchat:lib', - 'nimble:restivus', - 'sha' + 'nimble:restivus' ]); api.addFiles('server/api.js', 'server'); diff --git a/packages/rocketchat-api/server/v1/users.js b/packages/rocketchat-api/server/v1/users.js index 891d4063dfc31..aecbd4598164a 100644 --- a/packages/rocketchat-api/server/v1/users.js +++ b/packages/rocketchat-api/server/v1/users.js @@ -277,17 +277,11 @@ RocketChat.API.v1.addRoute('users.updateOwnBasicInfo', { authRequired: true }, { email: this.bodyParams.data.email, realname: this.bodyParams.data.name, username: this.bodyParams.data.username, - newPassword: this.bodyParams.data.newPassword + newPassword: this.bodyParams.data.newPassword, + typedPassword: this.bodyParams.data.currentPassword }; - if (this.bodyParams.data.currentPassword) { - userData.typedPassword = SHA256(this.bodyParams.data.currentPassword); - } - Meteor.runAsUser(this.userId, () => Meteor.call('saveUserProfile', userData, this.bodyParams.customFields)); - if (userData.email) { - Meteor.call('sendConfirmationEmail', userData.email); - } return RocketChat.API.v1.success({ user: RocketChat.models.Users.findOneById(this.userId, { fields: RocketChat.API.v1.defaultFieldsToExclude }) }); } diff --git a/packages/rocketchat-lib/server/functions/saveUser.js b/packages/rocketchat-lib/server/functions/saveUser.js index cf804f6234bc6..92e437daa2b44 100644 --- a/packages/rocketchat-lib/server/functions/saveUser.js +++ b/packages/rocketchat-lib/server/functions/saveUser.js @@ -183,7 +183,8 @@ RocketChat.saveUser = function(userId, userData) { } if (userData.email) { - RocketChat.setEmail(userData._id, userData.email); + const shouldSendVerificationEmailToUser = !Boolean(userData.verified); + RocketChat.setEmail(userData._id, userData.email, shouldSendVerificationEmailToUser); } if (userData.password && userData.password.trim() && RocketChat.authz.hasPermission(userId, 'edit-other-user-password')) { diff --git a/packages/rocketchat-lib/server/functions/setEmail.js b/packages/rocketchat-lib/server/functions/setEmail.js index b63f39cb83230..779f860f184bf 100644 --- a/packages/rocketchat-lib/server/functions/setEmail.js +++ b/packages/rocketchat-lib/server/functions/setEmail.js @@ -1,6 +1,6 @@ import s from 'underscore.string'; -RocketChat._setEmail = function(userId, email) { +RocketChat._setEmail = function(userId, email, shouldSendVerificationEmail) { email = s.trim(email); if (!userId) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { function: '_setEmail' }); @@ -27,6 +27,7 @@ RocketChat._setEmail = function(userId, email) { // Set new email RocketChat.models.Users.setEmail(user._id, email); user.email = email; + Meteor.call('sendConfirmationEmail', user.email, shouldSendVerificationEmail); return user; }; diff --git a/server/methods/sendConfirmationEmail.js b/server/methods/sendConfirmationEmail.js index 82d761a970e6d..7172df75c7640 100644 --- a/server/methods/sendConfirmationEmail.js +++ b/server/methods/sendConfirmationEmail.js @@ -1,11 +1,11 @@ Meteor.methods({ - sendConfirmationEmail(email) { + sendConfirmationEmail(email, shouldSendVerificationEmail = true) { check(email, String); email = email.trim(); const user = RocketChat.models.Users.findOneByEmailAddress(email); - if (user) { + if (user && shouldSendVerificationEmail) { if (RocketChat.settings.get('Verification_Customized')) { const subject = RocketChat.placeholders.replace(RocketChat.settings.get('Verification_Email_Subject') || ''); const html = RocketChat.placeholders.replace(RocketChat.settings.get('Verification_Email') || ''); diff --git a/tests/end-to-end/api/01-users.js b/tests/end-to-end/api/01-users.js index 56851bd9cf1e1..690b542d86a57 100644 --- a/tests/end-to-end/api/01-users.js +++ b/tests/end-to-end/api/01-users.js @@ -2,6 +2,7 @@ /* globals expect */ /* eslint no-unused-vars: 0 */ +import crypto from 'crypto'; import { getCredentials, api, @@ -336,7 +337,7 @@ describe('[Users]', function() { data: { name: editedName, username: editedUsername, - currentPassword: password, + currentPassword: crypto.createHash('sha256').update(password, 'utf8').digest('hex'), newPassword } }) @@ -401,7 +402,7 @@ describe('[Users]', function() { .send({ data: { email: editedEmail, - currentPassword: newPassword + currentPassword: crypto.createHash('sha256').update(newPassword, 'utf8').digest('hex') } }) .expect('Content-Type', 'application/json') From 79873b02d6617d81c9a8dee6ab35a2969d041cd9 Mon Sep 17 00:00:00 2001 From: Marcos Defendi Date: Fri, 9 Mar 2018 12:02:46 -0300 Subject: [PATCH 5/6] fix lint error --- packages/rocketchat-lib/server/functions/saveUser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rocketchat-lib/server/functions/saveUser.js b/packages/rocketchat-lib/server/functions/saveUser.js index 92e437daa2b44..35dffd07692d5 100644 --- a/packages/rocketchat-lib/server/functions/saveUser.js +++ b/packages/rocketchat-lib/server/functions/saveUser.js @@ -183,7 +183,7 @@ RocketChat.saveUser = function(userId, userData) { } if (userData.email) { - const shouldSendVerificationEmailToUser = !Boolean(userData.verified); + const shouldSendVerificationEmailToUser = !userData.verified; RocketChat.setEmail(userData._id, userData.email, shouldSendVerificationEmailToUser); } From b7259fce010c0a6a2c6858ba1e33c03626fa6ce3 Mon Sep 17 00:00:00 2001 From: Rodrigo Nascimento Date: Fri, 9 Mar 2018 14:47:47 -0300 Subject: [PATCH 6/6] Code improvements --- packages/rocketchat-lib/server/functions/saveUser.js | 2 +- packages/rocketchat-lib/server/functions/setEmail.js | 6 ++++-- server/methods/sendConfirmationEmail.js | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/rocketchat-lib/server/functions/saveUser.js b/packages/rocketchat-lib/server/functions/saveUser.js index 35dffd07692d5..c4d1b4089b778 100644 --- a/packages/rocketchat-lib/server/functions/saveUser.js +++ b/packages/rocketchat-lib/server/functions/saveUser.js @@ -183,7 +183,7 @@ RocketChat.saveUser = function(userId, userData) { } if (userData.email) { - const shouldSendVerificationEmailToUser = !userData.verified; + const shouldSendVerificationEmailToUser = userData.verified !== true; RocketChat.setEmail(userData._id, userData.email, shouldSendVerificationEmailToUser); } diff --git a/packages/rocketchat-lib/server/functions/setEmail.js b/packages/rocketchat-lib/server/functions/setEmail.js index 779f860f184bf..9d1819cfd1f53 100644 --- a/packages/rocketchat-lib/server/functions/setEmail.js +++ b/packages/rocketchat-lib/server/functions/setEmail.js @@ -1,6 +1,6 @@ import s from 'underscore.string'; -RocketChat._setEmail = function(userId, email, shouldSendVerificationEmail) { +RocketChat._setEmail = function(userId, email, shouldSendVerificationEmail = true) { email = s.trim(email); if (!userId) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { function: '_setEmail' }); @@ -27,7 +27,9 @@ RocketChat._setEmail = function(userId, email, shouldSendVerificationEmail) { // Set new email RocketChat.models.Users.setEmail(user._id, email); user.email = email; - Meteor.call('sendConfirmationEmail', user.email, shouldSendVerificationEmail); + if (shouldSendVerificationEmail === true) { + Meteor.call('sendConfirmationEmail', user.email); + } return user; }; diff --git a/server/methods/sendConfirmationEmail.js b/server/methods/sendConfirmationEmail.js index 7172df75c7640..82d761a970e6d 100644 --- a/server/methods/sendConfirmationEmail.js +++ b/server/methods/sendConfirmationEmail.js @@ -1,11 +1,11 @@ Meteor.methods({ - sendConfirmationEmail(email, shouldSendVerificationEmail = true) { + sendConfirmationEmail(email) { check(email, String); email = email.trim(); const user = RocketChat.models.Users.findOneByEmailAddress(email); - if (user && shouldSendVerificationEmail) { + if (user) { if (RocketChat.settings.get('Verification_Customized')) { const subject = RocketChat.placeholders.replace(RocketChat.settings.get('Verification_Email_Subject') || ''); const html = RocketChat.placeholders.replace(RocketChat.settings.get('Verification_Email') || '');