rack-attack 6.6.1 → 6.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +12 -8
  3. data/lib/rack/attack/base_proxy.rb +1 -0
  4. data/lib/rack/attack/cache.rb +10 -4
  5. data/lib/rack/attack/configuration.rb +7 -3
  6. data/lib/rack/attack/path_normalizer.rb +3 -1
  7. data/lib/rack/attack/railtie.rb +6 -0
  8. data/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb +16 -10
  9. data/lib/rack/attack/store_proxy/redis_proxy.rb +2 -1
  10. data/lib/rack/attack/throttle.rb +2 -1
  11. data/lib/rack/attack/version.rb +1 -1
  12. data/lib/rack/attack.rb +3 -1
  13. data/spec/acceptance/blocking_ip_spec.rb +13 -8
  14. data/spec/acceptance/blocking_spec.rb +16 -18
  15. data/spec/acceptance/blocking_subnet_spec.rb +7 -8
  16. data/spec/acceptance/cache_store_config_for_allow2ban_spec.rb +5 -3
  17. data/spec/acceptance/cache_store_config_for_fail2ban_spec.rb +7 -5
  18. data/spec/acceptance/cache_store_config_for_throttle_spec.rb +5 -3
  19. data/spec/acceptance/cache_store_config_with_rails_spec.rb +6 -4
  20. data/spec/acceptance/extending_request_object_spec.rb +3 -7
  21. data/spec/acceptance/fail2ban_spec.rb +42 -0
  22. data/spec/acceptance/rails_middleware_spec.rb +1 -1
  23. data/spec/acceptance/safelisting_ip_spec.rb +12 -4
  24. data/spec/acceptance/safelisting_spec.rb +14 -14
  25. data/spec/acceptance/safelisting_subnet_spec.rb +6 -4
  26. data/spec/acceptance/stores/active_support_mem_cache_store_pooled_spec.rb +5 -2
  27. data/spec/acceptance/stores/active_support_mem_cache_store_spec.rb +0 -1
  28. data/spec/acceptance/stores/active_support_memory_store_spec.rb +0 -2
  29. data/spec/acceptance/stores/active_support_redis_cache_store_pooled_spec.rb +5 -2
  30. data/spec/acceptance/stores/active_support_redis_cache_store_spec.rb +0 -1
  31. data/spec/acceptance/stores/connection_pool_dalli_client_spec.rb +0 -1
  32. data/spec/acceptance/stores/dalli_client_spec.rb +0 -1
  33. data/spec/acceptance/stores/redis_spec.rb +0 -1
  34. data/spec/acceptance/stores/redis_store_spec.rb +1 -3
  35. data/spec/acceptance/throttling_spec.rb +14 -23
  36. data/spec/acceptance/track_spec.rb +8 -9
  37. data/spec/acceptance/track_throttle_spec.rb +10 -16
  38. data/spec/configuration_spec.rb +33 -0
  39. data/spec/integration/offline_spec.rb +0 -12
  40. data/spec/rack_attack_instrumentation_spec.rb +25 -28
  41. data/spec/rack_attack_request_spec.rb +2 -4
  42. data/spec/rack_attack_reset_spec.rb +90 -0
  43. data/spec/rack_attack_spec.rb +4 -22
  44. data/spec/rack_attack_throttle_spec.rb +49 -28
  45. data/spec/rack_attack_track_spec.rb +4 -17
  46. data/spec/spec_helper.rb +7 -5
  47. data/spec/support/cache_store_helper.rb +31 -25
  48. data/spec/support/freeze_time_helper.rb +9 -0
  49. metadata +48 -28
  50. data/lib/rack/attack/store_proxy/active_support_redis_store_proxy.rb +0 -39
  51. data/spec/acceptance/stores/active_support_dalli_store_spec.rb +0 -25
  52. data/spec/acceptance/stores/active_support_redis_store_spec.rb +0 -20
@@ -3,6 +3,8 @@
3
3
  require_relative "../spec_helper"
4
4
 
5
5
  describe "#safelist" do
6
+ let(:notifications) { [] }
7
+
6
8
  before do
7
9
  Rack::Attack.blocklist do |request|
