2019-10-28 20:06:10 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2017-10-11 19:39:58 -04:00
|
|
|
require 'spec_helper'
|
|
|
|
|
2021-02-01 07:09:03 -05:00
|
|
|
RSpec.describe 'Rack Attack global throttles', :use_clean_rails_memory_store_caching do
|
2019-10-09 08:06:13 -04:00
|
|
|
include RackAttackSpecHelpers
|
2022-02-03 10:12:41 -05:00
|
|
|
include SessionHelpers
|
2019-10-09 08:06:13 -04:00
|
|
|
|
2017-10-11 19:39:58 -04:00
|
|
|
let(:settings) { Gitlab::CurrentSettings.current_application_settings }
|
|
|
|
|
2017-10-30 19:46:20 -04:00
|
|
|
# Start with really high limits and override them with low limits to ensure
|
|
|
|
# the right settings are being exercised
|
|
|
|
let(:settings_to_set) do
|
|
|
|
{
|
2021-09-14 11:12:05 -04:00
|
|
|
throttle_unauthenticated_api_requests_per_period: 100,
|
|
|
|
throttle_unauthenticated_api_period_in_seconds: 1,
|
2017-10-30 19:46:20 -04:00
|
|
|
throttle_unauthenticated_requests_per_period: 100,
|
|
|
|
throttle_unauthenticated_period_in_seconds: 1,
|
|
|
|
throttle_authenticated_api_requests_per_period: 100,
|
|
|
|
throttle_authenticated_api_period_in_seconds: 1,
|
|
|
|
throttle_authenticated_web_requests_per_period: 100,
|
2019-09-26 17:06:29 -04:00
|
|
|
throttle_authenticated_web_period_in_seconds: 1,
|
|
|
|
throttle_authenticated_protected_paths_request_per_period: 100,
|
2021-04-21 11:09:35 -04:00
|
|
|
throttle_authenticated_protected_paths_in_seconds: 1,
|
|
|
|
throttle_unauthenticated_packages_api_requests_per_period: 100,
|
|
|
|
throttle_unauthenticated_packages_api_period_in_seconds: 1,
|
|
|
|
throttle_authenticated_packages_api_requests_per_period: 100,
|
2021-09-01 14:08:49 -04:00
|
|
|
throttle_authenticated_packages_api_period_in_seconds: 1,
|
|
|
|
throttle_authenticated_git_lfs_requests_per_period: 100,
|
2021-09-09 08:09:09 -04:00
|
|
|
throttle_authenticated_git_lfs_period_in_seconds: 1,
|
|
|
|
throttle_unauthenticated_files_api_requests_per_period: 100,
|
|
|
|
throttle_unauthenticated_files_api_period_in_seconds: 1,
|
|
|
|
throttle_authenticated_files_api_requests_per_period: 100,
|
2021-09-28 08:11:10 -04:00
|
|
|
throttle_authenticated_files_api_period_in_seconds: 1,
|
|
|
|
throttle_unauthenticated_deprecated_api_requests_per_period: 100,
|
|
|
|
throttle_unauthenticated_deprecated_api_period_in_seconds: 1,
|
|
|
|
throttle_authenticated_deprecated_api_requests_per_period: 100,
|
|
|
|
throttle_authenticated_deprecated_api_period_in_seconds: 1
|
2017-10-30 19:46:20 -04:00
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2019-11-08 22:06:40 -05:00
|
|
|
let(:request_method) { 'GET' }
|
2017-10-30 19:46:20 -04:00
|
|
|
let(:requests_per_period) { 1 }
|
|
|
|
let(:period_in_seconds) { 10000 }
|
|
|
|
let(:period) { period_in_seconds.seconds }
|
|
|
|
|
2019-10-09 08:06:13 -04:00
|
|
|
include_context 'rack attack cache store'
|
2017-10-11 19:39:58 -04:00
|
|
|
|
2021-09-14 11:12:05 -04:00
|
|
|
describe 'unauthenticated API requests' do
|
|
|
|
it_behaves_like 'rate-limited unauthenticated requests' do
|
|
|
|
let(:throttle_name) { 'throttle_unauthenticated_api' }
|
|
|
|
let(:throttle_setting_prefix) { 'throttle_unauthenticated_api' }
|
|
|
|
let(:url_that_does_not_require_authentication) { '/api/v4/projects' }
|
|
|
|
let(:url_that_is_not_matched) { '/users/sign_in' }
|
2017-10-11 19:39:58 -04:00
|
|
|
end
|
2021-09-14 11:12:05 -04:00
|
|
|
end
|
2017-10-11 19:39:58 -04:00
|
|
|
|
2021-09-14 11:12:05 -04:00
|
|
|
describe 'unauthenticated web requests' do
|
|
|
|
it_behaves_like 'rate-limited unauthenticated requests' do
|
|
|
|
let(:throttle_name) { 'throttle_unauthenticated_web' }
|
|
|
|
let(:throttle_setting_prefix) { 'throttle_unauthenticated' }
|
|
|
|
let(:url_that_does_not_require_authentication) { '/users/sign_in' }
|
|
|
|
let(:url_that_is_not_matched) { '/api/v4/projects' }
|
2017-10-11 19:39:58 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-02-03 10:12:41 -05:00
|
|
|
describe 'API requests from the frontend', :api, :clean_gitlab_redis_sessions do
|
|
|
|
context 'when unauthenticated' do
|
|
|
|
it_behaves_like 'rate-limited frontend API requests' do
|
|
|
|
let(:throttle_setting_prefix) { 'throttle_unauthenticated' }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when authenticated' do
|
|
|
|
it_behaves_like 'rate-limited frontend API requests' do
|
|
|
|
let_it_be(:personal_access_token) { create(:personal_access_token) }
|
|
|
|
|
|
|
|
let(:throttle_setting_prefix) { 'throttle_authenticated' }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-10-13 20:05:18 -04:00
|
|
|
describe 'API requests authenticated with personal access token', :api do
|
2021-01-04 16:10:19 -05:00
|
|
|
let_it_be(:user) { create(:user) }
|
|
|
|
let_it_be(:token) { create(:personal_access_token, user: user) }
|
|
|
|
let_it_be(:other_user) { create(:user) }
|
|
|
|
let_it_be(:other_user_token) { create(:personal_access_token, user: other_user) }
|
2021-06-28 23:07:32 -04:00
|
|
|
|
2017-10-13 20:05:18 -04:00
|
|
|
let(:throttle_setting_prefix) { 'throttle_authenticated_api' }
|
2019-09-05 17:15:55 -04:00
|
|
|
let(:api_partial_url) { '/todos' }
|
2017-10-13 20:05:18 -04:00
|
|
|
|
|
|
|
context 'with the token in the query string' do
|
2021-01-05 19:10:23 -05:00
|
|
|
let(:request_args) { [api(api_partial_url, personal_access_token: token), {}] }
|
|
|
|
let(:other_user_request_args) { [api(api_partial_url, personal_access_token: other_user_token), {}] }
|
2017-10-13 20:05:18 -04:00
|
|
|
|
2022-05-02 20:08:25 -04:00
|
|
|
it_behaves_like 'rate-limited user based token-authenticated requests'
|
2017-10-13 20:05:18 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
context 'with the token in the headers' do
|
2019-11-08 22:06:40 -05:00
|
|
|
let(:request_args) { api_get_args_with_token_headers(api_partial_url, personal_access_token_headers(token)) }
|
|
|
|
let(:other_user_request_args) { api_get_args_with_token_headers(api_partial_url, personal_access_token_headers(other_user_token)) }
|
2017-10-13 20:05:18 -04:00
|
|
|
|
2022-05-02 20:08:25 -04:00
|
|
|
it_behaves_like 'rate-limited user based token-authenticated requests'
|
2017-10-13 20:05:18 -04:00
|
|
|
end
|
2021-01-04 16:10:19 -05:00
|
|
|
|
|
|
|
context 'with the token in the OAuth headers' do
|
|
|
|
let(:request_args) { api_get_args_with_token_headers(api_partial_url, oauth_token_headers(token)) }
|
|
|
|
let(:other_user_request_args) { api_get_args_with_token_headers(api_partial_url, oauth_token_headers(other_user_token)) }
|
|
|
|
|
2022-05-02 20:08:25 -04:00
|
|
|
it_behaves_like 'rate-limited user based token-authenticated requests'
|
2021-01-04 16:10:19 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
context 'with the token in basic auth' do
|
|
|
|
let(:request_args) { api_get_args_with_token_headers(api_partial_url, basic_auth_headers(user, token)) }
|
|
|
|
let(:other_user_request_args) { api_get_args_with_token_headers(api_partial_url, basic_auth_headers(other_user, other_user_token)) }
|
|
|
|
|
2022-05-02 20:08:25 -04:00
|
|
|
it_behaves_like 'rate-limited user based token-authenticated requests'
|
2021-01-04 16:10:19 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
context 'with a read_api scope' do
|
|
|
|
before do
|
|
|
|
token.update!(scopes: ['read_api'])
|
|
|
|
other_user_token.update!(scopes: ['read_api'])
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with the token in the headers' do
|
|
|
|
let(:request_args) { api_get_args_with_token_headers(api_partial_url, personal_access_token_headers(token)) }
|
|
|
|
let(:other_user_request_args) { api_get_args_with_token_headers(api_partial_url, personal_access_token_headers(other_user_token)) }
|
|
|
|
|
2022-05-02 20:08:25 -04:00
|
|
|
it_behaves_like 'rate-limited user based token-authenticated requests'
|
2021-01-04 16:10:19 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
context 'with the token in the OAuth headers' do
|
|
|
|
let(:request_args) { api_get_args_with_token_headers(api_partial_url, oauth_token_headers(token)) }
|
|
|
|
let(:other_user_request_args) { api_get_args_with_token_headers(api_partial_url, oauth_token_headers(other_user_token)) }
|
|
|
|
|
2022-05-02 20:08:25 -04:00
|
|
|
it_behaves_like 'rate-limited user based token-authenticated requests'
|
2021-01-04 16:10:19 -05:00
|
|
|
end
|
|
|
|
end
|
2017-10-13 20:05:18 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
describe 'API requests authenticated with OAuth token', :api do
|
|
|
|
let(:user) { create(:user) }
|
|
|
|
let(:application) { Doorkeeper::Application.create!(name: "MyApp", redirect_uri: "https://app.com", owner: user) }
|
|
|
|
let(:token) { Doorkeeper::AccessToken.create!(application_id: application.id, resource_owner_id: user.id, scopes: "api") }
|
2019-09-05 17:15:55 -04:00
|
|
|
|
2017-10-13 20:05:18 -04:00
|
|
|
let(:other_user) { create(:user) }
|
|
|
|
let(:other_user_application) { Doorkeeper::Application.create!(name: "MyApp", redirect_uri: "https://app.com", owner: other_user) }
|
|
|
|
let(:other_user_token) { Doorkeeper::AccessToken.create!(application_id: application.id, resource_owner_id: other_user.id, scopes: "api") }
|
2019-09-05 17:15:55 -04:00
|
|
|
|
2017-10-13 20:05:18 -04:00
|
|
|
let(:throttle_setting_prefix) { 'throttle_authenticated_api' }
|
2019-09-05 17:15:55 -04:00
|
|
|
let(:api_partial_url) { '/todos' }
|
2017-10-13 20:05:18 -04:00
|
|
|
|
|
|
|
context 'with the token in the query string' do
|
2021-01-05 19:10:23 -05:00
|
|
|
let(:request_args) { [api(api_partial_url, oauth_access_token: token), {}] }
|
|
|
|
let(:other_user_request_args) { [api(api_partial_url, oauth_access_token: other_user_token), {}] }
|
2017-10-13 20:05:18 -04:00
|
|
|
|
2022-05-02 20:08:25 -04:00
|
|
|
it_behaves_like 'rate-limited user based token-authenticated requests'
|
2017-10-13 20:05:18 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
context 'with the token in the headers' do
|
2019-11-08 22:06:40 -05:00
|
|
|
let(:request_args) { api_get_args_with_token_headers(api_partial_url, oauth_token_headers(token)) }
|
|
|
|
let(:other_user_request_args) { api_get_args_with_token_headers(api_partial_url, oauth_token_headers(other_user_token)) }
|
2017-10-13 20:05:18 -04:00
|
|
|
|
2022-05-02 20:08:25 -04:00
|
|
|
it_behaves_like 'rate-limited user based token-authenticated requests'
|
2017-10-13 20:05:18 -04:00
|
|
|
end
|
2021-01-04 16:10:19 -05:00
|
|
|
|
|
|
|
context 'with a read_api scope' do
|
|
|
|
let(:read_token) { Doorkeeper::AccessToken.create!(application_id: application.id, resource_owner_id: user.id, scopes: "read_api") }
|
|
|
|
let(:other_user_read_token) { Doorkeeper::AccessToken.create!(application_id: other_user_application.id, resource_owner_id: other_user.id, scopes: "read_api") }
|
|
|
|
let(:request_args) { api_get_args_with_token_headers(api_partial_url, oauth_token_headers(read_token)) }
|
|
|
|
let(:other_user_request_args) { api_get_args_with_token_headers(api_partial_url, oauth_token_headers(other_user_read_token)) }
|
|
|
|
|
2022-05-02 20:08:25 -04:00
|
|
|
it_behaves_like 'rate-limited user based token-authenticated requests'
|
2021-01-04 16:10:19 -05:00
|
|
|
end
|
2017-10-13 20:05:18 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
describe '"web" (non-API) requests authenticated with RSS token' do
|
|
|
|
let(:user) { create(:user) }
|
|
|
|
let(:other_user) { create(:user) }
|
|
|
|
let(:throttle_setting_prefix) { 'throttle_authenticated_web' }
|
|
|
|
|
|
|
|
context 'with the token in the query string' do
|
2019-11-08 22:06:40 -05:00
|
|
|
let(:request_args) { [rss_url(user), params: nil] }
|
|
|
|
let(:other_user_request_args) { [rss_url(other_user), params: nil] }
|
2017-10-13 20:05:18 -04:00
|
|
|
|
2022-05-02 20:08:25 -04:00
|
|
|
it_behaves_like 'rate-limited user based token-authenticated requests'
|
2017-10-13 20:05:18 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe 'web requests authenticated with regular login' do
|
2019-09-05 17:15:55 -04:00
|
|
|
let(:throttle_setting_prefix) { 'throttle_authenticated_web' }
|
2017-10-11 19:39:58 -04:00
|
|
|
let(:user) { create(:user) }
|
2019-09-05 17:15:55 -04:00
|
|
|
let(:url_that_requires_authentication) { '/dashboard/snippets' }
|
2017-10-11 19:39:58 -04:00
|
|
|
|
2019-09-05 17:15:55 -04:00
|
|
|
it_behaves_like 'rate-limited web authenticated requests'
|
2017-10-11 19:39:58 -04:00
|
|
|
end
|
2017-10-13 20:05:18 -04:00
|
|
|
|
2019-09-26 17:06:29 -04:00
|
|
|
describe 'protected paths' do
|
2019-11-08 22:06:40 -05:00
|
|
|
let(:request_method) { 'POST' }
|
|
|
|
|
2019-09-26 17:06:29 -04:00
|
|
|
context 'unauthenticated requests' do
|
|
|
|
let(:protected_path_that_does_not_require_authentication) do
|
2022-02-01 07:17:55 -05:00
|
|
|
# This is one of the default values for `application_settings.protected_paths`
|
2019-11-08 22:06:40 -05:00
|
|
|
'/users/sign_in'
|
2019-09-26 17:06:29 -04:00
|
|
|
end
|
2020-08-10 23:11:00 -04:00
|
|
|
|
2019-11-08 22:06:40 -05:00
|
|
|
let(:post_params) { { user: { login: 'username', password: 'password' } } }
|
2019-09-26 17:06:29 -04:00
|
|
|
|
2020-11-18 13:09:08 -05:00
|
|
|
def do_request
|
|
|
|
post protected_path_that_does_not_require_authentication, params: post_params
|
|
|
|
end
|
|
|
|
|
2019-09-26 17:06:29 -04:00
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_protected_paths_requests_per_period] = requests_per_period # 1
|
|
|
|
settings_to_set[:throttle_protected_paths_period_in_seconds] = period_in_seconds # 10_000
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when protected paths throttle is disabled' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_protected_paths_enabled] = false
|
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'allows requests over the rate limit' do
|
|
|
|
(1 + requests_per_period).times do
|
2020-11-18 13:09:08 -05:00
|
|
|
do_request
|
2020-01-27 10:08:51 -05:00
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
2019-09-26 17:06:29 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when protected paths throttle is enabled' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_protected_paths_enabled] = true
|
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'rejects requests over the rate limit' do
|
|
|
|
requests_per_period.times do
|
2020-11-18 13:09:08 -05:00
|
|
|
do_request
|
2020-01-27 10:08:51 -05:00
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
2019-09-26 17:06:29 -04:00
|
|
|
end
|
|
|
|
|
2019-11-08 22:06:40 -05:00
|
|
|
expect_rejection { post protected_path_that_does_not_require_authentication, params: post_params }
|
2019-09-26 17:06:29 -04:00
|
|
|
end
|
2020-11-18 13:09:08 -05:00
|
|
|
|
2022-02-01 07:17:55 -05:00
|
|
|
it 'allows non-POST requests to protected paths over the rate limit' do
|
|
|
|
(1 + requests_per_period).times do
|
|
|
|
get protected_path_that_does_not_require_authentication
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'allows POST requests to unprotected paths over the rate limit' do
|
|
|
|
(1 + requests_per_period).times do
|
|
|
|
post '/api/graphql'
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-11-18 13:09:08 -05:00
|
|
|
it_behaves_like 'tracking when dry-run mode is set' do
|
|
|
|
let(:throttle_name) { 'throttle_unauthenticated_protected_paths' }
|
|
|
|
end
|
2019-09-26 17:06:29 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'API requests authenticated with personal access token', :api do
|
|
|
|
let(:user) { create(:user) }
|
|
|
|
let(:token) { create(:personal_access_token, user: user) }
|
|
|
|
let(:other_user) { create(:user) }
|
|
|
|
let(:other_user_token) { create(:personal_access_token, user: other_user) }
|
|
|
|
let(:throttle_setting_prefix) { 'throttle_protected_paths' }
|
2019-11-08 22:06:40 -05:00
|
|
|
let(:api_partial_url) { '/user/emails' }
|
2019-09-26 17:06:29 -04:00
|
|
|
|
|
|
|
let(:protected_paths) do
|
|
|
|
[
|
2019-11-08 22:06:40 -05:00
|
|
|
'/api/v4/user/emails'
|
2019-09-26 17:06:29 -04:00
|
|
|
]
|
|
|
|
end
|
|
|
|
|
|
|
|
before do
|
|
|
|
settings_to_set[:protected_paths] = protected_paths
|
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with the token in the query string' do
|
2021-01-05 19:10:23 -05:00
|
|
|
let(:request_args) { [api(api_partial_url, personal_access_token: token), {}] }
|
|
|
|
let(:other_user_request_args) { [api(api_partial_url, personal_access_token: other_user_token), {}] }
|
2019-09-26 17:06:29 -04:00
|
|
|
|
2022-05-02 20:08:25 -04:00
|
|
|
it_behaves_like 'rate-limited user based token-authenticated requests'
|
2019-09-26 17:06:29 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
context 'with the token in the headers' do
|
2019-11-08 22:06:40 -05:00
|
|
|
let(:request_args) { api_get_args_with_token_headers(api_partial_url, personal_access_token_headers(token)) }
|
|
|
|
let(:other_user_request_args) { api_get_args_with_token_headers(api_partial_url, personal_access_token_headers(other_user_token)) }
|
2019-09-26 17:06:29 -04:00
|
|
|
|
2022-05-02 20:08:25 -04:00
|
|
|
it_behaves_like 'rate-limited user based token-authenticated requests'
|
2019-09-26 17:06:29 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe 'web requests authenticated with regular login' do
|
|
|
|
let(:throttle_setting_prefix) { 'throttle_protected_paths' }
|
|
|
|
let(:user) { create(:user) }
|
2019-11-08 22:06:40 -05:00
|
|
|
let(:url_that_requires_authentication) { '/users/confirmation' }
|
2019-09-26 17:06:29 -04:00
|
|
|
|
|
|
|
let(:protected_paths) do
|
|
|
|
[
|
|
|
|
url_that_requires_authentication
|
|
|
|
]
|
|
|
|
end
|
|
|
|
|
|
|
|
before do
|
|
|
|
settings_to_set[:protected_paths] = protected_paths
|
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
it_behaves_like 'rate-limited web authenticated requests'
|
|
|
|
end
|
|
|
|
end
|
2020-11-06 10:09:14 -05:00
|
|
|
|
2021-04-21 11:09:35 -04:00
|
|
|
describe 'Packages API' do
|
|
|
|
let(:request_method) { 'GET' }
|
|
|
|
|
|
|
|
context 'unauthenticated' do
|
|
|
|
let_it_be(:project) { create(:project, :public) }
|
|
|
|
|
|
|
|
let(:throttle_setting_prefix) { 'throttle_unauthenticated_packages_api' }
|
|
|
|
let(:packages_path_that_does_not_require_authentication) { "/api/v4/projects/#{project.id}/packages/conan/v1/ping" }
|
|
|
|
|
|
|
|
def do_request
|
|
|
|
get packages_path_that_does_not_require_authentication
|
|
|
|
end
|
|
|
|
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_unauthenticated_packages_api_requests_per_period] = requests_per_period
|
|
|
|
settings_to_set[:throttle_unauthenticated_packages_api_period_in_seconds] = period_in_seconds
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when unauthenticated packages api throttle is disabled' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_unauthenticated_packages_api_enabled] = false
|
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'allows requests over the rate limit' do
|
|
|
|
(1 + requests_per_period).times do
|
|
|
|
do_request
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when unauthenticated api throttle is enabled' do
|
|
|
|
before do
|
2021-09-14 11:12:05 -04:00
|
|
|
settings_to_set[:throttle_unauthenticated_api_requests_per_period] = requests_per_period
|
|
|
|
settings_to_set[:throttle_unauthenticated_api_period_in_seconds] = period_in_seconds
|
|
|
|
settings_to_set[:throttle_unauthenticated_api_enabled] = true
|
2021-04-21 11:09:35 -04:00
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'rejects requests over the unauthenticated api rate limit' do
|
|
|
|
requests_per_period.times do
|
|
|
|
do_request
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
|
|
end
|
|
|
|
|
|
|
|
expect_rejection { do_request }
|
|
|
|
end
|
|
|
|
end
|
2021-09-14 11:12:05 -04:00
|
|
|
|
|
|
|
context 'when unauthenticated web throttle is enabled' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_unauthenticated_web_requests_per_period] = requests_per_period
|
|
|
|
settings_to_set[:throttle_unauthenticated_web_period_in_seconds] = period_in_seconds
|
|
|
|
settings_to_set[:throttle_unauthenticated_web_enabled] = true
|
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'ignores unauthenticated web throttle' do
|
|
|
|
(1 + requests_per_period).times do
|
|
|
|
do_request
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2021-04-21 11:09:35 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
context 'when unauthenticated packages api throttle is enabled' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_unauthenticated_packages_api_requests_per_period] = requests_per_period # 1
|
|
|
|
settings_to_set[:throttle_unauthenticated_packages_api_period_in_seconds] = period_in_seconds # 10_000
|
|
|
|
settings_to_set[:throttle_unauthenticated_packages_api_enabled] = true
|
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'rejects requests over the rate limit' do
|
|
|
|
requests_per_period.times do
|
|
|
|
do_request
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
|
|
end
|
|
|
|
|
|
|
|
expect_rejection { do_request }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when unauthenticated api throttle is lower' do
|
|
|
|
before do
|
2021-09-14 11:12:05 -04:00
|
|
|
settings_to_set[:throttle_unauthenticated_api_requests_per_period] = 0
|
|
|
|
settings_to_set[:throttle_unauthenticated_api_period_in_seconds] = period_in_seconds
|
|
|
|
settings_to_set[:throttle_unauthenticated_api_enabled] = true
|
2021-04-21 11:09:35 -04:00
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'ignores unauthenticated api throttle' do
|
|
|
|
requests_per_period.times do
|
|
|
|
do_request
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
|
|
end
|
|
|
|
|
|
|
|
expect_rejection { do_request }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it_behaves_like 'tracking when dry-run mode is set' do
|
|
|
|
let(:throttle_name) { 'throttle_unauthenticated_packages_api' }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'authenticated', :api do
|
|
|
|
let_it_be(:project) { create(:project, :internal) }
|
|
|
|
let_it_be(:user) { create(:user) }
|
|
|
|
let_it_be(:token) { create(:personal_access_token, user: user) }
|
|
|
|
let_it_be(:other_user) { create(:user) }
|
|
|
|
let_it_be(:other_user_token) { create(:personal_access_token, user: other_user) }
|
|
|
|
|
|
|
|
let(:throttle_setting_prefix) { 'throttle_authenticated_packages_api' }
|
|
|
|
let(:api_partial_url) { "/projects/#{project.id}/packages/conan/v1/ping" }
|
|
|
|
|
|
|
|
before do
|
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with the token in the query string' do
|
|
|
|
let(:request_args) { [api(api_partial_url, personal_access_token: token), {}] }
|
|
|
|
let(:other_user_request_args) { [api(api_partial_url, personal_access_token: other_user_token), {}] }
|
|
|
|
|
2022-05-02 20:08:25 -04:00
|
|
|
it_behaves_like 'rate-limited user based token-authenticated requests'
|
2021-04-21 11:09:35 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
context 'with the token in the headers' do
|
|
|
|
let(:request_args) { api_get_args_with_token_headers(api_partial_url, personal_access_token_headers(token)) }
|
|
|
|
let(:other_user_request_args) { api_get_args_with_token_headers(api_partial_url, personal_access_token_headers(other_user_token)) }
|
|
|
|
|
2022-05-02 20:08:25 -04:00
|
|
|
it_behaves_like 'rate-limited user based token-authenticated requests'
|
2021-04-21 11:09:35 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
context 'precedence over authenticated api throttle' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_authenticated_packages_api_requests_per_period] = requests_per_period
|
|
|
|
settings_to_set[:throttle_authenticated_packages_api_period_in_seconds] = period_in_seconds
|
|
|
|
end
|
|
|
|
|
|
|
|
def do_request
|
|
|
|
get api(api_partial_url, personal_access_token: token)
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when authenticated packages api throttle is enabled' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_authenticated_packages_api_enabled] = true
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when authenticated api throttle is lower' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_authenticated_api_requests_per_period] = 0
|
|
|
|
settings_to_set[:throttle_authenticated_api_period_in_seconds] = period_in_seconds
|
|
|
|
settings_to_set[:throttle_authenticated_api_enabled] = true
|
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'ignores authenticated api throttle' do
|
|
|
|
requests_per_period.times do
|
|
|
|
do_request
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
|
|
end
|
|
|
|
|
|
|
|
expect_rejection { do_request }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when authenticated packages api throttle is disabled' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_authenticated_packages_api_enabled] = false
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when authenticated api throttle is enabled' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_authenticated_api_requests_per_period] = requests_per_period
|
|
|
|
settings_to_set[:throttle_authenticated_api_period_in_seconds] = period_in_seconds
|
|
|
|
settings_to_set[:throttle_authenticated_api_enabled] = true
|
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'rejects requests over the authenticated api rate limit' do
|
|
|
|
requests_per_period.times do
|
|
|
|
do_request
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
|
|
end
|
|
|
|
|
|
|
|
expect_rejection { do_request }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2022-05-02 20:08:25 -04:00
|
|
|
|
|
|
|
context 'authenticated via deploy token headers' do
|
|
|
|
let(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true, projects: [project]) }
|
|
|
|
let(:other_deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) }
|
|
|
|
|
|
|
|
let(:request_args) { [api(api_partial_url), { headers: deploy_token_headers(deploy_token) }] }
|
|
|
|
let(:other_user_request_args) { [api(api_partial_url), { headers: deploy_token_headers(other_deploy_token) }] }
|
|
|
|
|
|
|
|
it_behaves_like 'rate-limited deploy-token-authenticated requests'
|
|
|
|
end
|
2021-04-21 11:09:35 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-10-14 11:14:02 -04:00
|
|
|
describe 'dependency proxy' do
|
|
|
|
include DependencyProxyHelpers
|
|
|
|
|
|
|
|
let_it_be_with_reload(:group) { create(:group) }
|
|
|
|
let_it_be_with_reload(:other_group) { create(:group) }
|
|
|
|
let_it_be(:user) { create(:user) }
|
|
|
|
let_it_be(:other_user) { create(:user) }
|
|
|
|
|
|
|
|
let(:throttle_setting_prefix) { 'throttle_authenticated_web' }
|
|
|
|
let(:jwt_token) { build_jwt(user) }
|
|
|
|
let(:other_jwt_token) { build_jwt(other_user) }
|
|
|
|
let(:request_args) { [path, headers: jwt_token_authorization_headers(jwt_token)] }
|
|
|
|
let(:other_user_request_args) { [other_path, headers: jwt_token_authorization_headers(other_jwt_token)] }
|
|
|
|
|
|
|
|
before do
|
|
|
|
group.add_owner(user)
|
|
|
|
other_group.add_owner(other_user)
|
|
|
|
|
|
|
|
allow(Gitlab.config.dependency_proxy)
|
|
|
|
.to receive(:enabled).and_return(true)
|
|
|
|
token_response = { status: :success, token: 'abcd1234' }
|
|
|
|
allow_next_instance_of(DependencyProxy::RequestTokenService) do |instance|
|
|
|
|
allow(instance).to receive(:execute).and_return(token_response)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'getting a manifest' do
|
|
|
|
let_it_be(:manifest) { create(:dependency_proxy_manifest) }
|
|
|
|
|
|
|
|
let(:path) { "/v2/#{group.path}/dependency_proxy/containers/alpine/manifests/latest" }
|
|
|
|
let(:other_path) { "/v2/#{other_group.path}/dependency_proxy/containers/alpine/manifests/latest" }
|
|
|
|
let(:pull_response) { { status: :success, manifest: manifest, from_cache: false } }
|
2021-11-04 05:12:56 -04:00
|
|
|
let(:head_response) { { status: :success } }
|
2021-10-14 11:14:02 -04:00
|
|
|
|
|
|
|
before do
|
2021-12-01 10:13:55 -05:00
|
|
|
allow_next_instance_of(DependencyProxy::FindCachedManifestService) do |instance|
|
2021-10-14 11:14:02 -04:00
|
|
|
allow(instance).to receive(:execute).and_return(pull_response)
|
|
|
|
end
|
2021-11-04 05:12:56 -04:00
|
|
|
allow_next_instance_of(DependencyProxy::HeadManifestService) do |instance|
|
|
|
|
allow(instance).to receive(:execute).and_return(head_response)
|
|
|
|
end
|
2021-10-14 11:14:02 -04:00
|
|
|
end
|
|
|
|
|
2022-05-02 20:08:25 -04:00
|
|
|
it_behaves_like 'rate-limited user based token-authenticated requests'
|
2021-10-14 11:14:02 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
context 'getting a blob' do
|
|
|
|
let_it_be(:blob) { create(:dependency_proxy_blob) }
|
2022-01-05 01:13:32 -05:00
|
|
|
let_it_be(:other_blob) { create(:dependency_proxy_blob) }
|
2021-10-14 11:14:02 -04:00
|
|
|
|
2022-01-05 01:13:32 -05:00
|
|
|
let(:path) { "/v2/#{blob.group.path}/dependency_proxy/containers/alpine/blobs/sha256:a0d0a0d46f8b52473982a3c466318f479767577551a53ffc9074c9fa7035982e" }
|
|
|
|
let(:other_path) { "/v2/#{other_blob.group.path}/dependency_proxy/containers/alpine/blobs/sha256:a0d0a0d46f8b52473982a3c466318f479767577551a53ffc9074c9fa7035982e" }
|
2021-10-14 11:14:02 -04:00
|
|
|
|
2022-05-02 20:08:25 -04:00
|
|
|
it_behaves_like 'rate-limited user based token-authenticated requests'
|
2021-10-14 11:14:02 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-09-01 14:08:49 -04:00
|
|
|
describe 'authenticated git lfs requests', :api do
|
|
|
|
let_it_be(:project) { create(:project, :internal) }
|
|
|
|
let_it_be(:user) { create(:user) }
|
|
|
|
let_it_be(:token) { create(:personal_access_token, user: user) }
|
|
|
|
let_it_be(:other_user) { create(:user) }
|
|
|
|
let_it_be(:other_user_token) { create(:personal_access_token, user: other_user) }
|
|
|
|
|
|
|
|
let(:request_method) { 'GET' }
|
|
|
|
let(:throttle_setting_prefix) { 'throttle_authenticated_git_lfs' }
|
|
|
|
let(:git_lfs_url) { "/#{project.full_path}.git/info/lfs/locks" }
|
|
|
|
|
|
|
|
before do
|
|
|
|
allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
|
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with regular login' do
|
|
|
|
let(:url_that_requires_authentication) { git_lfs_url }
|
|
|
|
|
|
|
|
it_behaves_like 'rate-limited web authenticated requests'
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with the token in the headers' do
|
|
|
|
let(:request_args) { [git_lfs_url, { headers: basic_auth_headers(user, token) }] }
|
|
|
|
let(:other_user_request_args) { [git_lfs_url, { headers: basic_auth_headers(other_user, other_user_token) }] }
|
|
|
|
|
2022-05-02 20:08:25 -04:00
|
|
|
it_behaves_like 'rate-limited user based token-authenticated requests'
|
2021-09-01 14:08:49 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
context 'precedence over authenticated web throttle' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_authenticated_git_lfs_requests_per_period] = requests_per_period
|
|
|
|
settings_to_set[:throttle_authenticated_git_lfs_period_in_seconds] = period_in_seconds
|
|
|
|
end
|
|
|
|
|
|
|
|
def do_request
|
|
|
|
get git_lfs_url, headers: basic_auth_headers(user, token)
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when authenticated git lfs throttle is enabled' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_authenticated_git_lfs_enabled] = true
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when authenticated web throttle is lower' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_authenticated_web_requests_per_period] = 0
|
|
|
|
settings_to_set[:throttle_authenticated_web_period_in_seconds] = period_in_seconds
|
|
|
|
settings_to_set[:throttle_authenticated_web_enabled] = true
|
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'ignores authenticated web throttle' do
|
|
|
|
requests_per_period.times do
|
|
|
|
do_request
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
|
|
end
|
|
|
|
|
|
|
|
expect_rejection { do_request }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when authenticated git lfs throttle is disabled' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_authenticated_git_lfs_enabled] = false
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when authenticated web throttle is enabled' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_authenticated_web_requests_per_period] = requests_per_period
|
|
|
|
settings_to_set[:throttle_authenticated_web_period_in_seconds] = period_in_seconds
|
|
|
|
settings_to_set[:throttle_authenticated_web_enabled] = true
|
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'rejects requests over the authenticated web rate limit' do
|
|
|
|
requests_per_period.times do
|
|
|
|
do_request
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
|
|
end
|
|
|
|
|
|
|
|
expect_rejection { do_request }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-09-09 08:09:09 -04:00
|
|
|
describe 'Files API' do
|
|
|
|
let(:request_method) { 'GET' }
|
|
|
|
|
|
|
|
context 'unauthenticated' do
|
|
|
|
let_it_be(:project) { create(:project, :public, :custom_repo, files: { 'README' => 'foo' }) }
|
|
|
|
|
|
|
|
let(:throttle_setting_prefix) { 'throttle_unauthenticated_files_api' }
|
|
|
|
let(:files_path_that_does_not_require_authentication) { "/api/v4/projects/#{project.id}/repository/files/README?ref=master" }
|
|
|
|
|
|
|
|
def do_request
|
|
|
|
get files_path_that_does_not_require_authentication
|
|
|
|
end
|
|
|
|
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_unauthenticated_files_api_requests_per_period] = requests_per_period
|
|
|
|
settings_to_set[:throttle_unauthenticated_files_api_period_in_seconds] = period_in_seconds
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when unauthenticated files api throttle is disabled' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_unauthenticated_files_api_enabled] = false
|
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'allows requests over the rate limit' do
|
|
|
|
(1 + requests_per_period).times do
|
|
|
|
do_request
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when unauthenticated api throttle is enabled' do
|
|
|
|
before do
|
2021-09-14 11:12:05 -04:00
|
|
|
settings_to_set[:throttle_unauthenticated_api_requests_per_period] = requests_per_period
|
|
|
|
settings_to_set[:throttle_unauthenticated_api_period_in_seconds] = period_in_seconds
|
|
|
|
settings_to_set[:throttle_unauthenticated_api_enabled] = true
|
2021-09-09 08:09:09 -04:00
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'rejects requests over the unauthenticated api rate limit' do
|
|
|
|
requests_per_period.times do
|
|
|
|
do_request
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
|
|
end
|
|
|
|
|
|
|
|
expect_rejection { do_request }
|
|
|
|
end
|
|
|
|
end
|
2021-09-14 11:12:05 -04:00
|
|
|
|
|
|
|
context 'when unauthenticated web throttle is enabled' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_unauthenticated_web_requests_per_period] = requests_per_period
|
|
|
|
settings_to_set[:throttle_unauthenticated_web_period_in_seconds] = period_in_seconds
|
|
|
|
settings_to_set[:throttle_unauthenticated_web_enabled] = true
|
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'ignores unauthenticated web throttle' do
|
|
|
|
(1 + requests_per_period).times do
|
|
|
|
do_request
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2021-09-09 08:09:09 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
context 'when unauthenticated files api throttle is enabled' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_unauthenticated_files_api_requests_per_period] = requests_per_period # 1
|
|
|
|
settings_to_set[:throttle_unauthenticated_files_api_period_in_seconds] = period_in_seconds # 10_000
|
|
|
|
settings_to_set[:throttle_unauthenticated_files_api_enabled] = true
|
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'rejects requests over the rate limit' do
|
|
|
|
requests_per_period.times do
|
|
|
|
do_request
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
|
|
end
|
|
|
|
|
|
|
|
expect_rejection { do_request }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when unauthenticated api throttle is lower' do
|
|
|
|
before do
|
2021-09-14 11:12:05 -04:00
|
|
|
settings_to_set[:throttle_unauthenticated_api_requests_per_period] = 0
|
|
|
|
settings_to_set[:throttle_unauthenticated_api_period_in_seconds] = period_in_seconds
|
|
|
|
settings_to_set[:throttle_unauthenticated_api_enabled] = true
|
2021-09-09 08:09:09 -04:00
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'ignores unauthenticated api throttle' do
|
|
|
|
requests_per_period.times do
|
|
|
|
do_request
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
|
|
end
|
|
|
|
|
|
|
|
expect_rejection { do_request }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it_behaves_like 'tracking when dry-run mode is set' do
|
|
|
|
let(:throttle_name) { 'throttle_unauthenticated_files_api' }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'authenticated', :api do
|
|
|
|
let_it_be(:project) { create(:project, :internal, :custom_repo, files: { 'README' => 'foo' }) }
|
|
|
|
let_it_be(:user) { create(:user) }
|
|
|
|
let_it_be(:token) { create(:personal_access_token, user: user) }
|
|
|
|
let_it_be(:other_user) { create(:user) }
|
|
|
|
let_it_be(:other_user_token) { create(:personal_access_token, user: other_user) }
|
|
|
|
|
|
|
|
let(:throttle_setting_prefix) { 'throttle_authenticated_files_api' }
|
|
|
|
let(:api_partial_url) { "/projects/#{project.id}/repository/files/README?ref=master" }
|
|
|
|
|
|
|
|
before do
|
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with the token in the query string' do
|
|
|
|
let(:request_args) { [api(api_partial_url, personal_access_token: token), {}] }
|
|
|
|
let(:other_user_request_args) { [api(api_partial_url, personal_access_token: other_user_token), {}] }
|
|
|
|
|
2022-05-02 20:08:25 -04:00
|
|
|
it_behaves_like 'rate-limited user based token-authenticated requests'
|
2021-09-09 08:09:09 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
context 'with the token in the headers' do
|
|
|
|
let(:request_args) { api_get_args_with_token_headers(api_partial_url, personal_access_token_headers(token)) }
|
|
|
|
let(:other_user_request_args) { api_get_args_with_token_headers(api_partial_url, personal_access_token_headers(other_user_token)) }
|
|
|
|
|
2022-05-02 20:08:25 -04:00
|
|
|
it_behaves_like 'rate-limited user based token-authenticated requests'
|
2021-09-09 08:09:09 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
context 'precedence over authenticated api throttle' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_authenticated_files_api_requests_per_period] = requests_per_period
|
|
|
|
settings_to_set[:throttle_authenticated_files_api_period_in_seconds] = period_in_seconds
|
|
|
|
end
|
|
|
|
|
|
|
|
def do_request
|
|
|
|
get api(api_partial_url, personal_access_token: token)
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when authenticated files api throttle is enabled' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_authenticated_files_api_enabled] = true
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when authenticated api throttle is lower' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_authenticated_api_requests_per_period] = 0
|
|
|
|
settings_to_set[:throttle_authenticated_api_period_in_seconds] = period_in_seconds
|
|
|
|
settings_to_set[:throttle_authenticated_api_enabled] = true
|
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'ignores authenticated api throttle' do
|
|
|
|
requests_per_period.times do
|
|
|
|
do_request
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
|
|
end
|
|
|
|
|
|
|
|
expect_rejection { do_request }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when authenticated files api throttle is disabled' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_authenticated_files_api_enabled] = false
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when authenticated api throttle is enabled' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_authenticated_api_requests_per_period] = requests_per_period
|
|
|
|
settings_to_set[:throttle_authenticated_api_period_in_seconds] = period_in_seconds
|
|
|
|
settings_to_set[:throttle_authenticated_api_enabled] = true
|
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'rejects requests over the authenticated api rate limit' do
|
|
|
|
requests_per_period.times do
|
|
|
|
do_request
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
|
|
end
|
|
|
|
|
|
|
|
expect_rejection { do_request }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-09-28 08:11:10 -04:00
|
|
|
describe 'Deprecated API', :api do
|
|
|
|
let_it_be(:group) { create(:group, :public) }
|
|
|
|
|
|
|
|
let(:request_method) { 'GET' }
|
|
|
|
let(:path) { "/groups/#{group.id}" }
|
|
|
|
let(:params) { {} }
|
|
|
|
|
|
|
|
context 'unauthenticated' do
|
|
|
|
let(:throttle_setting_prefix) { 'throttle_unauthenticated_deprecated_api' }
|
|
|
|
|
|
|
|
def do_request
|
|
|
|
get(api(path), params: params)
|
|
|
|
end
|
|
|
|
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_unauthenticated_deprecated_api_requests_per_period] = requests_per_period
|
|
|
|
settings_to_set[:throttle_unauthenticated_deprecated_api_period_in_seconds] = period_in_seconds
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when unauthenticated deprecated api throttle is disabled' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_unauthenticated_deprecated_api_enabled] = false
|
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'allows requests over the rate limit' do
|
|
|
|
(1 + requests_per_period).times do
|
|
|
|
do_request
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when unauthenticated api throttle is enabled' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_unauthenticated_api_requests_per_period] = requests_per_period
|
|
|
|
settings_to_set[:throttle_unauthenticated_api_period_in_seconds] = period_in_seconds
|
|
|
|
settings_to_set[:throttle_unauthenticated_api_enabled] = true
|
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'rejects requests over the unauthenticated api rate limit' do
|
|
|
|
requests_per_period.times do
|
|
|
|
do_request
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
|
|
end
|
|
|
|
|
|
|
|
expect_rejection { do_request }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when unauthenticated web throttle is enabled' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_unauthenticated_web_requests_per_period] = requests_per_period
|
|
|
|
settings_to_set[:throttle_unauthenticated_web_period_in_seconds] = period_in_seconds
|
|
|
|
settings_to_set[:throttle_unauthenticated_web_enabled] = true
|
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'ignores unauthenticated web throttle' do
|
|
|
|
(1 + requests_per_period).times do
|
|
|
|
do_request
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when unauthenticated deprecated api throttle is enabled' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_unauthenticated_deprecated_api_requests_per_period] = requests_per_period # 1
|
|
|
|
settings_to_set[:throttle_unauthenticated_deprecated_api_period_in_seconds] = period_in_seconds # 10_000
|
|
|
|
settings_to_set[:throttle_unauthenticated_deprecated_api_enabled] = true
|
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when group endpoint is given with_project=false' do
|
|
|
|
let(:params) { { with_projects: false } }
|
|
|
|
|
|
|
|
it 'permits requests over the rate limit' do
|
|
|
|
(1 + requests_per_period).times do
|
|
|
|
do_request
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'rejects requests over the rate limit' do
|
|
|
|
requests_per_period.times do
|
|
|
|
do_request
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
|
|
end
|
|
|
|
|
|
|
|
expect_rejection { do_request }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when unauthenticated api throttle is lower' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_unauthenticated_api_requests_per_period] = 0
|
|
|
|
settings_to_set[:throttle_unauthenticated_api_period_in_seconds] = period_in_seconds
|
|
|
|
settings_to_set[:throttle_unauthenticated_api_enabled] = true
|
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'ignores unauthenticated api throttle' do
|
|
|
|
requests_per_period.times do
|
|
|
|
do_request
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
|
|
end
|
|
|
|
|
|
|
|
expect_rejection { do_request }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it_behaves_like 'tracking when dry-run mode is set' do
|
|
|
|
let(:throttle_name) { 'throttle_unauthenticated_deprecated_api' }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'authenticated' do
|
|
|
|
let_it_be(:user) { create(:user) }
|
|
|
|
let_it_be(:member) { group.add_owner(user) }
|
|
|
|
let_it_be(:token) { create(:personal_access_token, user: user) }
|
|
|
|
let_it_be(:other_user) { create(:user) }
|
|
|
|
let_it_be(:other_user_token) { create(:personal_access_token, user: other_user) }
|
|
|
|
|
|
|
|
let(:throttle_setting_prefix) { 'throttle_authenticated_deprecated_api' }
|
|
|
|
|
|
|
|
before do
|
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with the token in the query string' do
|
|
|
|
let(:request_args) { [api(path, personal_access_token: token), {}] }
|
|
|
|
let(:other_user_request_args) { [api(path, personal_access_token: other_user_token), {}] }
|
|
|
|
|
2022-05-02 20:08:25 -04:00
|
|
|
it_behaves_like 'rate-limited user based token-authenticated requests'
|
2021-09-28 08:11:10 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
context 'with the token in the headers' do
|
|
|
|
let(:request_args) { api_get_args_with_token_headers(path, personal_access_token_headers(token)) }
|
|
|
|
let(:other_user_request_args) { api_get_args_with_token_headers(path, personal_access_token_headers(other_user_token)) }
|
|
|
|
|
2022-05-02 20:08:25 -04:00
|
|
|
it_behaves_like 'rate-limited user based token-authenticated requests'
|
2021-09-28 08:11:10 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
context 'precedence over authenticated api throttle' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_authenticated_deprecated_api_requests_per_period] = requests_per_period
|
|
|
|
settings_to_set[:throttle_authenticated_deprecated_api_period_in_seconds] = period_in_seconds
|
|
|
|
end
|
|
|
|
|
|
|
|
def do_request
|
|
|
|
get(api(path, personal_access_token: token), params: params)
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when authenticated deprecated api throttle is enabled' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_authenticated_deprecated_api_enabled] = true
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when authenticated api throttle is lower' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_authenticated_api_requests_per_period] = 0
|
|
|
|
settings_to_set[:throttle_authenticated_api_period_in_seconds] = period_in_seconds
|
|
|
|
settings_to_set[:throttle_authenticated_api_enabled] = true
|
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'ignores authenticated api throttle' do
|
|
|
|
requests_per_period.times do
|
|
|
|
do_request
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
|
|
end
|
|
|
|
|
|
|
|
expect_rejection { do_request }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when authenticated deprecated api throttle is disabled' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_authenticated_deprecated_api_enabled] = false
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when authenticated api throttle is enabled' do
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_authenticated_api_requests_per_period] = requests_per_period
|
|
|
|
settings_to_set[:throttle_authenticated_api_period_in_seconds] = period_in_seconds
|
|
|
|
settings_to_set[:throttle_authenticated_api_enabled] = true
|
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'rejects requests over the authenticated api rate limit' do
|
|
|
|
requests_per_period.times do
|
|
|
|
do_request
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
|
|
end
|
|
|
|
|
|
|
|
expect_rejection { do_request }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-11-06 10:09:14 -05:00
|
|
|
describe 'throttle bypass header' do
|
|
|
|
let(:headers) { {} }
|
|
|
|
let(:bypass_header) { 'gitlab-bypass-rate-limiting' }
|
|
|
|
|
|
|
|
def do_request
|
|
|
|
get '/users/sign_in', headers: headers
|
|
|
|
end
|
|
|
|
|
|
|
|
before do
|
|
|
|
# Disabling protected paths throttle, otherwise requests to
|
|
|
|
# '/users/sign_in' are caught by this throttle.
|
|
|
|
settings_to_set[:throttle_protected_paths_enabled] = false
|
|
|
|
|
|
|
|
# Set low limits
|
|
|
|
settings_to_set[:throttle_unauthenticated_requests_per_period] = requests_per_period
|
|
|
|
settings_to_set[:throttle_unauthenticated_period_in_seconds] = period_in_seconds
|
|
|
|
|
|
|
|
stub_env('GITLAB_THROTTLE_BYPASS_HEADER', bypass_header)
|
|
|
|
settings_to_set[:throttle_unauthenticated_enabled] = true
|
|
|
|
|
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
shared_examples 'reject requests over the rate limit' do
|
|
|
|
it 'rejects requests over the rate limit' do
|
|
|
|
# At first, allow requests under the rate limit.
|
|
|
|
requests_per_period.times do
|
|
|
|
do_request
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
|
|
end
|
|
|
|
|
|
|
|
# the last straw
|
|
|
|
expect_rejection { do_request }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'without the bypass header set' do
|
|
|
|
it_behaves_like 'reject requests over the rate limit'
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with bypass header set to 1' do
|
|
|
|
let(:headers) { { bypass_header => '1' } }
|
|
|
|
|
|
|
|
it 'does not throttle' do
|
|
|
|
(1 + requests_per_period).times do
|
|
|
|
do_request
|
|
|
|
expect(response).to have_gitlab_http_status(:ok)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with bypass header set to some other value' do
|
|
|
|
let(:headers) { { bypass_header => 'some other value' } }
|
|
|
|
|
|
|
|
it_behaves_like 'reject requests over the rate limit'
|
|
|
|
end
|
|
|
|
end
|
2021-07-20 08:10:29 -04:00
|
|
|
|
|
|
|
describe 'Gitlab::RackAttack::Request#unauthenticated?' do
|
|
|
|
let_it_be(:url) { "/api/v4/projects" }
|
|
|
|
let_it_be(:user) { create(:user) }
|
|
|
|
|
|
|
|
def expect_unauthenticated_request
|
|
|
|
expect_next_instance_of(Rack::Attack::Request) do |instance|
|
|
|
|
expect(instance.unauthenticated?).to be true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def expect_authenticated_request
|
|
|
|
expect_next_instance_of(Rack::Attack::Request) do |instance|
|
|
|
|
expect(instance.unauthenticated?).to be false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
before do
|
|
|
|
settings_to_set[:throttle_unauthenticated_enabled] = true
|
|
|
|
stub_application_setting(settings_to_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'without authentication' do
|
|
|
|
it 'request is unauthenticated' do
|
|
|
|
expect_unauthenticated_request
|
|
|
|
|
|
|
|
get url
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'authenticated by a runner token' do
|
|
|
|
let_it_be(:runner) { create(:ci_runner) }
|
|
|
|
|
|
|
|
it 'request is authenticated' do
|
|
|
|
expect_authenticated_request
|
|
|
|
|
|
|
|
get url, params: { token: runner.token }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'authenticated with personal access token' do
|
|
|
|
let_it_be(:personal_access_token) { create(:personal_access_token, user: user) }
|
|
|
|
|
|
|
|
it 'request is authenticated by token in query string' do
|
|
|
|
expect_authenticated_request
|
|
|
|
|
|
|
|
get url, params: { private_token: personal_access_token.token }
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'request is authenticated by token in the headers' do
|
|
|
|
expect_authenticated_request
|
|
|
|
|
|
|
|
get url, headers: personal_access_token_headers(personal_access_token)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'request is authenticated by token in the OAuth headers' do
|
|
|
|
expect_authenticated_request
|
|
|
|
|
|
|
|
get url, headers: oauth_token_headers(personal_access_token)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'request is authenticated by token in basic auth' do
|
|
|
|
expect_authenticated_request
|
|
|
|
|
|
|
|
get url, headers: basic_auth_headers(user, personal_access_token)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'authenticated with OAuth token' do
|
|
|
|
let(:application) { Doorkeeper::Application.create!(name: "MyApp", redirect_uri: "https://app.com", owner: user) }
|
|
|
|
let(:oauth_token) { Doorkeeper::AccessToken.create!(application_id: application.id, resource_owner_id: user.id, scopes: "api") }
|
|
|
|
|
|
|
|
it 'request is authenticated by token in query string' do
|
|
|
|
expect_authenticated_request
|
|
|
|
|
|
|
|
get url, params: { access_token: oauth_token.token }
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'request is authenticated by token in the headers' do
|
|
|
|
expect_authenticated_request
|
|
|
|
|
|
|
|
get url, headers: oauth_token_headers(oauth_token)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'authenticated with lfs token' do
|
2021-09-30 14:11:31 -04:00
|
|
|
let(:lfs_url) { '/namespace/repo.git/info/lfs/objects/batch' }
|
|
|
|
let(:lfs_token) { Gitlab::LfsToken.new(user) }
|
|
|
|
let(:encoded_login) { ["#{user.username}:#{lfs_token.token}"].pack('m0') }
|
|
|
|
let(:headers) { { 'AUTHORIZATION' => "Basic #{encoded_login}" } }
|
2021-07-20 08:10:29 -04:00
|
|
|
|
2021-09-30 14:11:31 -04:00
|
|
|
it 'request is authenticated by token in basic auth' do
|
2021-07-20 08:10:29 -04:00
|
|
|
expect_authenticated_request
|
|
|
|
|
2021-09-30 14:11:31 -04:00
|
|
|
get lfs_url, headers: headers
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'request is not authenticated with API URL' do
|
|
|
|
expect_unauthenticated_request
|
|
|
|
|
|
|
|
get url, headers: headers
|
2021-07-20 08:10:29 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'authenticated with regular login' do
|
2021-09-30 14:11:31 -04:00
|
|
|
let(:encoded_login) { ["#{user.username}:#{user.password}"].pack('m0') }
|
|
|
|
let(:headers) { { 'AUTHORIZATION' => "Basic #{encoded_login}" } }
|
|
|
|
|
2021-07-20 08:10:29 -04:00
|
|
|
it 'request is authenticated after login' do
|
|
|
|
login_as(user)
|
|
|
|
|
|
|
|
expect_authenticated_request
|
|
|
|
|
|
|
|
get url
|
|
|
|
end
|
|
|
|
|
2021-09-30 14:11:31 -04:00
|
|
|
it 'request is not authenticated by credentials in basic auth' do
|
|
|
|
expect_unauthenticated_request
|
2021-07-20 08:10:29 -04:00
|
|
|
|
2021-09-30 14:11:31 -04:00
|
|
|
get url, headers: headers
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with POST git-upload-pack' do
|
|
|
|
it 'request is authenticated by credentials in basic auth' do
|
|
|
|
expect(::Gitlab::Workhorse).to receive(:verify_api_request!)
|
|
|
|
|
|
|
|
expect_authenticated_request
|
2021-07-20 08:10:29 -04:00
|
|
|
|
2021-09-30 14:11:31 -04:00
|
|
|
post '/namespace/repo.git/git-upload-pack', headers: headers
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with GET info/refs' do
|
|
|
|
it 'request is authenticated by credentials in basic auth' do
|
|
|
|
expect(::Gitlab::Workhorse).to receive(:verify_api_request!)
|
|
|
|
|
|
|
|
expect_authenticated_request
|
|
|
|
|
|
|
|
get '/namespace/repo.git/info/refs?service=git-upload-pack', headers: headers
|
|
|
|
end
|
2021-07-20 08:10:29 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2017-10-11 19:39:58 -04:00
|
|
|
end
|