From a60ad68dfd80d5819fddc656c3be44c38bb593ab Mon Sep 17 00:00:00 2001 From: dblythy Date: Mon, 1 Feb 2021 00:17:27 +1100 Subject: [PATCH 01/13] new: validate cloud validators --- spec/CloudCode.Validator.spec.js | 58 ++++++++++++++++++++++ src/cloud-code/Parse.Cloud.js | 82 ++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) diff --git a/spec/CloudCode.Validator.spec.js b/spec/CloudCode.Validator.spec.js index d15bc2479d..c514214b2e 100644 --- a/spec/CloudCode.Validator.spec.js +++ b/spec/CloudCode.Validator.spec.js @@ -1266,4 +1266,62 @@ describe('cloud validator', () => { done(); } }); + + it('Logs on invalid config', () => { + const logger = require('../lib/logger').logger; + spyOn(logger, 'error').and.callFake(() => {}); + Parse.Cloud.define('myFunction', () => {}, { + requiredUser: true, + requireUser: ['foo'], + requireMaster: ['foo'], + validateMasterKey: ['foo'], + skipWithMasterKey: ['foo'], + requireUserKeys: true, + fields: true, + }); + expect(logger.error).toHaveBeenCalledWith( + 'requiredUser is not a supported paramter for Parse.Cloud validators.' + ); + expect(logger.error).toHaveBeenCalledWith( + 'Invalid type for Parse.Cloud validator key requireUser. Expected boolean, actual array' + ); + expect(logger.error).toHaveBeenCalledWith( + 'Invalid type for Parse.Cloud validator key requireMaster. Expected boolean, actual array' + ); + expect(logger.error).toHaveBeenCalledWith( + 'Invalid type for Parse.Cloud validator key validateMasterKey. Expected boolean, actual array' + ); + expect(logger.error).toHaveBeenCalledWith( + 'Invalid type for Parse.Cloud validator key skipWithMasterKey. Expected boolean, actual array' + ); + expect(logger.error).toHaveBeenCalledWith( + 'Invalid type for Parse.Cloud validator key fields. Expected array|object, actual boolean' + ); + expect(logger.error).toHaveBeenCalledWith( + 'Invalid type for Parse.Cloud validator key requireUserKeys. Expected array|object, actual boolean' + ); + }); + + it('Logs on invalid config', () => { + const logger = require('../lib/logger').logger; + spyOn(logger, 'error').and.callFake(() => {}); + Parse.Cloud.define('myFunction', () => {}, { + fields: { + name: { + constant: ['foo'], + required: ['foo'], + error: ['foo'], + }, + }, + }); + expect(logger.error).toHaveBeenCalledWith( + 'Invalid type for Parse.Cloud validator key constant. Expected boolean, actual array' + ); + expect(logger.error).toHaveBeenCalledWith( + 'Invalid type for Parse.Cloud validator key required. Expected boolean, actual array' + ); + expect(logger.error).toHaveBeenCalledWith( + 'Invalid type for Parse.Cloud validator key error. Expected string, actual array' + ); + }); }); diff --git a/src/cloud-code/Parse.Cloud.js b/src/cloud-code/Parse.Cloud.js index 80eead1f31..08e391e08f 100644 --- a/src/cloud-code/Parse.Cloud.js +++ b/src/cloud-code/Parse.Cloud.js @@ -13,6 +13,74 @@ function getClassName(parseClass) { return parseClass; } +function validateValidator(validator) { + if (!validator || typeof validator === 'function') { + return; + } + const fieldOptions = { + type: ['Any'], + constant: [Boolean], + default: ['Any'], + options: [Array, 'function', 'Any'], + required: [Boolean], + error: [String], + }; + const allowedKeys = { + requireUser: [Boolean], + requireMaster: [Boolean], + validateMasterKey: [Boolean], + skipWithMasterKey: [Boolean], + requireUserKeys: [Array, Object], + fields: [Array, Object], + }; + const config = Config.get(Parse.applicationId); + const logger = config.loggerController; + const getType = fn => { + if (Array.isArray(fn)) { + return 'array'; + } + if (fn === 'Any') { + return fn; + } + const type = typeof fn; + if (typeof fn === 'function') { + const match = fn && fn.toString().match(/^\s*function (\w+)/); + return (match ? match[1] : '').toLowerCase(); + } + return type; + }; + const checkKey = (key, data, validatorParam) => { + const parameter = data[key]; + if (!parameter) { + logger.error(`${key} is not a supported paramter for Parse.Cloud validators.`); + return; + } + const types = parameter.map(type => getType(type)); + const type = getType(validatorParam); + if (!types.includes(type) && !types.includes('Any')) { + logger.error( + `Invalid type for Parse.Cloud validator key ${key}. Expected ${types.join( + '|' + )}, actual ${type}` + ); + } + }; + for (const key in validator) { + checkKey(key, allowedKeys, validator[key]); + if (key === 'fields' || key === 'requireUserKeys') { + const values = validator[key]; + if (Array.isArray(values)) { + continue; + } + for (const value in values) { + const data = values[value]; + for (const subKey in data) { + checkKey(subKey, fieldOptions, data[subKey]); + } + } + } + } +} /** @namespace * @name Parse * @description The Parse SDK. @@ -50,6 +118,7 @@ var ParseCloud = {}; * @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.FunctionRequest}, or a {@link Parse.Cloud.ValidatorObject}. */ ParseCloud.define = function (functionName, handler, validationHandler) { + validateValidator(validationHandler); triggers.addFunction(functionName, handler, validationHandler, Parse.applicationId); }; @@ -96,6 +165,7 @@ ParseCloud.job = function (functionName, handler) { */ ParseCloud.beforeSave = function (parseClass, handler, validationHandler) { var className = getClassName(parseClass); + validateValidator(validationHandler); triggers.addTrigger( triggers.Types.beforeSave, className, @@ -131,6 +201,7 @@ ParseCloud.beforeSave = function (parseClass, handler, validationHandler) { */ ParseCloud.beforeDelete = function (parseClass, handler, validationHandler) { var className = getClassName(parseClass); + validateValidator(validationHandler); triggers.addTrigger( triggers.Types.beforeDelete, className, @@ -260,6 +331,7 @@ ParseCloud.afterLogout = function (handler) { */ ParseCloud.afterSave = function (parseClass, handler, validationHandler) { var className = getClassName(parseClass); + validateValidator(validationHandler); triggers.addTrigger( triggers.Types.afterSave, className, @@ -295,6 +367,7 @@ ParseCloud.afterSave = function (parseClass, handler, validationHandler) { */ ParseCloud.afterDelete = function (parseClass, handler, validationHandler) { var className = getClassName(parseClass); + validateValidator(validationHandler); triggers.addTrigger( triggers.Types.afterDelete, className, @@ -330,6 +403,7 @@ ParseCloud.afterDelete = function (parseClass, handler, validationHandler) { */ ParseCloud.beforeFind = function (parseClass, handler, validationHandler) { var className = getClassName(parseClass); + validateValidator(validationHandler); triggers.addTrigger( triggers.Types.beforeFind, className, @@ -365,6 +439,7 @@ ParseCloud.beforeFind = function (parseClass, handler, validationHandler) { */ ParseCloud.afterFind = function (parseClass, handler, validationHandler) { const className = getClassName(parseClass); + validateValidator(validationHandler); triggers.addTrigger( triggers.Types.afterFind, className, @@ -397,6 +472,7 @@ ParseCloud.afterFind = function (parseClass, handler, validationHandler) { * @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.FileTriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}. */ ParseCloud.beforeSaveFile = function (handler, validationHandler) { + validateValidator(validationHandler); triggers.addFileTrigger( triggers.Types.beforeSaveFile, handler, @@ -428,6 +504,7 @@ ParseCloud.beforeSaveFile = function (handler, validationHandler) { * @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.FileTriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}. */ ParseCloud.afterSaveFile = function (handler, validationHandler) { + validateValidator(validationHandler); triggers.addFileTrigger( triggers.Types.afterSaveFile, handler, @@ -459,6 +536,7 @@ ParseCloud.afterSaveFile = function (handler, validationHandler) { * @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.FileTriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}. */ ParseCloud.beforeDeleteFile = function (handler, validationHandler) { + validateValidator(validationHandler); triggers.addFileTrigger( triggers.Types.beforeDeleteFile, handler, @@ -490,6 +568,7 @@ ParseCloud.beforeDeleteFile = function (handler, validationHandler) { * @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.FileTriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}. */ ParseCloud.afterDeleteFile = function (handler, validationHandler) { + validateValidator(validationHandler); triggers.addFileTrigger( triggers.Types.afterDeleteFile, handler, @@ -521,6 +600,7 @@ ParseCloud.afterDeleteFile = function (handler, validationHandler) { * @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.ConnectTriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}. */ ParseCloud.beforeConnect = function (handler, validationHandler) { + validateValidator(validationHandler); triggers.addConnectTrigger( triggers.Types.beforeConnect, handler, @@ -585,6 +665,7 @@ ParseCloud.sendEmail = function (data) { * @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.TriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}. */ ParseCloud.beforeSubscribe = function (parseClass, handler, validationHandler) { + validateValidator(validationHandler); var className = getClassName(parseClass); triggers.addTrigger( triggers.Types.beforeSubscribe, @@ -624,6 +705,7 @@ ParseCloud.onLiveQueryEvent = function (handler) { */ ParseCloud.afterLiveQueryEvent = function (parseClass, handler, validationHandler) { const className = getClassName(parseClass); + validateValidator(validationHandler); triggers.addTrigger( triggers.Types.afterEvent, className, From 37b78f2e19c57bc6d657b941cb06b1515dc2d79f Mon Sep 17 00:00:00 2001 From: dblythy Date: Mon, 1 Feb 2021 00:19:30 +1100 Subject: [PATCH 02/13] add otherKey --- spec/CloudCode.Validator.spec.js | 6 +++++- src/cloud-code/Parse.Cloud.js | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/spec/CloudCode.Validator.spec.js b/spec/CloudCode.Validator.spec.js index c514214b2e..40688e7bae 100644 --- a/spec/CloudCode.Validator.spec.js +++ b/spec/CloudCode.Validator.spec.js @@ -1280,7 +1280,7 @@ describe('cloud validator', () => { fields: true, }); expect(logger.error).toHaveBeenCalledWith( - 'requiredUser is not a supported paramter for Parse.Cloud validators.' + 'requiredUser is not a supported parameter for Parse.Cloud validators.' ); expect(logger.error).toHaveBeenCalledWith( 'Invalid type for Parse.Cloud validator key requireUser. Expected boolean, actual array' @@ -1311,9 +1311,13 @@ describe('cloud validator', () => { constant: ['foo'], required: ['foo'], error: ['foo'], + otherKey: true, }, }, }); + expect(logger.error).toHaveBeenCalledWith( + 'otherKey is not a supported parameter for Parse.Cloud validators.' + ); expect(logger.error).toHaveBeenCalledWith( 'Invalid type for Parse.Cloud validator key constant. Expected boolean, actual array' ); diff --git a/src/cloud-code/Parse.Cloud.js b/src/cloud-code/Parse.Cloud.js index 08e391e08f..4ff63f820d 100644 --- a/src/cloud-code/Parse.Cloud.js +++ b/src/cloud-code/Parse.Cloud.js @@ -52,7 +52,7 @@ function validateValidator(validator) { const checkKey = (key, data, validatorParam) => { const parameter = data[key]; if (!parameter) { - logger.error(`${key} is not a supported paramter for Parse.Cloud validators.`); + logger.error(`${key} is not a supported parameter for Parse.Cloud validators.`); return; } const types = parameter.map(type => getType(type)); From f716b189615a37ff47711eff944f15471082c86e Mon Sep 17 00:00:00 2001 From: dblythy Date: Mon, 1 Feb 2021 00:40:08 +1100 Subject: [PATCH 03/13] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a12739f78..8276bc6342 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ __BREAKING CHANGES:__ - NEW: Added file upload restriction. File upload is now only allowed for authenticated users by default for improved security. To allow file upload also for Anonymous Users or Public, set the `fileUpload` parameter in the [Parse Server Options](https://parseplatform.org/parse-server/api/master/ParseServerOptions.html). [#7071](https://github.com/parse-community/parse-server/pull/7071). Thanks to [dblythy](https://github.com/dblythy). ___ +- IMPROVE: Cloud Validators will now log on invalid configuration [#7154](https://github.com/parse-community/parse-server/pull/7154). Thanks to [dblythy](https://github.com/dblythy) - IMPROVE: Optimize queries on classes with pointer permissions. [#7061](https://github.com/parse-community/parse-server/pull/7061). Thanks to [Pedro Diaz](https://github.com/pdiaz) - FIX: request.context for afterFind triggers. [#7078](https://github.com/parse-community/parse-server/pull/7078). Thanks to [dblythy](https://github.com/dblythy) - NEW: Added convenience method Parse.Cloud.sendEmail(...) to send email via email adapter in Cloud Code. [#7089](https://github.com/parse-community/parse-server/pull/7089). Thanks to [dblythy](https://github.com/dblythy) From 12bb8b1589503bae307ec3adac7b605431d4763a Mon Sep 17 00:00:00 2001 From: dblythy Date: Tue, 9 Feb 2021 20:09:22 +1100 Subject: [PATCH 04/13] Update CloudCode.Validator.spec.js --- spec/CloudCode.Validator.spec.js | 67 ++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 24 deletions(-) diff --git a/spec/CloudCode.Validator.spec.js b/spec/CloudCode.Validator.spec.js index 40688e7bae..812e58ff47 100644 --- a/spec/CloudCode.Validator.spec.js +++ b/spec/CloudCode.Validator.spec.js @@ -92,9 +92,7 @@ describe('cloud validator', () => { }, async () => { await new Promise(resolve => { - setTimeout(() => { - resolve(); - }, 1000); + setTimeout(resolve, 1000); }); throw 'async error'; } @@ -132,7 +130,7 @@ describe('cloud validator', () => { await Parse.Cloud.run('myFunction'); }); - it('require user on cloud functions', done => { + it('require user on cloud functions', async done => { Parse.Cloud.define( 'hello1', () => { @@ -142,16 +140,14 @@ describe('cloud validator', () => { requireUser: true, } ); - - Parse.Cloud.run('hello1', {}) - .then(() => { - fail('function should have failed.'); - }) - .catch(error => { - expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual('Validation failed. Please login to continue.'); - done(); - }); + try { + await Parse.Cloud.run('hello1', {}); + fail('function should have failed.'); + } catch (error) { + expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); + expect(error.message).toEqual('Validation failed. Please login to continue.'); + done(); + } }); it('require master on cloud functions', done => { @@ -587,16 +583,10 @@ describe('cloud validator', () => { expect(obj.get('foo')).toBe('bar'); const query = new Parse.Query('beforeFind'); - try { - const first = await query.first({ useMasterKey: true }); - expect(first).toBeDefined(); - expect(first.id).toBe(obj.id); - done(); - } catch (e) { - console.log(e); - console.log(e.code); - throw e; - } + const first = await query.first({ useMasterKey: true }); + expect(first).toBeDefined(); + expect(first.id).toBe(obj.id); + done(); }); it('basic beforeDelete skipWithMasterKey', async function (done) { @@ -1267,6 +1257,35 @@ describe('cloud validator', () => { } }); + it('does not log on valid config', () => { + const logger = require('../lib/logger').logger; + spyOn(logger, 'error').and.callFake(() => {}); + Parse.Cloud.define('myFunction', () => {}, { + requireUser: true, + requireMaster: true, + validateMasterKey: false, + skipWithMasterKey: true, + requireUserKeys: { + Acc: { + constant: true, + options: ['A', 'B'], + required: true, + default: 'f', + type: String, + }, + }, + fields: { + Acc: { + constant: true, + options: ['A', 'B'], + required: true, + default: 'f', + type: String, + }, + }, + }); + expect(logger.error).not.toHaveBeenCalled(); + }); it('Logs on invalid config', () => { const logger = require('../lib/logger').logger; spyOn(logger, 'error').and.callFake(() => {}); From f61db7bb7e5a913b85732261794b8451e8372a37 Mon Sep 17 00:00:00 2001 From: dblythy Date: Tue, 9 Feb 2021 20:11:15 +1100 Subject: [PATCH 05/13] Update CloudCode.Validator.spec.js --- spec/CloudCode.Validator.spec.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/CloudCode.Validator.spec.js b/spec/CloudCode.Validator.spec.js index 812e58ff47..e0e1ab1ce9 100644 --- a/spec/CloudCode.Validator.spec.js +++ b/spec/CloudCode.Validator.spec.js @@ -1271,6 +1271,7 @@ describe('cloud validator', () => { options: ['A', 'B'], required: true, default: 'f', + error: 'a', type: String, }, }, @@ -1280,6 +1281,7 @@ describe('cloud validator', () => { options: ['A', 'B'], required: true, default: 'f', + error: 'a', type: String, }, }, From 7b707da0fb1f74eab8fb1a6c973ac430dd2228f1 Mon Sep 17 00:00:00 2001 From: dblythy Date: Mon, 1 Feb 2021 00:17:27 +1100 Subject: [PATCH 06/13] new: validate cloud validators --- spec/CloudCode.Validator.spec.js | 58 ++++++++++++++++++++++ src/cloud-code/Parse.Cloud.js | 82 ++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) diff --git a/spec/CloudCode.Validator.spec.js b/spec/CloudCode.Validator.spec.js index 36a7fc96b1..319486d297 100644 --- a/spec/CloudCode.Validator.spec.js +++ b/spec/CloudCode.Validator.spec.js @@ -1428,4 +1428,62 @@ describe('cloud validator', () => { done(); } }); + + it('Logs on invalid config', () => { + const logger = require('../lib/logger').logger; + spyOn(logger, 'error').and.callFake(() => {}); + Parse.Cloud.define('myFunction', () => {}, { + requiredUser: true, + requireUser: ['foo'], + requireMaster: ['foo'], + validateMasterKey: ['foo'], + skipWithMasterKey: ['foo'], + requireUserKeys: true, + fields: true, + }); + expect(logger.error).toHaveBeenCalledWith( + 'requiredUser is not a supported paramter for Parse.Cloud validators.' + ); + expect(logger.error).toHaveBeenCalledWith( + 'Invalid type for Parse.Cloud validator key requireUser. Expected boolean, actual array' + ); + expect(logger.error).toHaveBeenCalledWith( + 'Invalid type for Parse.Cloud validator key requireMaster. Expected boolean, actual array' + ); + expect(logger.error).toHaveBeenCalledWith( + 'Invalid type for Parse.Cloud validator key validateMasterKey. Expected boolean, actual array' + ); + expect(logger.error).toHaveBeenCalledWith( + 'Invalid type for Parse.Cloud validator key skipWithMasterKey. Expected boolean, actual array' + ); + expect(logger.error).toHaveBeenCalledWith( + 'Invalid type for Parse.Cloud validator key fields. Expected array|object, actual boolean' + ); + expect(logger.error).toHaveBeenCalledWith( + 'Invalid type for Parse.Cloud validator key requireUserKeys. Expected array|object, actual boolean' + ); + }); + + it('Logs on invalid config', () => { + const logger = require('../lib/logger').logger; + spyOn(logger, 'error').and.callFake(() => {}); + Parse.Cloud.define('myFunction', () => {}, { + fields: { + name: { + constant: ['foo'], + required: ['foo'], + error: ['foo'], + }, + }, + }); + expect(logger.error).toHaveBeenCalledWith( + 'Invalid type for Parse.Cloud validator key constant. Expected boolean, actual array' + ); + expect(logger.error).toHaveBeenCalledWith( + 'Invalid type for Parse.Cloud validator key required. Expected boolean, actual array' + ); + expect(logger.error).toHaveBeenCalledWith( + 'Invalid type for Parse.Cloud validator key error. Expected string, actual array' + ); + }); }); diff --git a/src/cloud-code/Parse.Cloud.js b/src/cloud-code/Parse.Cloud.js index 5ed6aa728f..e666d15342 100644 --- a/src/cloud-code/Parse.Cloud.js +++ b/src/cloud-code/Parse.Cloud.js @@ -13,6 +13,74 @@ function getClassName(parseClass) { return parseClass; } +function validateValidator(validator) { + if (!validator || typeof validator === 'function') { + return; + } + const fieldOptions = { + type: ['Any'], + constant: [Boolean], + default: ['Any'], + options: [Array, 'function', 'Any'], + required: [Boolean], + error: [String], + }; + const allowedKeys = { + requireUser: [Boolean], + requireMaster: [Boolean], + validateMasterKey: [Boolean], + skipWithMasterKey: [Boolean], + requireUserKeys: [Array, Object], + fields: [Array, Object], + }; + const config = Config.get(Parse.applicationId); + const logger = config.loggerController; + const getType = fn => { + if (Array.isArray(fn)) { + return 'array'; + } + if (fn === 'Any') { + return fn; + } + const type = typeof fn; + if (typeof fn === 'function') { + const match = fn && fn.toString().match(/^\s*function (\w+)/); + return (match ? match[1] : '').toLowerCase(); + } + return type; + }; + const checkKey = (key, data, validatorParam) => { + const parameter = data[key]; + if (!parameter) { + logger.error(`${key} is not a supported paramter for Parse.Cloud validators.`); + return; + } + const types = parameter.map(type => getType(type)); + const type = getType(validatorParam); + if (!types.includes(type) && !types.includes('Any')) { + logger.error( + `Invalid type for Parse.Cloud validator key ${key}. Expected ${types.join( + '|' + )}, actual ${type}` + ); + } + }; + for (const key in validator) { + checkKey(key, allowedKeys, validator[key]); + if (key === 'fields' || key === 'requireUserKeys') { + const values = validator[key]; + if (Array.isArray(values)) { + continue; + } + for (const value in values) { + const data = values[value]; + for (const subKey in data) { + checkKey(subKey, fieldOptions, data[subKey]); + } + } + } + } +} /** @namespace * @name Parse * @description The Parse SDK. @@ -50,6 +118,7 @@ var ParseCloud = {}; * @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.FunctionRequest}, or a {@link Parse.Cloud.ValidatorObject}. */ ParseCloud.define = function (functionName, handler, validationHandler) { + validateValidator(validationHandler); triggers.addFunction(functionName, handler, validationHandler, Parse.applicationId); }; @@ -96,6 +165,7 @@ ParseCloud.job = function (functionName, handler) { */ ParseCloud.beforeSave = function (parseClass, handler, validationHandler) { var className = getClassName(parseClass); + validateValidator(validationHandler); triggers.addTrigger( triggers.Types.beforeSave, className, @@ -131,6 +201,7 @@ ParseCloud.beforeSave = function (parseClass, handler, validationHandler) { */ ParseCloud.beforeDelete = function (parseClass, handler, validationHandler) { var className = getClassName(parseClass); + validateValidator(validationHandler); triggers.addTrigger( triggers.Types.beforeDelete, className, @@ -260,6 +331,7 @@ ParseCloud.afterLogout = function (handler) { */ ParseCloud.afterSave = function (parseClass, handler, validationHandler) { var className = getClassName(parseClass); + validateValidator(validationHandler); triggers.addTrigger( triggers.Types.afterSave, className, @@ -295,6 +367,7 @@ ParseCloud.afterSave = function (parseClass, handler, validationHandler) { */ ParseCloud.afterDelete = function (parseClass, handler, validationHandler) { var className = getClassName(parseClass); + validateValidator(validationHandler); triggers.addTrigger( triggers.Types.afterDelete, className, @@ -330,6 +403,7 @@ ParseCloud.afterDelete = function (parseClass, handler, validationHandler) { */ ParseCloud.beforeFind = function (parseClass, handler, validationHandler) { var className = getClassName(parseClass); + validateValidator(validationHandler); triggers.addTrigger( triggers.Types.beforeFind, className, @@ -365,6 +439,7 @@ ParseCloud.beforeFind = function (parseClass, handler, validationHandler) { */ ParseCloud.afterFind = function (parseClass, handler, validationHandler) { const className = getClassName(parseClass); + validateValidator(validationHandler); triggers.addTrigger( triggers.Types.afterFind, className, @@ -397,6 +472,7 @@ ParseCloud.afterFind = function (parseClass, handler, validationHandler) { * @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.FileTriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}. */ ParseCloud.beforeSaveFile = function (handler, validationHandler) { + validateValidator(validationHandler); triggers.addFileTrigger( triggers.Types.beforeSaveFile, handler, @@ -428,6 +504,7 @@ ParseCloud.beforeSaveFile = function (handler, validationHandler) { * @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.FileTriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}. */ ParseCloud.afterSaveFile = function (handler, validationHandler) { + validateValidator(validationHandler); triggers.addFileTrigger( triggers.Types.afterSaveFile, handler, @@ -459,6 +536,7 @@ ParseCloud.afterSaveFile = function (handler, validationHandler) { * @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.FileTriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}. */ ParseCloud.beforeDeleteFile = function (handler, validationHandler) { + validateValidator(validationHandler); triggers.addFileTrigger( triggers.Types.beforeDeleteFile, handler, @@ -490,6 +568,7 @@ ParseCloud.beforeDeleteFile = function (handler, validationHandler) { * @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.FileTriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}. */ ParseCloud.afterDeleteFile = function (handler, validationHandler) { + validateValidator(validationHandler); triggers.addFileTrigger( triggers.Types.afterDeleteFile, handler, @@ -521,6 +600,7 @@ ParseCloud.afterDeleteFile = function (handler, validationHandler) { * @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.ConnectTriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}. */ ParseCloud.beforeConnect = function (handler, validationHandler) { + validateValidator(validationHandler); triggers.addConnectTrigger( triggers.Types.beforeConnect, handler, @@ -585,6 +665,7 @@ ParseCloud.sendEmail = function (data) { * @param {(Object|Function)} validator An optional function to help validating cloud code. This function can be an async function and should take one parameter a {@link Parse.Cloud.TriggerRequest}, or a {@link Parse.Cloud.ValidatorObject}. */ ParseCloud.beforeSubscribe = function (parseClass, handler, validationHandler) { + validateValidator(validationHandler); var className = getClassName(parseClass); triggers.addTrigger( triggers.Types.beforeSubscribe, @@ -624,6 +705,7 @@ ParseCloud.onLiveQueryEvent = function (handler) { */ ParseCloud.afterLiveQueryEvent = function (parseClass, handler, validationHandler) { const className = getClassName(parseClass); + validateValidator(validationHandler); triggers.addTrigger( triggers.Types.afterEvent, className, From 13576e7da72ce1e2fe2d20deff5fe077e0b2aae2 Mon Sep 17 00:00:00 2001 From: dblythy Date: Mon, 1 Feb 2021 00:19:30 +1100 Subject: [PATCH 07/13] add otherKey --- spec/CloudCode.Validator.spec.js | 6 +++++- src/cloud-code/Parse.Cloud.js | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/spec/CloudCode.Validator.spec.js b/spec/CloudCode.Validator.spec.js index 319486d297..c5360b61f9 100644 --- a/spec/CloudCode.Validator.spec.js +++ b/spec/CloudCode.Validator.spec.js @@ -1442,7 +1442,7 @@ describe('cloud validator', () => { fields: true, }); expect(logger.error).toHaveBeenCalledWith( - 'requiredUser is not a supported paramter for Parse.Cloud validators.' + 'requiredUser is not a supported parameter for Parse.Cloud validators.' ); expect(logger.error).toHaveBeenCalledWith( 'Invalid type for Parse.Cloud validator key requireUser. Expected boolean, actual array' @@ -1473,9 +1473,13 @@ describe('cloud validator', () => { constant: ['foo'], required: ['foo'], error: ['foo'], + otherKey: true, }, }, }); + expect(logger.error).toHaveBeenCalledWith( + 'otherKey is not a supported parameter for Parse.Cloud validators.' + ); expect(logger.error).toHaveBeenCalledWith( 'Invalid type for Parse.Cloud validator key constant. Expected boolean, actual array' ); diff --git a/src/cloud-code/Parse.Cloud.js b/src/cloud-code/Parse.Cloud.js index e666d15342..8ac15e52c8 100644 --- a/src/cloud-code/Parse.Cloud.js +++ b/src/cloud-code/Parse.Cloud.js @@ -52,7 +52,7 @@ function validateValidator(validator) { const checkKey = (key, data, validatorParam) => { const parameter = data[key]; if (!parameter) { - logger.error(`${key} is not a supported paramter for Parse.Cloud validators.`); + logger.error(`${key} is not a supported parameter for Parse.Cloud validators.`); return; } const types = parameter.map(type => getType(type)); From 7513c0756824af6f0373532dee10bfa4e47d78a4 Mon Sep 17 00:00:00 2001 From: dblythy Date: Mon, 1 Feb 2021 00:40:08 +1100 Subject: [PATCH 08/13] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ffcb4e4e37..87bd518248 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ ___ - IMPROVE: Added new account lockout policy option `accountLockout.unlockOnPasswordReset` to automatically unlock account on password reset. [#7146](https://github.com/parse-community/parse-server/pull/7146). Thanks to [Manuel Trezza](https://github.com/mtrezza). - IMPROVE: Parse Server is from now on continuously tested against all recent MongoDB versions that have not reached their end-of-life support date. Added MongoDB compatibility table to Parse Server docs. [7161](https://github.com/parse-community/parse-server/pull/7161). Thanks to [Manuel Trezza](https://github.com/mtrezza). - IMPROVE: Parse Server is from now on continuously tested against all recent Node.js versions that have not reached their end-of-life support date. [7161](https://github.com/parse-community/parse-server/pull/7177). Thanks to [Manuel Trezza](https://github.com/mtrezza). +- IMPROVE: Cloud Validators will now log on invalid configuration [#7154](https://github.com/parse-community/parse-server/pull/7154). Thanks to [dblythy](https://github.com/dblythy) - IMPROVE: Optimize queries on classes with pointer permissions. [#7061](https://github.com/parse-community/parse-server/pull/7061). Thanks to [Pedro Diaz](https://github.com/pdiaz) - IMPROVE: Parse Server will from now on be continuously tested against all relevant Postgres versions (minor versions). Added Postgres compatibility table to Parse Server docs. [#7176](https://github.com/parse-community/parse-server/pull/7176). Thanks to [Corey Baker](https://github.com/cbaker6). - FIX: Fix error when a not yet inserted job is updated [#7196](https://github.com/parse-community/parse-server/pull/7196). Thanks to [Antonio Davi Macedo Coelho de Castro](https://github.com/davimacedo). From 2d310454fb474efdf092b97e423269c6858b9fd6 Mon Sep 17 00:00:00 2001 From: dblythy Date: Tue, 9 Feb 2021 20:09:22 +1100 Subject: [PATCH 09/13] Update CloudCode.Validator.spec.js --- spec/CloudCode.Validator.spec.js | 67 ++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 24 deletions(-) diff --git a/spec/CloudCode.Validator.spec.js b/spec/CloudCode.Validator.spec.js index c5360b61f9..9549555b08 100644 --- a/spec/CloudCode.Validator.spec.js +++ b/spec/CloudCode.Validator.spec.js @@ -92,9 +92,7 @@ describe('cloud validator', () => { }, async () => { await new Promise(resolve => { - setTimeout(() => { - resolve(); - }, 1000); + setTimeout(resolve, 1000); }); throw 'async error'; } @@ -132,7 +130,7 @@ describe('cloud validator', () => { await Parse.Cloud.run('myFunction'); }); - it('require user on cloud functions', done => { + it('require user on cloud functions', async done => { Parse.Cloud.define( 'hello1', () => { @@ -142,16 +140,14 @@ describe('cloud validator', () => { requireUser: true, } ); - - Parse.Cloud.run('hello1', {}) - .then(() => { - fail('function should have failed.'); - }) - .catch(error => { - expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); - expect(error.message).toEqual('Validation failed. Please login to continue.'); - done(); - }); + try { + await Parse.Cloud.run('hello1', {}); + fail('function should have failed.'); + } catch (error) { + expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR); + expect(error.message).toEqual('Validation failed. Please login to continue.'); + done(); + } }); it('require master on cloud functions', done => { @@ -605,16 +601,10 @@ describe('cloud validator', () => { expect(obj.get('foo')).toBe('bar'); const query = new Parse.Query('beforeFind'); - try { - const first = await query.first({ useMasterKey: true }); - expect(first).toBeDefined(); - expect(first.id).toBe(obj.id); - done(); - } catch (e) { - console.log(e); - console.log(e.code); - throw e; - } + const first = await query.first({ useMasterKey: true }); + expect(first).toBeDefined(); + expect(first.id).toBe(obj.id); + done(); }); it('basic beforeDelete skipWithMasterKey', async function (done) { @@ -1429,6 +1419,35 @@ describe('cloud validator', () => { } }); + it('does not log on valid config', () => { + const logger = require('../lib/logger').logger; + spyOn(logger, 'error').and.callFake(() => {}); + Parse.Cloud.define('myFunction', () => {}, { + requireUser: true, + requireMaster: true, + validateMasterKey: false, + skipWithMasterKey: true, + requireUserKeys: { + Acc: { + constant: true, + options: ['A', 'B'], + required: true, + default: 'f', + type: String, + }, + }, + fields: { + Acc: { + constant: true, + options: ['A', 'B'], + required: true, + default: 'f', + type: String, + }, + }, + }); + expect(logger.error).not.toHaveBeenCalled(); + }); it('Logs on invalid config', () => { const logger = require('../lib/logger').logger; spyOn(logger, 'error').and.callFake(() => {}); From bca92540b393e9191fbcee3103a23bc8cf48923f Mon Sep 17 00:00:00 2001 From: dblythy Date: Tue, 9 Feb 2021 20:11:15 +1100 Subject: [PATCH 10/13] Update CloudCode.Validator.spec.js --- spec/CloudCode.Validator.spec.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/CloudCode.Validator.spec.js b/spec/CloudCode.Validator.spec.js index 9549555b08..3641719bb1 100644 --- a/spec/CloudCode.Validator.spec.js +++ b/spec/CloudCode.Validator.spec.js @@ -1433,6 +1433,7 @@ describe('cloud validator', () => { options: ['A', 'B'], required: true, default: 'f', + error: 'a', type: String, }, }, @@ -1442,6 +1443,7 @@ describe('cloud validator', () => { options: ['A', 'B'], required: true, default: 'f', + error: 'a', type: String, }, }, From 8cca81150fb88a351dd5925f8b58988cd386dbb0 Mon Sep 17 00:00:00 2001 From: dblythy Date: Mon, 22 Feb 2021 00:57:12 +1100 Subject: [PATCH 11/13] Update Parse.Cloud.js --- src/cloud-code/Parse.Cloud.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cloud-code/Parse.Cloud.js b/src/cloud-code/Parse.Cloud.js index 8ac15e52c8..dba2b3b7b1 100644 --- a/src/cloud-code/Parse.Cloud.js +++ b/src/cloud-code/Parse.Cloud.js @@ -27,6 +27,8 @@ function validateValidator(validator) { }; const allowedKeys = { requireUser: [Boolean], + requireAnyUserRoles: [Boolean], + requireAllUserRoles: [Boolean], requireMaster: [Boolean], validateMasterKey: [Boolean], skipWithMasterKey: [Boolean], From cd13ef6f8a46862ef111b778cb4d921772200e58 Mon Sep 17 00:00:00 2001 From: dblythy Date: Mon, 22 Feb 2021 00:58:44 +1100 Subject: [PATCH 12/13] Update CHANGELOG.md --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a80a898431..87bd518248 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,6 @@ __BREAKING CHANGES:__ - NEW: Added file upload restriction. File upload is now only allowed for authenticated users by default for improved security. To allow file upload also for Anonymous Users or Public, set the `fileUpload` parameter in the [Parse Server Options](https://parseplatform.org/parse-server/api/master/ParseServerOptions.html). [#7071](https://github.com/parse-community/parse-server/pull/7071). Thanks to [dblythy](https://github.com/dblythy), [Manuel Trezza](https://github.com/mtrezza). ___ -- IMPROVE: Cloud Validators will now log on invalid configuration [#7154](https://github.com/parse-community/parse-server/pull/7154). Thanks to [dblythy](https://github.com/dblythy) - NEW (EXPERIMENTAL): Added new page router with placeholder rendering and localization of custom and feature pages such as password reset and email verification. **Caution, this is an experimental feature that may not be appropriate for production.** [#6891](https://github.com/parse-community/parse-server/issues/6891). Thanks to [Manuel Trezza](https://github.com/mtrezza). - NEW: Added convenience method `Parse.Cloud.sendEmail(...)` to send email via email adapter in Cloud Code. [#7089](https://github.com/parse-community/parse-server/pull/7089). Thanks to [dblythy](https://github.com/dblythy) - NEW: LiveQuery support for $and, $nor, $containedBy, $geoWithin, $geoIntersects queries [#7113](https://github.com/parse-community/parse-server/pull/7113). Thanks to [dplewis](https://github.com/dplewis) From 5c1618e1bd8dbfe34e4ca28769be00b7b54a5f06 Mon Sep 17 00:00:00 2001 From: dblythy Date: Wed, 24 Feb 2021 02:25:51 +1100 Subject: [PATCH 13/13] Change to throw error --- CHANGELOG.md | 2 +- spec/CloudCode.Validator.spec.js | 173 ++++++++++++++++++++----------- src/cloud-code/Parse.Cloud.js | 21 ++-- 3 files changed, 124 insertions(+), 72 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91a3d96cc6..03637bb83e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,7 +26,7 @@ ___ - IMPROVE: Added new account lockout policy option `accountLockout.unlockOnPasswordReset` to automatically unlock account on password reset. [#7146](https://github.com/parse-community/parse-server/pull/7146). Thanks to [Manuel Trezza](https://github.com/mtrezza). - IMPROVE: Parse Server is from now on continuously tested against all recent MongoDB versions that have not reached their end-of-life support date. Added MongoDB compatibility table to Parse Server docs. [7161](https://github.com/parse-community/parse-server/pull/7161). Thanks to [Manuel Trezza](https://github.com/mtrezza). - IMPROVE: Parse Server is from now on continuously tested against all recent Node.js versions that have not reached their end-of-life support date. [7161](https://github.com/parse-community/parse-server/pull/7177). Thanks to [Manuel Trezza](https://github.com/mtrezza). -- IMPROVE: Cloud Validators will now log on invalid configuration [#7154](https://github.com/parse-community/parse-server/pull/7154). Thanks to [dblythy](https://github.com/dblythy) +- IMPROVE: Throw error on invalid Cloud Function validation configuration. [#7154](https://github.com/parse-community/parse-server/pull/7154). Thanks to [dblythy](https://github.com/dblythy) - IMPROVE: Allow Cloud Validator `options` to be async [#7155](https://github.com/parse-community/parse-server/pull/7155). Thanks to [dblythy](https://github.com/dblythy) - IMPROVE: Optimize queries on classes with pointer permissions. [#7061](https://github.com/parse-community/parse-server/pull/7061). Thanks to [Pedro Diaz](https://github.com/pdiaz) - IMPROVE: Parse Server will from now on be continuously tested against all relevant Postgres versions (minor versions). Added Postgres compatibility table to Parse Server docs. [#7176](https://github.com/parse-community/parse-server/pull/7176). Thanks to [Corey Baker](https://github.com/cbaker6). diff --git a/spec/CloudCode.Validator.spec.js b/spec/CloudCode.Validator.spec.js index 685a6663e3..0ee0debbae 100644 --- a/spec/CloudCode.Validator.spec.js +++ b/spec/CloudCode.Validator.spec.js @@ -1420,8 +1420,6 @@ describe('cloud validator', () => { }); it('does not log on valid config', () => { - const logger = require('../lib/logger').logger; - spyOn(logger, 'error').and.callFake(() => {}); Parse.Cloud.define('myFunction', () => {}, { requireUser: true, requireMaster: true, @@ -1448,68 +1446,127 @@ describe('cloud validator', () => { }, }, }); - expect(logger.error).not.toHaveBeenCalled(); }); it('Logs on invalid config', () => { - const logger = require('../lib/logger').logger; - spyOn(logger, 'error').and.callFake(() => {}); - Parse.Cloud.define('myFunction', () => {}, { - requiredUser: true, - requireUser: ['foo'], - requireMaster: ['foo'], - validateMasterKey: ['foo'], - skipWithMasterKey: ['foo'], - requireUserKeys: true, - fields: true, - }); - expect(logger.error).toHaveBeenCalledWith( - 'requiredUser is not a supported parameter for Parse.Cloud validators.' - ); - expect(logger.error).toHaveBeenCalledWith( - 'Invalid type for Parse.Cloud validator key requireUser. Expected boolean, actual array' - ); - expect(logger.error).toHaveBeenCalledWith( - 'Invalid type for Parse.Cloud validator key requireMaster. Expected boolean, actual array' - ); - expect(logger.error).toHaveBeenCalledWith( - 'Invalid type for Parse.Cloud validator key validateMasterKey. Expected boolean, actual array' - ); - expect(logger.error).toHaveBeenCalledWith( - 'Invalid type for Parse.Cloud validator key skipWithMasterKey. Expected boolean, actual array' - ); - expect(logger.error).toHaveBeenCalledWith( - 'Invalid type for Parse.Cloud validator key fields. Expected array|object, actual boolean' - ); - expect(logger.error).toHaveBeenCalledWith( - 'Invalid type for Parse.Cloud validator key requireUserKeys. Expected array|object, actual boolean' - ); + const fields = [ + { + field: 'requiredUser', + value: true, + error: 'requiredUser is not a supported parameter for Cloud Function validations.', + }, + { + field: 'requireUser', + value: [], + error: + 'Invalid type for Cloud Function validation key requireUser. Expected boolean, actual array', + }, + { + field: 'requireMaster', + value: [], + error: + 'Invalid type for Cloud Function validation key requireMaster. Expected boolean, actual array', + }, + { + field: 'validateMasterKey', + value: [], + error: + 'Invalid type for Cloud Function validation key validateMasterKey. Expected boolean, actual array', + }, + { + field: 'skipWithMasterKey', + value: [], + error: + 'Invalid type for Cloud Function validation key skipWithMasterKey. Expected boolean, actual array', + }, + { + field: 'requireAllUserRoles', + value: true, + error: + 'Invalid type for Cloud Function validation key requireAllUserRoles. Expected array|function, actual boolean', + }, + { + field: 'requireAnyUserRoles', + value: true, + error: + 'Invalid type for Cloud Function validation key requireAnyUserRoles. Expected array|function, actual boolean', + }, + { + field: 'fields', + value: true, + error: + 'Invalid type for Cloud Function validation key fields. Expected array|object, actual boolean', + }, + { + field: 'requireUserKeys', + value: true, + error: + 'Invalid type for Cloud Function validation key requireUserKeys. Expected array|object, actual boolean', + }, + ]; + for (const field of fields) { + try { + Parse.Cloud.define('myFunction', () => {}, { + [field.field]: field.value, + }); + fail(`Expected error registering invalid Cloud Function validation ${field.field}.`); + } catch (e) { + expect(e).toBe(field.error); + } + } }); it('Logs on invalid config', () => { - const logger = require('../lib/logger').logger; - spyOn(logger, 'error').and.callFake(() => {}); - Parse.Cloud.define('myFunction', () => {}, { - fields: { - name: { - constant: ['foo'], - required: ['foo'], - error: ['foo'], - otherKey: true, - }, + const fields = [ + { + field: 'otherKey', + value: true, + error: 'otherKey is not a supported parameter for Cloud Function validations.', }, - }); - expect(logger.error).toHaveBeenCalledWith( - 'otherKey is not a supported parameter for Parse.Cloud validators.' - ); - expect(logger.error).toHaveBeenCalledWith( - 'Invalid type for Parse.Cloud validator key constant. Expected boolean, actual array' - ); - expect(logger.error).toHaveBeenCalledWith( - 'Invalid type for Parse.Cloud validator key required. Expected boolean, actual array' - ); - expect(logger.error).toHaveBeenCalledWith( - 'Invalid type for Parse.Cloud validator key error. Expected string, actual array' - ); + { + field: 'constant', + value: [], + error: + 'Invalid type for Cloud Function validation key constant. Expected boolean, actual array', + }, + { + field: 'required', + value: [], + error: + 'Invalid type for Cloud Function validation key required. Expected boolean, actual array', + }, + { + field: 'error', + value: [], + error: + 'Invalid type for Cloud Function validation key error. Expected string, actual array', + }, + ]; + for (const field of fields) { + try { + Parse.Cloud.define('myFunction', () => {}, { + fields: { + name: { + [field.field]: field.value, + }, + }, + }); + fail(`Expected error registering invalid Cloud Function validation ${field.field}.`); + } catch (e) { + expect(e).toBe(field.error); + } + try { + Parse.Cloud.define('myFunction', () => {}, { + requireUserKeys: { + name: { + [field.field]: field.value, + }, + }, + }); + fail(`Expected error registering invalid Cloud Function validation ${field.field}.`); + } catch (e) { + expect(e).toBe(field.error); + } + } }); it('set params options function async', async () => { diff --git a/src/cloud-code/Parse.Cloud.js b/src/cloud-code/Parse.Cloud.js index dba2b3b7b1..d16fe28ad4 100644 --- a/src/cloud-code/Parse.Cloud.js +++ b/src/cloud-code/Parse.Cloud.js @@ -27,44 +27,39 @@ function validateValidator(validator) { }; const allowedKeys = { requireUser: [Boolean], - requireAnyUserRoles: [Boolean], - requireAllUserRoles: [Boolean], + requireAnyUserRoles: [Array, 'function'], + requireAllUserRoles: [Array, 'function'], requireMaster: [Boolean], validateMasterKey: [Boolean], skipWithMasterKey: [Boolean], requireUserKeys: [Array, Object], fields: [Array, Object], }; - const config = Config.get(Parse.applicationId); - const logger = config.loggerController; const getType = fn => { if (Array.isArray(fn)) { return 'array'; } - if (fn === 'Any') { + if (fn === 'Any' || fn === 'function') { return fn; } const type = typeof fn; if (typeof fn === 'function') { const match = fn && fn.toString().match(/^\s*function (\w+)/); - return (match ? match[1] : '').toLowerCase(); + return (match ? match[1] : 'function').toLowerCase(); } return type; }; const checkKey = (key, data, validatorParam) => { const parameter = data[key]; if (!parameter) { - logger.error(`${key} is not a supported parameter for Parse.Cloud validators.`); - return; + throw `${key} is not a supported parameter for Cloud Function validations.`; } const types = parameter.map(type => getType(type)); const type = getType(validatorParam); if (!types.includes(type) && !types.includes('Any')) { - logger.error( - `Invalid type for Parse.Cloud validator key ${key}. Expected ${types.join( - '|' - )}, actual ${type}` - ); + throw `Invalid type for Cloud Function validation key ${key}. Expected ${types.join( + '|' + )}, actual ${type}`; } }; for (const key in validator) {