8
10
  request.ip == "1.2.3.4"
@@ -38,23 +40,23 @@ describe "#safelist" do
38
40
  end
39
41
 
40
42
  it "notifies when the request is safe" do
41
- notification_matched = nil
42
- notification_type = nil
43
-
44
43
  ActiveSupport::Notifications.subscribe("rack.attack") do |_name, _start, _finish, _id, payload|
45
- notification_matched = payload[:request].env["rack.attack.matched"]
46
- notification_type = payload[:request].env["rack.attack.match_type"]
44
+ notifications.push(payload)
47
45
  end
48
46
 
49
47
  get "/safe_space", {}, "REMOTE_ADDR" => "1.2.3.4"
50
48
 
51
49
  assert_equal 200, last_response.status
52
- assert_nil notification_matched
53
- assert_equal :safelist, notification_type
50
+ assert_equal 1, notifications.size
51
+ notification = notifications.pop
52
+ assert_nil notification[:request].env["rack.attack.matched"]
53
+ assert_equal :safelist, notification[:request].env["rack.attack.match_type"]
54
54
  end
55
55
  end
56
56
 
57
57
  describe "#safelist with name" do
58
+ let(:notifications) { [] }
59
+
58
60
  before do
59
61
  Rack::Attack.blocklist("block 1.2.3.4") do |request|
60
62
  request.ip == "1.2.3.4"
@@ -90,18 +92,16 @@ describe "#safelist with name" do
90
92
  end
91
93
 
92
94
  it "notifies when the request is safe" do
93
- notification_matched = nil
94
- notification_type = nil
95
-
96
95
  ActiveSupport::Notifications.subscribe("safelist.rack_attack") do |_name, _start, _finish, _id, payload|
97
- notification_matched = payload[:request].env["rack.attack.matched"]
98
- notification_type = payload[:request].env["rack.attack.match_type"]
96
+ notifications.push(payload)
99
97
  end
100
98
 
101
99
  get "/safe_space", {}, "REMOTE_ADDR" => "1.2.3.4"
102
100
 
103
101
  assert_equal 200, last_response.status
104
- assert_equal "safe path", notification_matched
105
- assert_equal :safelist, notification_type
102
+ assert_equal 1, notifications.size
103
+ notification = notifications.pop
104
+ assert_equal "safe path", notification[:request].env["rack.attack.matched"]
105
+ assert_equal :safelist, notification[:request].env["rack.attack.match_type"]
106
106
  end
107
107
  end
@@ -3,6 +3,8 @@
3
3
  require_relative "../spec_helper"
4
4
 
5
5
  describe "Safelisting an IP subnet" do
6
+ let(:notifications) { [] }
7
+
6
8
  before do
7
9
  Rack::Attack.blocklist("admin") do |request|
8
10
  request.path == "/admin"
@@ -36,15 +38,15 @@ describe "Safelisting an IP subnet" do
36
38
  end
37
39
 
38
40
  it "notifies when the request is safe" do
39
- notification_type = nil
40
-
41
41
  ActiveSupport::Notifications.subscribe("safelist.rack_attack") do |_name, _start, _finish, _id, payload|
42
- notification_type = payload[:request].env["rack.attack.match_type"]
42
+ notifications.push(payload)
43
43
  end
44
44
 
45
45
  get "/admin", {}, "REMOTE_ADDR" => "5.6.0.0"
46
46
 
47
47
  assert_equal 200, last_response.status
48
- assert_equal :safelist, notification_type
48
+ assert_equal 1, notifications.size
49
+ notification = notifications.pop
50
+ assert_equal :safelist, notification[:request].env["rack.attack.match_type"]
49
51
  end
50
52
  end
@@ -4,11 +4,14 @@ require_relative "../../spec_helper"
4
4
 
5
5
  if defined?(::ConnectionPool) && defined?(::Dalli)
6
6
  require_relative "../../support/cache_store_helper"
7
- require "timecop"
8
7
 
9
8
  describe "ActiveSupport::Cache::MemCacheStore (pooled) as a cache backend" do
10
9
  before do
