Commit 2ab7e8ac authored by Dmytro Zaporozhets's avatar Dmytro Zaporozhets

Merge branch '55241-rate-limit-issue-creation-api' into 'master'

Introduce rate limit for creating issues via API

See merge request gitlab-org/gitlab!28130
parents 46d7ecb6 8a981930
---
title: Introduce rate limit for creating issues via API
merge_request: 28130
author:
type: performance
...@@ -740,6 +740,14 @@ the `weight` parameter: ...@@ -740,6 +740,14 @@ the `weight` parameter:
**Note**: The `closed_by` attribute was [introduced in GitLab 10.6](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17042). This value will only be present for issues which were closed after GitLab 10.6 and when the user account that closed the issue still exists. **Note**: The `closed_by` attribute was [introduced in GitLab 10.6](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17042). This value will only be present for issues which were closed after GitLab 10.6 and when the user account that closed the issue still exists.
## Rate limits
To help avoid abuse, users are limited to:
| Request Type | Limit |
| ---------------- | --------------------------- |
| Create | 300 issues per minute |
## Edit issue ## Edit issue
Updates an existing project issue. This call is also used to mark an issue as Updates an existing project issue. This call is also used to mark an issue as
......
# frozen_string_literal: true
module API
module Helpers
module RateLimiter
def check_rate_limit!(key, scope)
if rate_limiter.throttled?(key, scope: scope)
log_request(key)
render_exceeded_limit_error!
end
end
private
def rate_limiter
::Gitlab::ApplicationRateLimiter
end
def render_exceeded_limit_error!
render_api_error!({ error: _('This endpoint has been requested too many times. Try again later.') }, 429)
end
def log_request(key)
rate_limiter.log_request(request, "#{key}_request_limit".to_sym, current_user)
end
end
end
end
...@@ -4,6 +4,7 @@ module API ...@@ -4,6 +4,7 @@ module API
class Issues < Grape::API class Issues < Grape::API
include PaginationParams include PaginationParams
helpers Helpers::IssuesHelpers helpers Helpers::IssuesHelpers
helpers Helpers::RateLimiter
helpers ::Gitlab::IssuableMetadata helpers ::Gitlab::IssuableMetadata
before { authenticate_non_get! } before { authenticate_non_get! }
...@@ -211,6 +212,8 @@ module API ...@@ -211,6 +212,8 @@ module API
post ':id/issues' do post ':id/issues' do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42320') Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42320')
check_rate_limit! :issues_create, [current_user, :issues_create]
authorize! :create_issue, user_project authorize! :create_issue, user_project
params.delete(:created_at) unless current_user.can?(:set_issue_created_at, user_project) params.delete(:created_at) unless current_user.can?(:set_issue_created_at, user_project)
......
...@@ -2,15 +2,8 @@ ...@@ -2,15 +2,8 @@
module API module API
class ProjectExport < Grape::API class ProjectExport < Grape::API
helpers do helpers Helpers::RateLimiter
def throttled?(action)
rate_limiter.throttled?(action, scope: [current_user, action, user_project])
end
def rate_limiter
::Gitlab::ApplicationRateLimiter
end
end
before do before do
not_found! unless Gitlab::CurrentSettings.project_export_enabled? not_found! unless Gitlab::CurrentSettings.project_export_enabled?
authorize_admin_project authorize_admin_project
...@@ -32,9 +25,7 @@ module API ...@@ -32,9 +25,7 @@ module API
detail 'This feature was introduced in GitLab 10.6.' detail 'This feature was introduced in GitLab 10.6.'
end end
get ':id/export/download' do get ':id/export/download' do
if throttled?(:project_download_export) check_rate_limit! :project_download_export, [current_user, :project_download_export, user_project]
render_api_error!({ error: 'This endpoint has been requested too many times. Try again later.' }, 429)
end
if user_project.export_file_exists? if user_project.export_file_exists?
present_carrierwave_file!(user_project.export_file) present_carrierwave_file!(user_project.export_file)
...@@ -54,9 +45,7 @@ module API ...@@ -54,9 +45,7 @@ module API
end end
end end
post ':id/export' do post ':id/export' do
if throttled?(:project_export) check_rate_limit! :project_export, [current_user, :project_export, user_project]
render_api_error!({ error: 'This endpoint has been requested too many times. Try again later.' }, 429)
end
project_export_params = declared_params(include_missing: false) project_export_params = declared_params(include_missing: false)
after_export_params = project_export_params.delete(:upload) || {} after_export_params = project_export_params.delete(:upload) || {}
......
...@@ -8,19 +8,12 @@ module API ...@@ -8,19 +8,12 @@ module API
helpers Helpers::ProjectsHelpers helpers Helpers::ProjectsHelpers
helpers Helpers::FileUploadHelpers helpers Helpers::FileUploadHelpers
helpers Helpers::RateLimiter
helpers do helpers do
def import_params def import_params
declared_params(include_missing: false) declared_params(include_missing: false)
end end
def throttled?(key, scope)
rate_limiter.throttled?(key, scope: scope)
end
def rate_limiter
::Gitlab::ApplicationRateLimiter
end
end end
before do before do
...@@ -69,13 +62,7 @@ module API ...@@ -69,13 +62,7 @@ module API
post 'import' do post 'import' do
require_gitlab_workhorse! require_gitlab_workhorse!
key = "project_import".to_sym check_rate_limit! :project_import, [current_user, :project_import]
if throttled?(key, [current_user, key])
rate_limiter.log_request(request, "#{key}_request_limit".to_sym, current_user)
render_api_error!({ error: _('This endpoint has been requested too many times. Try again later.') }, 429)
end
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42437') Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42437')
......
...@@ -381,6 +381,20 @@ describe API::Issues do ...@@ -381,6 +381,20 @@ describe API::Issues do
end.not_to change { project.labels.count } end.not_to change { project.labels.count }
end end
end end
context 'when request exceeds the rate limit' do
before do
allow(::Gitlab::ApplicationRateLimiter).to receive(:throttled?).and_return(true)
end
it 'prevents users from creating more issues' do
post api("/projects/#{project.id}/issues", user),
params: { title: 'new issue', labels: 'label, label2', weight: 3, assignee_ids: [user2.id] }
expect(response).to have_gitlab_http_status(:too_many_requests)
expect(json_response['message']['error']).to eq('This endpoint has been requested too many times. Try again later.')
end
end
end end
describe 'POST /projects/:id/issues with spam filtering' do describe 'POST /projects/:id/issues with spam filtering' do
......
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