-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathratelimit-factory.js
134 lines (120 loc) · 6.4 KB
/
ratelimit-factory.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import { RateLimiterRegistry } from './RateLimiterRegistry'
import { check, Match } from 'meteor/check'
export const isObject = Match.Where(x => typeof x === 'object' && x !== null)
/**
* Registers an array of method definitions to the rate limiter
* @param methods an array of method definitions
* @see {rateLimitMethod}
* @return {Array}
*/
export const rateLimitMethods = methods => {
check(methods, [isObject])
return methods.map(rateLimitMethod)
}
/**
* Registers a method for rate-limiting.
* @param name {String} Name of the method
* @param userId {String|Function|undefined} optional The user ID attempting the method or subscription
* @param connectionId {String|Function|undefined} A string representing the user's DDP connection
* @param clientAddress {String|Function|undefined} The IP address of the user
* @param numRequests {Number} number of requests allowed per time interval. Default = 10.
* @param timeInterval {Number} time interval in milliseconds after which rule's counters are reset. Default = 1000.
* @see https://docs.meteor.com/api/methods.html#DDPRateLimiter-addRule
* @return {Boolean}
*/
export const rateLimitMethod = ({ name, userId, connectionId, clientAddress, numRequests, timeInterval }) => {
// no need to check since RateLimiterRegistry does that already
return RateLimiterRegistry.addMethod({ name, userId, connectionId, clientAddress, numRequests, timeInterval })
}
/**
* Registers an array of publication definitions
* @param publications {Array} an array of publication definitions
* @return {Array}
*/
export const rateLimitPublications = publications => {
check(publications, [isObject])
return publications.map(rateLimitPublication)
}
/**
* Registers a publication for rate limiting
* @param name {String} Name of the method
* @param userId {String|Function|undefined} optional The user ID attempting the method or subscription
* @param connectionId {String|Function|undefined} A string representing the user's DDP connection
* @param clientAddress {String|Function|undefined} The IP address of the user
* @param numRequests {Number} number of requests allowed per time interval. Default = 10.
* @param timeInterval {Number} time interval in milliseconds after which rule's counters are reset. Default = 1000.
* @see https://docs.meteor.com/api/methods.html#DDPRateLimiter-addRule
* @return {Boolean}
*/
export const rateLimitPublication = ({ name, userId, connectionId, clientAddress, numRequests, timeInterval }) => {
// no need to check since RateLimiterRegistry does that already
return RateLimiterRegistry.addPublication({ name, userId, connectionId, clientAddress, numRequests, timeInterval })
}
/**
* Rate-limits by default all builtin methods and publications. You can extend / override accountMethods and accountPublications
* @param numRequestsMethods {Number} number of requests allowed per time interval. Default = 10.
* @param timIntervalMethods {Number} time interval in milliseconds after which rule's counters are reset. Default = 1000.
* @param numRequestsPublications {Number} number of requests allowed per time interval. Default = 10.
* @param timeIntervalPublications {Number} time interval in milliseconds after which rule's counters are reset. Default = 1000.
* @param accountMethods {Array} list with strings of all builtin methods
* @param accountPublications {Array} list with strings of all builtin publications
*/
export const rateLimitAccounts = ({
numRequestsMethods = 10,
timIntervalMethods = 1000,
numRequestsPublications = 10,
timeIntervalPublications = 1000,
accountMethods = [
'login',
'loginWithToken',
'logout',
'logoutOtherClients',
'getNewToken',
'removeOtherTokens',
'configureLoginService',
'changePassword',
'forgotPassword',
'resetPassword',
'verifyEmail',
'createUser',
'ATRemoveService',
'ATCreateUserServer',
'ATResendVerificationEmail'
],
accountPublications = [
'meteor.loginServiceConfiguration',
'meteor_autoupdate_clientVersions',
'_roles'
]
} = {}) => {
Accounts.removeDefaultRateLimit()
accountMethods.forEach(name => RateLimiterRegistry.addMethod({
name,
numRequests: numRequestsMethods,
timeInterval: timIntervalMethods
}))
accountPublications.forEach(name => RateLimiterRegistry.addPublication({
name,
timeInterval: timeIntervalPublications,
numRequests: numRequestsPublications
}))
}
/**
* This finally runs through all registered methods/publications and internally adds the rules for each.
* Can perform a simple sanity check to see, if there is no non-rate-limited method or publication left.
* If you want the sanity check to be valid you should run this after all methods and publications and builtins
* have been registered for rate limiting.
*
* @param limitExceededCallback {Function} function to be called if a rate-limit is exceeded at runtime
* @param sanityCheck {Boolean} performs a sanity check before adding the rules
* @param throwIfNotRatelimited {Boolean} throws an Error if any entry in Meteor.server.method_handlers or
* Meteor.server.publication_handlers is found that is not registered for rate limiting.
*/
export const runRateLimiter = (limitExceededCallback, sanityCheck = true, throwIfNotRatelimited = false) => {
if (sanityCheck) {
console.info(`[RateLimiterRegistry]: run sanity check, strict-mode=${throwIfNotRatelimited}`)
RateLimiterRegistry.sanityCheck(throwIfNotRatelimited)
console.info('[RateLimiterRegistry]: sanity check passed')
}
RateLimiterRegistry.run(limitExceededCallback)
}