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

skipWithMasterKey on Built-In Validator #6972

Merged
merged 3 commits into from
Oct 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions spec/CloudCode.Validator.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,131 @@ describe('cloud validator', () => {
done();
});

it('basic beforeSave skipWithMasterKey', async function (done) {
Parse.Cloud.beforeSave(
'BeforeSave',
() => {
throw 'before save should have resolved using masterKey.';
},
{
skipWithMasterKey: true,
}
);
const obj = new Parse.Object('BeforeSave');
obj.set('foo', 'bar');
await obj.save(null, { useMasterKey: true });
expect(obj.get('foo')).toBe('bar');
done();
});

it('basic beforeFind skipWithMasterKey', async function (done) {
Parse.Cloud.beforeFind(
'beforeFind',
() => {
throw 'before find should have resolved using masterKey.';
},
{
skipWithMasterKey: true,
}
);
const obj = new Parse.Object('beforeFind');
obj.set('foo', 'bar');
await obj.save();
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;
}
});

it('basic beforeDelete skipWithMasterKey', async function (done) {
Parse.Cloud.beforeDelete(
'beforeFind',
() => {
throw 'before find should have resolved using masterKey.';
},
{
skipWithMasterKey: true,
}
);
const obj = new Parse.Object('beforeFind');
obj.set('foo', 'bar');
await obj.save();
expect(obj.get('foo')).toBe('bar');
await obj.destroy({ useMasterKey: true });
done();
});

it('basic beforeSaveFile skipWithMasterKey', async done => {
Parse.Cloud.beforeSaveFile(
() => {
throw 'beforeSaveFile should have resolved using master key.';
},
{
skipWithMasterKey: true,
}
);
const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
const result = await file.save({ useMasterKey: true });
expect(result).toBe(file);
done();
});

it('beforeSave validateMasterKey and skipWithMasterKey fail', async function (done) {
Parse.Cloud.beforeSave(
'BeforeSave',
() => {
throw 'beforeSaveFile should have resolved using master key.';
},
{
fields: ['foo'],
validateMasterKey: true,
skipWithMasterKey: true,
}
);

const obj = new Parse.Object('BeforeSave');
try {
await obj.save(null, { useMasterKey: true });
fail('function should have failed.');
} catch (error) {
expect(error.code).toEqual(Parse.Error.VALIDATION_ERROR);
expect(error.message).toEqual('Validation failed. Please specify data for foo.');
done();
}
});

it('beforeSave validateMasterKey and skipWithMasterKey success', async function (done) {
Parse.Cloud.beforeSave(
'BeforeSave',
() => {
throw 'beforeSaveFile should have resolved using master key.';
},
{
fields: ['foo'],
validateMasterKey: true,
skipWithMasterKey: true,
}
);

const obj = new Parse.Object('BeforeSave');
obj.set('foo', 'bar');
try {
await obj.save(null, { useMasterKey: true });
done();
} catch (error) {
fail('error should not have been called.');
}
});

