Add Rack::Attack throttle specs

This commit is contained in:
Michael Kozono 2017-10-11 16:39:58 -07:00 committed by Francisco Lopez
parent dc9266fbea
commit 143369e45b
1 changed files with 286 additions and 0 deletions

View File

@ -0,0 +1,286 @@
require 'spec_helper'
describe Rack::Attack do
let(:settings) { Gitlab::CurrentSettings.current_application_settings }
before do
# Ensure throttles are defined, because this is normally skipped for tests
described_class.define_throttles
# Instead of test environment's :null_store
Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new
# Start with really high limits to ensure the right settings are being exercised.
# Also note, settings will be saved later.
settings.throttle_unauthenticated_requests_per_period = 100
settings.throttle_unauthenticated_period_in_seconds = 1
settings.throttle_authenticated_api_requests_per_period = 100
settings.throttle_authenticated_api_period_in_seconds = 1
settings.throttle_authenticated_web_requests_per_period = 100
settings.throttle_authenticated_web_period_in_seconds = 1
end
# Make time-dependent tests deterministic
around do |example|
Timecop.freeze { example.run }
end
describe 'unauthenticated requests' do
let(:requests_per_period) { settings.throttle_unauthenticated_requests_per_period }
let(:period) { settings.throttle_unauthenticated_period_in_seconds.seconds }
before do
# Set low limits
settings.throttle_unauthenticated_requests_per_period = 1
settings.throttle_unauthenticated_period_in_seconds = 10
end
context 'when the throttle is enabled' do
before do
settings.throttle_unauthenticated_enabled = true
settings.save!
end
it 'rejects requests over the rate limit' do
# At first, allow requests under the rate limit.
requests_per_period.times do
get '/users/sign_in'
expect(response).to have_http_status 200
end
# the last straw
get '/users/sign_in'
expect(response).to have_http_status 429
end
it 'allows requests after throttling and then waiting for the next period' do
requests_per_period.times do
get '/users/sign_in'
expect(response).to have_http_status 200
end
get '/users/sign_in'
expect(response).to have_http_status 429
Timecop.travel(period.from_now) do
requests_per_period.times do
get '/users/sign_in'
expect(response).to have_http_status 200
end
get '/users/sign_in'
expect(response).to have_http_status 429
end
end
it 'counts requests from different IPs separately' do
requests_per_period.times do
get '/users/sign_in'
expect(response).to have_http_status 200
end
expect_any_instance_of(Rack::Attack::Request).to receive(:ip).and_return('1.2.3.4')
# would be over limit for the same IP
get '/users/sign_in'
expect(response).to have_http_status 200
end
end
context 'when the throttle is disabled' do
before do
settings.throttle_unauthenticated_enabled = false
settings.save!
end
it 'allows requests over the rate limit' do
(1 + requests_per_period).times do
get '/users/sign_in'
expect(response).to have_http_status 200
end
end
end
end
describe 'authenticated API requests', :api do
let(:requests_per_period) { settings.throttle_authenticated_api_requests_per_period }
let(:period) { settings.throttle_authenticated_api_period_in_seconds.seconds }
let(:user) { create(:user) }
before do
# Set low limits
settings.throttle_authenticated_api_requests_per_period = 1
settings.throttle_authenticated_api_period_in_seconds = 10
end
context 'when the throttle is enabled' do
before do
settings.throttle_authenticated_api_enabled = true
settings.save!
end
it 'rejects requests over the rate limit' do
# At first, allow requests under the rate limit.
requests_per_period.times do
get api('/todos', user)
expect(response).to have_http_status 200
end
# the last straw
get api('/todos', user)
expect(response).to have_http_status 429
end
it 'allows requests after throttling and then waiting for the next period' do
requests_per_period.times do
get api('/todos', user)
expect(response).to have_http_status 200
end
get api('/todos', user)
expect(response).to have_http_status 429
Timecop.travel(period.from_now) do
requests_per_period.times do
get api('/todos', user)
expect(response).to have_http_status 200
end
get api('/todos', user)
expect(response).to have_http_status 429
end
end
it 'counts requests from different users separately, even from the same IP' do
other_user = create(:user)
requests_per_period.times do
get api('/todos', user)
expect(response).to have_http_status 200
end
# would be over the limit if this wasn't a different user
get api('/todos', other_user)
expect(response).to have_http_status 200
end
it 'counts all requests from the same user, even via different IPs' do
requests_per_period.times do
get api('/todos', user)
expect(response).to have_http_status 200
end
expect_any_instance_of(Rack::Attack::Request).to receive(:ip).and_return('1.2.3.4')
get api('/todos', user)
expect(response).to have_http_status 429
end
end
context 'when the throttle is disabled' do
before do
settings.throttle_authenticated_api_enabled = false
settings.save!
end
it 'allows requests over the rate limit' do
(1 + requests_per_period).times do
get api('/todos', user)
expect(response).to have_http_status 200
end
end
end
end
describe 'authenticated web requests' do
let(:requests_per_period) { settings.throttle_authenticated_web_requests_per_period }
let(:period) { settings.throttle_authenticated_web_period_in_seconds.seconds }
let(:user) { create(:user) }
before do
login_as(user)
# Set low limits
settings.throttle_authenticated_web_requests_per_period = 1
settings.throttle_authenticated_web_period_in_seconds = 10
end
context 'when the throttle is enabled' do
before do
settings.throttle_authenticated_web_enabled = true
settings.save!
end
it 'rejects requests over the rate limit' do
# At first, allow requests under the rate limit.
requests_per_period.times do
get '/dashboard/snippets'
expect(response).to have_http_status 200
end
# the last straw
get '/dashboard/snippets'
expect(response).to have_http_status 429
end
it 'allows requests after throttling and then waiting for the next period' do
requests_per_period.times do
get '/dashboard/snippets'
expect(response).to have_http_status 200
end
get '/dashboard/snippets'
expect(response).to have_http_status 429
Timecop.travel(period.from_now) do
requests_per_period.times do
get '/dashboard/snippets'
expect(response).to have_http_status 200
end
get '/dashboard/snippets'
expect(response).to have_http_status 429
end
end
it 'counts requests from different users separately, even from the same IP' do
requests_per_period.times do
get '/dashboard/snippets'
expect(response).to have_http_status 200
end
# would be over the limit if this wasn't a different user
login_as(create(:user))
get '/dashboard/snippets'
expect(response).to have_http_status 200
end
it 'counts all requests from the same user, even via different IPs' do
requests_per_period.times do
get '/dashboard/snippets'
expect(response).to have_http_status 200
end
expect_any_instance_of(Rack::Attack::Request).to receive(:ip).and_return('1.2.3.4')
get '/dashboard/snippets'
expect(response).to have_http_status 429
end
end
context 'when the throttle is disabled' do
before do
settings.throttle_authenticated_web_enabled = false
settings.save!
end
it 'allows requests over the rate limit' do
(1 + requests_per_period).times do
get '/dashboard/snippets'
expect(response).to have_http_status 200
end
end
end
end
end