fix: throttle reports api endpoint (#10620)
- Throttle reports API requests at the account level - Throttle reports API requests at the user level for dashboard users as well as API users Co-authored-by: Sojan Jose <sojan@pepalo.com>
This commit is contained in:
@@ -154,12 +154,53 @@ class Rack::Attack
|
|||||||
match_data[:account_id] if match_data.present?
|
match_data[:account_id] if match_data.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Throttle by individual user (based on uid)
|
||||||
|
throttle('/api/v2/accounts/:account_id/reports/user', limit: ENV.fetch('RATE_LIMIT_REPORTS_API_USER_LEVEL', '100').to_i, period: 1.minute) do |req|
|
||||||
|
match_data = %r{/api/v2/accounts/(?<account_id>\d+)/reports}.match(req.path)
|
||||||
|
# Extract user identification (uid for web, api_access_token for API requests)
|
||||||
|
user_uid = req.get_header('HTTP_UID')
|
||||||
|
api_access_token = req.get_header('HTTP_API_ACCESS_TOKEN') || req.get_header('api_access_token')
|
||||||
|
|
||||||
|
# Use uid if present, otherwise fallback to api_access_token for tracking
|
||||||
|
user_identifier = user_uid.presence || api_access_token.presence
|
||||||
|
|
||||||
|
"#{user_identifier}:#{match_data[:account_id]}" if match_data.present? && user_identifier.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
## Prevent abuse of reports api at account level
|
||||||
|
throttle('/api/v2/accounts/:account_id/reports', limit: ENV.fetch('RATE_LIMIT_REPORTS_API_ACCOUNT_LEVEL', '1000').to_i, period: 1.minute) do |req|
|
||||||
|
match_data = %r{/api/v2/accounts/(?<account_id>\d+)/reports}.match(req.path)
|
||||||
|
match_data[:account_id] if match_data.present?
|
||||||
|
end
|
||||||
|
|
||||||
## ----------------------------------------------- ##
|
## ----------------------------------------------- ##
|
||||||
end
|
end
|
||||||
|
|
||||||
# Log blocked events
|
# Log blocked events
|
||||||
ActiveSupport::Notifications.subscribe('throttle.rack_attack') do |_name, _start, _finish, _request_id, payload|
|
ActiveSupport::Notifications.subscribe('throttle.rack_attack') do |_name, _start, _finish, _request_id, payload|
|
||||||
Rails.logger.warn "[Rack::Attack][Blocked] remote_ip: \"#{payload[:request].remote_ip}\", path: \"#{payload[:request].path}\""
|
req = payload[:request]
|
||||||
|
|
||||||
|
user_uid = req.get_header('HTTP_UID')
|
||||||
|
api_access_token = req.get_header('HTTP_API_ACCESS_TOKEN') || req.get_header('api_access_token')
|
||||||
|
|
||||||
|
# Mask the token if present
|
||||||
|
masked_api_token = api_access_token.present? ? "#{api_access_token[0..4]}...[REDACTED]" : nil
|
||||||
|
|
||||||
|
# Use uid if present, otherwise fallback to masked api_access_token for tracking
|
||||||
|
user_identifier = user_uid.presence || masked_api_token.presence || 'unknown_user'
|
||||||
|
|
||||||
|
# Extract account ID if present
|
||||||
|
account_match = %r{/accounts/(?<account_id>\d+)}.match(req.path)
|
||||||
|
account_id = account_match ? account_match[:account_id] : 'unknown_account'
|
||||||
|
|
||||||
|
Rails.logger.warn(
|
||||||
|
"[Rack::Attack][Blocked] remote_ip: \"#{req.remote_ip}\", " \
|
||||||
|
"path: \"#{req.path}\", " \
|
||||||
|
"user_identifier: \"#{user_identifier}\", " \
|
||||||
|
"account_id: \"#{account_id}\", " \
|
||||||
|
"method: \"#{req.request_method}\", " \
|
||||||
|
"user_agent: \"#{req.user_agent}\""
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
Rack::Attack.enabled = Rails.env.production? ? ActiveModel::Type::Boolean.new.cast(ENV.fetch('ENABLE_RACK_ATTACK', true)) : false
|
Rack::Attack.enabled = Rails.env.production? ? ActiveModel::Type::Boolean.new.cast(ENV.fetch('ENABLE_RACK_ATTACK', true)) : false
|
||||||
|
|||||||
Reference in New Issue
Block a user