it('basic beforeSave requireUserKey on User Class', async function (done) {
Parse.Cloud.beforeSave(Parse.User, () => {}, {
requireUser: true,
Expand Down
20 changes: 19 additions & 1 deletion spec/CloudCode.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,24 @@ describe('Cloud Code', () => {
);
});

it('beforeFind can throw string', async function (done) {
Parse.Cloud.beforeFind('beforeFind', () => {
throw 'throw beforeFind';
});
const obj = new Parse.Object('beforeFind');
obj.set('foo', 'bar');
await obj.save();
expect(obj.get('foo')).toBe('bar');
try {
const query = new Parse.Query('beforeFind');
await query.first();
} catch (e) {
expect(e.code).toBe(141);
expect(e.message).toBe('throw beforeFind');
done();
}
});

it('beforeSave rejection with custom error code', function (done) {
Parse.Cloud.beforeSave('BeforeSaveFailWithErrorCode', function () {
throw new Parse.Error(999, 'Nope');
Expand Down Expand Up @@ -1896,7 +1914,7 @@ describe('beforeFind hooks', () => {
done();
},
err => {
expect(err.code).toBe(1);
expect(err.code).toBe(141);
expect(err.message).toEqual('Do not run that query');
done();
}
Expand Down
1 change: 1 addition & 0 deletions src/cloud-code/Parse.Cloud.js
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,7 @@ module.exports = ParseCloud;
* @property {Boolean} requireUser whether the cloud trigger requires a user.
* @property {Boolean} requireMaster whether the cloud trigger requires a master key.
* @property {Boolean} validateMasterKey whether the validator should run if masterKey is provided. Defaults to false.
* @property {Boolean} skipWithMasterKey whether the cloud code function should be ignored using a masterKey.
*
* @property {Array<String>|Object} requireUserKeys If set, keys required on request.user to make the request.
* @property {String} requireUserKeys.field If requireUserKeys is an object, name of field to validate on request user
Expand Down
46 changes: 34 additions & 12 deletions src/triggers.js
Original file line number Diff line number Diff line change
Expand Up @@ -331,13 +331,11 @@ export function getResponseObject(request, resolve, reject) {
return resolve(response);
},
error: function (error) {
if (error instanceof Parse.Error) {
reject(error);
} else if (error instanceof Error) {
reject(new Parse.Error(Parse.Error.SCRIPT_FAILED, error.message));
} else {
reject(new Parse.Error(Parse.Error.SCRIPT_FAILED, error));
}
const e = resolveError(error, {
code: Parse.Error.SCRIPT_FAILED,
message: 'Script failed. Unknown error.',
});
reject(e);
},
};
}
Expand Down Expand Up @@ -420,6 +418,9 @@ export function maybeRunAfterFindTrigger(triggerType, auth, className, objects,
return maybeRunValidator(request, `${triggerType}.${className}`);
})
.then(() => {
if (request.skipWithMasterKey) {
return request.objects;
}
const response = trigger(request);
if (response && typeof response.then === 'function') {
return response.then(results => {
Expand Down Expand Up @@ -482,6 +483,9 @@ export function maybeRunQueryTrigger(
return maybeRunValidator(requestObject, `${triggerType}.${className}`);
})
.then(() => {
if (requestObject.skipWithMasterKey) {
return requestObject.query;
}
return trigger(requestObject);
})
.then(
Expand Down Expand Up @@ -544,11 +548,11 @@ export function maybeRunQueryTrigger(
};
},
err => {
if (typeof err === 'string') {
throw new Parse.Error(1, err);
} else {
throw err;
}
const error = resolveError(err, {
code: Parse.Error.SCRIPT_FAILED,
message: 'Script failed. Unknown error.',
});
throw error;
}
);
}
Expand Down Expand Up @@ -583,6 +587,9 @@ export function maybeRunValidator(request, functionName) {
if (!theValidator) {
return;
}
if (typeof theValidator === 'object' && theValidator.skipWithMasterKey && request.master) {
request.skipWithMasterKey = true;
}
return new Promise((resolve, reject) => {
return Promise.resolve()
.then(() => {
Expand Down Expand Up @@ -797,6 +804,9 @@ export function maybeRunTrigger(
return maybeRunValidator(request, `${triggerType}.${parseObject.className}`);
})
.then(() => {
if (request.skipWithMasterKey) {
return Promise.resolve();
}
const promise = trigger(request);
if (
triggerType === Types.afterSave ||
Expand Down Expand Up @@ -873,6 +883,9 @@ export async function maybeRunFileTrigger(triggerType, fileObject, config, auth)
try {
const request = getRequestFileObject(triggerType, auth, fileObject, config);
await maybeRunValidator(request, `${triggerType}.${FileClassName}`);
if (request.skipWithMasterKey) {
return fileObject;
}
const result = await fileTrigger(request);
logTriggerSuccessBeforeHook(
triggerType,
Expand Down Expand Up @@ -903,6 +916,9 @@ export async function maybeRunConnectTrigger(triggerType, request) {
}
request.user = await userForSessionToken(request.sessionToken);
await maybeRunValidator(request, `${triggerType}.${ConnectClassName}`);
if (request.skipWithMasterKey) {
return;
}
return trigger(request);
}

Expand All @@ -916,6 +932,9 @@ export async function maybeRunSubscribeTrigger(triggerType, className, request)
request.query = parseQuery;
request.user = await userForSessionToken(request.sessionToken);
await maybeRunValidator(request, `${triggerType}.${className}`);
if (request.skipWithMasterKey) {
return;
}
await trigger(request);
const query = request.query.toJSON();
if (query.keys) {
Expand All @@ -937,6 +956,9 @@ export async function maybeRunAfterEventTrigger(triggerType, className, request)
}
request.user = await userForSessionToken(request.sessionToken);
await maybeRunValidator(request, `${triggerType}.${className}`);
if (request.skipWithMasterKey) {
return;
}
return trigger(request);
}

Expand Down