Skip to content

Commit ad9495b

Browse files
committed
Merge pull request #992 from ParsePlatform/flovilmart.afterSaveFullObjects
Sanitizes RestWrite.data before passing to inflated object
2 parents cc4e751 + cadd6fe commit ad9495b

File tree

2 files changed

+53
-23
lines changed

2 files changed

+53
-23
lines changed

spec/ParseUser.spec.js

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -905,7 +905,7 @@ describe('Parse.User testing', () => {
905905
}
906906
};
907907
};
908-
908+
909909
var getMockMyOauthProvider = function() {
910910
return {
911911
authData: {
@@ -1329,7 +1329,7 @@ describe('Parse.User testing', () => {
13291329
}
13301330
});
13311331
});
1332-
1332+
13331333
it("link multiple providers", (done) => {
13341334
var provider = getMockFacebookProvider();
13351335
var mockProvider = getMockMyOauthProvider();
@@ -1351,7 +1351,7 @@ describe('Parse.User testing', () => {
13511351
ok(model._isLinked("facebook"), "User should be linked to facebook");
13521352
ok(model._isLinked("myoauth"), "User should be linked to myoauth");
13531353
done();
1354-
},
1354+
},
13551355
error: function(error) {
13561356
console.error(error);
13571357
fail('SHould not fail');
@@ -1437,9 +1437,9 @@ describe('Parse.User testing', () => {
14371437
}
14381438
});
14391439
});
1440-
1440+
14411441
it('should have authData in beforeSave and afterSave', (done) => {
1442-
1442+
14431443
Parse.Cloud.beforeSave('_User', (request, response) => {
14441444
let authData = request.object.get('authData');
14451445
expect(authData).not.toBeUndefined();
@@ -1451,7 +1451,7 @@ describe('Parse.User testing', () => {
14511451
}
14521452
response.success();
14531453
});
1454-
1454+
14551455
Parse.Cloud.afterSave('_User', (request, response) => {
14561456
let authData = request.object.get('authData');
14571457
expect(authData).not.toBeUndefined();
@@ -1463,7 +1463,7 @@ describe('Parse.User testing', () => {
14631463
}
14641464
response.success();
14651465
});
1466-
1466+
14671467
var provider = getMockFacebookProvider();
14681468
Parse.User._registerAuthenticationProvider(provider);
14691469
Parse.User._logInWith("facebook", {
@@ -1970,9 +1970,9 @@ describe('Parse.User testing', () => {
19701970
}
19711971
});
19721972
});
1973-
1973+
19741974
// Sometimes the authData still has null on that keys
1975-
// https://github.com/ParsePlatform/parse-server/issues/935
1975+
// https://github.com/ParsePlatform/parse-server/issues/935
19761976
it('should cleanup null authData keys', (done) => {
19771977
let database = new Config(Parse.applicationId).database;
19781978
database.create('_User', {
@@ -2003,8 +2003,26 @@ describe('Parse.User testing', () => {
20032003
done();
20042004
}).catch((err) => {
20052005
fail('this should not fail');
2006-
done();
2006+
done();
20072007
})
20082008
});
2009-
});
20102009

2010+
it('should aftersave with full object', (done) => {
2011+
var hit = 0;
2012+
Parse.Cloud.afterSave('_User', (req, res) => {
2013+
hit++;
2014+
expect(req.object.get('username')).toEqual('User');
2015+
res.success();
2016+
});
2017+
let user = new Parse.User()
2018+
user.setUsername('User');
2019+
user.setPassword('pass');
2020+
user.signUp().then(()=> {
2021+
user.set('hello', 'world');
2022+
return user.save();
2023+
}).then(() => {
2024+
Parse.Cloud._removeHook('Triggers', 'afterSave', '_User');
2025+
done();
2026+
});
2027+
})
2028+
});

src/RestWrite.js

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ function RestWrite(config, auth, className, query, data, originalData) {
3232
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'objectId ' +
3333
'is an invalid field name.');
3434
}
35-
35+
3636
// When the operation is complete, this.response may have several
3737
// fields.
3838
// response: the actual data to be returned
@@ -136,7 +136,7 @@ RestWrite.prototype.runBeforeTrigger = function() {
136136
if (this.response) {
137137
return;
138138
}
139-
139+
140140
// Avoid doing any setup for triggers if there is no 'beforeSave' trigger for this class.
141141
if (!triggers.triggerExists(this.className, triggers.Types.beforeSave, this.config.applicationId)) {
142142
return Promise.resolve();
@@ -154,7 +154,7 @@ RestWrite.prototype.runBeforeTrigger = function() {
154154
// This is an update for existing object.
155155
originalObject = triggers.inflate(extraData, this.originalData);
156156
}
157-
updatedObject.set(Parse._decode(undefined, this.data));
157+
updatedObject.set(this.sanitizedData());
158158

159159
return Promise.resolve().then(() => {
160160
return triggers.maybeRunTrigger(triggers.Types.beforeSave, this.auth, updatedObject, originalObject, this.config.applicationId);
@@ -254,14 +254,14 @@ RestWrite.prototype.findUsersWithAuthData = function(authData) {
254254
}, []).filter((q) => {
255255
return typeof q !== undefined;
256256
});
257-
257+
258258
let findPromise = Promise.resolve([]);
259259
if (query.length > 0) {
260260
findPromise = this.config.database.find(
261261
this.className,
262262
{'$or': query}, {})
263263
}
264-
264+
265265
return findPromise;
266266
}
267267

@@ -276,9 +276,9 @@ RestWrite.prototype.handleAuthData = function(authData) {
276276
throw new Parse.Error(Parse.Error.ACCOUNT_ALREADY_LINKED,
277277
'this auth is already used');
278278
}
279-
279+
280280
this.storage['authProvider'] = Object.keys(authData).join(',');
281-
281+
282282
if (results.length == 0) {
283283
this.data.username = cryptoUtils.newToken();
284284
} else if (!this.query) {
@@ -404,7 +404,7 @@ RestWrite.prototype.transformUser = function() {
404404

405405
// Handles any followup logic
406406
RestWrite.prototype.handleFollowup = function() {
407-
407+
408408
if (this.storage && this.storage['clearSessions']) {
409409
var sessionQuery = {
410410
user: {
@@ -417,7 +417,7 @@ RestWrite.prototype.handleFollowup = function() {
417417
this.config.database.destroy('_Session', sessionQuery)
418418
.then(this.handleFollowup.bind(this));
419419
}
420-
420+
421421
if (this.storage && this.storage['sendVerificationEmail']) {
422422
delete this.storage['sendVerificationEmail'];
423423
// Fire and forget!
@@ -695,7 +695,7 @@ RestWrite.prototype.runDatabaseOperation = function() {
695695
throw new Parse.Error(Parse.Error.SESSION_MISSING,
696696
'cannot modify user ' + this.query.objectId);
697697
}
698-
698+
699699
if (this.className === '_Product' && this.data.download) {
700700
this.data.downloadName = this.data.download.name;
701701
}
@@ -722,7 +722,7 @@ RestWrite.prototype.runDatabaseOperation = function() {
722722
ACL[this.data.objectId] = { read: true, write: true };
723723
ACL['*'] = { read: true, write: false };
724724
this.data.ACL = ACL;
725-
}
725+
}
726726
// Run a create
727727
return this.config.database.create(this.className, this.data, this.runOptions)
728728
.then(() => {
@@ -770,7 +770,7 @@ RestWrite.prototype.runAfterTrigger = function() {
770770
// Build the inflated object, different from beforeSave, originalData is not empty
771771
// since developers can change data in the beforeSave.
772772
let updatedObject = triggers.inflate(extraData, this.originalData);
773-
updatedObject.set(Parse._decode(undefined, this.data));
773+
updatedObject.set(this.sanitizedData());
774774
updatedObject._handleSaveResponse(this.response.response, this.response.status || 200);
775775

776776
triggers.maybeRunTrigger(triggers.Types.afterSave, this.auth, updatedObject, originalObject, this.config.applicationId);
@@ -789,5 +789,17 @@ RestWrite.prototype.objectId = function() {
789789
return this.data.objectId || this.query.objectId;
790790
};
791791

792+
// Returns a copy of the data and delete bad keys (_auth_data, _hashed_password...)
793+
RestWrite.prototype.sanitizedData = function() {
794+
let data = Object.keys(this.data).reduce((data, key) => {
795+
// Regexp comes from Parse.Object.prototype.validate
796+
if (!(/^[A-Za-z][0-9A-Za-z_]*$/).test(key)) {
797+
delete data[key];
798+
}
799+
return data;
800+
}, deepcopy(this.data));
801+
return Parse._decode(undefined, data);
802+
}
803+
792804
export default RestWrite;
793805
module.exports = RestWrite;

0 commit comments

Comments
 (0)