11
- Rack::Attack.cache.store = ActiveSupport::Cache::MemCacheStore.new(pool_size: 2)
10
+ Rack::Attack.cache.store = if ActiveSupport.gem_version >= Gem::Version.new("7.2.0")
11
+ ActiveSupport::Cache::MemCacheStore.new(pool: true)
12
+ else
13
+ ActiveSupport::Cache::MemCacheStore.new(pool_size: 2)
14
+ end
12
15
  end
13
16
 
14
17
  after do
@@ -4,7 +4,6 @@ require_relative "../../spec_helper"
4
4
 
5
5
  if defined?(::Dalli)
6
6
  require_relative "../../support/cache_store_helper"
7
- require "timecop"
8
7
 
9
8
  describe "ActiveSupport::Cache::MemCacheStore as a cache backend" do
10
9
  before do
@@ -3,8 +3,6 @@
3
3
  require_relative "../../spec_helper"
4
4
  require_relative "../../support/cache_store_helper"
5
5
 
6
- require "timecop"
7
-
8
6
  describe "ActiveSupport::Cache::MemoryStore as a cache backend" do
9
7
  before do
10
8
  Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new
@@ -10,11 +10,14 @@ should_run =
10
10
 
11
11
  if should_run
12
12
  require_relative "../../support/cache_store_helper"
13
- require "timecop"
14
13
 
15
14
  describe "ActiveSupport::Cache::RedisCacheStore (pooled) as a cache backend" do
16
15
  before do
17
- Rack::Attack.cache.store = ActiveSupport::Cache::RedisCacheStore.new(pool_size: 2)
16
+ Rack::Attack.cache.store = if ActiveSupport.gem_version >= Gem::Version.new("7.2.0")
17
+ ActiveSupport::Cache::RedisCacheStore.new(pool: true)
18
+ else
19
+ ActiveSupport::Cache::RedisCacheStore.new(pool_size: 2)
20
+ end
18
21
  end
19
22
 
20
23
  after do
@@ -9,7 +9,6 @@ should_run =
9
9
 
10
10
  if should_run
11
11
  require_relative "../../support/cache_store_helper"
12
- require "timecop"
13
12
 
14
13
  describe "ActiveSupport::Cache::RedisCacheStore as a cache backend" do
15
14
  before do
@@ -6,7 +6,6 @@ if defined?(::Dalli) && defined?(::ConnectionPool)
6
6
  require_relative "../../support/cache_store_helper"
7
7
  require "connection_pool"
8
8
  require "dalli"
9
- require "timecop"
10
9
 
11
10
  describe "ConnectionPool with Dalli::Client as a cache backend" do
12
11
  before do
@@ -5,7 +5,6 @@ require_relative "../../spec_helper"
5
5
  if defined?(::Dalli)
6
6
  require_relative "../../support/cache_store_helper"
7
7
  require "dalli"
8
- require "timecop"
9
8
 
10
9
  describe "Dalli::Client as a cache backend" do
11
10
  before do
@@ -4,7 +4,6 @@ require_relative "../../spec_helper"
4
4
 
5
5
  if defined?(::Redis)
6
6
  require_relative "../../support/cache_store_helper"
7
- require "timecop"
8
7
 
9
8
  describe "Plain redis as a cache backend" do
10
9
  before do
@@ -4,9 +4,7 @@ require_relative "../../spec_helper"
4
4
  require_relative "../../support/cache_store_helper"
5
5
 
6
6
  if defined?(::Redis::Store)
7
- require "timecop"
8
-
9
- describe "ActiveSupport::Cache::RedisStore as a cache backend" do
7
+ describe "Redis::Store as a cache backend" do
10
8
  before do
11
9
  Rack::Attack.cache.store = ::Redis::Store.new
12
10
  end
@@ -4,6 +4,8 @@ require_relative "../spec_helper"
4
4
  require "timecop"
5
5
 
6
6
  describe "#throttle" do
7
+ let(:notifications) { [] }
8
+
7
9
  before do
8
10
  Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new
9
11
  end
@@ -138,42 +140,31 @@ describe "#throttle" do
138
140
  request.ip
139
141
  end
140
142
 
