From c2abbae92d64a915cb7388a77aa285557b27c34f Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Sun, 28 May 2017 20:34:49 -0400 Subject: [PATCH] fix(Users): Makes sure verifying emails triggers hooks and liveQuery (#3851) * Use RestWrite when verifying emails so hooks are called (as master) * Fixes tests for postgres * nit * Makes rest.update support a full where instead of objectId * Use rest.update to guaranteed proper beforeSave and liveQuery calls --- spec/ParseInstallation.spec.js | 28 +++++++++---------- spec/rest.spec.js | 6 ++-- .../Postgres/PostgresStorageAdapter.js | 3 +- src/Controllers/UserController.js | 11 ++------ src/RestWrite.js | 3 +- src/Routers/ClassesRouter.js | 3 +- src/rest.js | 6 ++-- 7 files changed, 29 insertions(+), 31 deletions(-) diff --git a/spec/ParseInstallation.spec.js b/spec/ParseInstallation.spec.js index b92c2adb..ea895fe4 100644 --- a/spec/ParseInstallation.spec.js +++ b/spec/ParseInstallation.spec.js @@ -339,11 +339,11 @@ describe('Installations', () => { .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { expect(results.length).toEqual(1); - var id = results[0].objectId; + var objectId = results[0].objectId; var update = { 'channels': ['baz'] }; - return rest.update(config, auth.nobody(config), '_Installation', id, update); + return rest.update(config, auth.nobody(config), '_Installation', { objectId }, update); }) .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { @@ -370,7 +370,7 @@ describe('Installations', () => { .then(results => { expect(results.length).toEqual(1); input = { 'installationId': installId2 }; - return rest.update(config, auth.nobody(config), '_Installation', results[0].objectId, input); + return rest.update(config, auth.nobody(config), '_Installation', { objectId: results[0].objectId }, input); }).then(() => { fail('Updating the installation should have failed.'); done(); @@ -393,7 +393,7 @@ describe('Installations', () => { .then(results => { expect(results.length).toEqual(1); input = { 'deviceToken': b }; - return rest.update(config, auth.nobody(config), '_Installation', results[0].objectId, input); + return rest.update(config, auth.nobody(config), '_Installation', { objectId: results[0].objectId }, input); }).then(() => { fail('Updating the installation should have failed.'); }).catch((error) => { @@ -421,7 +421,7 @@ describe('Installations', () => { 'deviceToken': u, 'deviceType': 'ios' }; - return rest.update(config, auth.nobody(config), '_Installation', results[0].objectId, input); + return rest.update(config, auth.nobody(config), '_Installation', { objectId: results[0].objectId }, input); }) .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { @@ -448,7 +448,7 @@ describe('Installations', () => { input = { 'deviceType': 'ios' }; - return rest.update(config, auth.nobody(config), '_Installation', results[0].objectId, input); + return rest.update(config, auth.nobody(config), '_Installation', { objectId: results[0].objectId }, input); }).then(() => { fail('Should not have been able to update Installation.'); done(); @@ -472,7 +472,7 @@ describe('Installations', () => { input = { 'custom': 'allowed' }; - return rest.update(config, auth.nobody(config), '_Installation', results[0].objectId, input); + return rest.update(config, auth.nobody(config), '_Installation', { objectId: results[0].objectId }, input); }) .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { @@ -514,7 +514,7 @@ describe('Installations', () => { 'objectId': secondObject.objectId, 'deviceToken': t }; - return rest.update(config, auth.nobody(config), '_Installation', secondObject.objectId, input); + return rest.update(config, auth.nobody(config), '_Installation', { objectId: secondObject.objectId }, input); }) .then(() => database.adapter.find('_Installation', installationSchema, {objectId: firstObject.objectId}, {})) .then(results => { @@ -562,7 +562,7 @@ describe('Installations', () => { 'installationId': installId2, 'deviceToken': t }; - return rest.update(config, auth.nobody(config), '_Installation', secondObject.objectId, input); + return rest.update(config, auth.nobody(config), '_Installation', { objectId: secondObject.objectId }, input); }) .then(() => delay(100)) .then(() => database.adapter.find('_Installation', installationSchema, {objectId: firstObject.objectId}, {})) @@ -619,7 +619,7 @@ describe('Installations', () => { 'deviceToken': t, 'channels': [] }; - return rest.update(config, auth.nobody(config), '_Installation', results[0].objectId, input); + return rest.update(config, auth.nobody(config), '_Installation', { objectId: results[0].objectId }, input); }) .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { @@ -657,7 +657,7 @@ describe('Installations', () => { 'installationId': installId, 'deviceType': 'ios' }; - return rest.update(config, auth.nobody(config), '_Installation', results[0].objectId, input); + return rest.update(config, auth.nobody(config), '_Installation', { objectId: results[0].objectId }, input); }) .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { @@ -699,7 +699,7 @@ describe('Installations', () => { 'amount': 1 } }; - return rest.update(config, auth.nobody(config), '_Installation', results[0].objectId, input); + return rest.update(config, auth.nobody(config), '_Installation', { objectId: results[0].objectId }, input); }) .then(() => database.adapter.find('_Installation', installationSchema, {}, {})) .then(results => { @@ -744,7 +744,7 @@ describe('Installations', () => { 'deviceToken': t, 'deviceType': 'ios' }; - return rest.update(config, auth.nobody(config), '_Installation', installObj.objectId, input); + return rest.update(config, auth.nobody(config), '_Installation', { objectId: installObj.objectId }, input); }) .then(() => database.adapter.find('_Installation', installationSchema, { objectId: tokenObj.objectId }, {})) .then(results => { @@ -791,7 +791,7 @@ describe('Installations', () => { 'amount': 1 } }; - return rest.update(config, auth.nobody(config), '_Installation', installObj.objectId, input); + return rest.update(config, auth.nobody(config), '_Installation', { objectId: installObj.objectId }, input); }) .then(() => database.adapter.find('_Installation', installationSchema, { objectId: tokenObj.objectId }, {})) .then(results => { diff --git a/spec/rest.spec.js b/spec/rest.spec.js index 75ba42b6..63261807 100644 --- a/spec/rest.spec.js +++ b/spec/rest.spec.js @@ -64,7 +64,7 @@ describe('rest create', () => { expect(mob.subdoc.wu).toBe('tan'); expect(typeof mob.objectId).toEqual('string'); const obj = { 'subdoc.wu': 'clan' }; - return rest.update(config, auth.nobody(config), 'MyClass', mob.objectId, obj) + return rest.update(config, auth.nobody(config), 'MyClass', { objectId: mob.objectId }, obj) }) .then(() => database.adapter.find('MyClass', { fields: {} }, {}, {})) .then(results => { @@ -195,7 +195,7 @@ describe('rest create', () => { objectId = r.response.objectId; return auth.getAuthForSessionToken({config, sessionToken: r.response.sessionToken }) }).then((sessionAuth) => { - return rest.update(config, sessionAuth, '_User', objectId, updatedData); + return rest.update(config, sessionAuth, '_User', { objectId }, updatedData); }).then(() => { return Parse.User.logOut().then(() => { return Parse.User.logIn('hello', 'world'); @@ -435,7 +435,7 @@ describe('rest update', () => { createdAt: {__type: "Date", iso: newCreatedAt}, // should be ignored }; - return rest.update(config, nobody, className, objectId, restObject).then(() => { + return rest.update(config, nobody, className, { objectId }, restObject).then(() => { const restWhere = { objectId: objectId, }; diff --git a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js index 557ba78a..f545ba9c 100644 --- a/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js +++ b/src/Adapters/Storage/Postgres/PostgresStorageAdapter.js @@ -58,7 +58,8 @@ const toPostgresValue = value => { } const transformValue = value => { - if (value.__type === 'Pointer') { + if (typeof value === 'object' && + value.__type === 'Pointer') { return value.objectId; } return value; diff --git a/src/Controllers/UserController.js b/src/Controllers/UserController.js index 305fdccf..e8b79775 100644 --- a/src/Controllers/UserController.js +++ b/src/Controllers/UserController.js @@ -59,18 +59,13 @@ export class UserController extends AdaptableController { updateFields._email_verify_token_expires_at = {__op: 'Delete'}; } - + const masterAuth = Auth.master(this.config); var checkIfAlreadyVerified = new RestQuery(this.config, Auth.master(this.config), '_User', {username: username, emailVerified: true}); return checkIfAlreadyVerified.execute().then(result => { if (result.results.length) { return Promise.resolve(result.results.length[0]); } - return this.config.database.update('_User', query, updateFields).then((document) => { - if (!document) { - throw undefined - } - return Promise.resolve(document); - }) + return rest.update(this.config, masterAuth, '_User', query, updateFields); }); } @@ -229,7 +224,7 @@ export class UserController extends AdaptableController { // Mark this private function updateUserPassword(userId, password, config) { - return rest.update(config, Auth.master(config), '_User', userId, { + return rest.update(config, Auth.master(config), '_User', { objectId: userId }, { password: password }); } diff --git a/src/RestWrite.js b/src/RestWrite.js index 3f6e5427..530b2dba 100644 --- a/src/RestWrite.js +++ b/src/RestWrite.js @@ -360,7 +360,8 @@ RestWrite.prototype.transformUser = function() { throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, error); } - if (this.query) { + // Do not cleanup session if objectId is not set + if (this.query && this.objectId()) { // If we're updating a _User object, we need to clear out the cache for that user. Find all their // session tokens, and remove them from the cache. promise = new RestQuery(this.config, Auth.master(this.config), '_Session', { diff --git a/src/Routers/ClassesRouter.js b/src/Routers/ClassesRouter.js index 2e954619..6cfb8eb6 100644 --- a/src/Routers/ClassesRouter.js +++ b/src/Routers/ClassesRouter.js @@ -103,7 +103,8 @@ export class ClassesRouter extends PromiseRouter { } handleUpdate(req) { - return rest.update(req.config, req.auth, req.params.className, req.params.objectId, req.body, req.info.clientSDK); + const where = { objectId: req.params.objectId } + return rest.update(req.config, req.auth, req.params.className, where, req.body, req.info.clientSDK); } handleDelete(req) { diff --git a/src/rest.js b/src/rest.js index 69fe7bc0..2b4098fe 100644 --- a/src/rest.js +++ b/src/rest.js @@ -113,14 +113,14 @@ function create(config, auth, className, restObject, clientSDK) { // Returns a promise that contains the fields of the update that the // REST API is supposed to return. // Usually, this is just updatedAt. -function update(config, auth, className, objectId, restObject, clientSDK) { +function update(config, auth, className, restWhere, restObject, clientSDK) { enforceRoleSecurity('update', className, auth); return Promise.resolve().then(() => { const hasTriggers = checkTriggers(className, config, ['beforeSave', 'afterSave']); const hasLiveQuery = checkLiveQuery(className, config); if (hasTriggers || hasLiveQuery) { - return find(config, Auth.master(config), className, {objectId: objectId}); + return find(config, Auth.master(config), className, restWhere); } return Promise.resolve({}); }).then((response) => { @@ -129,7 +129,7 @@ function update(config, auth, className, objectId, restObject, clientSDK) { originalRestObject = response.results[0]; } - var write = new RestWrite(config, auth, className, {objectId: objectId}, restObject, originalRestObject, clientSDK); + var write = new RestWrite(config, auth, className, restWhere, restObject, originalRestObject, clientSDK); return write.execute(); }); }