rack-attack/lib/rack/attack.rb
2019-10-23 01:33:49 +03:00

117 lines
3.6 KiB
Ruby

# frozen_string_literal: true
require 'rack'
require 'forwardable'
require 'rack/attack/cache'
require 'rack/attack/configuration'
require 'rack/attack/path_normalizer'
require 'rack/attack/request'
require 'rack/attack/railtie' if defined?(::Rails)
module Rack
class Attack
class Error < StandardError; end
class MisconfiguredStoreError < Error; end
class MissingStoreError < Error; end
class IncompatibleStoreError < Error; end
autoload :Check, 'rack/attack/check'
autoload :Throttle, 'rack/attack/throttle'
autoload :Safelist, 'rack/attack/safelist'
autoload :Blocklist, 'rack/attack/blocklist'
autoload :Track, 'rack/attack/track'
autoload :StoreProxy, 'rack/attack/store_proxy'
autoload :DalliProxy, 'rack/attack/store_proxy/dalli_proxy'
autoload :MemCacheStoreProxy, 'rack/attack/store_proxy/mem_cache_store_proxy'
autoload :RedisProxy, 'rack/attack/store_proxy/redis_proxy'
autoload :RedisStoreProxy, 'rack/attack/store_proxy/redis_store_proxy'
autoload :RedisCacheStoreProxy, 'rack/attack/store_proxy/redis_cache_store_proxy'
autoload :ActiveSupportRedisStoreProxy, 'rack/attack/store_proxy/active_support_redis_store_proxy'
autoload :Fail2Ban, 'rack/attack/fail2ban'
autoload :Allow2Ban, 'rack/attack/allow2ban'
class << self
attr_accessor :enabled, :notifier, :discriminator_normalizer
attr_reader :configuration
def instrument(request)
if notifier
event_type = request.env["rack.attack.match_type"]
notifier.instrument("#{event_type}.rack_attack", request: request)
# Deprecated: Keeping just for backwards compatibility
notifier.instrument("rack.attack", request: request)
end
end
def cache
@cache ||= Cache.new
end
def clear!
warn "[DEPRECATION] Rack::Attack.clear! is deprecated. Please use Rack::Attack.clear_configuration instead"
@configuration.clear_configuration
end
def reset!
cache.reset!
end
extend Forwardable
def_delegators(
:@configuration,
:safelist,
:blocklist,
:blocklist_ip,
:safelist_ip,
:throttle,
:track,
:blocklisted_response,
:blocklisted_response=,
:throttled_response,
:throttled_response=,
:throttled_response_retry_after_header,
:throttled_response_retry_after_header=,
:clear_configuration,
:safelists,
:blocklists,
:throttles,
:tracks
)
end
# Set defaults
@enabled = true
@notifier = ActiveSupport::Notifications if defined?(ActiveSupport::Notifications)
@discriminator_normalizer = lambda do |discriminator|
discriminator.to_s.strip.downcase
end
@configuration = Configuration.new
attr_reader :configuration
def initialize(app)
@app = app
@configuration = self.class.configuration
end
def call(env)
return @app.call(env) unless self.class.enabled
env['PATH_INFO'] = PathNormalizer.normalize_path(env['PATH_INFO'])
request = Rack::Attack::Request.new(env)
if configuration.safelisted?(request)
@app.call(env)
elsif configuration.blocklisted?(request)
configuration.blocklisted_response.call(env)
elsif configuration.throttled?(request)
configuration.throttled_response.call(env)
else
configuration.tracked?(request)
@app.call(env)
end
end
end
end