Skip to content

Checking Limits Without Incrementing? #599

Closed

Description

@6f6d6172

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?

Activity

  1. 6f6d6172 commented on Dec 1, 2022

    @6f6d6172
    Author

    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
  2. locked and limited conversation to collaborators on Dec 12, 2022
  3. converted this issue into a discussion #602 on Dec 12, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions