diff --git a/spec/CloudCode.spec.js b/spec/CloudCode.spec.js index 7ee53ca27c..3854b9241e 100644 --- a/spec/CloudCode.spec.js +++ b/spec/CloudCode.spec.js @@ -181,12 +181,12 @@ describe('Cloud Code', () => { it('test afterSave ran on created object and returned a promise', function(done) { Parse.Cloud.afterSave('AfterSaveTest2', function(req) { const obj = req.object; - if(!obj.existed()) + if (!obj.existed()) { const promise = new Parse.Promise(); - setTimeout(function(){ + setTimeout(function() { obj.set('proof', obj.id); - obj.save().then(function(){ + obj.save().then(function() { promise.resolve(); }); }, 1000); @@ -196,7 +196,7 @@ describe('Cloud Code', () => { }); const obj = new Parse.Object('AfterSaveTest2'); - obj.save().then(function(){ + obj.save().then(function() { const query = new Parse.Query('AfterSaveTest2'); query.equalTo('proof', obj.id); query.find().then(function(results) { @@ -1778,14 +1778,22 @@ describe('afterFind hooks', () => { }) .then(() => done()); }); +}); +describe('Trigger Testing', () => { it('should validate triggers correctly', () => { + expect(() => { + Parse.Cloud.beforeFind('_Session', () => {}); + }).toThrow('beforeFind and afterFind triggers are not allowed for _Session class.'); + expect(() => { + Parse.Cloud.afterFind('_Session', () => {}); + }).toThrow('beforeFind and afterFind triggers are not allowed for _Session class.'); expect(() => { Parse.Cloud.beforeSave('_Session', () => {}); - }).toThrow('Triggers are not supported for _Session class.'); + }).not.toThrow('Triggers are not supported for _Session class.'); expect(() => { Parse.Cloud.afterSave('_Session', () => {}); - }).toThrow('Triggers are not supported for _Session class.'); + }).not.toThrow('Triggers are not supported for _Session class.'); expect(() => { Parse.Cloud.beforeSave('_PushStatus', () => {}); }).toThrow('Only afterSave is allowed on _PushStatus'); @@ -1793,4 +1801,51 @@ describe('afterFind hooks', () => { Parse.Cloud.afterSave('_PushStatus', () => {}); }).not.toThrow(); }); + + it('beforeSave _Session should not modify class', (done) => { + var hasCalled = false; + Parse.Cloud.beforeSave('_Session', (req, res) => { + req.object.set('foo', 'bing'); + expect(() => { + req.object.set('createdWith', 'test') + }).toThrow(); + expect(() => { + req.object.set('expiresAt', new Date()) + }).toThrow(); + expect(() => { + req.object.set('installationId', 'test') + }).toThrow(); + expect(() => { + req.object.set('restricted', 'test') + }).toThrow(); + expect(() => { + req.object.set('sessionToken', 'test') + }).toThrow(); + expect(() => { + req.object.set('user', null) + }).toThrow(); + + hasCalled = true; + res.success(); + }); + + var user = new Parse.User(); + user.set("username", "zxcv"); + user.set("email", "asdf@example.com"); + user.set("password", "asdf"); + user.signUp(null, { + success: function() { + Parse.Session.current().then((result) => { + expect(hasCalled).toBe(true); + expect(result).toBeDefined(); + expect(result.get('foo')).toBeUndefined(); + done(); + }); + }, + error: function() { + fail('Failed to save user'); + done(); + } + }); + }); }); diff --git a/src/RestWrite.js b/src/RestWrite.js index 424284d5ba..5a904c95bf 100644 --- a/src/RestWrite.js +++ b/src/RestWrite.js @@ -167,7 +167,7 @@ RestWrite.prototype.runBeforeTrigger = function() { return Promise.resolve().then(() => { return triggers.maybeRunTrigger(triggers.Types.beforeSave, this.auth, updatedObject, originalObject, this.config); }).then((response) => { - if (response && response.object) { + if (this.className !== '_Session' && response && response.object) { this.storage.fieldsChangedByTrigger = _.reduce(response.object, (result, value, key) => { if (!_.isEqual(this.data[key], value)) { result.push(key); @@ -1161,19 +1161,24 @@ RestWrite.prototype.objectId = function() { }; // Returns a copy of the data and delete bad keys (_auth_data, _hashed_password...) -RestWrite.prototype.sanitizedData = function() { - const data = Object.keys(this.data).reduce((data, key) => { +RestWrite.prototype.sanitizeData = function() { + 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); + + return this.data; } // Returns an updated copy of the object RestWrite.prototype.buildUpdatedObject = function (extraData) { + if (extraData.className == '_Session') { + return triggers.inflate(extraData, this.sanitizeData()); + } + const updatedObject = triggers.inflate(extraData, this.originalData); Object.keys(this.data).reduce(function (data, key) { if (key.indexOf(".") > 0) { @@ -1191,7 +1196,7 @@ RestWrite.prototype.buildUpdatedObject = function (extraData) { return data; }, deepcopy(this.data)); - updatedObject.set(this.sanitizedData()); + updatedObject.set(Parse._decode(undefined, this.sanitizeData())); return updatedObject; }; diff --git a/src/Routers/PublicAPIRouter.js b/src/Routers/PublicAPIRouter.js index a126423cb0..b43ceb16ff 100644 --- a/src/Routers/PublicAPIRouter.js +++ b/src/Routers/PublicAPIRouter.js @@ -15,7 +15,7 @@ export class PublicAPIRouter extends PromiseRouter { const appId = req.params.appId; const config = Config.get(appId); - if(!config){ + if(!config) { this.invalidRequest(); } diff --git a/src/triggers.js b/src/triggers.js index a11a71f7f4..fb6674effe 100644 --- a/src/triggers.js +++ b/src/triggers.js @@ -31,11 +31,10 @@ const baseStore = function() { }; function validateClassNameForTriggers(className, type) { - const restrictedClassNames = [ '_Session' ]; - if (restrictedClassNames.indexOf(className) != -1) { - throw `Triggers are not supported for ${className} class.`; + if ((type == Types.beforeFind || type == Types.afterFind) && className === '_Session') { + throw 'beforeFind and afterFind triggers are not allowed for _Session class.'; } - if (type == Types.beforeSave && className === '_PushStatus') { + else if (type == Types.beforeSave && className === '_PushStatus') { // _PushStatus uses undocumented nested key increment ops // allowing beforeSave would mess up the objects big time // TODO: Allow proper documented way of using nested increment ops @@ -203,7 +202,7 @@ export function getResponseObject(request, resolve, reject) { return { success: function(response) { if (request.triggerName === Types.afterFind) { - if(!response){ + if(!response) { response = request.objects; } response = response.map(object => {