Merge pull request #315 from grzuy/help_debug_cache_issues

Give clearer error message for misconfigured cache store for allow/fail2ban
This commit is contained in:
Gonzalo Rodriguez 2018-03-23 16:35:16 -03:00 committed by GitHub
commit 7435d4da34
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 133 additions and 23 deletions

View file

@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file.
## [Unreleased] ## [Unreleased]
### Added
- Throw helpful error message when using `allow2ban` but cache store is misconfigured ([#315](https://github.com/kickstarter/rack-attack/issues/315))
- Throw helpful error message when using `fail2ban` but cache store is misconfigured ([#315](https://github.com/kickstarter/rack-attack/issues/315))
## [5.1.0] - 2018-03-10 ## [5.1.0] - 2018-03-10

View file

@ -20,6 +20,9 @@ module Rack
end end
def read(unprefixed_key) def read(unprefixed_key)
enforce_store_presence!
enforce_store_method_presence!(:read)
store.read("#{prefix}:#{unprefixed_key}") store.read("#{prefix}:#{unprefixed_key}")
end end
@ -46,18 +49,29 @@ module Rack
end end
def do_count(key, expires_in) def do_count(key, expires_in)
enforce_store_presence!
enforce_store_method_presence!(:increment)
result = store.increment(key, 1, :expires_in => expires_in)
# NB: Some stores return nil when incrementing uninitialized values
if result.nil?
enforce_store_method_presence!(:write)
store.write(key, 1, :expires_in => expires_in)
end
result || 1
end
def enforce_store_presence!
if store.nil? if store.nil?
raise Rack::Attack::MissingStoreError raise Rack::Attack::MissingStoreError
elsif !store.respond_to?(:increment) end
raise Rack::Attack::MisconfiguredStoreError, "Store needs to respond to #increment" end
else
result = store.increment(key, 1, :expires_in => expires_in)
# NB: Some stores return nil when incrementing uninitialized values def enforce_store_method_presence!(method_name)
if result.nil? if !store.respond_to?(method_name)
store.write(key, 1, :expires_in => expires_in) raise Rack::Attack::MisconfiguredStoreError, "Store needs to respond to ##{method_name}"
end
result || 1
end end
end end
end end

View file

@ -9,18 +9,64 @@ describe "Cache store config when using allow2ban" do
end end
end end
it "gives error if no store was configured" do it "gives semantic error if no store was configured" do
assert_raises do assert_raises(Rack::Attack::MissingStoreError) do
get "/" get "/scarce-resource"
end end
end end
it "gives error if incompatible store was configured" do it "gives semantic error if store is missing #read method" do
Rack::Attack.cache.store = Object.new basic_store_class = Class.new do
def write(key, value)
end
assert_raises do def increment(key, count, options = {})
get "/" end
end end
Rack::Attack.cache.store = basic_store_class.new
raised_exception = assert_raises(Rack::Attack::MisconfiguredStoreError) do
get "/scarce-resource"
end
assert_equal "Store needs to respond to #read", raised_exception.message
end
it "gives semantic error if store is missing #write method" do
basic_store_class = Class.new do
def read(key)
end
def increment(key, count, options = {})
end
end
Rack::Attack.cache.store = basic_store_class.new
raised_exception = assert_raises(Rack::Attack::MisconfiguredStoreError) do
get "/scarce-resource"
end
assert_equal "Store needs to respond to #write", raised_exception.message
end
it "gives semantic error if store is missing #increment method" do
basic_store_class = Class.new do
def read(key)
end
def write(key, value)
end
end
Rack::Attack.cache.store = basic_store_class.new
raised_exception = assert_raises(Rack::Attack::MisconfiguredStoreError) do
get "/scarce-resource"
end
assert_equal "Store needs to respond to #increment", raised_exception.message
end end
it "works with any object that responds to #read, #write and #increment" do it "works with any object that responds to #read, #write and #increment" do

View file

@ -9,18 +9,64 @@ describe "Cache store config when using fail2ban" do
end end
end end
it "gives error if no store was configured" do it "gives semantic error if no store was configured" do
assert_raises do assert_raises(Rack::Attack::MissingStoreError) do
get "/" get "/private-place"
end end
end end
it "gives error if incompatible store was configured" do it "gives semantic error if store is missing #read method" do
Rack::Attack.cache.store = Object.new basic_store_class = Class.new do
def write(key, value)
end
assert_raises do def increment(key, count, options = {})
get "/" end
end end
Rack::Attack.cache.store = basic_store_class.new
raised_exception = assert_raises(Rack::Attack::MisconfiguredStoreError) do
get "/private-place"
end
assert_equal "Store needs to respond to #read", raised_exception.message
end
it "gives semantic error if store is missing #write method" do
basic_store_class = Class.new do
def read(key)
end
def increment(key, count, options = {})
end
end
Rack::Attack.cache.store = basic_store_class.new
raised_exception = assert_raises(Rack::Attack::MisconfiguredStoreError) do
get "/private-place"
end
assert_equal "Store needs to respond to #write", raised_exception.message
end
it "gives semantic error if store is missing #increment method" do
basic_store_class = Class.new do
def read(key)
end
def write(key, value)
end
end
Rack::Attack.cache.store = basic_store_class.new
raised_exception = assert_raises(Rack::Attack::MisconfiguredStoreError) do
get "/private-place"
end
assert_equal "Store needs to respond to #increment", raised_exception.message
end end
it "works with any object that responds to #read, #write and #increment" do it "works with any object that responds to #read, #write and #increment" do