RateLimiterMiddleware.js 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. const RateLimiter = require('../../infrastructure/RateLimiter')
  2. const logger = require('logger-sharelatex')
  3. const AuthenticationController = require('../Authentication/AuthenticationController')
  4. const LoginRateLimiter = require('./LoginRateLimiter')
  5. const settings = require('@overleaf/settings')
  6. /*
  7. Do not allow more than opts.maxRequests from a single client in
  8. opts.timeInterval. Pass an array of opts.params to segment this based on
  9. parameters in the request URL, e.g.:
  10. app.get "/project/:project_id", RateLimiterMiddleware.rateLimit(endpointName: "open-editor", params: ["project_id"])
  11. will rate limit each project_id separately.
  12. Unique clients are identified by user_id if logged in, and IP address if not.
  13. */
  14. function rateLimit(opts) {
  15. return function (req, res, next) {
  16. const userId = AuthenticationController.getLoggedInUserId(req) || req.ip
  17. if (
  18. settings.smokeTest &&
  19. settings.smokeTest.userId &&
  20. settings.smokeTest.userId.toString() === userId.toString()
  21. ) {
  22. // ignore smoke test user
  23. return next()
  24. }
  25. const params = (opts.params || []).map(p => req.params[p])
  26. params.push(userId)
  27. let subjectName = params.join(':')
  28. if (opts.ipOnly) {
  29. subjectName = req.ip
  30. }
  31. if (opts.endpointName == null) {
  32. throw new Error('no endpointName provided')
  33. }
  34. const options = {
  35. endpointName: opts.endpointName,
  36. timeInterval: opts.timeInterval || 60,
  37. subjectName,
  38. throttle: opts.maxRequests || 6,
  39. }
  40. return RateLimiter.addCount(options, function (error, canContinue) {
  41. if (error != null) {
  42. return next(error)
  43. }
  44. if (canContinue) {
  45. return next()
  46. } else {
  47. logger.warn(options, 'rate limit exceeded')
  48. res.status(429) // Too many requests
  49. res.write('Rate limit reached, please try again later')
  50. return res.end()
  51. }
  52. })
  53. }
  54. }
  55. function loginRateLimit(req, res, next) {
  56. const { email } = req.body
  57. if (!email) {
  58. return next()
  59. }
  60. LoginRateLimiter.processLoginRequest(email, function (err, isAllowed) {
  61. if (err) {
  62. return next(err)
  63. }
  64. if (isAllowed) {
  65. return next()
  66. } else {
  67. logger.warn({ email }, 'rate limit exceeded')
  68. res.status(429) // Too many requests
  69. res.write('Rate limit reached, please try again later')
  70. return res.end()
  71. }
  72. })
  73. }
  74. const RateLimiterMiddleware = {
  75. rateLimit,
  76. loginRateLimit,
  77. }
  78. module.exports = RateLimiterMiddleware