Files
kami-parse-server/spec/RateLimit.spec.js

392 lines
12 KiB
JavaScript

describe('rate limit', () => {
it('can limit cloud functions', async () => {
Parse.Cloud.define('test', () => 'Abc');
await reconfigureServer({
rateLimit: [
{
requestPath: '/functions/*',
requestTimeWindow: 10000,
requestCount: 1,
errorResponseMessage: 'Too many requests',
includeInternalRequests: true,
},
],
});
const response1 = await Parse.Cloud.run('test');
expect(response1).toBe('Abc');
await expectAsync(Parse.Cloud.run('test')).toBeRejectedWith(
new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests')
);
});
it('can limit cloud functions with user session token', async () => {
await Parse.User.signUp('myUser', 'password');
Parse.Cloud.define('test', () => 'Abc');
await reconfigureServer({
rateLimit: [
{
requestPath: '/functions/*',
requestTimeWindow: 10000,
requestCount: 1,
errorResponseMessage: 'Too many requests',
includeInternalRequests: true,
},
],
});
const response1 = await Parse.Cloud.run('test');
expect(response1).toBe('Abc');
await expectAsync(Parse.Cloud.run('test')).toBeRejectedWith(
new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests')
);
});
it('can add global limit', async () => {
Parse.Cloud.define('test', () => 'Abc');
await reconfigureServer({
rateLimit: {
requestPath: '*',
requestTimeWindow: 10000,
requestCount: 1,
errorResponseMessage: 'Too many requests',
includeInternalRequests: true,
},
});
const response1 = await Parse.Cloud.run('test');
expect(response1).toBe('Abc');
await expectAsync(Parse.Cloud.run('test')).toBeRejectedWith(
new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests')
);
await expectAsync(new Parse.Object('Test').save()).toBeRejectedWith(
new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests')
);
});
it('can limit cloud with validator', async () => {
Parse.Cloud.define('test', () => 'Abc', {
rateLimit: {
requestTimeWindow: 10000,
requestCount: 1,
errorResponseMessage: 'Too many requests',
includeInternalRequests: true,
},
});
const response1 = await Parse.Cloud.run('test');
expect(response1).toBe('Abc');
await expectAsync(Parse.Cloud.run('test')).toBeRejectedWith(
new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests')
);
});
it('can skip with masterKey', async () => {
Parse.Cloud.define('test', () => 'Abc');
await reconfigureServer({
rateLimit: [
{
requestPath: '/functions/*',
requestTimeWindow: 10000,
requestCount: 1,
errorResponseMessage: 'Too many requests',
includeInternalRequests: true,
},
],
});
const response1 = await Parse.Cloud.run('test', null, { useMasterKey: true });
expect(response1).toBe('Abc');
const response2 = await Parse.Cloud.run('test', null, { useMasterKey: true });
expect(response2).toBe('Abc');
});
it('should run with masterKey', async () => {
Parse.Cloud.define('test', () => 'Abc');
await reconfigureServer({
rateLimit: [
{
requestPath: '/functions/*',
requestTimeWindow: 10000,
requestCount: 1,
includeMasterKey: true,
errorResponseMessage: 'Too many requests',
includeInternalRequests: true,
},
],
});
const response1 = await Parse.Cloud.run('test', null, { useMasterKey: true });
expect(response1).toBe('Abc');
await expectAsync(Parse.Cloud.run('test')).toBeRejectedWith(
new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests')
);
});
it('can limit saving objects', async () => {
await reconfigureServer({
rateLimit: [
{
requestPath: '/classes/*',
requestTimeWindow: 10000,
requestCount: 1,
errorResponseMessage: 'Too many requests',
includeInternalRequests: true,
},
],
});
const obj = new Parse.Object('Test');
await obj.save();
await expectAsync(obj.save()).toBeRejectedWith(
new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests')
);
});
it('can set method to post', async () => {
await reconfigureServer({
rateLimit: [
{
requestPath: '/classes/*',
requestTimeWindow: 10000,
requestCount: 1,
requestMethods: 'POST',
errorResponseMessage: 'Too many requests',
includeInternalRequests: true,
},
],
});
const obj = new Parse.Object('Test');
await obj.save();
await obj.save();
const obj2 = new Parse.Object('Test');
await expectAsync(obj2.save()).toBeRejectedWith(
new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests')
);
});
it('can use a validator for post', async () => {
Parse.Cloud.beforeSave('Test', () => {}, {
rateLimit: {
requestTimeWindow: 10000,
requestCount: 1,
errorResponseMessage: 'Too many requests',
includeInternalRequests: true,
},
});
const obj = new Parse.Object('Test');
await obj.save();
await expectAsync(obj.save()).toBeRejectedWith(
new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests')
);
});
it('can use a validator for file', async () => {
Parse.Cloud.beforeSave(Parse.File, () => {}, {
rateLimit: {
requestTimeWindow: 10000,
requestCount: 1,
errorResponseMessage: 'Too many requests',
includeInternalRequests: true,
},
});
const file = new Parse.File('yolo.txt', [1, 2, 3], 'text/plain');
await file.save();
const file2 = new Parse.File('yolo.txt', [1, 2, 3], 'text/plain');
await expectAsync(file2.save()).toBeRejectedWith(
new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests')
);
});
it('can set method to get', async () => {
await reconfigureServer({
rateLimit: [
{
requestPath: '/classes/Test',
requestTimeWindow: 10000,
requestCount: 1,
requestMethods: 'GET',
errorResponseMessage: 'Too many requests',
includeInternalRequests: true,
},
],
});
const obj = new Parse.Object('Test');
await obj.save();
await obj.save();
await new Parse.Query('Test').first();
await expectAsync(new Parse.Query('Test').first()).toBeRejectedWith(
new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests')
);
});
it('can use a validator', async () => {
await reconfigureServer({ silent: false });
Parse.Cloud.beforeFind('TestObject', () => {}, {
rateLimit: {
requestTimeWindow: 10000,
requestCount: 1,
errorResponseMessage: 'Too many requests',
includeInternalRequests: true,
},
});
const obj = new Parse.Object('TestObject');
await obj.save();
await obj.save();
await new Parse.Query('TestObject').first();
await expectAsync(new Parse.Query('TestObject').first()).toBeRejectedWith(
new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests')
);
await expectAsync(new Parse.Query('TestObject').get('abc')).toBeRejectedWith(
new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests')
);
});
it('can set method to delete', async () => {
await reconfigureServer({
rateLimit: [
{
requestPath: '/classes/Test/*',
requestTimeWindow: 10000,
requestCount: 1,
requestMethods: 'DELETE',
errorResponseMessage: 'Too many requests',
includeInternalRequests: true,
},
],
});
const obj = new Parse.Object('Test');
await obj.save();
await obj.destroy();
await expectAsync(obj.destroy()).toBeRejectedWith(
new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests')
);
});
it('can set beforeDelete', async () => {
const obj = new Parse.Object('TestDelete');
await obj.save();
Parse.Cloud.beforeDelete('TestDelete', () => {}, {
rateLimit: {
requestTimeWindow: 10000,
requestCount: 1,
errorResponseMessage: 'Too many requests',
includeInternalRequests: true,
},
});
await obj.destroy();
await expectAsync(obj.destroy()).toBeRejectedWith(
new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests')
);
});
it('can set beforeLogin', async () => {
Parse.Cloud.beforeLogin(() => {}, {
rateLimit: {
requestTimeWindow: 10000,
requestCount: 1,
errorResponseMessage: 'Too many requests',
includeInternalRequests: true,
},
});
await Parse.User.signUp('myUser', 'password');
await Parse.User.logIn('myUser', 'password');
await expectAsync(Parse.User.logIn('myUser', 'password')).toBeRejectedWith(
new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests')
);
});
it('can define limits via rateLimit and define', async () => {
await reconfigureServer({
rateLimit: [
{
requestPath: '/functions/*',
requestTimeWindow: 10000,
requestCount: 100,
errorResponseMessage: 'Too many requests',
includeInternalRequests: true,
},
],
});
Parse.Cloud.define('test', () => 'Abc', {
rateLimit: {
requestTimeWindow: 10000,
requestCount: 1,
includeInternalRequests: true,
},
});
const response1 = await Parse.Cloud.run('test');
expect(response1).toBe('Abc');
await expectAsync(Parse.Cloud.run('test')).toBeRejectedWith(
new Parse.Error(Parse.Error.CONNECTION_FAILED, 'Too many requests.')
);
});
it('does not limit internal calls', async () => {
await reconfigureServer({
rateLimit: [
{
requestPath: '/functions/*',
requestTimeWindow: 10000,
requestCount: 1,
errorResponseMessage: 'Too many requests',
},
],
});
Parse.Cloud.define('test1', () => 'Abc');
Parse.Cloud.define('test2', async () => {
await Parse.Cloud.run('test1');
await Parse.Cloud.run('test1');
});
await Parse.Cloud.run('test2');
});
it('can validate rateLimit', async () => {
const Config = require('../lib/Config');
const validateRateLimit = ({ rateLimit }) => Config.validateRateLimit(rateLimit);
expect(() =>
validateRateLimit({ rateLimit: 'a', requestTimeWindow: 1000, requestCount: 3 })
).toThrow('rateLimit must be an array or object');
expect(() => validateRateLimit({ rateLimit: ['a'] })).toThrow(
'rateLimit must be an array of objects'
);
expect(() => validateRateLimit({ rateLimit: [{ requestPath: [] }] })).toThrow(
'rateLimit.requestPath must be a string'
);
expect(() =>
validateRateLimit({ rateLimit: [{ requestTimeWindow: [], requestPath: 'a' }] })
).toThrow('rateLimit.requestTimeWindow must be a number');
expect(() =>
validateRateLimit({
rateLimit: [
{
includeInternalRequests: [],
requestTimeWindow: 1000,
requestCount: 3,
requestPath: 'a',
},
],
})
).toThrow('rateLimit.includeInternalRequests must be a boolean');
expect(() =>
validateRateLimit({
rateLimit: [{ requestCount: [], requestTimeWindow: 1000, requestPath: 'a' }],
})
).toThrow('rateLimit.requestCount must be a number');
expect(() =>
validateRateLimit({
rateLimit: [
{ errorResponseMessage: [], requestTimeWindow: 1000, requestCount: 3, requestPath: 'a' },
],
})
).toThrow('rateLimit.errorResponseMessage must be a string');
expect(() =>
validateRateLimit({ rateLimit: [{ requestCount: 3, requestPath: 'abc' }] })
).toThrow('rateLimit.requestTimeWindow must be defined');
expect(() =>
validateRateLimit({ rateLimit: [{ requestTimeWindow: 3, requestPath: 'abc' }] })
).toThrow('rateLimit.requestCount must be defined');
expect(() =>
validateRateLimit({ rateLimit: [{ requestTimeWindow: 3, requestCount: 'abc' }] })
).toThrow('rateLimit.requestPath must be defined');
await expectAsync(
reconfigureServer({
rateLimit: [{ requestTimeWindow: 3, requestCount: 1, path: 'abc', requestPath: 'a' }],
})
).toBeRejectedWith(`Invalid rate limit option "path"`);
});
});