Skip to content

Commit

Permalink
Fix reset! when using namespaced cache store (#673)
Browse files Browse the repository at this point in the history
  • Loading branch information
santib authored Dec 20, 2024
1 parent 8d5b210 commit 86650c4
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 24 deletions.
2 changes: 1 addition & 1 deletion lib/rack/attack/cache.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def delete(unprefixed_key)

def reset!
if store.respond_to?(:delete_matched)
store.delete_matched("#{prefix}*")
store.delete_matched(/#{prefix}*/)
else
raise(
Rack::Attack::IncompatibleStoreError,
Expand Down
4 changes: 4 additions & 0 deletions lib/rack/attack/store_proxy/redis_cache_store_proxy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ def read(name, options = {})
def write(name, value, options = {})
super(name, value, options.merge!(raw: true))
end

def delete_matched(matcher, options = nil)
super(matcher.source, options)
end
end
end
end
Expand Down
3 changes: 2 additions & 1 deletion lib/rack/attack/store_proxy/redis_proxy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,12 @@ def delete(key, _options = {})

def delete_matched(matcher, _options = nil)
cursor = "0"
source = matcher.source

rescuing do
# Fetch keys in batches using SCAN to avoid blocking the Redis server.
loop do
cursor, keys = scan(cursor, match: matcher, count: 1000)
cursor, keys = scan(cursor, match: source, count: 1000)
del(*keys) unless keys.empty?
break if cursor == "0"
end
Expand Down
90 changes: 90 additions & 0 deletions spec/rack_attack_reset_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# frozen_string_literal: true

require_relative "spec_helper"

describe "Rack::Attack.reset!" do
it "raises an error when is not supported by cache store" do
Rack::Attack.cache.store = Class.new
assert_raises(Rack::Attack::IncompatibleStoreError) do
Rack::Attack.reset!
end
end

if defined?(Redis)
it "should delete rack attack keys" do
redis = Redis.new
redis.set("key", "value")
redis.set("#{Rack::Attack.cache.prefix}::key", "value")
Rack::Attack.cache.store = redis
Rack::Attack.reset!

_(redis.get("key")).must_equal "value"
_(redis.get("#{Rack::Attack.cache.prefix}::key")).must_be_nil
end
end

if defined?(Redis::Store)
it "should delete rack attack keys" do
redis_store = Redis::Store.new
redis_store.set("key", "value")
redis_store.set("#{Rack::Attack.cache.prefix}::key", "value")
Rack::Attack.cache.store = redis_store
Rack::Attack.reset!

_(redis_store.get("key")).must_equal "value"
_(redis_store.get("#{Rack::Attack.cache.prefix}::key")).must_be_nil
end
end

if defined?(Redis) && defined?(ActiveSupport::Cache::RedisCacheStore)
it "should delete rack attack keys" do
redis_cache_store = ActiveSupport::Cache::RedisCacheStore.new
redis_cache_store.write("key", "value")
redis_cache_store.write("#{Rack::Attack.cache.prefix}::key", "value")
Rack::Attack.cache.store = redis_cache_store
Rack::Attack.reset!

_(redis_cache_store.read("key")).must_equal "value"
_(redis_cache_store.read("#{Rack::Attack.cache.prefix}::key")).must_be_nil
end

describe "with a namespaced cache" do
it "should delete rack attack keys" do
redis_cache_store = ActiveSupport::Cache::RedisCacheStore.new(namespace: "ns")
redis_cache_store.write("key", "value")
redis_cache_store.write("#{Rack::Attack.cache.prefix}::key", "value")
Rack::Attack.cache.store = redis_cache_store
Rack::Attack.reset!

_(redis_cache_store.read("key")).must_equal "value"
_(redis_cache_store.read("#{Rack::Attack.cache.prefix}::key")).must_be_nil
end
end
end

if defined?(ActiveSupport::Cache::MemoryStore)
it "should delete rack attack keys" do
memory_store = ActiveSupport::Cache::MemoryStore.new
memory_store.write("key", "value")
memory_store.write("#{Rack::Attack.cache.prefix}::key", "value")
Rack::Attack.cache.store = memory_store
Rack::Attack.reset!

_(memory_store.read("key")).must_equal "value"
_(memory_store.read("#{Rack::Attack.cache.prefix}::key")).must_be_nil
end

describe "with a namespaced cache" do
it "should delete rack attack keys" do
memory_store = ActiveSupport::Cache::MemoryStore.new(namespace: "ns")
memory_store.write("key", "value")
memory_store.write("#{Rack::Attack.cache.prefix}::key", "value")
Rack::Attack.cache.store = memory_store
Rack::Attack.reset!

_(memory_store.read("key")).must_equal "value"
_(memory_store.read("#{Rack::Attack.cache.prefix}::key")).must_be_nil
end
end
end
end
22 changes: 0 additions & 22 deletions spec/rack_attack_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -103,26 +103,4 @@
end
end
end

describe 'reset!' do
it 'raises an error when is not supported by cache store' do
Rack::Attack.cache.store = Class.new
assert_raises(Rack::Attack::IncompatibleStoreError) do
Rack::Attack.reset!
end
end

if defined?(Redis)
it 'should delete rack attack keys' do
redis = Redis.new
redis.set('key', 'value')
redis.set("#{Rack::Attack.cache.prefix}::key", 'value')
Rack::Attack.cache.store = redis
Rack::Attack.reset!

_(redis.get('key')).must_equal 'value'
_(redis.get("#{Rack::Attack.cache.prefix}::key")).must_be_nil
end
end
end
end

0 comments on commit 86650c4

Please sign in to comment.