mirror of
https://github.com/samsonjs/rack-attack.git
synced 2026-04-26 14:57:47 +00:00
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:
commit
7435d4da34
4 changed files with 133 additions and 23 deletions
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue