From 9d781c481f9673a8976ecfbf5422e9d541b24477 Mon Sep 17 00:00:00 2001 From: Manuel Trezza Date: Mon, 28 Oct 2019 02:28:06 +0100 Subject: [PATCH] Throw error when setting authData to null (#6154) * added ignore authData field * add fix for Postgres * add test for mongoDB * add test login with provider despite invalid authData * removed fit * fixed ignoring authData in postgres * Fix postgres test * Throw error instead of ignore * improve tests * Add mongo test * allow authData when not user class * fix tests * more tests * add condition to synthesize authData field only in _User class it is forbidden to add a custom field name beginning with `_`, so if the object is not `_User` , the transform should throw * add warning log when ignoring invalid `authData` in `_User` * add test to throw when custom field begins with underscore --- spec/MongoTransform.spec.js | 40 +++++++++++++++ spec/ParseObject.spec.js | 10 ++++ spec/ParseUser.spec.js | 51 ++++++++++++++++++++ src/Adapters/Storage/Mongo/MongoTransform.js | 11 ++++- src/RestWrite.js | 15 +++++- 5 files changed, 125 insertions(+), 2 deletions(-) diff --git a/spec/MongoTransform.spec.js b/spec/MongoTransform.spec.js index d1449550..e0ce0cf8 100644 --- a/spec/MongoTransform.spec.js +++ b/spec/MongoTransform.spec.js @@ -479,6 +479,46 @@ describe('parseObjectToMongoObjectForCreate', () => { }).toThrow(); done(); }); + + it('ignores User authData field in DB so it can be synthesized in code', done => { + const input = { + _id: '123', + _auth_data_acme: { id: 'abc' }, + authData: null, + }; + const output = transform.mongoObjectToParseObject('_User', input, { + fields: {}, + }); + expect(output.authData.acme.id).toBe('abc'); + done(); + }); + + it('can set authData when not User class', done => { + const input = { + _id: '123', + authData: 'random', + }; + const output = transform.mongoObjectToParseObject('TestObject', input, { + fields: {}, + }); + expect(output.authData).toBe('random'); + done(); + }); +}); + +it('cannot have a custom field name beginning with underscore', done => { + const input = { + _id: '123', + _thisFieldNameIs: 'invalid', + }; + try { + transform.mongoObjectToParseObject('TestObject', input, { + fields: {}, + }); + } catch (e) { + expect(e).toBeDefined(); + } + done(); }); describe('transformUpdate', () => { diff --git a/spec/ParseObject.spec.js b/spec/ParseObject.spec.js index 3220f606..a2deb821 100644 --- a/spec/ParseObject.spec.js +++ b/spec/ParseObject.spec.js @@ -276,6 +276,16 @@ describe('Parse.Object testing', () => { done(); }); + it('can set authData when not user class', async () => { + const obj = new Parse.Object('TestObject'); + obj.set('authData', 'random'); + await obj.save(); + expect(obj.get('authData')).toBe('random'); + const query = new Parse.Query('TestObject'); + const object = await query.get(obj.id, { useMasterKey: true }); + expect(object.get('authData')).toBe('random'); + }); + it('invalid class name', function(done) { const item = new Parse.Object('Foo^bar'); item.save().then( diff --git a/spec/ParseUser.spec.js b/spec/ParseUser.spec.js index b2bdfd02..c04c33de 100644 --- a/spec/ParseUser.spec.js +++ b/spec/ParseUser.spec.js @@ -1246,6 +1246,32 @@ describe('Parse.User testing', () => { done(); }); + it('can not set authdata to null', async () => { + try { + const provider = getMockFacebookProvider(); + Parse.User._registerAuthenticationProvider(provider); + const user = await Parse.User._logInWith('facebook'); + user.set('authData', null); + await user.save(); + fail(); + } catch (e) { + expect(e.message).toBe('This authentication method is unsupported.'); + } + }); + + it('ignore setting authdata to undefined', async () => { + const provider = getMockFacebookProvider(); + Parse.User._registerAuthenticationProvider(provider); + const user = await Parse.User._logInWith('facebook'); + user.set('authData', undefined); + await user.save(); + let authData = user.get('authData'); + expect(authData).toBe(undefined); + await user.fetch(); + authData = user.get('authData'); + expect(authData.facebook.id).toBeDefined(); + }); + it('user authData should be available in cloudcode (#2342)', async done => { Parse.Cloud.define('checkLogin', req => { expect(req.user).not.toBeUndefined(); @@ -3924,4 +3950,29 @@ describe('Security Advisory GHSA-8w3j-g983-8jh5', function() { done(); } ); + it_only_db('mongo')('should ignore authData field', async () => { + // Add User to Database with authData + const database = Config.get(Parse.applicationId).database; + const collection = await database.adapter._adaptiveCollection('_User'); + await collection.insertOne({ + _id: '1234ABCDEF', + name: '', + email: '', + username: '', + _hashed_password: '', + _auth_data_custom: { + id: 'linkedID', + }, + sessionToken: '', + authData: null, // should ignore + }); + const provider = { + getAuthType: () => 'custom', + restoreAuthentication: () => true, + }; + Parse.User._registerAuthenticationProvider(provider); + const query = new Parse.Query(Parse.User); + const user = await query.get('1234ABCDEF', { useMasterKey: true }); + expect(user.get('authData')).toEqual({ custom: { id: 'linkedID' } }); + }); }); diff --git a/src/Adapters/Storage/Mongo/MongoTransform.js b/src/Adapters/Storage/Mongo/MongoTransform.js index 1c5b4e84..ff025cfd 100644 --- a/src/Adapters/Storage/Mongo/MongoTransform.js +++ b/src/Adapters/Storage/Mongo/MongoTransform.js @@ -1403,10 +1403,19 @@ const mongoObjectToParseObject = (className, mongoObject, schema) => { case 'times_used': restObject['timesUsed'] = mongoObject[key]; break; + case 'authData': + if (className === '_User') { + log.warn( + 'ignoring authData in _User as this key is reserved to be synthesized of `_auth_data_*` keys' + ); + } else { + restObject['authData'] = mongoObject[key]; + } + break; default: // Check other auth data keys var authDataMatch = key.match(/^_auth_data_([a-zA-Z0-9_]+)$/); - if (authDataMatch) { + if (authDataMatch && className === '_User') { var provider = authDataMatch[1]; restObject['authData'] = restObject['authData'] || {}; restObject['authData'][provider] = mongoObject[key]; diff --git a/src/RestWrite.js b/src/RestWrite.js index f1d8aafe..4041078e 100644 --- a/src/RestWrite.js +++ b/src/RestWrite.js @@ -417,8 +417,21 @@ RestWrite.prototype.validateAuthData = function() { } } - if (!this.data.authData || !Object.keys(this.data.authData).length) { + if ( + (this.data.authData && !Object.keys(this.data.authData).length) || + !Object.prototype.hasOwnProperty.call(this.data, 'authData') + ) { + // Handle saving authData to {} or if authData doesn't exist return; + } else if ( + Object.prototype.hasOwnProperty.call(this.data, 'authData') && + !this.data.authData + ) { + // Handle saving authData to null + throw new Parse.Error( + Parse.Error.UNSUPPORTED_SERVICE, + 'This authentication method is unsupported.' + ); } var authData = this.data.authData;