141
- notification_matched = nil
142
- notification_type = nil
143
- notification_data = nil
144
- notification_discriminator = nil
145
-
146
143
  ActiveSupport::Notifications.subscribe("throttle.rack_attack") do |_name, _start, _finish, _id, payload|
147
- notification_matched = payload[:request].env["rack.attack.matched"]
148
- notification_type = payload[:request].env["rack.attack.match_type"]
149
- notification_data = payload[:request].env['rack.attack.match_data']
150
- notification_discriminator = payload[:request].env['rack.attack.match_discriminator']
144
+ notifications.push(payload)
151
145
  end
152
146
 
153
147
  get "/", {}, "REMOTE_ADDR" => "5.6.7.8"
154
148
 
155
149
  assert_equal 200, last_response.status
156
- assert_nil notification_matched
157
- assert_nil notification_type
158
- assert_nil notification_data
159
- assert_nil notification_discriminator
150
+ assert notifications.empty?
160
151
 
161
152
  get "/", {}, "REMOTE_ADDR" => "1.2.3.4"
162
153
 
163
154
  assert_equal 200, last_response.status
164
- assert_nil notification_matched
165
- assert_nil notification_type
166
- assert_nil notification_data
167
- assert_nil notification_discriminator
155
+ assert notifications.empty?
168
156
 
169
157
  get "/", {}, "REMOTE_ADDR" => "1.2.3.4"
170
158
 
171
159
  assert_equal 429, last_response.status
172
- assert_equal "by ip", notification_matched
173
- assert_equal :throttle, notification_type
174
- assert_equal 60, notification_data[:period]
175
- assert_equal 1, notification_data[:limit]
176
- assert_equal 2, notification_data[:count]
177
- assert_equal "1.2.3.4", notification_discriminator
160
+
161
+ assert_equal 1, notifications.size
162
+ notification = notifications.pop
163
+ assert_equal "by ip", notification[:request].env["rack.attack.matched"]
164
+ assert_equal :throttle, notification[:request].env["rack.attack.match_type"]
165
+ assert_equal 60, notification[:request].env["rack.attack.match_data"][:period]
166
+ assert_equal 1, notification[:request].env["rack.attack.match_data"][:limit]
167
+ assert_equal 2, notification[:request].env["rack.attack.match_data"][:count]
168
+ assert_equal "1.2.3.4", notification[:request].env["rack.attack.match_discriminator"]
178
169
  end
179
170
  end
@@ -3,27 +3,26 @@
3
3
  require_relative "../spec_helper"
4
4
 
5
5
  describe "#track" do
6
+ let(:notifications) { [] }
7
+
6
8
  it "notifies when track block returns true" do
7
9
  Rack::Attack.track("ip 1.2.3.4") do |request|
8
10
  request.ip == "1.2.3.4"
9
11
  end
10
12
 
11
- notification_matched = nil
12
- notification_type = nil
13
-
14
13
  ActiveSupport::Notifications.subscribe("track.rack_attack") do |_name, _start, _finish, _id, payload|
15
- notification_matched = payload[:request].env["rack.attack.matched"]
16
- notification_type = payload[:request].env["rack.attack.match_type"]
14
+ notifications.push(payload)
17
15
  end
18
16
 
19
17
  get "/", {}, "REMOTE_ADDR" => "5.6.7.8"
20
18
 
21
- assert_nil notification_matched
22
- assert_nil notification_type
19
+ assert notifications.empty?
23
20
 
24
21
  get "/", {}, "REMOTE_ADDR" => "1.2.3.4"
25
22
 
26
- assert_equal "ip 1.2.3.4", notification_matched
27
- assert_equal :track, notification_type
23
+ assert_equal 1, notifications.size
24
+ notification = notifications.pop
25
+ assert_equal "ip 1.2.3.4", notification[:request].env["rack.attack.matched"]
26
+ assert_equal :track, notification[:request].env["rack.attack.match_type"]
28
27
  end
29
28
  end
@@ -4,6 +4,8 @@ require_relative "../spec_helper"
4
4
  require "timecop"
5
5
 
6
6
  describe "#track with throttle-ish options" do
7
+ let(:notifications) { [] }
8
+
7
9
  it "notifies when throttle goes over the limit without actually throttling requests" do
