| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091 |
- import logger from '@overleaf/logger'
- import SessionManager from '../Authentication/SessionManager.mjs'
- import LoginRateLimiter from './LoginRateLimiter.mjs'
- import settings from '@overleaf/settings'
- /**
- * Return a rate limiting middleware
- *
- * Pass an array of opts.params to segment this based on parameters in the
- * request URL, e.g.:
- *
- * app.get "/project/:project_id", RateLimiterMiddleware.rateLimit(
- * rateLimiter, params: ["project_id"]
- * )
- *
- * will rate limit each project_id separately.
- *
- * Unique clients are identified by user_id if logged in, and IP address if not.
- * The method label is used to identify this in our metrics.
- */
- function rateLimit(rateLimiter, opts = {}) {
- const getUserId =
- opts.getUserId || (req => SessionManager.getLoggedInUserId(req.session))
- return function (req, res, next) {
- const clientId = opts.ipOnly ? req.ip : getUserId(req) || req.ip
- const method = clientId === req.ip ? 'ip' : 'userId'
- if (
- settings.smokeTest &&
- settings.smokeTest.userId &&
- settings.smokeTest.userId.toString() === clientId.toString()
- ) {
- // ignore smoke test user
- return next()
- }
- let key = clientId
- if (!opts.ipOnly) {
- const params = (opts.params || []).map(p => req.params[p])
- params.push(clientId)
- key = params.join(':')
- }
- rateLimiter
- .consume(key, 1, { method })
- .then(() => next())
- .catch(err => {
- if (err instanceof Error) {
- next(err)
- } else {
- res.status(429) // Too many requests
- res.write('Rate limit reached, please try again later')
- res.end()
- }
- })
- }
- }
- function loginRateLimitEmail(emailField = 'email') {
- return function (req, res, next) {
- const email = req.body[emailField]
- if (!email) {
- return next()
- }
- LoginRateLimiter.processLoginRequest(email, (err, isAllowed) => {
- if (err) {
- return next(err)
- }
- if (isAllowed) {
- next()
- } else {
- logger.warn({ email }, 'rate limit exceeded')
- res.status(429) // Too many requests
- res.json({
- message: {
- type: 'error',
- text: req.i18n.translate('to_many_login_requests_2_mins'),
- key: 'to-many-login-requests-2-mins',
- },
- })
- }
- })
- }
- }
- const RateLimiterMiddleware = {
- rateLimit,
- loginRateLimitEmail,
- }
- export default RateLimiterMiddleware
|