2019-05-29 05:21:06 -04:00
|
|
|
const RateLimiter = require('../../infrastructure/RateLimiter')
|
|
|
|
const logger = require('logger-sharelatex')
|
2021-07-28 04:51:20 -04:00
|
|
|
const SessionManager = require('../Authentication/SessionManager')
|
2019-11-08 09:05:16 -05:00
|
|
|
const LoginRateLimiter = require('./LoginRateLimiter')
|
2021-07-07 05:38:56 -04:00
|
|
|
const settings = require('@overleaf/settings')
|
2019-05-29 05:21:06 -04:00
|
|
|
|
2019-11-08 09:05:16 -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.:
|
2019-05-29 05:21:06 -04:00
|
|
|
|
2019-11-08 09:05:16 -05:00
|
|
|
app.get "/project/:project_id", RateLimiterMiddleware.rateLimit(endpointName: "open-editor", params: ["project_id"])
|
2019-05-29 05:21:06 -04:00
|
|
|
|
2019-11-08 09:05:16 -05:00
|
|
|
will rate limit each project_id separately.
|
2019-05-29 05:21:06 -04:00
|
|
|
|
2019-11-08 09:05:16 -05:00
|
|
|
Unique clients are identified by user_id if logged in, and IP address if not.
|
|
|
|
*/
|
|
|
|
function rateLimit(opts) {
|
2021-04-14 09:17:21 -04:00
|
|
|
return function (req, res, next) {
|
2021-07-28 04:51:20 -04:00
|
|
|
const userId = SessionManager.getLoggedInUserId(req.session) || req.ip
|
2019-11-08 09:05:16 -05:00
|
|
|
if (
|
|
|
|
settings.smokeTest &&
|
|
|
|
settings.smokeTest.userId &&
|
|
|
|
settings.smokeTest.userId.toString() === userId.toString()
|
|
|
|
) {
|
|
|
|
// ignore smoke test user
|
|
|
|
return next()
|
|
|
|
}
|
|
|
|
const params = (opts.params || []).map(p => req.params[p])
|
|
|
|
params.push(userId)
|
|
|
|
let subjectName = params.join(':')
|
|
|
|
if (opts.ipOnly) {
|
|
|
|
subjectName = req.ip
|
|
|
|
}
|
|
|
|
if (opts.endpointName == null) {
|
|
|
|
throw new Error('no endpointName provided')
|
|
|
|
}
|
|
|
|
const options = {
|
|
|
|
endpointName: opts.endpointName,
|
|
|
|
timeInterval: opts.timeInterval || 60,
|
|
|
|
subjectName,
|
2021-04-27 03:52:58 -04:00
|
|
|
throttle: opts.maxRequests || 6,
|
2019-11-08 09:05:16 -05:00
|
|
|
}
|
2021-04-14 09:17:21 -04:00
|
|
|
return RateLimiter.addCount(options, function (error, canContinue) {
|
2019-11-08 09:05:16 -05:00
|
|
|
if (error != null) {
|
|
|
|
return next(error)
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
2019-11-08 09:05:16 -05:00
|
|
|
if (canContinue) {
|
|
|
|
return next()
|
|
|
|
} else {
|
|
|
|
logger.warn(options, 'rate limit exceeded')
|
|
|
|
res.status(429) // Too many requests
|
|
|
|
res.write('Rate limit reached, please try again later')
|
|
|
|
return res.end()
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
2019-11-08 09:05:16 -05:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function loginRateLimit(req, res, next) {
|
|
|
|
const { email } = req.body
|
|
|
|
if (!email) {
|
|
|
|
return next()
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
2021-04-14 09:17:21 -04:00
|
|
|
LoginRateLimiter.processLoginRequest(email, function (err, isAllowed) {
|
2019-11-08 09:05:16 -05:00
|
|
|
if (err) {
|
|
|
|
return next(err)
|
|
|
|
}
|
|
|
|
if (isAllowed) {
|
|
|
|
return next()
|
|
|
|
} else {
|
|
|
|
logger.warn({ email }, 'rate limit exceeded')
|
|
|
|
res.status(429) // Too many requests
|
|
|
|
res.write('Rate limit reached, please try again later')
|
|
|
|
return res.end()
|
|
|
|
}
|
|
|
|
})
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
2019-11-08 09:05:16 -05:00
|
|
|
|
|
|
|
const RateLimiterMiddleware = {
|
|
|
|
rateLimit,
|
2021-04-27 03:52:58 -04:00
|
|
|
loginRateLimit,
|
2019-11-08 09:05:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = RateLimiterMiddleware
|