8
10
  Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new
9
11
 
@@ -11,43 +13,35 @@ describe "#track with throttle-ish options" do
11
13
  request.ip
12
14
  end
13
15
 
14
- notification_matched = nil
15
- notification_type = nil
16
-
17
16
  ActiveSupport::Notifications.subscribe("track.rack_attack") do |_name, _start, _finish, _id, payload|
18
- notification_matched = payload[:request].env["rack.attack.matched"]
19
- notification_type = payload[:request].env["rack.attack.match_type"]
17
+ notifications.push(payload)
20
18
  end
21
19
 
22
20
  get "/", {}, "REMOTE_ADDR" => "1.2.3.4"
23
21
 
24
- assert_nil notification_matched
25
- assert_nil notification_type
22
+ assert notifications.empty?
26
23
 
27
24
  assert_equal 200, last_response.status
28
25
 
29
26
  get "/", {}, "REMOTE_ADDR" => "5.6.7.8"
30
27
 
31
- assert_nil notification_matched
32
- assert_nil notification_type
28
+ assert notifications.empty?
33
29
 
34
30
  assert_equal 200, last_response.status
35
31
 
36
32
  get "/", {}, "REMOTE_ADDR" => "1.2.3.4"
37
33
 
38
- assert_equal "by ip", notification_matched
39
- assert_equal :track, notification_type
34
+ assert_equal 1, notifications.size
35
+ notification = notifications.pop
36
+ assert_equal "by ip", notification[:request].env["rack.attack.matched"]
37
+ assert_equal :track, notification[:request].env["rack.attack.match_type"]
40
38
 
41
39
  assert_equal 200, last_response.status
42
40
 
43
41
  Timecop.travel(60) do
44
- notification_matched = nil
45
- notification_type = nil
46
-
47
42
  get "/", {}, "REMOTE_ADDR" => "1.2.3.4"
48
43
 
49
- assert_nil notification_matched
50
- assert_nil notification_type
44
+ assert notifications.empty?
51
45
 
52
46
  assert_equal 200, last_response.status
53
47
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "spec_helper"
4
+
5
+ describe Rack::Attack::Configuration do
6
+ subject { Rack::Attack::Configuration.new }
7
+
8
+ describe 'attributes' do
9
+ it 'exposes the safelists attribute' do
10
+ _(subject.safelists).must_equal({})
11
+ end
12
+
13
+ it 'exposes the blocklists attribute' do
14
+ _(subject.blocklists).must_equal({})
15
+ end
16
+
17
+ it 'exposes the throttles attribute' do
18
+ _(subject.throttles).must_equal({})
19
+ end
20
+
21
+ it 'exposes the tracks attribute' do
22
+ _(subject.tracks).must_equal({})
23
+ end
24
+
25
+ it 'exposes the anonymous_blocklists attribute' do
26
+ _(subject.anonymous_blocklists).must_equal([])
27
+ end
28
+
29
+ it 'exposes the anonymous_safelists attribute' do
30
+ _(subject.anonymous_safelists).must_equal([])
31
+ end
32
+ end
33
+ end
@@ -21,18 +21,6 @@ OfflineExamples = Minitest::SharedExamples.new do
21
21
  end
22
22
  end
23
23
 
24
- if defined?(::ActiveSupport::Cache::RedisStore)
25
- describe 'when Redis is offline' do
26
- include OfflineExamples
27
-
28
- before do
29
- @cache = Rack::Attack::Cache.new
30
- # Use presumably unused port for Redis client
31
- @cache.store = ActiveSupport::Cache::RedisStore.new(host: '127.0.0.1', port: 3333)
32
- end
33
- end
34
- end
35
-
36
24
  if defined?(Redis) && defined?(ActiveSupport::Cache::RedisCacheStore) && Redis::VERSION >= '4'
37
25
  describe 'when Redis is offline' do
38
26
  include OfflineExamples
@@ -1,42 +1,39 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "spec_helper"
4
+ require 'active_support'
5
+ require 'active_support/subscriber'
4
6
 
