overleaf/services/web/app/coffee/Features/Security/RateLimiterMiddleware.coffee

42 lines
1.4 KiB
CoffeeScript
Raw Normal View History

2015-02-04 10:05:26 -05:00
RateLimiter = require "../../infrastructure/RateLimiter"
2015-02-05 04:52:40 -05:00
logger = require "logger-sharelatex"
2016-09-05 10:58:31 -04:00
AuthenticationController = require('../Authentication/AuthenticationController')
2015-02-04 10:05:26 -05:00
module.exports = RateLimiterMiddleware =
2015-02-04 10:05:26 -05:00
###
Do not allow more than opts.maxRequests from a single client in
opts.timeInterval. Pass an array of opts.params to segment this based on
parameters in the request URL, e.g.:
2016-09-05 10:58:31 -04:00
app.get "/project/:project_id", RateLimiterMiddleware.rateLimit(endpointName: "open-editor", params: ["project_id"])
2016-09-05 10:58:31 -04:00
2015-02-04 10:05:26 -05:00
will rate limit each project_id separately.
2016-09-05 10:58:31 -04:00
2015-02-04 10:05:26 -05:00
Unique clients are identified by user_id if logged in, and IP address if not.
###
rateLimit: (opts) ->
return (req, res, next) ->
2016-09-05 10:58:31 -04:00
user_id = AuthenticationController.getLoggedInUserId(req) || req.ip
2015-02-04 10:05:26 -05:00
params = (opts.params or []).map (p) -> req.params[p]
params.push user_id
subjectName = params.join(":")
if opts.ipOnly
subjectName = req.ip
2015-02-04 10:05:26 -05:00
if !opts.endpointName?
throw new Error("no endpointName provided")
2015-02-05 04:52:40 -05:00
options = {
2015-02-04 10:05:26 -05:00
endpointName: opts.endpointName
timeInterval: opts.timeInterval or 60
subjectName: subjectName
2015-02-04 10:05:26 -05:00
throttle: opts.maxRequests or 6
2015-02-05 04:52:40 -05:00
}
RateLimiter.addCount options, (error, canContinue)->
2015-02-04 10:05:26 -05:00
return next(error) if error?
if canContinue
next()
else
2015-02-05 04:52:40 -05:00
logger.warn options, "rate limit exceeded"
2015-02-04 10:05:26 -05:00
res.status(429) # Too many requests
res.write("Rate limit reached, please try again later")
2016-09-05 10:58:31 -04:00
res.end()