Skip to content

Commit c962dc1

Browse files
authored
Merge pull request #438 from fatkodima/case-insensitive-discriminator
Make discriminators case-insensitive by default
2 parents 8fcd6c8 + a3dff70 commit c962dc1

3 files changed

Lines changed: 57 additions & 3 deletions

File tree

lib/rack/attack.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class MissingStoreError < Error; end
3131
autoload :Allow2Ban, 'rack/attack/allow2ban'
3232

3333
class << self
34-
attr_accessor :enabled, :notifier
34+
attr_accessor :enabled, :notifier, :discriminator_normalizer
3535
attr_reader :configuration
3636

3737
def instrument(request)
@@ -79,6 +79,9 @@ def clear!
7979
# Set defaults
8080
@enabled = true
8181
@notifier = ActiveSupport::Notifications if defined?(ActiveSupport::Notifications)
82+
@discriminator_normalizer = lambda do |discriminator|
83+
discriminator.to_s.strip.downcase
84+
end
8285
@configuration = Configuration.new
8386

8487
attr_reader :configuration

lib/rack/attack/throttle.rb

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ def cache
2222
end
2323

2424
def matched_by?(request)
25-
discriminator = block.call(request)
26-
25+
discriminator = discriminator_for(request)
2726
return false unless discriminator
2827

2928
current_period = period_for(request)
@@ -49,6 +48,14 @@ def matched_by?(request)
4948

5049
private
5150

51+
def discriminator_for(request)
52+
discriminator = block.call(request)
53+
if discriminator && Rack::Attack.discriminator_normalizer
54+
discriminator = Rack::Attack.discriminator_normalizer.call(discriminator)
55+
end
56+
discriminator
57+
end
58+
5259
def period_for(request)
5360
period.respond_to?(:call) ? period.call(request) : period
5461
end

spec/rack_attack_throttle_spec.rb

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,47 @@
144144
end
145145
end
146146
end
147+
148+
describe 'Rack::Attack.throttle with discriminator_normalizer' do
149+
before do
150+
@period = 60
151+
@emails = [
152+
"person@example.com",
153+
"PERSON@example.com ",
154+
" person@example.com\r\n ",
155+
]
156+
Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new
157+
Rack::Attack.throttle('logins/email', limit: 4, period: @period) do |req|
158+
if req.path == '/login' && req.post?
159+
req.params['email']
160+
end
161+
end
162+
end
163+
164+
it 'should not differentiate requests when discriminator_normalizer is enabled' do
165+
post_logins
166+
key = "rack::attack:#{Time.now.to_i / @period}:logins/email:person@example.com"
167+
_(Rack::Attack.cache.store.read(key)).must_equal 3
168+
end
169+
170+
it 'should differentiate requests when discriminator_normalizer is disabled' do
171+
begin
172+
prev = Rack::Attack.discriminator_normalizer
173+
Rack::Attack.discriminator_normalizer = nil
174+
175+
post_logins
176+
@emails.each do |email|
177+
key = "rack::attack:#{Time.now.to_i / @period}:logins/email:#{email}"
178+
_(Rack::Attack.cache.store.read(key)).must_equal 1
179+
end
180+
ensure
181+
Rack::Attack.discriminator_normalizer = prev
182+
end
183+
end
184+
185+
def post_logins
186+
@emails.each do |email|
187+
post '/login', email: email
188+
end
189+
end
190+
end

0 commit comments

Comments
 (0)