summaryrefslogtreecommitdiff
path: root/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/support/shared_examples/requests/rack_attack_shared_examples.rb')
-rw-r--r--spec/support/shared_examples/requests/rack_attack_shared_examples.rb202
1 files changed, 198 insertions, 4 deletions
diff --git a/spec/support/shared_examples/requests/rack_attack_shared_examples.rb b/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
index 95817624658..2a19ff6f590 100644
--- a/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
+++ b/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
#
# Requires let variables:
-# * throttle_setting_prefix: "throttle_authenticated_api", "throttle_authenticated_web", "throttle_protected_paths", "throttle_authenticated_packages_api"
+# * throttle_setting_prefix: "throttle_authenticated_api", "throttle_authenticated_web", "throttle_protected_paths", "throttle_authenticated_packages_api", "throttle_authenticated_git_lfs", "throttle_authenticated_files_api"
# * request_method
# * request_args
# * other_user_request_args
@@ -14,7 +14,9 @@ RSpec.shared_examples 'rate-limited token-authenticated requests' do
"throttle_protected_paths" => "throttle_authenticated_protected_paths_api",
"throttle_authenticated_api" => "throttle_authenticated_api",
"throttle_authenticated_web" => "throttle_authenticated_web",
- "throttle_authenticated_packages_api" => "throttle_authenticated_packages_api"
+ "throttle_authenticated_packages_api" => "throttle_authenticated_packages_api",
+ "throttle_authenticated_git_lfs" => "throttle_authenticated_git_lfs",
+ "throttle_authenticated_files_api" => "throttle_authenticated_files_api"
}
end
@@ -165,7 +167,7 @@ RSpec.shared_examples 'rate-limited token-authenticated requests' do
end
# Requires let variables:
-# * throttle_setting_prefix: "throttle_authenticated_web" or "throttle_protected_paths"
+# * throttle_setting_prefix: "throttle_authenticated_web", "throttle_protected_paths", "throttle_authenticated_git_lfs"
# * user
# * url_that_requires_authentication
# * request_method
@@ -176,7 +178,8 @@ RSpec.shared_examples 'rate-limited web authenticated requests' do
let(:throttle_types) do
{
"throttle_protected_paths" => "throttle_authenticated_protected_paths_web",
- "throttle_authenticated_web" => "throttle_authenticated_web"
+ "throttle_authenticated_web" => "throttle_authenticated_web",
+ "throttle_authenticated_git_lfs" => "throttle_authenticated_git_lfs"
}
end
@@ -385,3 +388,194 @@ RSpec.shared_examples 'tracking when dry-run mode is set' do
end
end
end
+
+# Requires let variables:
+# * throttle_name: "throttle_unauthenticated_api", "throttle_unauthenticated_web"
+# * throttle_setting_prefix: "throttle_unauthenticated_api", "throttle_unauthenticated"
+# * url_that_does_not_require_authentication
+# * url_that_is_not_matched
+# * requests_per_period
+# * period_in_seconds
+# * period
+RSpec.shared_examples 'rate-limited unauthenticated requests' do
+ before do
+ # Set low limits
+ settings_to_set[:"#{throttle_setting_prefix}_requests_per_period"] = requests_per_period
+ settings_to_set[:"#{throttle_setting_prefix}_period_in_seconds"] = period_in_seconds
+ end
+
+ context 'when the throttle is enabled' do
+ before do
+ settings_to_set[:"#{throttle_setting_prefix}_enabled"] = true
+ stub_application_setting(settings_to_set)
+ end
+
+ it 'rejects requests over the rate limit' do
+ # At first, allow requests under the rate limit.
+ requests_per_period.times do
+ get url_that_does_not_require_authentication
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+
+ # the last straw
+ expect_rejection { get url_that_does_not_require_authentication }
+ end
+
+ context 'with custom response text' do
+ before do
+ stub_application_setting(rate_limiting_response_text: 'Custom response')
+ end
+
+ it 'rejects requests over the rate limit' do
+ # At first, allow requests under the rate limit.
+ requests_per_period.times do
+ get url_that_does_not_require_authentication
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+
+ # the last straw
+ expect_rejection { get url_that_does_not_require_authentication }
+ expect(response.body).to eq("Custom response\n")
+ end
+ end
+
+ it 'allows requests after throttling and then waiting for the next period' do
+ requests_per_period.times do
+ get url_that_does_not_require_authentication
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+
+ expect_rejection { get url_that_does_not_require_authentication }
+
+ travel_to(period.from_now) do
+ requests_per_period.times do
+ get url_that_does_not_require_authentication
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+
+ expect_rejection { get url_that_does_not_require_authentication }
+ end
+ end
+
+ it 'counts requests from different IPs separately' do
+ requests_per_period.times do
+ get url_that_does_not_require_authentication
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+
+ expect_next_instance_of(Rack::Attack::Request) do |instance|
+ expect(instance).to receive(:ip).at_least(:once).and_return('1.2.3.4')
+ end
+
+ # would be over limit for the same IP
+ get url_that_does_not_require_authentication
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+
+ context 'when the request is not matched by the throttle' do
+ it 'does not throttle the requests' do
+ (1 + requests_per_period).times do
+ get url_that_is_not_matched
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+ end
+
+ context 'when the request is to the api internal endpoints' do
+ it 'allows requests over the rate limit' do
+ (1 + requests_per_period).times do
+ get '/api/v4/internal/check', params: { secret_token: Gitlab::Shell.secret_token }
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+ end
+
+ context 'when the request is authenticated by a runner token' do
+ let(:request_jobs_url) { '/api/v4/jobs/request' }
+ let(:runner) { create(:ci_runner) }
+
+ it 'does not count as unauthenticated' do
+ (1 + requests_per_period).times do
+ post request_jobs_url, params: { token: runner.token }
+ expect(response).to have_gitlab_http_status(:no_content)
+ end
+ end
+ end
+
+ context 'when the request is to a health endpoint' do
+ let(:health_endpoint) { '/-/metrics' }
+
+ it 'does not throttle the requests' do
+ (1 + requests_per_period).times do
+ get health_endpoint
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+ end
+
+ context 'when the request is to a container registry notification endpoint' do
+ let(:secret_token) { 'secret_token' }
+ let(:events) { [{ action: 'push' }] }
+ let(:registry_endpoint) { '/api/v4/container_registry_event/events' }
+ let(:registry_headers) { { 'Content-Type' => ::API::ContainerRegistryEvent::DOCKER_DISTRIBUTION_EVENTS_V1_JSON } }
+
+ before do
+ allow(Gitlab.config.registry).to receive(:notification_secret) { secret_token }
+
+ event = spy(:event)
+ allow(::ContainerRegistry::Event).to receive(:new).and_return(event)
+ allow(event).to receive(:supported?).and_return(true)
+ end
+
+ it 'does not throttle the requests' do
+ (1 + requests_per_period).times do
+ post registry_endpoint,
+ params: { events: events }.to_json,
+ headers: registry_headers.merge('Authorization' => secret_token)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+ end
+
+ it 'logs RackAttack info into structured logs' do
+ requests_per_period.times do
+ get url_that_does_not_require_authentication
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+
+ arguments = a_hash_including({
+ message: 'Rack_Attack',
+ env: :throttle,
+ remote_ip: '127.0.0.1',
+ request_method: 'GET',
+ path: url_that_does_not_require_authentication,
+ matched: throttle_name
+ })
+
+ expect(Gitlab::AuthLogger).to receive(:error).with(arguments)
+
+ get url_that_does_not_require_authentication
+ end
+
+ it_behaves_like 'tracking when dry-run mode is set' do
+ def do_request
+ get url_that_does_not_require_authentication
+ end
+ end
+ end
+
+ context 'when the throttle is disabled' do
+ before do
+ settings_to_set[:"#{throttle_setting_prefix}_enabled"] = false
+ stub_application_setting(settings_to_set)
+ end
+
+ it 'allows requests over the rate limit' do
+ (1 + requests_per_period).times do
+ get url_that_does_not_require_authentication
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+ end
+end