5
- # ActiveSupport::Subscribers added in ~> 4.0.2.0
6
- if ActiveSupport::VERSION::MAJOR > 3
7
- require_relative 'spec_helper'
8
- require 'active_support/subscriber'
9
- class CustomSubscriber < ActiveSupport::Subscriber
10
- @notification_count = 0
7
+ class CustomSubscriber < ActiveSupport::Subscriber
8
+ @notification_count = 0
11
9
 
12
- class << self
13
- attr_accessor :notification_count
14
- end
10
+ class << self
11
+ attr_accessor :notification_count
12
+ end
15
13
 
16
- def throttle(_event)
17
- self.class.notification_count += 1
18
- end
14
+ def throttle(_event)
15
+ self.class.notification_count += 1
19
16
  end
17
+ end
20
18
 
21
- describe 'Rack::Attack.instrument' do
22
- before do
23
- @period = 60 # Use a long period; failures due to cache key rotation less likely
24
- Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new
25
- Rack::Attack.throttle('ip/sec', limit: 1, period: @period) { |req| req.ip }
26
- end
19
+ describe 'Rack::Attack.instrument' do
20
+ before do
21
+ @period = 60 # Use a long period; failures due to cache key rotation less likely
22
+ Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new
23
+ Rack::Attack.throttle('ip/sec', limit: 1, period: @period) { |req| req.ip }
24
+ end
27
25
 
28
- describe "with throttling" do
29
- before do
30
- ActiveSupport::Notifications.stub(:notifier, ActiveSupport::Notifications::Fanout.new) do
31
- CustomSubscriber.attach_to("rack_attack")
32
- 2.times { get '/', {}, 'REMOTE_ADDR' => '1.2.3.4' }
33
- end
26
+ describe "with throttling" do
27
+ before do
28
+ ActiveSupport::Notifications.stub(:notifier, ActiveSupport::Notifications::Fanout.new) do
29
+ CustomSubscriber.attach_to("rack_attack")
30
+ 2.times { get '/', {}, 'REMOTE_ADDR' => '1.2.3.4' }
34
31
  end
32
+ end
35
33
 
36
- it 'should instrument without error' do
37
- _(last_response.status).must_equal 429
38
- assert_equal 1, CustomSubscriber.notification_count
39
- end
34
+ it 'should instrument without error' do
35
+ _(last_response.status).must_equal 429
36
+ assert_equal 1, CustomSubscriber.notification_count
40
37
  end
41
38
  end
42
39
  end
@@ -5,10 +5,8 @@ require_relative 'spec_helper'
5
5
  describe 'Rack::Attack' do
6
6
  describe 'helpers' do
7
7
  before do
8
- class Rack::Attack::Request
9
- def remote_ip
10
- ip
11
- end
8
+ Rack::Attack::Request.define_method :remote_ip do
9
+ ip
12
10
  end
13
11
 
