Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New: allow options to be async on Cloud Validator #7155

Merged
merged 10 commits into from
Feb 23, 2021
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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), [Manuel Trezza](https://github.com/mtrezza).
___
- 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)
dblythy marked this conversation as resolved.
Show resolved Hide resolved
- 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)
Expand Down
74 changes: 74 additions & 0 deletions spec/CloudCode.Validator.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1284,4 +1284,78 @@ describe('cloud validator', () => {
done();
}
});

it('set params options function async', async () => {
Parse.Cloud.define(
'hello',
() => {
return 'Hello world!';
},
{
fields: {
data: {
type: String,
required: true,
options: async val => {
await new Promise(resolve => {
setTimeout(resolve, 500);
});
return val === 'f';
},
error: 'Validation failed.',
},
},
}
);
try {
await Parse.Cloud.run('hello', { data: 'd' });
} catch (error) {
expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR);
dblythy marked this conversation as resolved.
Show resolved Hide resolved
expect(error.message).toEqual('Validation failed.');
}
await Parse.Cloud.run('hello', { data: 'f' });
dblythy marked this conversation as resolved.
Show resolved Hide resolved
});

it('basic beforeSave requireUserKey as custom async function', async () => {
Parse.Cloud.beforeSave(Parse.User, () => {}, {
fields: {
accType: {
default: 'normal',
constant: true,
},
},
});
Parse.Cloud.define(
'secureFunction',
() => {
return "Here's all the secure data!";
},
{
requireUserKeys: {
accType: {
options: async val => {
await new Promise(resolve => {
setTimeout(resolve, 500);
});
return ['admin', 'admin2'].includes(val);
},
error: 'Unauthorized.',
},
},
}
);
const user = new Parse.User();
user.set('username', 'testuser');
user.set('password', 'p@ssword');
user.set('accType', 'admin');
await user.signUp();
expect(user.get('accType')).toBe('normal');
try {
await Parse.Cloud.run('secureFunction');
fail('function should only be available to admin users');
} catch (error) {
expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR);
expect(error.message).toEqual('Unauthorized.');
}
});
});
14 changes: 9 additions & 5 deletions src/triggers.js
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,7 @@ export function maybeRunValidator(request, functionName) {
});
});
}
function builtInTriggerValidator(options, request) {
async function builtInTriggerValidator(options, request) {
if (request.master && !options.validateMasterKey) {
return;
dblythy marked this conversation as resolved.
Show resolved Hide resolved
}
Expand Down Expand Up @@ -647,11 +647,11 @@ function builtInTriggerValidator(options, request) {
}
};

const validateOptions = (opt, key, val) => {
const validateOptions = async (opt, key, val) => {
let opts = opt.options;
if (typeof opts === 'function') {
try {
const result = opts(val);
const result = await opts(val);
if (!result && result != null) {
throw opt.error || `Validation failed. Invalid value for ${key}.`;
}
Expand Down Expand Up @@ -684,6 +684,7 @@ function builtInTriggerValidator(options, request) {
requiredParam(key);
}
} else {
const optionPromises = [];
for (const key in options.fields) {
const opt = options.fields[key];
let val = params[key];
Expand Down Expand Up @@ -716,10 +717,11 @@ function builtInTriggerValidator(options, request) {
}
}
if (opt.options) {
validateOptions(opt, key, val);
optionPromises.push(validateOptions(opt, key, val));
}
}
}
await Promise.all(optionPromises);
}
const userKeys = options.requireUserKeys || [];
if (Array.isArray(userKeys)) {
Expand All @@ -733,12 +735,14 @@ function builtInTriggerValidator(options, request) {
}
}
} else if (typeof userKeys === 'object') {
const optionPromises = [];
for (const key in options.requireUserKeys) {
const opt = options.requireUserKeys[key];
if (opt.options) {
validateOptions(opt, key, reqUser.get(key));
optionPromises.push(validateOptions(opt, key, reqUser.get(key)));
}
}
await Promise.all(optionPromises);
}
}

Expand Down