summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Kozono <mkozono@gmail.com>2017-10-11 16:39:58 -0700
committerFrancisco Lopez <fjlopez@gitlab.com>2017-11-17 09:58:18 +0100
commit143369e45b5c32c21d9abd315662eca186d95ab6 (patch)
tree324f3ce3e614ef6b39a1826d63ce7b85baab92d5
parentdc9266fbeacd24446b52e4dad328c8286be40b31 (diff)
downloadgitlab-ce-143369e45b5c32c21d9abd315662eca186d95ab6.tar.gz
Add Rack::Attack throttle specs
-rw-r--r--spec/requests/rack_attack_spec.rb286
1 files changed, 286 insertions, 0 deletions
diff --git a/spec/requests/rack_attack_spec.rb b/spec/requests/rack_attack_spec.rb
new file mode 100644
index 00000000000..4f406f03993
--- /dev/null
+++ b/spec/requests/rack_attack_spec.rb
@@ -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