skipWithMasterKey on Built-In Validator (#6972)

* Initial Commit

* Change to resolveMasterKey

* Change to skipWithMasterKey
This commit is contained in:
dblythy
2020-10-27 06:49:30 +11:00
committed by GitHub
parent 7f3ea3fe80
commit 6fc3afce71
4 changed files with 179 additions and 13 deletions

View File

@@ -554,6 +554,131 @@ describe('cloud validator', () => {
done(); 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) { it('basic beforeSave requireUserKey on User Class', async function (done) {
Parse.Cloud.beforeSave(Parse.User, () => {}, { Parse.Cloud.beforeSave(Parse.User, () => {}, {
requireUser: true, requireUser: true,

View File

@@ -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) { it('beforeSave rejection with custom error code', function (done) {
Parse.Cloud.beforeSave('BeforeSaveFailWithErrorCode', function () { Parse.Cloud.beforeSave('BeforeSaveFailWithErrorCode', function () {
throw new Parse.Error(999, 'Nope'); throw new Parse.Error(999, 'Nope');
@@ -1894,7 +1912,7 @@ describe('beforeFind hooks', () => {
done(); done();
}, },
err => { err => {
expect(err.code).toBe(1); expect(err.code).toBe(141);
expect(err.message).toEqual('Do not run that query'); expect(err.message).toEqual('Do not run that query');
done(); done();
} }

View File

@@ -712,6 +712,7 @@ module.exports = ParseCloud;
* @property {Boolean} requireUser whether the cloud trigger requires a user. * @property {Boolean} requireUser whether the cloud trigger requires a user.
* @property {Boolean} requireMaster whether the cloud trigger requires a master key. * @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} 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 {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 * @property {String} requireUserKeys.field If requireUserKeys is an object, name of field to validate on request user

View File

@@ -331,13 +331,11 @@ export function getResponseObject(request, resolve, reject) {
return resolve(response); return resolve(response);
}, },
error: function (error) { error: function (error) {
if (error instanceof Parse.Error) { const e = resolveError(error, {
reject(error); code: Parse.Error.SCRIPT_FAILED,
} else if (error instanceof Error) { message: 'Script failed. Unknown error.',
reject(new Parse.Error(Parse.Error.SCRIPT_FAILED, error.message)); });
} else { reject(e);
reject(new Parse.Error(Parse.Error.SCRIPT_FAILED, error));
}
}, },
}; };
} }
@@ -420,6 +418,9 @@ export function maybeRunAfterFindTrigger(triggerType, auth, className, objects,
return maybeRunValidator(request, `${triggerType}.${className}`); return maybeRunValidator(request, `${triggerType}.${className}`);
}) })
.then(() => { .then(() => {
if (request.skipWithMasterKey) {
return request.objects;
}
const response = trigger(request); const response = trigger(request);
if (response && typeof response.then === 'function') { if (response && typeof response.then === 'function') {
return response.then(results => { return response.then(results => {
@@ -482,6 +483,9 @@ export function maybeRunQueryTrigger(
return maybeRunValidator(requestObject, `${triggerType}.${className}`); return maybeRunValidator(requestObject, `${triggerType}.${className}`);
}) })
.then(() => { .then(() => {
if (requestObject.skipWithMasterKey) {
return requestObject.query;
}
return trigger(requestObject); return trigger(requestObject);
}) })
.then( .then(
@@ -544,11 +548,11 @@ export function maybeRunQueryTrigger(
}; };
}, },
err => { err => {
if (typeof err === 'string') { const error = resolveError(err, {
throw new Parse.Error(1, err); code: Parse.Error.SCRIPT_FAILED,
} else { message: 'Script failed. Unknown error.',
throw err; });
} throw error;
} }
); );
} }
@@ -583,6 +587,9 @@ export function maybeRunValidator(request, functionName) {
if (!theValidator) { if (!theValidator) {
return; return;
} }
if (typeof theValidator === 'object' && theValidator.skipWithMasterKey && request.master) {
request.skipWithMasterKey = true;
}
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
return Promise.resolve() return Promise.resolve()
.then(() => { .then(() => {
@@ -797,6 +804,9 @@ export function maybeRunTrigger(
return maybeRunValidator(request, `${triggerType}.${parseObject.className}`); return maybeRunValidator(request, `${triggerType}.${parseObject.className}`);
}) })
.then(() => { .then(() => {
if (request.skipWithMasterKey) {
return Promise.resolve();
}
const promise = trigger(request); const promise = trigger(request);
if ( if (
triggerType === Types.afterSave || triggerType === Types.afterSave ||
@@ -873,6 +883,9 @@ export async function maybeRunFileTrigger(triggerType, fileObject, config, auth)
try { try {
const request = getRequestFileObject(triggerType, auth, fileObject, config); const request = getRequestFileObject(triggerType, auth, fileObject, config);
await maybeRunValidator(request, `${triggerType}.${FileClassName}`); await maybeRunValidator(request, `${triggerType}.${FileClassName}`);
if (request.skipWithMasterKey) {
return fileObject;
}
const result = await fileTrigger(request); const result = await fileTrigger(request);
logTriggerSuccessBeforeHook( logTriggerSuccessBeforeHook(
triggerType, triggerType,
@@ -903,6 +916,9 @@ export async function maybeRunConnectTrigger(triggerType, request) {
} }
request.user = await userForSessionToken(request.sessionToken); request.user = await userForSessionToken(request.sessionToken);
await maybeRunValidator(request, `${triggerType}.${ConnectClassName}`); await maybeRunValidator(request, `${triggerType}.${ConnectClassName}`);
if (request.skipWithMasterKey) {
return;
}
return trigger(request); return trigger(request);
} }
@@ -916,6 +932,9 @@ export async function maybeRunSubscribeTrigger(triggerType, className, request)
request.query = parseQuery; request.query = parseQuery;
request.user = await userForSessionToken(request.sessionToken); request.user = await userForSessionToken(request.sessionToken);
await maybeRunValidator(request, `${triggerType}.${className}`); await maybeRunValidator(request, `${triggerType}.${className}`);
if (request.skipWithMasterKey) {
return;
}
await trigger(request); await trigger(request);
const query = request.query.toJSON(); const query = request.query.toJSON();
if (query.keys) { if (query.keys) {
@@ -937,6 +956,9 @@ export async function maybeRunAfterEventTrigger(triggerType, className, request)
} }
request.user = await userForSessionToken(request.sessionToken); request.user = await userForSessionToken(request.sessionToken);
await maybeRunValidator(request, `${triggerType}.${className}`); await maybeRunValidator(request, `${triggerType}.${className}`);
if (request.skipWithMasterKey) {
return;
}
return trigger(request); return trigger(request);
} }