Commit 5f21e69a authored by Thong Kuah's avatar Thong Kuah

Merge branch 'rate-limit-imports' into 'master'

Rate limit project imports

See merge request gitlab-org/gitlab!22644
parents a1e25532 ba72aa4c
# frozen_string_literal: true # frozen_string_literal: true
class Import::BaseController < ApplicationController class Import::BaseController < ApplicationController
before_action :import_rate_limit, only: [:create]
private private
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
...@@ -37,4 +39,18 @@ class Import::BaseController < ApplicationController ...@@ -37,4 +39,18 @@ class Import::BaseController < ApplicationController
def project_save_error(project) def project_save_error(project)
project.errors.full_messages.join(', ') project.errors.full_messages.join(', ')
end end
def import_rate_limit
key = "project_import".to_sym
if rate_limiter.throttled?(key, scope: [current_user, key])
rate_limiter.log_request(request, "#{key}_request_limit".to_sym, current_user)
redirect_back_or_default(options: { alert: _('This endpoint has been requested too many times. Try again later.') })
end
end
def rate_limiter
::Gitlab::ApplicationRateLimiter
end
end end
---
title: Add rate limiter to Project Imports
merge_request: 22644
author:
type: other
...@@ -123,3 +123,13 @@ NOTE: **Note:** ...@@ -123,3 +123,13 @@ NOTE: **Note:**
If use of the `Internal` visibility level If use of the `Internal` visibility level
[is restricted](../../../public_access/public_access.md#restricting-the-use-of-public-or-internal-projects), [is restricted](../../../public_access/public_access.md#restricting-the-use-of-public-or-internal-projects),
all imported projects are given the visibility of `Private`. all imported projects are given the visibility of `Private`.
## Rate limits
To help avoid abuse, users are rate limited to:
| Request Type | Limit |
| ---------------- | --------------------------- |
| Export | 1 project per 5 minutes |
| Download export | 10 projects per 10 minutes |
| Import | 30 projects per 10 minutes |
...@@ -22,6 +22,7 @@ module Gitlab ...@@ -22,6 +22,7 @@ module Gitlab
project_export: { threshold: 1, interval: 5.minutes }, project_export: { threshold: 1, interval: 5.minutes },
project_download_export: { threshold: 10, interval: 10.minutes }, project_download_export: { threshold: 10, interval: 10.minutes },
project_generate_new_export: { threshold: 1, interval: 5.minutes }, project_generate_new_export: { threshold: 1, interval: 5.minutes },
project_import: { threshold: 30, interval: 10.minutes },
play_pipeline_schedule: { threshold: 1, interval: 1.minute }, play_pipeline_schedule: { threshold: 1, interval: 1.minute },
show_raw_controller: { threshold: -> { Gitlab::CurrentSettings.current_application_settings.raw_blob_request_limit }, interval: 1.minute } show_raw_controller: { threshold: -> { Gitlab::CurrentSettings.current_application_settings.raw_blob_request_limit }, interval: 1.minute }
}.freeze }.freeze
......
...@@ -136,6 +136,8 @@ describe Import::BitbucketController do ...@@ -136,6 +136,8 @@ describe Import::BitbucketController do
expect(response).to have_gitlab_http_status(422) expect(response).to have_gitlab_http_status(422)
end end
it_behaves_like 'project import rate limiter'
context "when the repository owner is the Bitbucket user" do context "when the repository owner is the Bitbucket user" do
context "when the Bitbucket user and GitLab user's usernames match" do context "when the Bitbucket user and GitLab user's usernames match" do
it "takes the current user's namespace" do it "takes the current user's namespace" do
......
...@@ -102,6 +102,8 @@ describe Import::BitbucketServerController do ...@@ -102,6 +102,8 @@ describe Import::BitbucketServerController do
expect(response).to have_gitlab_http_status(422) expect(response).to have_gitlab_http_status(422)
end end
it_behaves_like 'project import rate limiter'
end end
describe 'POST configure' do describe 'POST configure' do
......
...@@ -75,4 +75,8 @@ describe Import::FogbugzController do ...@@ -75,4 +75,8 @@ describe Import::FogbugzController do
expect(assigns(:repos)).to eq([]) expect(assigns(:repos)).to eq([])
end end
end end
describe 'POST create' do
it_behaves_like 'project import rate limiter'
end
end end
...@@ -41,6 +41,8 @@ describe Import::GiteaController do ...@@ -41,6 +41,8 @@ describe Import::GiteaController do
assign_host_url assign_host_url
end end
end end
it_behaves_like 'project import rate limiter'
end end
describe "GET realtime_changes" do describe "GET realtime_changes" do
......
...@@ -71,6 +71,8 @@ describe Import::GithubController do ...@@ -71,6 +71,8 @@ describe Import::GithubController do
describe "POST create" do describe "POST create" do
it_behaves_like 'a GitHub-ish import controller: POST create' it_behaves_like 'a GitHub-ish import controller: POST create'
it_behaves_like 'project import rate limiter'
end end
describe "GET realtime_changes" do describe "GET realtime_changes" do
......
...@@ -282,6 +282,8 @@ describe Import::GitlabController do ...@@ -282,6 +282,8 @@ describe Import::GitlabController do
expect(response).to have_gitlab_http_status(422) expect(response).to have_gitlab_http_status(422)
end end
end end
it_behaves_like 'project import rate limiter'
end end
end end
end end
...@@ -36,5 +36,7 @@ describe Import::GitlabProjectsController do ...@@ -36,5 +36,7 @@ describe Import::GitlabProjectsController do
expect(response).to have_gitlab_http_status(302) expect(response).to have_gitlab_http_status(302)
end end
end end
it_behaves_like 'project import rate limiter'
end end
end end
...@@ -58,4 +58,8 @@ describe Import::GoogleCodeController do ...@@ -58,4 +58,8 @@ describe Import::GoogleCodeController do
expect(assigns(:incompatible_repos)).to eq([@repo]) expect(assigns(:incompatible_repos)).to eq([@repo])
end end
end end
describe "POST create" do
it_behaves_like 'project import rate limiter'
end
end end
...@@ -88,5 +88,7 @@ describe Import::PhabricatorController do ...@@ -88,5 +88,7 @@ describe Import::PhabricatorController do
expect { post_create }.not_to change { current_user.namespace.projects.reload.size } expect { post_create }.not_to change { current_user.namespace.projects.reload.size }
end end
end end
it_behaves_like 'project import rate limiter'
end end
end end
# frozen_string_literal: true
shared_examples 'project import rate limiter' do
let(:user) { create(:user) }
before do
sign_in(user)
end
context 'when limit exceeds' do
before do
allow(Gitlab::ApplicationRateLimiter).to receive(:throttled?).and_return(true)
end
it 'notifies and redirects user' do
post :create, params: {}
expect(flash[:alert]).to eq('This endpoint has been requested too many times. Try again later.')
expect(response).to have_gitlab_http_status(302)
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment