feat: Add request rate limiter based on IP address (#8174)

This commit is contained in:
Daniel
2023-01-06 23:39:02 +11:00
committed by GitHub
parent 0eac5dc6d4
commit 6c79f6a69e
13 changed files with 713 additions and 50 deletions

View File

@@ -1,6 +1,7 @@
import { Parse } from 'parse/node';
import * as triggers from '../triggers';
import Deprecator from '../Deprecator/Deprecator';
import { addRateLimit } from '../middlewares';
const Config = require('../Config');
function isParseObjectConstructor(object) {
@@ -28,6 +29,7 @@ function validateValidator(validator) {
skipWithMasterKey: [Boolean],
requireUserKeys: [Array, Object],
fields: [Array, Object],
rateLimit: [Object],
};
const getType = fn => {
if (Array.isArray(fn)) {
@@ -72,6 +74,18 @@ function validateValidator(validator) {
}
}
}
const getRoute = parseClass => {
const route =
{
_User: 'users',
_Session: 'sessions',
'@File': 'files',
}[parseClass] || 'classes';
if (parseClass === '@File') {
return `/${route}/:id?*`;
}
return `/${route}/${parseClass}/:id?*`;
};
/** @namespace
* @name Parse
* @description The Parse SDK.
@@ -111,6 +125,12 @@ var ParseCloud = {};
ParseCloud.define = function (functionName, handler, validationHandler) {
validateValidator(validationHandler);
triggers.addFunction(functionName, handler, validationHandler, Parse.applicationId);
if (validationHandler && validationHandler.rateLimit) {
addRateLimit(
{ requestPath: `/functions/${functionName}`, ...validationHandler.rateLimit },
Parse.applicationId
);
}
};
/**
@@ -164,6 +184,16 @@ ParseCloud.beforeSave = function (parseClass, handler, validationHandler) {
Parse.applicationId,
validationHandler
);
if (validationHandler && validationHandler.rateLimit) {
addRateLimit(
{
requestPath: getRoute(className),
requestMethods: ['POST', 'PUT'],
...validationHandler.rateLimit,
},
Parse.applicationId
);
}
};
/**
@@ -200,6 +230,16 @@ ParseCloud.beforeDelete = function (parseClass, handler, validationHandler) {
Parse.applicationId,
validationHandler
);
if (validationHandler && validationHandler.rateLimit) {
addRateLimit(
{
requestPath: getRoute(className),
requestMethods: 'DELETE',
...validationHandler.rateLimit,
},
Parse.applicationId
);
}
};
/**
@@ -225,15 +265,22 @@ ParseCloud.beforeDelete = function (parseClass, handler, validationHandler) {
* @name Parse.Cloud.beforeLogin
* @param {Function} func The function to run before a login. This function can be async and should take one parameter a {@link Parse.Cloud.TriggerRequest};
*/
ParseCloud.beforeLogin = function (handler) {
ParseCloud.beforeLogin = function (handler, validationHandler) {
let className = '_User';
if (typeof handler === 'string' || isParseObjectConstructor(handler)) {
// validation will occur downstream, this is to maintain internal
// code consistency with the other hook types.
className = triggers.getClassName(handler);
handler = arguments[1];
validationHandler = arguments.length >= 2 ? arguments[2] : null;
}
triggers.addTrigger(triggers.Types.beforeLogin, className, handler, Parse.applicationId);
if (validationHandler && validationHandler.rateLimit) {
addRateLimit(
{ requestPath: `/login`, requestMethods: 'POST', ...validationHandler.rateLimit },
Parse.applicationId
);
}
};
/**
@@ -402,6 +449,16 @@ ParseCloud.beforeFind = function (parseClass, handler, validationHandler) {
Parse.applicationId,
validationHandler
);
if (validationHandler && validationHandler.rateLimit) {
addRateLimit(
{
requestPath: getRoute(className),
requestMethods: 'GET',
...validationHandler.rateLimit,
},
Parse.applicationId
);
}
};
/**