14
12
  Rack::Attack.safelist('valid IP') do |req|
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "spec_helper"
4
+
5
+ describe "Rack::Attack.reset!" do
6
+ it "raises an error when is not supported by cache store" do
7
+ Rack::Attack.cache.store = Class.new
8
+ assert_raises(Rack::Attack::IncompatibleStoreError) do
9
+ Rack::Attack.reset!
10
+ end
11
+ end
12
+
13
+ if defined?(Redis)
14
+ it "should delete rack attack keys" do
15
+ redis = Redis.new
16
+ redis.set("key", "value")
17
+ redis.set("#{Rack::Attack.cache.prefix}::key", "value")
18
+ Rack::Attack.cache.store = redis
19
+ Rack::Attack.reset!
20
+
21
+ _(redis.get("key")).must_equal "value"
22
+ _(redis.get("#{Rack::Attack.cache.prefix}::key")).must_be_nil
23
+ end
24
+ end
25
+
26
+ if defined?(Redis::Store)
27
+ it "should delete rack attack keys" do
28
+ redis_store = Redis::Store.new
29
+ redis_store.set("key", "value")
30
+ redis_store.set("#{Rack::Attack.cache.prefix}::key", "value")
31
+ Rack::Attack.cache.store = redis_store
32
+ Rack::Attack.reset!
33
+
34
+ _(redis_store.get("key")).must_equal "value"
35
+ _(redis_store.get("#{Rack::Attack.cache.prefix}::key")).must_be_nil
36
+ end
37
+ end
38
+
39
+ if defined?(Redis) && defined?(ActiveSupport::Cache::RedisCacheStore)
40
+ it "should delete rack attack keys" do
41
+ redis_cache_store = ActiveSupport::Cache::RedisCacheStore.new
42
+ redis_cache_store.write("key", "value")
43
+ redis_cache_store.write("#{Rack::Attack.cache.prefix}::key", "value")
44
+ Rack::Attack.cache.store = redis_cache_store
45
+ Rack::Attack.reset!
46
+
47
+ _(redis_cache_store.read("key")).must_equal "value"
48
+ _(redis_cache_store.read("#{Rack::Attack.cache.prefix}::key")).must_be_nil
49
+ end
50
+
51
+ describe "with a namespaced cache" do
52
+ it "should delete rack attack keys" do
53
+ redis_cache_store = ActiveSupport::Cache::RedisCacheStore.new(namespace: "ns")
54
+ redis_cache_store.write("key", "value")
55
+ redis_cache_store.write("#{Rack::Attack.cache.prefix}::key", "value")
56
+ Rack::Attack.cache.store = redis_cache_store
57
+ Rack::Attack.reset!
58
+
59
+ _(redis_cache_store.read("key")).must_equal "value"
60
+ _(redis_cache_store.read("#{Rack::Attack.cache.prefix}::key")).must_be_nil
61
+ end
62
+ end
63
+ end
64
+
65
+ if defined?(ActiveSupport::Cache::MemoryStore)
66
+ it "should delete rack attack keys" do
67
+ memory_store = ActiveSupport::Cache::MemoryStore.new
68
+ memory_store.write("key", "value")
69
+ memory_store.write("#{Rack::Attack.cache.prefix}::key", "value")
70
+ Rack::Attack.cache.store = memory_store
71
+ Rack::Attack.reset!
72
+
73
+ _(memory_store.read("key")).must_equal "value"
74
+ _(memory_store.read("#{Rack::Attack.cache.prefix}::key")).must_be_nil
75
+ end
76
+
77
+ describe "with a namespaced cache" do
78
+ it "should delete rack attack keys" do
79
+ memory_store = ActiveSupport::Cache::MemoryStore.new(namespace: "ns")
80
+ memory_store.write("key", "value")
81
+ memory_store.write("#{Rack::Attack.cache.prefix}::key", "value")
82
+ Rack::Attack.cache.store = memory_store
83
+ Rack::Attack.reset!
84
+
85
+ _(memory_store.read("key")).must_equal "value"
86
+ _(memory_store.read("#{Rack::Attack.cache.prefix}::key")).must_be_nil
87
+ end
88
+ end
89
+ end
90
+ end
@@ -11,6 +11,10 @@ describe 'Rack::Attack' do
11
11
  end
12
12
 
13
13
  it 'blocks requests with trailing slash' do
14
+ if Rack::Attack::PathNormalizer == Rack::Attack::FallbackPathNormalizer
15
+ skip "Normalization is only present on Rails"
16
+ end
17
+
14
18
  get '/foo/'
15
19
  _(last_response.status).must_equal 403
16
20
  end
@@ -99,26 +103,4 @@ describe 'Rack::Attack' do
99
103
  end
100
104
  end
101
105
  end
102
-
103
- describe 'reset!' do
104
- it 'raises an error when is not supported by cache store' do
105
- Rack::Attack.cache.store = Class.new
106
- assert_raises(Rack::Attack::IncompatibleStoreError) do
107
- Rack::Attack.reset!
108
- end
109
- end
110
-
111
- if defined?(Redis)
112
- it 'should delete rack attack keys' do
113
- redis = Redis.new
114
- redis.set('key', 'value')
115
- redis.set("#{Rack::Attack.cache.prefix}::key", 'value')
116
- Rack::Attack.cache.store = redis
117
- Rack::Attack.reset!
118
-
119
- _(redis.get('key')).must_equal 'value'
120
- _(redis.get("#{Rack::Attack.cache.prefix}::key")).must_be_nil
121
- end
122
- end
123
- end
124
106
  end