diff --git a/docs/client/full-api/api/accounts.md b/docs/client/full-api/api/accounts.md
index e1477ed836..130867ee5e 100644
--- a/docs/client/full-api/api/accounts.md
+++ b/docs/client/full-api/api/accounts.md
@@ -401,8 +401,14 @@ client, no arguments are passed.
Rate Limiting
-By default, there are rules added to the [`DDPRateLimiter`](#DDPRateLimiter) that rate limit logins, new user registration and password reset calls to a limit of 5 requests per 10 seconds per IP address. These are a basic solution to dictionary attacks where a malicious user attempts to guess the passwords of legitimate users by attempting all possible passwords.
+By default, there are rules added to the [`DDPRateLimiter`](#DDPRateLimiter)
+that rate limit logins, new user registration and password reset calls to a
+limit of 5 requests per 10 seconds per IP address. These are a basic solution
+to dictionary attacks where a malicious user attempts to guess the passwords
+of legitimate users by attempting all possible passwords.
-These rate limiting rules can be removed by calling `Accounts.removeDefaultRateLimit()`. Please see the [`DDPRateLimiter`](#DDPRateLimiter) docs for more information.
+These rate limiting rules can be removed by calling
+`Accounts.removeDefaultRateLimit()`. Please see the
+[`DDPRateLimiter`](#DDPRateLimiter) docs for more information.
{{/template}}
diff --git a/packages/accounts-base/accounts_rate_limit.js b/packages/accounts-base/accounts_rate_limit.js
index 11552fa848..9801b09f7a 100644
--- a/packages/accounts-base/accounts_rate_limit.js
+++ b/packages/accounts-base/accounts_rate_limit.js
@@ -7,15 +7,16 @@ Ap._defaultRateLimiterRuleId = DDPRateLimiter.addRule({
userId: null,
ipAddr: null,
type: 'method',
- name: function(name) {
- return _.contains(['login', 'createUser', 'resetPassword', 'forgotPassword'], name);
+ name: function (name) {
+ return _.contains(['login', 'createUser', 'resetPassword',
+ 'forgotPassword'], name);
},
- sessionId: function(sessionId) {
- return true;
+ sessionId: function (sessionId) {
+ return true;
}
}, 5, 10000);
// Removes default rate limiting rule
Ap.removeDefaultRateLimit = function () {
- return DDPRateLimiter.removeRule(Ap._defaultRateLimiterRuleId);
+ return DDPRateLimiter.removeRule(Ap._defaultRateLimiterRuleId);
}
\ No newline at end of file
diff --git a/packages/rate-limit/rate-limit.js b/packages/rate-limit/rate-limit.js
index 615e37ea71..dead5df21c 100644
--- a/packages/rate-limit/rate-limit.js
+++ b/packages/rate-limit/rate-limit.js
@@ -3,82 +3,11 @@
// * RateLimiter - a general rate limiter that stores rules and determines
// whether inputs are allowed
//
-// ** Rules **
-// Rules are composed of the following fields:
-// - id Random id generated and used to key the rule in the rate limiter
-// - options Object that contains the intervalTime after which the rule is reset, and the
-// number of calls that are allowed in the specified interval time
-// - matchers Dictionary of keys that are searched for in the input provided to match
-// with values that define the set of values that match this rule. Values can be
-// objects or they can be functions that return a boolean of whether the provided
-// input matches. For example, if we only want to match all even ids, plus any other fields
-// , we could have a rule that included a key value pair as follows:
-// {
-// ...
-// id: function (id) {
-// return id % 2 === 0;
-// },
-// ...
-// }
-// A rule is only said to apply to a given input if every key in the matcher
-// matches to the input values.
-// - _lastResetTime Last time this rule's counters were reset. Last reset time is used to
-// keep track if the interval time has passed
-// - counters Dictionary that stores the current state of inputs and number times they've
-// been passed to the rate limiter. Unique keys are made per input per rule that
-// create a concatenated string of all keys in the rule with the values from the
-// input
-//
-// For example, if we had a rule with matchers as such:
-// {
-// userId: function(userId) {
-// return true;
-// },
-// methodName: 'hello'
-// }
-// and we were passed an input as follows:
-// {
-// userId: 'meteor'
-// methodName: 'hello'
-// }
-//
-// The key generated would be 'userIdmeteormethodNamehello'.
-//
-// These counters are checked on every invocation to determine whether a rate limit
-// has been reached.
-//
-// The methods provided are as follows:
-// * match(input) : Checks whether the rule applies to the provided input by comparing every
-// field in the matcher to the provided input. Order of input doesn't matter,
-// they just must contain the appropriate keys and the values must be allowed by
-// the matcher
-// * _generateKeyString(input) : generates a key string by concatenating all the keys in the matcher
-// with the corresponding values in the input
-// * apply(input) : applies the provided input and returns the key string, the time since last
-// reset and time to next reset
-// * resetCounter() : resets the counters for this rule and sets _lastResetTime to be the current time
-//
-// ** Rate Limiter **
-// A rate limiter stores a dictionary of rules keyed by their rule Id. It provides the following methods:
-//
-// * addRule(rule, numRequests, intervalTime) : adds a Rule to dictionary of rules and returns the user
-// the rule Id
-// * removeRule(id) : removes a rule from the dictionary and returns boolean detailing success
-// * check(input) : checks all rules that apply to this input to see if any rate limits have been exceeded
-// Returns an object as follows:
-// {
-// allowed: boolean - is this input allowed
-// timeToReset: integer - returns time to reset in milliseconds
-// numInvocationsLeft: integer - returns number of calls left before limit is reached
-// }
-// If multiple rules match, the least number of invocations left is returned. If the rate limit
-// has been reached, the longest timeToReset is returned.
-//
-// * increment(input) : increments counters in all rules that apply to this input.
-// * _findAllMatchingRules(input) : returns an array of all rules that apply to provided input
-//
-
-
+// Rate limiters analyze a series of "inputs" (which are POJOs) by running
+// them against a set of "rules". Rules specify which inputs they match by
+// running configurable "matcher" functions on keys in the event object). A
+// `check` method returns whether this input should be allowed, the time
+// until next reset and the number of calls for this input left.
// Default time interval (in milliseconds) to reset rate limit counters
var DEFAULT_INTERVAL_TIME_IN_MILLISECONDS = 1000;
@@ -107,7 +36,9 @@ var Rule = function (options, matchers) {
_.extend(Rule.prototype, {
// Determine if this rule applies to the given input by comparing all
// rule.matchers. If the match fails, search short circuits instead of
- // iterating through all matchers.
+ // iterating through all matchers. The order of the input doesn't matter,
+ // it just must contain the appropriate keys and their respective values
+ // must be allowed by the matcher.
match: function (input) {
var self = this;
var ruleMatches = true;
@@ -134,7 +65,8 @@ _.extend(Rule.prototype, {
return ruleMatches;
},
- // Generates unique key string for provided input
+ // Generates unique key string for provided input by concatenating all the
+ // keys in the matcher with the corresponding values in the input.
// Only called if rule matches input.
_generateKeyString: function (input) {
var self = this;
@@ -153,8 +85,8 @@ _.extend(Rule.prototype, {
return returnString;
},
- // Generates the key, timeSinceLastReset and timeToNextReset once the rule
- // is applied
+ // Applies the provided input and returns the key string, time since last
+ // reset and time to next reset.
apply: function (input) {
var self = this;
var keyString = self._generateKeyString(input);
@@ -166,8 +98,9 @@ _.extend(Rule.prototype, {
timeToNextReset: timeToNextReset
};
},
- // Reset all keys for this specific rule. Called once the timeSinceLastReset
- // has exceeded the intervalTime.
+ // Reset counter dictionary for this specific rule. Called once the
+ // timeSinceLastReset has exceeded the intervalTime. _lastResetTime is
+ // set to be the current time in milliseconds.
resetCounter: function () {
var self = this;
@@ -189,9 +122,16 @@ RateLimiter = function () {
/**
* Checks if this input has exceeded any rate limits.
- * @param {object} input dictionary containing key-value pairs of attributes that match to rules
- * @return {object} Returns object of whether input is allowed, time
- * to next reset and number invocations left
+ * @param {object} input dictionary containing key-value pairs of attributes
+ * that match to rules
+ * @return {object} Returns object of following structure
+ * { 'allowed': boolean - is this input allowed
+ * 'timeToReset': integer - returns time to reset in milliseconds
+ * 'numInvocationsLeft': integer - returns number of calls left before limit
+ * is reached
+ * }
+ * If multiple rules match, the least number of invocations left is returned.
+ * If the rate limit has been reached, the longest timeToReset is returned.
*/
RateLimiter.prototype.check = function (input) {
var self = this;
@@ -225,10 +165,10 @@ RateLimiter.prototype.check = function (input) {
reply.allowed = false;
reply.numInvocationsLeft = 0;
} else {
- // If this is an allowed attempt and we haven't failed on any of the other rules that
- // match, update the reply field.
- if (rule.options.numRequestsAllowed - numInvocations < reply.numInvocationsLeft &&
- reply.allowed) {
+ // If this is an allowed attempt and we haven't failed on any of the
+ // other rules that match, update the reply field.
+ if (rule.options.numRequestsAllowed - numInvocations <
+ reply.numInvocationsLeft && reply.allowed) {
reply.allowed = true;
reply.timeToReset = ruleResult.timeToNextReset < 0 ?
rule.options.intervalTime :
@@ -241,17 +181,58 @@ RateLimiter.prototype.check = function (input) {
return reply;
}
+// Each rule is composed of an `id`, an options object that contains the
+// `intervalTime` after which the rule is reset, and `numRequestsAllowed` in
+// the specified interval time, a dictionary of `matchers` whose keys are
+// searched for in the input provided to determine if there is a match. If the
+// values match, then the rules counters are incremented. Values can be objects
+// or they can be functions that return a boolean of whether the provided input
+// matches. For example, if we only want to match all even ids, plusany other
+// fields, we could have a rule that included a key-value pair as follows:
+// {
+// ...
+// id: function (id) {
+// return id % 2 === 0;
+// },
+// ...
+// }
+// A rule is only said to apply to a given input if every key in the matcher
+// matchesto the input values. There is also a dictionary of `counters` that
+// store the current state of inputs and number of times they've been passed
+// to the rate limiter. Unique keys are made per input per rule that create
+// a concatenated string of all keys in the rule with the values from the
+// input. For example, if we had a rule with matchers as such:
+// {
+// userId: function(userId) {
+// return true;
+// },
+// methodName: 'hello'
+// }
+// and we were passed an input as follows:
+// {
+// userId: 'meteor'
+// methodName: 'hello'
+// }
+// The key generated would be 'userIdmeteormethodNamehello'.
+// These counters are checked on every invocation to determine whether a rate
+// limit has been reached.
+
/**
- * Adds a rule to dictionary of rules that are checked against on every call. Only inputs that
- * pass all of the rules will be allowed and order doesn't matter.
- * @param {object} rule Input dictionary defining certain attributes and rules associated with them.
- * Each attribute's value can either be a value, a function or null. All functions must return a boolean
- * of whether the input is matched by that attribute's rule or not
+ * Adds a rule to dictionary of rules that are checked against on every call.
+ * Only inputs that pass all of the rules will be allowed and order doesn't
+ * matter. Returns unique rule id that can be passed to 'removeRule'.
+ * @param {object} rule Input dictionary defining certain attributes and
+ * rules associated with them.
+ * Each attribute's value can either be a value, a function or null. All
+ * functions must return a boolean of whether the input is matched by that
+ * attribute's rule or not
* @param {integer} numRequestsAllowed Number of requests allowed per interval
- * @param {integer} intervalTime Number of milliseconds before interval is reset
- * @return {string} Returns the randomly generated rule id
+ * @param {integer} intervalTime Number of milliseconds before interval
+ * is reset
+ * @return {string} Returns unique rule id
*/
-RateLimiter.prototype.addRule = function (rule, numRequestsAllowed, intervalTime) {
+RateLimiter.prototype.addRule = function (rule, numRequestsAllowed,
+ intervalTime) {
var self = this;
var options = {
@@ -266,8 +247,8 @@ RateLimiter.prototype.addRule = function (rule, numRequestsAllowed, intervalTime
/**
* Increment counters in every rule that match to this input
- * @param {object} input Dictionary object containing attributes that may match to
- * rules
+ * @param {object} input Dictionary object containing attributes that may
+ * match to rules
*/
RateLimiter.prototype.increment = function (input) {
var self = this;
@@ -291,6 +272,7 @@ RateLimiter.prototype.increment = function (input) {
});
}
+// Returns an array of all rules that apply to provided input
RateLimiter.prototype._findAllMatchingRules = function (input) {
var self = this;
@@ -302,7 +284,8 @@ RateLimiter.prototype._findAllMatchingRules = function (input) {
return matchingRules;
}
/**
- * Provides a mechanism to remove rules from the rate limiter
+ * Provides a mechanism to remove rules from the rate limiter. Returns boolean
+ * about success.
* @param {string} id Rule id returned from #addRule
* @return {boolean} Returns true if rule was found and deleted, else false.
*/