Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Checking Limits Without Incrementing? #599

Closed
6f6d6172 opened this issue Dec 1, 2022 · 1 comment
Closed

Checking Limits Without Incrementing? #599

6f6d6172 opened this issue Dec 1, 2022 · 1 comment

Comments

@6f6d6172
Copy link

6f6d6172 commented Dec 1, 2022

Hi, it's me again.

Is it possible to check if a client is under the limit, without incrementing the count? I can see the cache class has a read method, but it looks like it's only used by fail2ban. My usecases revolve around checking if someone is at or above the limit and rendering a template differently (e.g. deciding to rendering a form with or without a captcha shouldn't count towards the limit).

From what I gather, this isn't possible because the method that returns the current count /also/ increments it. Is my understanding correct?

@6f6d6172
Copy link
Author

6f6d6172 commented Dec 1, 2022

Through a series of monkeypatches, I've achieved the behavior I'm looking for. Hopefully this can do a decent job of explaining my usecase. This allows me to use it in a controller like like

  def show
    if limiter.at_limit?(request)
      @warning_message = "Attempts exceeded."
      @challenge = new_captcha_form
    end
  end

  def create
    if limiter.matched_by?(request)
      validate_captcha
    end

    rest_of_the_action
  end

  def limiter
    limiter = Rack::Attack.track("requests from bucket", limit: 5, period: 3600) do |req|
      bucket
    end
  end

monkeypatch:

Rack::Attack::Configuration.class_eval do
  # nop'd because otherwise all Rack::Attack::Throttles are incremented
  # even if the gated functionality is never exercised
  def tracked?(request)
  end
end

Rack::Attack::Cache.class_eval do
  def get_count(unprefixed_key, period)
    enforce_store_presence!
    enforce_store_method_presence!(:read)

    key, expires_in = key_and_expiry(unprefixed_key, period)
    result = store.read(key)
    result || 0
  end

end

Rack::Attack::Track.class_eval do
  def at_limit?(request)
    filter.at_limit?(request)
  end
end

Rack::Attack::Throttle.class_eval do
  def at_limit?(request)
    discriminator = discriminator_for(request)
    return false unless discriminator

    current_period  = period_for(request)
    current_limit   = limit_for(request)
    count           = cache.get_count("#{name}:#{discriminator}", current_period)
    
    count > current_limit
  end
end

@rack rack locked and limited conversation to collaborators Dec 12, 2022
@grzuy grzuy converted this issue into discussion #602 Dec 12, 2022

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant