Skip to content

New: requireAnyUserRoles and requireAllUserRoles for Parse Cloud Validator #7097

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

Merged
merged 12 commits into from
Feb 12, 2021
54 changes: 54 additions & 0 deletions spec/CloudCode.Validator.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -860,6 +860,60 @@ describe('cloud validator', () => {
});
});

it('basic validator requireUserRole', async function (done) {
Parse.Cloud.define(
'cloudFunction',
() => {
return true;
},
{
requireUser: true,
requireUserRole: ['Admin'],
}
);
const user = await Parse.User.signUp('testuser', 'p@ssword');
try {
await Parse.Cloud.run('cloudFunction');
fail('cloud validator should have failed.');
} catch (e) {
expect(e.message).toBe('Validation failed. User does not match the required roles.');
}
const roleACL = new Parse.ACL();
roleACL.setPublicReadAccess(true);
const role = new Parse.Role('Admin', roleACL);
role.getUsers().add(user);
await role.save({ useMasterKey: true });
await Parse.Cloud.run('cloudFunction');
done();
});

it('basic string requireUserRole', async function (done) {
Parse.Cloud.define(
'cloudFunction',
() => {
return true;
},
{
requireUser: true,
requireUserRole: 'Admin',
}
);
const user = await Parse.User.signUp('testuser', 'p@ssword');
try {
await Parse.Cloud.run('cloudFunction');
fail('cloud validator should have failed.');
} catch (e) {
expect(e.message).toBe('Validation failed. User does not match the required roles.');
}
const roleACL = new Parse.ACL();
roleACL.setPublicReadAccess(true);
const role = new Parse.Role('Admin', roleACL);
role.getUsers().add(user);
await role.save({ useMasterKey: true });
await Parse.Cloud.run('cloudFunction');
done();
});

it('basic beforeSave requireMaster', function (done) {
Parse.Cloud.beforeSave('BeforeSaveFail', () => {}, {
requireMaster: true,
Expand Down
2 changes: 2 additions & 0 deletions src/cloud-code/Parse.Cloud.js
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,8 @@ module.exports = ParseCloud;
* @property {Array|function|Any} requireUserKeys.field.options array of options that the field can be, function to validate field, or single value. Throw an error if value is invalid.
* @property {String} requireUserKeys.field.error custom error message if field is invalid.
*
* @property {Array<String>|String} requireUserRole If set, string or array of roles allowed on request.user to make the request.
*
* @property {Object|Array<String>} fields if an array of strings, validator will look for keys in request.params, and throw if not provided. If Object, fields to validate. If the trigger is a cloud function, `request.params` will be validated, otherwise `request.object`.
* @property {String} fields.field name of field to validate.
* @property {String} fields.field.type expected type of data for field.
Expand Down
15 changes: 14 additions & 1 deletion 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;
}
Expand Down Expand Up @@ -722,6 +722,19 @@ function builtInTriggerValidator(options, request) {
}
}
}
let userRoles = options.requireUserRole || [];
if (typeof userRoles === 'string') {
userRoles = [userRoles];
}
if (userRoles.length != 0 && reqUser) {
const roleQuery = new Parse.Query(Parse.Role);
roleQuery.containedIn('name', userRoles);
roleQuery.equalTo('users', reqUser);
const role = await roleQuery.first({ useMasterKey: true });
if (!role) {
throw `Validation failed. User does not match the required roles.`;
}
}
const userKeys = options.requireUserKeys || [];
if (Array.isArray(userKeys)) {
for (const key of userKeys) {
Expand Down