diff --git a/spec/ParseUser.spec.js b/spec/ParseUser.spec.js index 1b2e04bc2d..35467752ce 100644 --- a/spec/ParseUser.spec.js +++ b/spec/ParseUser.spec.js @@ -905,7 +905,7 @@ describe('Parse.User testing', () => { } }; }; - + var getMockMyOauthProvider = function() { return { authData: { @@ -1329,7 +1329,7 @@ describe('Parse.User testing', () => { } }); }); - + it("link multiple providers", (done) => { var provider = getMockFacebookProvider(); var mockProvider = getMockMyOauthProvider(); @@ -1351,7 +1351,7 @@ describe('Parse.User testing', () => { ok(model._isLinked("facebook"), "User should be linked to facebook"); ok(model._isLinked("myoauth"), "User should be linked to myoauth"); done(); - }, + }, error: function(error) { console.error(error); fail('SHould not fail'); @@ -1437,9 +1437,9 @@ describe('Parse.User testing', () => { } }); }); - + it('should have authData in beforeSave and afterSave', (done) => { - + Parse.Cloud.beforeSave('_User', (request, response) => { let authData = request.object.get('authData'); expect(authData).not.toBeUndefined(); @@ -1451,7 +1451,7 @@ describe('Parse.User testing', () => { } response.success(); }); - + Parse.Cloud.afterSave('_User', (request, response) => { let authData = request.object.get('authData'); expect(authData).not.toBeUndefined(); @@ -1463,7 +1463,7 @@ describe('Parse.User testing', () => { } response.success(); }); - + var provider = getMockFacebookProvider(); Parse.User._registerAuthenticationProvider(provider); Parse.User._logInWith("facebook", { @@ -1970,9 +1970,9 @@ describe('Parse.User testing', () => { } }); }); - + // Sometimes the authData still has null on that keys - // https://github.com/ParsePlatform/parse-server/issues/935 + // https://github.com/ParsePlatform/parse-server/issues/935 it('should cleanup null authData keys', (done) => { let database = new Config(Parse.applicationId).database; database.create('_User', { @@ -2003,8 +2003,26 @@ describe('Parse.User testing', () => { done(); }).catch((err) => { fail('this should not fail'); - done(); + done(); }) }); -}); + it('should aftersave with full object', (done) => { + var hit = 0; + Parse.Cloud.afterSave('_User', (req, res) => { + hit++; + expect(req.object.get('username')).toEqual('User'); + res.success(); + }); + let user = new Parse.User() + user.setUsername('User'); + user.setPassword('pass'); + user.signUp().then(()=> { + user.set('hello', 'world'); + return user.save(); + }).then(() => { + Parse.Cloud._removeHook('Triggers', 'afterSave', '_User'); + done(); + }); + }) +}); diff --git a/src/RestWrite.js b/src/RestWrite.js index 0aaa5d5aa1..e241a98854 100644 --- a/src/RestWrite.js +++ b/src/RestWrite.js @@ -32,7 +32,7 @@ function RestWrite(config, auth, className, query, data, originalData) { throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'objectId ' + 'is an invalid field name.'); } - + // When the operation is complete, this.response may have several // fields. // response: the actual data to be returned @@ -136,7 +136,7 @@ RestWrite.prototype.runBeforeTrigger = function() { if (this.response) { return; } - + // Avoid doing any setup for triggers if there is no 'beforeSave' trigger for this class. if (!triggers.triggerExists(this.className, triggers.Types.beforeSave, this.config.applicationId)) { return Promise.resolve(); @@ -154,7 +154,7 @@ RestWrite.prototype.runBeforeTrigger = function() { // This is an update for existing object. originalObject = triggers.inflate(extraData, this.originalData); } - updatedObject.set(Parse._decode(undefined, this.data)); + updatedObject.set(this.sanitizedData()); return Promise.resolve().then(() => { return triggers.maybeRunTrigger(triggers.Types.beforeSave, this.auth, updatedObject, originalObject, this.config.applicationId); @@ -254,14 +254,14 @@ RestWrite.prototype.findUsersWithAuthData = function(authData) { }, []).filter((q) => { return typeof q !== undefined; }); - + let findPromise = Promise.resolve([]); if (query.length > 0) { findPromise = this.config.database.find( this.className, {'$or': query}, {}) } - + return findPromise; } @@ -276,9 +276,9 @@ RestWrite.prototype.handleAuthData = function(authData) { throw new Parse.Error(Parse.Error.ACCOUNT_ALREADY_LINKED, 'this auth is already used'); } - + this.storage['authProvider'] = Object.keys(authData).join(','); - + if (results.length == 0) { this.data.username = cryptoUtils.newToken(); } else if (!this.query) { @@ -404,7 +404,7 @@ RestWrite.prototype.transformUser = function() { // Handles any followup logic RestWrite.prototype.handleFollowup = function() { - + if (this.storage && this.storage['clearSessions']) { var sessionQuery = { user: { @@ -417,7 +417,7 @@ RestWrite.prototype.handleFollowup = function() { this.config.database.destroy('_Session', sessionQuery) .then(this.handleFollowup.bind(this)); } - + if (this.storage && this.storage['sendVerificationEmail']) { delete this.storage['sendVerificationEmail']; // Fire and forget! @@ -695,7 +695,7 @@ RestWrite.prototype.runDatabaseOperation = function() { throw new Parse.Error(Parse.Error.SESSION_MISSING, 'cannot modify user ' + this.query.objectId); } - + if (this.className === '_Product' && this.data.download) { this.data.downloadName = this.data.download.name; } @@ -722,7 +722,7 @@ RestWrite.prototype.runDatabaseOperation = function() { ACL[this.data.objectId] = { read: true, write: true }; ACL['*'] = { read: true, write: false }; this.data.ACL = ACL; - } + } // Run a create return this.config.database.create(this.className, this.data, this.runOptions) .then(() => { @@ -770,7 +770,7 @@ RestWrite.prototype.runAfterTrigger = function() { // Build the inflated object, different from beforeSave, originalData is not empty // since developers can change data in the beforeSave. let updatedObject = triggers.inflate(extraData, this.originalData); - updatedObject.set(Parse._decode(undefined, this.data)); + updatedObject.set(this.sanitizedData()); updatedObject._handleSaveResponse(this.response.response, this.response.status || 200); triggers.maybeRunTrigger(triggers.Types.afterSave, this.auth, updatedObject, originalObject, this.config.applicationId); @@ -789,5 +789,17 @@ RestWrite.prototype.objectId = function() { return this.data.objectId || this.query.objectId; }; +// Returns a copy of the data and delete bad keys (_auth_data, _hashed_password...) +RestWrite.prototype.sanitizedData = function() { + let data = Object.keys(this.data).reduce((data, key) => { + // Regexp comes from Parse.Object.prototype.validate + if (!(/^[A-Za-z][0-9A-Za-z_]*$/).test(key)) { + delete data[key]; + } + return data; + }, deepcopy(this.data)); + return Parse._decode(undefined, data); +} + export default RestWrite; module.exports = RestWrite;