78 lines
2.5 KiB
Ruby
78 lines
2.5 KiB
Ruby
Rack::Attack.throttled_response_retry_after_header = true
|
|
|
|
module Rack
|
|
class Attack
|
|
ADMIN_API_CACHE_KEY = "rack_attack_admin_api_keys".freeze
|
|
ADMIN_ROLES = %w[admin super_admin tech_admin].freeze
|
|
|
|
# Method that checks API Key from the request and returns true if it
|
|
# belongs to an admin, false otherwise
|
|
def self.admin_api_key?(request)
|
|
api_key = request.env["HTTP_API_KEY"]
|
|
return false if api_key.nil?
|
|
|
|
# Admin API Secrets are cached to avoid making DB queries on each request
|
|
admin_keys = Rails.cache.fetch(ADMIN_API_CACHE_KEY, expires_in: 24.hours) do
|
|
ApiSecret.joins(user: :roles)
|
|
.where(roles: { name: ADMIN_ROLES })
|
|
.group("api_secrets.id")
|
|
.pluck(:secret)
|
|
end
|
|
|
|
admin_keys.include?(api_key)
|
|
end
|
|
|
|
class Request < ::Rack::Request
|
|
def track_and_return_ip
|
|
if ApplicationConfig["FASTLY_API_KEY"].present?
|
|
Honeycomb.add_field("fastly_client_ip", env["HTTP_FASTLY_CLIENT_IP"])
|
|
env["HTTP_FASTLY_CLIENT_IP"]
|
|
else
|
|
ActionDispatch::Request.new(env).remote_ip
|
|
end
|
|
end
|
|
end
|
|
|
|
throttle("search_throttle", limit: 5, period: 1) do |request|
|
|
if request.path.starts_with?("/search/")
|
|
request.track_and_return_ip
|
|
end
|
|
end
|
|
|
|
throttle("api_throttle", limit: 3, period: 1) do |request|
|
|
api_endpoint = request.path.starts_with?("/api/")
|
|
if api_endpoint && request.get? && !admin_api_key?(request)
|
|
request.track_and_return_ip
|
|
end
|
|
end
|
|
|
|
throttle("forgot_password_throttle", limit: 3, period: 1) do |request|
|
|
if request.path.starts_with?("/users/password") && request.post?
|
|
request.track_and_return_ip
|
|
end
|
|
end
|
|
|
|
throttle("api_write_throttle", limit: 1, period: 1) do |request|
|
|
api_endpoint = request.path.starts_with?("/api/")
|
|
if api_endpoint && (request.put? || request.post? || request.delete?)
|
|
Honeycomb.add_field("user_api_key", request.env["HTTP_API_KEY"])
|
|
unless admin_api_key?(request)
|
|
ip_address = request.track_and_return_ip
|
|
if request.env["HTTP_API_KEY"].present?
|
|
"#{ip_address}-#{request.env['HTTP_API_KEY']}"
|
|
elsif ip_address.present?
|
|
ip_address
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
throttle("site_hits", limit: 40, period: 2, &:track_and_return_ip)
|
|
|
|
throttle("tag_throttle", limit: 2, period: 1) do |request|
|
|
if request.path.include?("/t/")
|
|
request.track_and_return_ip
|
|
end
|
|
end
|
|
end
|
|
end
|