mirror of
https://github.com/samsonjs/rack-attack.git
synced 2026-04-25 14:47:43 +00:00
Merge pull request #67 from chiliburger/track_only
Limit and period options for track
This commit is contained in:
commit
105c3ba575
6 changed files with 42 additions and 11 deletions
|
|
@ -188,11 +188,16 @@ end
|
||||||
### Tracks
|
### Tracks
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
# Track requests from a special user agent
|
# Track requests from a special user agent.
|
||||||
Rack::Attack.track("special_agent") do |req|
|
Rack::Attack.track("special_agent") do |req|
|
||||||
req.user_agent == "SpecialAgent"
|
req.user_agent == "SpecialAgent"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Supports optional limit and period, triggers the notification only when the limit is reached.
|
||||||
|
Rack::Attack.track("special_agent", :limit 6, :period => 60.seconds) do |req|
|
||||||
|
req.user_agent == "SpecialAgent"
|
||||||
|
end
|
||||||
|
|
||||||
# Track it using ActiveSupport::Notification
|
# Track it using ActiveSupport::Notification
|
||||||
ActiveSupport::Notifications.subscribe("rack.attack") do |name, start, finish, request_id, req|
|
ActiveSupport::Notifications.subscribe("rack.attack") do |name, start, finish, request_id, req|
|
||||||
if req.env['rack.attack.matched'] == "special_agent" && req.env['rack.attack.match_type'] == :track
|
if req.env['rack.attack.matched'] == "special_agent" && req.env['rack.attack.match_type'] == :track
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,8 @@ class Rack::Attack
|
||||||
self.throttles[name] = Throttle.new(name, options, block)
|
self.throttles[name] = Throttle.new(name, options, block)
|
||||||
end
|
end
|
||||||
|
|
||||||
def track(name, &block)
|
def track(name, options = {}, &block)
|
||||||
self.tracks[name] = Track.new(name, block)
|
self.tracks[name] = Track.new(name, options, block)
|
||||||
end
|
end
|
||||||
|
|
||||||
def whitelists; @whitelists ||= {}; end
|
def whitelists; @whitelists ||= {}; end
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ module Rack
|
||||||
class Attack
|
class Attack
|
||||||
class Check
|
class Check
|
||||||
attr_reader :name, :block, :type
|
attr_reader :name, :block, :type
|
||||||
def initialize(name, block)
|
def initialize(name, options = {}, block)
|
||||||
@name, @block = name, block
|
@name, @block = name, block
|
||||||
@type = nil
|
@type = options.fetch(:type, nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
def [](req)
|
def [](req)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ module Rack
|
||||||
class Attack
|
class Attack
|
||||||
class Throttle
|
class Throttle
|
||||||
MANDATORY_OPTIONS = [:limit, :period]
|
MANDATORY_OPTIONS = [:limit, :period]
|
||||||
attr_reader :name, :limit, :period, :block
|
attr_reader :name, :limit, :period, :block, :type
|
||||||
def initialize(name, options, block)
|
def initialize(name, options, block)
|
||||||
@name, @block = name, block
|
@name, @block = name, block
|
||||||
MANDATORY_OPTIONS.each do |opt|
|
MANDATORY_OPTIONS.each do |opt|
|
||||||
|
|
@ -10,6 +10,7 @@ module Rack
|
||||||
end
|
end
|
||||||
@limit = options[:limit]
|
@limit = options[:limit]
|
||||||
@period = options[:period].to_i
|
@period = options[:period].to_i
|
||||||
|
@type = options.fetch(:type, :throttle)
|
||||||
end
|
end
|
||||||
|
|
||||||
def cache
|
def cache
|
||||||
|
|
@ -34,7 +35,7 @@ module Rack
|
||||||
if throttled
|
if throttled
|
||||||
req.env['rack.attack.matched'] = name
|
req.env['rack.attack.matched'] = name
|
||||||
req.env['rack.attack.match_discriminator'] = discriminator
|
req.env['rack.attack.match_discriminator'] = discriminator
|
||||||
req.env['rack.attack.match_type'] = :throttle
|
req.env['rack.attack.match_type'] = type
|
||||||
req.env['rack.attack.match_data'] = data
|
req.env['rack.attack.match_data'] = data
|
||||||
Rack::Attack.instrument(req)
|
Rack::Attack.instrument(req)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,21 @@
|
||||||
module Rack
|
module Rack
|
||||||
class Attack
|
class Attack
|
||||||
class Track < Check
|
class Track
|
||||||
def initialize(name, block)
|
extend Forwardable
|
||||||
super
|
|
||||||
@type = :track
|
attr_reader :filter
|
||||||
|
|
||||||
|
def initialize(name, options = {}, block)
|
||||||
|
options[:type] = :track
|
||||||
|
|
||||||
|
if options[:limit] && options[:period]
|
||||||
|
@filter = Throttle.new(name, options, block)
|
||||||
|
else
|
||||||
|
@filter = Check.new(name, options, block)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def_delegator :@filter, :[]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -41,4 +41,18 @@ describe 'Rack::Attack.track' do
|
||||||
Counter.check.must_equal 2
|
Counter.check.must_equal 2
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "without limit and period options" do
|
||||||
|
it "should assign the track filter to a Check instance" do
|
||||||
|
tracker = Rack::Attack.track("homepage") { |req| req.path == "/"}
|
||||||
|
tracker.filter.class.must_equal Rack::Attack::Check
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "with limit and period options" do
|
||||||
|
it "should assign the track filter to a Throttle instance" do
|
||||||
|
tracker = Rack::Attack.track("homepage", :limit => 10, :period => 10) { |req| req.path == "/"}
|
||||||
|
tracker.filter.class.must_equal Rack::Attack::Throttle
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue