Skip to content
Snippets Groups Projects
Unverified Commit a865b62e authored by Eugen Rochko's avatar Eugen Rochko Committed by GitHub
Browse files

Rate limit by user instead of IP when API user is authenticated (#5923)

* Fix #668 - Rate limit by user instead of IP when API user is authenticated

* Fix code style issue

* Use request decorator provided by Doorkeeper
parent 84cebad4
No related branches found
No related tags found
No related merge requests found
......@@ -44,7 +44,8 @@ module RateLimitHeaders
end
def api_throttle_data
request.env['rack.attack.throttle_data']['api']
request.env['rack.attack.throttle_data']['throttle_authenticated_api'] ||
request.env['rack.attack.throttle_data']['throttle_unauthenticated_api']
end
def request_time
......
# frozen_string_literal: true
class Rack::Attack
class Request
def authenticated_token
return @token if defined?(@token)
@token = Doorkeeper::OAuth::Token.authenticate(
Doorkeeper::Grape::AuthorizationDecorator.new(self),
*Doorkeeper.configuration.access_token_methods
)
end
def authenticated_user_id
authenticated_token&.resource_owner_id
end
def unauthenticated?
!authenticated_user_id
end
def api_request?
path.start_with?('/api')
end
def web_request?
!api_request?
end
end
PROTECTED_PATHS = %w(
/auth/sign_in
/auth
/auth/password
).freeze
PROTECTED_PATHS_REGEX = Regexp.union(PROTECTED_PATHS.map { |path| /\A#{Regexp.escape(path)}/ })
# Always allow requests from localhost
# (blocklist & throttles are skipped)
Rack::Attack.safelist('allow from localhost') do |req|
......@@ -8,24 +43,16 @@ class Rack::Attack
'127.0.0.1' == req.ip || '::1' == req.ip
end
# Rate limits for the API
throttle('api', limit: 300, period: 5.minutes) do |req|
req.ip if req.path =~ /\A\/api\/v/
end
# Rate limit logins
throttle('login', limit: 5, period: 5.minutes) do |req|
req.ip if req.path == '/auth/sign_in' && req.post?
throttle('throttle_authenticated_api', limit: 300, period: 5.minutes) do |req|
req.api_request? && req.authenticated_user_id
end
# Rate limit sign-ups
throttle('register', limit: 5, period: 5.minutes) do |req|
req.ip if req.path == '/auth' && req.post?
throttle('throttle_unauthenticated_api', limit: 300, period: 5.minutes) do |req|
req.ip if req.api_request? && req.unauthenticated?
end
# Rate limit forgotten passwords
throttle('reminder', limit: 5, period: 5.minutes) do |req|
req.ip if req.path == '/auth/password' && req.post?
throttle('protected_paths', limit: 5, period: 5.minutes) do |req|
req.ip if req.post? && req.path =~ PROTECTED_PATHS_REGEX
end
self.throttled_response = lambda do |env|
......
......@@ -34,7 +34,7 @@ describe ApplicationController do
let(:start_time) { DateTime.new(2017, 1, 1, 12, 0, 0).utc }
before do
request.env['rack.attack.throttle_data'] = { 'api' => { limit: 100, count: 20, period: 10 } }
request.env['rack.attack.throttle_data'] = { 'throttle_authenticated_api' => { limit: 100, count: 20, period: 10 } }
travel_to start_time do
get 'show'
end
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment