class Rack::Attack::Cache

Attributes

last_epoch_time[R]
prefix[RW]
store[R]

Public Class Methods

new() click to toggle source
# File lib/rack/attack/cache.rb, line 9
def initialize
  self.store = ::Rails.cache if defined?(::Rails.cache)
  @prefix = 'rack::attack'
end

Public Instance Methods

count(unprefixed_key, period) click to toggle source
# File lib/rack/attack/cache.rb, line 25
def count(unprefixed_key, period)
  key, expires_in = key_and_expiry(unprefixed_key, period)
  do_count(key, expires_in)
end
delete(unprefixed_key) click to toggle source
# File lib/rack/attack/cache.rb, line 46
def delete(unprefixed_key)
  store.delete("#{prefix}:#{unprefixed_key}")
end
read(unprefixed_key) click to toggle source
# File lib/rack/attack/cache.rb, line 30
def read(unprefixed_key)
  enforce_store_presence!
  enforce_store_method_presence!(:read)

  store.read("#{prefix}:#{unprefixed_key}")
end
reset!() click to toggle source
# File lib/rack/attack/cache.rb, line 50
def reset!
  if store.respond_to?(:delete_matched)
    store.delete_matched("#{prefix}*")
  else
    raise(
      Rack::Attack::IncompatibleStoreError,
      "Configured store #{store.class.name} doesn't respond to #delete_matched method"
    )
  end
end
reset_count(unprefixed_key, period) click to toggle source
# File lib/rack/attack/cache.rb, line 41
def reset_count(unprefixed_key, period)
  key, _ = key_and_expiry(unprefixed_key, period)
  store.delete(key)
end
store=(store) click to toggle source
# File lib/rack/attack/cache.rb, line 16
def store=(store)
  @store =
    if (proxy = BaseProxy.lookup(store))
      proxy.new(store)
    else
      store
    end
end
write(unprefixed_key, value, expires_in) click to toggle source
# File lib/rack/attack/cache.rb, line 37
def write(unprefixed_key, value, expires_in)
  store.write("#{prefix}:#{unprefixed_key}", value, expires_in: expires_in)
end

Private Instance Methods

do_count(key, expires_in) click to toggle source
# File lib/rack/attack/cache.rb, line 70
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
enforce_store_method_presence!(method_name) click to toggle source
# File lib/rack/attack/cache.rb, line 91
def enforce_store_method_presence!(method_name)
  if !store.respond_to?(method_name)
    raise(
      Rack::Attack::MisconfiguredStoreError,
      "Configured store #{store.class.name} doesn't respond to ##{method_name} method"
    )
  end
end
enforce_store_presence!() click to toggle source
# File lib/rack/attack/cache.rb, line 85
def enforce_store_presence!
  if store.nil?
    raise Rack::Attack::MissingStoreError
  end
end
key_and_expiry(unprefixed_key, period) click to toggle source
# File lib/rack/attack/cache.rb, line 63
def key_and_expiry(unprefixed_key, period)
  @last_epoch_time = Time.now.to_i
  # Add 1 to expires_in to avoid timing error: https://git.io/i1PHXA
  expires_in = (period - (@last_epoch_time % period) + 1).to_i
  ["#{prefix}:#{(@last_epoch_time / period).to_i}:#{unprefixed_key}", expires_in]
end