Commit 9be381bf authored by Jonas Wälter's avatar Jonas Wälter Committed by David Fernandez

Add specific rate limits for package registry

parent 0f1e36f4
...@@ -310,9 +310,15 @@ module ApplicationSettingsHelper ...@@ -310,9 +310,15 @@ module ApplicationSettingsHelper
:throttle_authenticated_web_enabled, :throttle_authenticated_web_enabled,
:throttle_authenticated_web_period_in_seconds, :throttle_authenticated_web_period_in_seconds,
:throttle_authenticated_web_requests_per_period, :throttle_authenticated_web_requests_per_period,
:throttle_authenticated_packages_api_enabled,
:throttle_authenticated_packages_api_period_in_seconds,
:throttle_authenticated_packages_api_requests_per_period,
:throttle_unauthenticated_enabled, :throttle_unauthenticated_enabled,
:throttle_unauthenticated_period_in_seconds, :throttle_unauthenticated_period_in_seconds,
:throttle_unauthenticated_requests_per_period, :throttle_unauthenticated_requests_per_period,
:throttle_unauthenticated_packages_api_enabled,
:throttle_unauthenticated_packages_api_period_in_seconds,
:throttle_unauthenticated_packages_api_requests_per_period,
:throttle_protected_paths_enabled, :throttle_protected_paths_enabled,
:throttle_protected_paths_period_in_seconds, :throttle_protected_paths_period_in_seconds,
:throttle_protected_paths_requests_per_period, :throttle_protected_paths_requests_per_period,
......
...@@ -434,6 +434,14 @@ class ApplicationSetting < ApplicationRecord ...@@ -434,6 +434,14 @@ class ApplicationSetting < ApplicationRecord
presence: true, presence: true,
numericality: { only_integer: true, greater_than: 0 } numericality: { only_integer: true, greater_than: 0 }
validates :throttle_unauthenticated_packages_api_requests_per_period,
presence: true,
numericality: { only_integer: true, greater_than: 0 }
validates :throttle_unauthenticated_packages_api_period_in_seconds,
presence: true,
numericality: { only_integer: true, greater_than: 0 }
validates :throttle_authenticated_api_requests_per_period, validates :throttle_authenticated_api_requests_per_period,
presence: true, presence: true,
numericality: { only_integer: true, greater_than: 0 } numericality: { only_integer: true, greater_than: 0 }
...@@ -450,6 +458,14 @@ class ApplicationSetting < ApplicationRecord ...@@ -450,6 +458,14 @@ class ApplicationSetting < ApplicationRecord
presence: true, presence: true,
numericality: { only_integer: true, greater_than: 0 } numericality: { only_integer: true, greater_than: 0 }
validates :throttle_authenticated_packages_api_requests_per_period,
presence: true,
numericality: { only_integer: true, greater_than: 0 }
validates :throttle_authenticated_packages_api_period_in_seconds,
presence: true,
numericality: { only_integer: true, greater_than: 0 }
validates :throttle_protected_paths_requests_per_period, validates :throttle_protected_paths_requests_per_period,
presence: true, presence: true,
numericality: { only_integer: true, greater_than: 0 } numericality: { only_integer: true, greater_than: 0 }
......
...@@ -156,6 +156,9 @@ module ApplicationSettingImplementation ...@@ -156,6 +156,9 @@ module ApplicationSettingImplementation
throttle_authenticated_web_enabled: false, throttle_authenticated_web_enabled: false,
throttle_authenticated_web_period_in_seconds: 3600, throttle_authenticated_web_period_in_seconds: 3600,
throttle_authenticated_web_requests_per_period: 7200, throttle_authenticated_web_requests_per_period: 7200,
throttle_authenticated_packages_api_enabled: false,
throttle_authenticated_packages_api_period_in_seconds: 15,
throttle_authenticated_packages_api_requests_per_period: 1000,
throttle_incident_management_notification_enabled: false, throttle_incident_management_notification_enabled: false,
throttle_incident_management_notification_per_period: 3600, throttle_incident_management_notification_per_period: 3600,
throttle_incident_management_notification_period_in_seconds: 3600, throttle_incident_management_notification_period_in_seconds: 3600,
...@@ -165,6 +168,9 @@ module ApplicationSettingImplementation ...@@ -165,6 +168,9 @@ module ApplicationSettingImplementation
throttle_unauthenticated_enabled: false, throttle_unauthenticated_enabled: false,
throttle_unauthenticated_period_in_seconds: 3600, throttle_unauthenticated_period_in_seconds: 3600,
throttle_unauthenticated_requests_per_period: 3600, throttle_unauthenticated_requests_per_period: 3600,
throttle_unauthenticated_packages_api_enabled: false,
throttle_unauthenticated_packages_api_period_in_seconds: 15,
throttle_unauthenticated_packages_api_requests_per_period: 800,
time_tracking_limit_to_hours: false, time_tracking_limit_to_hours: false,
two_factor_grace_period: 48, two_factor_grace_period: 48,
unique_ips_limit_enabled: false, unique_ips_limit_enabled: false,
......
= form_for @application_setting, url: network_admin_application_settings_path(anchor: 'js-packages-limits-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
%h5
= _('Unauthenticated API request rate limit')
.form-group
.form-check
= f.check_box :throttle_unauthenticated_packages_api_enabled, class: 'form-check-input', data: { qa_selector: 'throttle_unauthenticated_packages_api_checkbox' }
= f.label :throttle_unauthenticated_packages_api_enabled, class: 'form-check-label label-bold' do
= _('Enable unauthenticated API request rate limit')
%span.form-text.text-muted
= _('Helps reduce request volume (e.g. from crawlers or abusive bots)')
.form-group
= f.label :throttle_unauthenticated_packages_api_requests_per_period, 'Max unauthenticated API requests per period per IP', class: 'label-bold'
= f.number_field :throttle_unauthenticated_packages_api_requests_per_period, class: 'form-control gl-form-input'
.form-group
= f.label :throttle_unauthenticated_packages_api_period_in_seconds, 'Unauthenticated API rate limit period in seconds', class: 'label-bold'
= f.number_field :throttle_unauthenticated_packages_api_period_in_seconds, class: 'form-control gl-form-input'
%hr
%h5
= _('Authenticated API request rate limit')
.form-group
.form-check
= f.check_box :throttle_authenticated_packages_api_enabled, class: 'form-check-input', data: { qa_selector: 'throttle_authenticated_packages_api_checkbox' }
= f.label :throttle_authenticated_packages_api_enabled, class: 'form-check-label label-bold' do
= _('Enable authenticated API request rate limit')
%span.form-text.text-muted
= _('Helps reduce request volume (e.g. from crawlers or abusive bots)')
.form-group
= f.label :throttle_authenticated_packages_api_requests_per_period, 'Max authenticated API requests per period per user', class: 'label-bold'
= f.number_field :throttle_authenticated_packages_api_requests_per_period, class: 'form-control gl-form-input'
.form-group
= f.label :throttle_authenticated_packages_api_period_in_seconds, 'Authenticated API rate limit period in seconds', class: 'label-bold'
= f.number_field :throttle_authenticated_packages_api_period_in_seconds, class: 'form-control gl-form-input'
= f.submit 'Save changes', class: "gl-button btn btn-confirm", data: { qa_selector: 'save_changes_button' }
...@@ -24,6 +24,17 @@ ...@@ -24,6 +24,17 @@
.settings-content .settings-content
= render 'ip_limits' = render 'ip_limits'
%section.settings.as-packages-limits.no-animate#js-packages-limits-settings{ class: ('expanded' if expanded_by_default?), data: { qa_selector: 'packages_limits_content' } }
.settings-header
%h4
= _('Package Registry Rate Limits')
%button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
= _('Configure specific limits for Packages API requests that supersede the general user and IP rate limits.')
.settings-content
= render 'package_registry_limits'
%section.settings.as-outbound.no-animate#js-outbound-settings{ class: ('expanded' if expanded_by_default?), data: { qa_selector: 'outbound_requests_content' } } %section.settings.as-outbound.no-animate#js-outbound-settings{ class: ('expanded' if expanded_by_default?), data: { qa_selector: 'outbound_requests_content' } }
.settings-header .settings-header
%h4 %h4
......
---
title: Add specific rate limits for Package Registry (Package API)
merge_request: 57029
author: Jonas Wälter @wwwjon
type: added
# frozen_string_literal: true
class AddThrottlePackageRegistryColumns < ActiveRecord::Migration[6.0]
DOWNTIME = false
def change
add_column :application_settings, :throttle_unauthenticated_packages_api_requests_per_period, :integer, default: 800, null: false
add_column :application_settings, :throttle_unauthenticated_packages_api_period_in_seconds, :integer, default: 15, null: false
add_column :application_settings, :throttle_authenticated_packages_api_requests_per_period, :integer, default: 1000, null: false
add_column :application_settings, :throttle_authenticated_packages_api_period_in_seconds, :integer, default: 15, null: false
add_column :application_settings, :throttle_unauthenticated_packages_api_enabled, :boolean, default: false, null: false
add_column :application_settings, :throttle_authenticated_packages_api_enabled, :boolean, default: false, null: false
end
end
28b1e8add8ac7249be55ccd25e60c8a181d2ff036a7d69ac861bcdb5bf5e84e1
\ No newline at end of file
...@@ -9445,6 +9445,12 @@ CREATE TABLE application_settings ( ...@@ -9445,6 +9445,12 @@ CREATE TABLE application_settings (
encrypted_external_pipeline_validation_service_token text, encrypted_external_pipeline_validation_service_token text,
encrypted_external_pipeline_validation_service_token_iv text, encrypted_external_pipeline_validation_service_token_iv text,
external_pipeline_validation_service_url text, external_pipeline_validation_service_url text,
throttle_unauthenticated_packages_api_requests_per_period integer DEFAULT 800 NOT NULL,
throttle_unauthenticated_packages_api_period_in_seconds integer DEFAULT 15 NOT NULL,
throttle_authenticated_packages_api_requests_per_period integer DEFAULT 1000 NOT NULL,
throttle_authenticated_packages_api_period_in_seconds integer DEFAULT 15 NOT NULL,
throttle_unauthenticated_packages_api_enabled boolean DEFAULT false NOT NULL,
throttle_authenticated_packages_api_enabled boolean DEFAULT false NOT NULL,
CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)), CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)),
CONSTRAINT app_settings_ext_pipeline_validation_service_url_text_limit CHECK ((char_length(external_pipeline_validation_service_url) <= 255)), CONSTRAINT app_settings_ext_pipeline_validation_service_url_text_limit CHECK ((char_length(external_pipeline_validation_service_url) <= 255)),
CONSTRAINT app_settings_registry_exp_policies_worker_capacity_positive CHECK ((container_registry_expiration_policies_worker_capacity >= 0)), CONSTRAINT app_settings_registry_exp_policies_worker_capacity_positive CHECK ((container_registry_expiration_policies_worker_capacity >= 0)),
...@@ -69,6 +69,15 @@ Read more on [protected path rate limits](../user/admin_area/settings/protected_ ...@@ -69,6 +69,15 @@ Read more on [protected path rate limits](../user/admin_area/settings/protected_
- **Default rate limit** - After 10 requests, the client must wait 60 seconds before trying again - **Default rate limit** - After 10 requests, the client must wait 60 seconds before trying again
### Package Registry
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57029) in GitLab 13.12.
This setting limits the request rate on the Packages API per user or IP. For more information, see
[Package Registry Rate Limits](../user/admin_area/settings/package_registry_rate_limits.md).
- **Default rate limit:** Disabled by default
### Import/Export ### Import/Export
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/35728) in GitLab 13.2. > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/35728) in GitLab 13.2.
......
...@@ -98,6 +98,12 @@ To enable the Packages feature: ...@@ -98,6 +98,12 @@ To enable the Packages feature:
1. [Restart GitLab](../restart_gitlab.md#helm-chart-installations "How to reconfigure Helm GitLab") for the changes to take effect. 1. [Restart GitLab](../restart_gitlab.md#helm-chart-installations "How to reconfigure Helm GitLab") for the changes to take effect.
## Rate limits
When downloading packages as dependencies in downstream projects, many requests are made through the
Packages API. You may therefore reach enforced user and IP rate limits. To address this issue, you
can define specific rate limits for the Packages API. For more details, see [Package Registry Rate Limits](../../user/admin_area/settings/package_registry_rate_limits.md).
## Changing the storage path ## Changing the storage path
By default, the packages are stored locally, but you can change the default By default, the packages are stored locally, but you can change the default
......
...@@ -33,6 +33,7 @@ These are rate limits you can set in the Admin Area of your instance: ...@@ -33,6 +33,7 @@ These are rate limits you can set in the Admin Area of your instance:
- [Protected paths](../user/admin_area/settings/protected_paths.md) - [Protected paths](../user/admin_area/settings/protected_paths.md)
- [Raw endpoints rate limits](../user/admin_area/settings/rate_limits_on_raw_endpoints.md) - [Raw endpoints rate limits](../user/admin_area/settings/rate_limits_on_raw_endpoints.md)
- [User and IP rate limits](../user/admin_area/settings/user_and_ip_rate_limits.md) - [User and IP rate limits](../user/admin_area/settings/user_and_ip_rate_limits.md)
- [Package registry rate limits](../user/admin_area/settings/package_registry_rate_limits.md)
## Non-configurable limits ## Non-configurable limits
......
...@@ -91,6 +91,7 @@ Access the default page for admin area settings by navigating to **Admin Area > ...@@ -91,6 +91,7 @@ Access the default page for admin area settings by navigating to **Admin Area >
| ------ | ----------- | | ------ | ----------- |
| Performance optimization | [Write to "authorized_keys" file](../../../administration/operations/fast_ssh_key_lookup.md#setting-up-fast-lookup-via-gitlab-shell) and [Push event activities limit and bulk push events](push_event_activities_limit.md). Various settings that affect GitLab performance. | | Performance optimization | [Write to "authorized_keys" file](../../../administration/operations/fast_ssh_key_lookup.md#setting-up-fast-lookup-via-gitlab-shell) and [Push event activities limit and bulk push events](push_event_activities_limit.md). Various settings that affect GitLab performance. |
| [User and IP rate limits](user_and_ip_rate_limits.md) | Configure limits for web and API requests. | | [User and IP rate limits](user_and_ip_rate_limits.md) | Configure limits for web and API requests. |
| [Package Registry Rate Limits](package_registry_rate_limits.md) | Configure specific limits for Packages API requests that supersede the user and IP rate limits. |
| [Outbound requests](../../../security/webhooks.md) | Allow requests to the local network from hooks and services. | | [Outbound requests](../../../security/webhooks.md) | Allow requests to the local network from hooks and services. |
| [Protected Paths](protected_paths.md) | Configure paths to be protected by Rack Attack. | | [Protected Paths](protected_paths.md) | Configure paths to be protected by Rack Attack. |
| [Incident Management](../../../operations/incident_management/index.md) Limits | Configure limits on the number of inbound alerts able to be sent to a project. | | [Incident Management](../../../operations/incident_management/index.md) Limits | Configure limits on the number of inbound alerts able to be sent to a project. |
......
---
stage: Package
group: Package
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
type: reference
---
# Package Registry Rate Limits **(FREE SELF)**
Rate limiting is a common technique used to improve the security and durability of a web
application. For more details, see [Rate limits](../../../security/rate_limits.md). General user and
IP rate limits can be enforced in **Admin Area > Settings > Network > User and IP rate limits**.
For more details, see [User and IP rate limits](user_and_ip_rate_limits.md).
With the [GitLab Package Registry](../../packages/package_registry/index.md),
you can use GitLab as a private or public registry for a variety of common package managers. You can
publish and share packages, which others can consume as a dependency in downstream projects through
the [Packages API](../../../api/packages.md).
When downloading such dependencies in downstream projects, many requests are made through the
Packages API. You may therefore reach enforced user and IP rate limits. To address this issue, you
can define specific rate limits for the Packages API in
**Admin Area > Settings > Network > Package Registry Rate Limits**:
- Unauthenticated Packages API requests
- Authenticated Packages API requests
These limits are disabled by default. When enabled, they supersede the general user and IP rate
limits for requests to the Packages API. You can therefore keep the general user and IP rate limits,
and increase (if necessary) the rate limits for the Packages API.
Besides this precedence, there are no differences in functionality compared to the general user and
IP rate limits. For more details, see [User and IP rate limits](user_and_ip_rate_limits.md).
...@@ -133,6 +133,8 @@ The possible names are: ...@@ -133,6 +133,8 @@ The possible names are:
- `throttle_unauthenticated_protected_paths` - `throttle_unauthenticated_protected_paths`
- `throttle_authenticated_protected_paths_api` - `throttle_authenticated_protected_paths_api`
- `throttle_authenticated_protected_paths_web` - `throttle_authenticated_protected_paths_web`
- `throttle_unauthenticated_packages_api`
- `throttle_authenticated_packages_api`
For example, to try out throttles for all authenticated requests to For example, to try out throttles for all authenticated requests to
non-protected paths can be done by setting non-protected paths can be done by setting
......
...@@ -19,7 +19,8 @@ module Gitlab ...@@ -19,7 +19,8 @@ module Gitlab
:throttle_authenticated_api, :throttle_authenticated_api,
:throttle_authenticated_web, :throttle_authenticated_web,
:throttle_authenticated_protected_paths_api, :throttle_authenticated_protected_paths_api,
:throttle_authenticated_protected_paths_web :throttle_authenticated_protected_paths_web,
:throttle_authenticated_packages_api
].freeze ].freeze
PAYLOAD_KEYS = [ PAYLOAD_KEYS = [
......
...@@ -83,16 +83,13 @@ module Gitlab ...@@ -83,16 +83,13 @@ module Gitlab
def self.configure_throttles(rack_attack) def self.configure_throttles(rack_attack)
throttle_or_track(rack_attack, 'throttle_unauthenticated', Gitlab::Throttle.unauthenticated_options) do |req| throttle_or_track(rack_attack, 'throttle_unauthenticated', Gitlab::Throttle.unauthenticated_options) do |req|
if !req.should_be_skipped? && if req.throttle_unauthenticated?
Gitlab::Throttle.settings.throttle_unauthenticated_enabled &&
req.unauthenticated?
req.ip req.ip
end end
end end
throttle_or_track(rack_attack, 'throttle_authenticated_api', Gitlab::Throttle.authenticated_api_options) do |req| throttle_or_track(rack_attack, 'throttle_authenticated_api', Gitlab::Throttle.authenticated_api_options) do |req|
if req.api_request? && if req.throttle_authenticated_api?
Gitlab::Throttle.settings.throttle_authenticated_api_enabled
req.throttled_user_id([:api]) req.throttled_user_id([:api])
end end
end end
...@@ -107,40 +104,41 @@ module Gitlab ...@@ -107,40 +104,41 @@ module Gitlab
end end
throttle_or_track(rack_attack, 'throttle_authenticated_web', Gitlab::Throttle.authenticated_web_options) do |req| throttle_or_track(rack_attack, 'throttle_authenticated_web', Gitlab::Throttle.authenticated_web_options) do |req|
if req.web_request? && if req.throttle_authenticated_web?
Gitlab::Throttle.settings.throttle_authenticated_web_enabled
req.throttled_user_id([:api, :rss, :ics]) req.throttled_user_id([:api, :rss, :ics])
end end
end end
throttle_or_track(rack_attack, 'throttle_unauthenticated_protected_paths', Gitlab::Throttle.protected_paths_options) do |req| throttle_or_track(rack_attack, 'throttle_unauthenticated_protected_paths', Gitlab::Throttle.protected_paths_options) do |req|
if req.post? && if req.throttle_unauthenticated_protected_paths?
!req.should_be_skipped? &&
req.protected_path? &&
Gitlab::Throttle.protected_paths_enabled? &&
req.unauthenticated?
req.ip req.ip
end end
end end
throttle_or_track(rack_attack, 'throttle_authenticated_protected_paths_api', Gitlab::Throttle.protected_paths_options) do |req| throttle_or_track(rack_attack, 'throttle_authenticated_protected_paths_api', Gitlab::Throttle.protected_paths_options) do |req|
if req.post? && if req.throttle_authenticated_protected_paths_api?
req.api_request? &&
req.protected_path? &&
Gitlab::Throttle.protected_paths_enabled?
req.throttled_user_id([:api]) req.throttled_user_id([:api])
end end
end end
throttle_or_track(rack_attack, 'throttle_authenticated_protected_paths_web', Gitlab::Throttle.protected_paths_options) do |req| throttle_or_track(rack_attack, 'throttle_authenticated_protected_paths_web', Gitlab::Throttle.protected_paths_options) do |req|
if req.post? && if req.throttle_authenticated_protected_paths_web?
req.web_request? &&
req.protected_path? &&
Gitlab::Throttle.protected_paths_enabled?
req.throttled_user_id([:api, :rss, :ics]) req.throttled_user_id([:api, :rss, :ics])
end end
end end
throttle_or_track(rack_attack, 'throttle_unauthenticated_packages_api', Gitlab::Throttle.unauthenticated_packages_api_options) do |req|
if req.throttle_unauthenticated_packages_api?
req.ip
end
end
throttle_or_track(rack_attack, 'throttle_authenticated_packages_api', Gitlab::Throttle.authenticated_packages_api_options) do |req|
if req.throttle_authenticated_packages_api?
req.throttled_user_id([:api])
end
end
rack_attack.safelist('throttle_bypass_header') do |req| rack_attack.safelist('throttle_bypass_header') do |req|
Gitlab::Throttle.bypass_header.present? && Gitlab::Throttle.bypass_header.present? &&
req.get_header(Gitlab::Throttle.bypass_header) == '1' req.get_header(Gitlab::Throttle.bypass_header) == '1'
......
...@@ -58,6 +58,57 @@ module Gitlab ...@@ -58,6 +58,57 @@ module Gitlab
path =~ protected_paths_regex path =~ protected_paths_regex
end end
def throttle_unauthenticated?
!should_be_skipped? &&
!throttle_unauthenticated_packages_api? &&
Gitlab::Throttle.settings.throttle_unauthenticated_enabled &&
unauthenticated?
end
def throttle_authenticated_api?
api_request? &&
!throttle_authenticated_packages_api? &&
Gitlab::Throttle.settings.throttle_authenticated_api_enabled
end
def throttle_authenticated_web?
web_request? &&
Gitlab::Throttle.settings.throttle_authenticated_web_enabled
end
def throttle_unauthenticated_protected_paths?
post? &&
!should_be_skipped? &&
protected_path? &&
Gitlab::Throttle.protected_paths_enabled? &&
unauthenticated?
end
def throttle_authenticated_protected_paths_api?
post? &&
api_request? &&
protected_path? &&
Gitlab::Throttle.protected_paths_enabled?
end
def throttle_authenticated_protected_paths_web?
post? &&
web_request? &&
protected_path? &&
Gitlab::Throttle.protected_paths_enabled?
end
def throttle_unauthenticated_packages_api?
packages_api_path? &&
Gitlab::Throttle.settings.throttle_unauthenticated_packages_api_enabled &&
unauthenticated?
end
def throttle_authenticated_packages_api?
packages_api_path? &&
Gitlab::Throttle.settings.throttle_authenticated_packages_api_enabled
end
private private
def authenticated_user_id(request_formats) def authenticated_user_id(request_formats)
...@@ -75,6 +126,10 @@ module Gitlab ...@@ -75,6 +126,10 @@ module Gitlab
def protected_paths_regex def protected_paths_regex
Regexp.union(protected_paths.map { |path| /\A#{Regexp.escape(path)}/ }) Regexp.union(protected_paths.map { |path| /\A#{Regexp.escape(path)}/ })
end end
def packages_api_path?
path =~ ::Gitlab::Regex::Packages::API_PATH_REGEX
end
end end
end end
end end
......
...@@ -6,6 +6,8 @@ module Gitlab ...@@ -6,6 +6,8 @@ module Gitlab
CONAN_RECIPE_FILES = %w[conanfile.py conanmanifest.txt conan_sources.tgz conan_export.tgz].freeze CONAN_RECIPE_FILES = %w[conanfile.py conanmanifest.txt conan_sources.tgz conan_export.tgz].freeze
CONAN_PACKAGE_FILES = %w[conaninfo.txt conanmanifest.txt conan_package.tgz].freeze CONAN_PACKAGE_FILES = %w[conaninfo.txt conanmanifest.txt conan_package.tgz].freeze
API_PATH_REGEX = %r{^/api/v\d+/(projects/[^/]+/|groups?/[^/]+/-/)?packages/[A-Za-z]+}.freeze
def conan_package_reference_regex def conan_package_reference_regex
@conan_package_reference_regex ||= %r{\A[A-Za-z0-9]+\z}.freeze @conan_package_reference_regex ||= %r{\A[A-Za-z0-9]+\z}.freeze
end end
......
...@@ -49,6 +49,20 @@ module Gitlab ...@@ -49,6 +49,20 @@ module Gitlab
{ limit: limit_proc, period: period_proc } { limit: limit_proc, period: period_proc }
end end
def self.unauthenticated_packages_api_options
limit_proc = proc { |req| settings.throttle_unauthenticated_packages_api_requests_per_period }
period_proc = proc { |req| settings.throttle_unauthenticated_packages_api_period_in_seconds.seconds }
{ limit: limit_proc, period: period_proc }
end
def self.authenticated_packages_api_options
limit_proc = proc { |req| settings.throttle_authenticated_packages_api_requests_per_period }
period_proc = proc { |req| settings.throttle_authenticated_packages_api_period_in_seconds.seconds }
{ limit: limit_proc, period: period_proc }
end
def self.rate_limiting_response_text def self.rate_limiting_response_text
(settings.rate_limiting_response_text.presence || DEFAULT_RATE_LIMITING_RESPONSE_TEXT) + "\n" (settings.rate_limiting_response_text.presence || DEFAULT_RATE_LIMITING_RESPONSE_TEXT) + "\n"
end end
......
...@@ -8305,6 +8305,9 @@ msgstr "" ...@@ -8305,6 +8305,9 @@ msgstr ""
msgid "Configure repository mirroring." msgid "Configure repository mirroring."
msgstr "" msgstr ""
msgid "Configure specific limits for Packages API requests that supersede the general user and IP rate limits."
msgstr ""
msgid "Configure storage path settings." msgid "Configure storage path settings."
msgstr "" msgstr ""
...@@ -12041,6 +12044,9 @@ msgstr "" ...@@ -12041,6 +12044,9 @@ msgstr ""
msgid "Enable two-factor authentication" msgid "Enable two-factor authentication"
msgstr "" msgstr ""
msgid "Enable unauthenticated API request rate limit"
msgstr ""
msgid "Enable unauthenticated request rate limit" msgid "Enable unauthenticated request rate limit"
msgstr "" msgstr ""
...@@ -22574,6 +22580,9 @@ msgstr "" ...@@ -22574,6 +22580,9 @@ msgstr ""
msgid "Package Registry" msgid "Package Registry"
msgstr "" msgstr ""
msgid "Package Registry Rate Limits"
msgstr ""
msgid "Package already exists" msgid "Package already exists"
msgstr "" msgstr ""
...@@ -33582,6 +33591,9 @@ msgstr "" ...@@ -33582,6 +33591,9 @@ msgstr ""
msgid "Unassigned" msgid "Unassigned"
msgstr "" msgstr ""
msgid "Unauthenticated API request rate limit"
msgstr ""
msgid "Unauthenticated rate limit period in seconds" msgid "Unauthenticated rate limit period in seconds"
msgstr "" msgstr ""
......
...@@ -726,4 +726,134 @@ RSpec.describe Gitlab::Regex do ...@@ -726,4 +726,134 @@ RSpec.describe Gitlab::Regex do
it { is_expected.not_to match('v../../../../../1.2.3') } it { is_expected.not_to match('v../../../../../1.2.3') }
it { is_expected.not_to match('v%2e%2e%2f1.2.3') } it { is_expected.not_to match('v%2e%2e%2f1.2.3') }
end end
describe 'Packages::API_PATH_REGEX' do
subject { described_class::Packages::API_PATH_REGEX }
it { is_expected.to match('/api/v4/group/12345/-/packages/composer/p/123456789') }
it { is_expected.to match('/api/v4/group/12345/-/packages/composer/p2/pkg_name') }
it { is_expected.to match('/api/v4/group/12345/-/packages/composer/packages') }
it { is_expected.to match('/api/v4/group/12345/-/packages/composer/pkg_name') }
it { is_expected.to match('/api/v4/groups/1234/-/packages/maven/a/path/file.jar') }
it { is_expected.to match('/api/v4/groups/1234/-/packages/nuget/index') }
it { is_expected.to match('/api/v4/groups/1234/-/packages/nuget/metadata/pkg_name/1.3.4') }
it { is_expected.to match('/api/v4/groups/1234/-/packages/nuget/metadata/pkg_name/index') }
it { is_expected.to match('/api/v4/groups/1234/-/packages/nuget/query') }
it { is_expected.to match('/api/v4/packages/conan/v1/conans/pkg_name/1.2.3/username/stable') }
it { is_expected.to match('/api/v4/packages/conan/v1/conans/pkg_name/1.2.3/username/stable/digest') }
it { is_expected.to match('/api/v4/packages/conan/v1/conans/pkg_name/1.2.3/username/stable/download_urls') }
it { is_expected.to match('/api/v4/packages/conan/v1/conans/pkg_name/1.2.3/username/stable/packages/pkg_ref') }
it { is_expected.to match('/api/v4/packages/conan/v1/conans/pkg_name/1.2.3/username/stable/packages/pkg_ref/digest') }
it { is_expected.to match('/api/v4/packages/conan/v1/conans/pkg_name/1.2.3/username/stable/packages/pkg_ref/download_urls') }
it { is_expected.to match('/api/v4/packages/conan/v1/conans/pkg_name/1.2.3/username/stable/packages/pkg_ref/upload_urls') }
it { is_expected.to match('/api/v4/packages/conan/v1/conans/pkg_name/1.2.3/username/stable/upload_urls') }
it { is_expected.to match('/api/v4/packages/conan/v1/conans/search') }
it { is_expected.to match('/api/v4/packages/conan/v1/files/pkg_name/1.2.3/username/stable/2.3/export/file.name') }
it { is_expected.to match('/api/v4/packages/conan/v1/files/pkg_name/1.2.3/username/stable/2.3/export/file.name/authorize') }
it { is_expected.to match('/api/v4/packages/conan/v1/files/pkg_name/1.2.3/username/stable/2.3/package/pkg_ref/pkg_revision/file.name') }
it { is_expected.to match('/api/v4/packages/conan/v1/files/pkg_name/1.2.3/username/stable/2.3/package/pkg_ref/pkg_revision/file.name/authorize') }
it { is_expected.to match('/api/v4/packages/conan/v1/ping') }
it { is_expected.to match('/api/v4/packages/conan/v1/users/authenticate') }
it { is_expected.to match('/api/v4/packages/conan/v1/users/check_credentials') }
it { is_expected.to match('/api/v4/packages/maven/a/path/file.jar') }
it { is_expected.to match('/api/v4/packages/npm/-/package/pkg_name/dist-tags') }
it { is_expected.to match('/api/v4/packages/npm/-/package/pkg_name/dist-tags/tag') }
it { is_expected.to match('/api/v4/packages/npm/pkg_name') }
it { is_expected.to match('/api/v4/projects/1234/packages/composer') }
it { is_expected.to match('/api/v4/projects/1234/packages/composer/archives/pkg_name') }
it { is_expected.to match('/api/v4/projects/1234/packages/conan/v1/conans/pkg_name/1.2.3/username/stable') }
it { is_expected.to match('/api/v4/projects/1234/packages/conan/v1/conans/pkg_name/1.2.3/username/stable/digest') }
it { is_expected.to match('/api/v4/projects/1234/packages/conan/v1/conans/pkg_name/1.2.3/username/stable/download_urls') }
it { is_expected.to match('/api/v4/projects/1234/packages/conan/v1/conans/pkg_name/1.2.3/username/stable/packages/pkg_ref') }
it { is_expected.to match('/api/v4/projects/1234/packages/conan/v1/conans/pkg_name/1.2.3/username/stable/packages/pkg_ref/digest') }
it { is_expected.to match('/api/v4/projects/1234/packages/conan/v1/conans/pkg_name/1.2.3/username/stable/packages/pkg_ref/download_urls') }
it { is_expected.to match('/api/v4/projects/1234/packages/conan/v1/conans/pkg_name/1.2.3/username/stable/packages/pkg_ref/upload_urls') }
it { is_expected.to match('/api/v4/projects/1234/packages/conan/v1/conans/pkg_name/1.2.3/username/stable/upload_urls') }
it { is_expected.to match('/api/v4/projects/1234/packages/conan/v1/conans/search') }
it { is_expected.to match('/api/v4/projects/1234/packages/conan/v1/files/pkg_name/1.2.3/username/stable/2.3/export/file.name') }
it { is_expected.to match('/api/v4/projects/1234/packages/conan/v1/files/pkg_name/1.2.3/username/stable/2.3/export/file.name/authorize') }
it { is_expected.to match('/api/v4/projects/1234/packages/conan/v1/files/pkg_name/1.2.3/username/stable/2.3/package/pkg_ref/pkg_revision/file.name') }
it { is_expected.to match('/api/v4/projects/1234/packages/conan/v1/files/pkg_name/1.2.3/username/stable/2.3/package/pkg_ref/pkg_revision/file.name/authorize') }
it { is_expected.to match('/api/v4/projects/1234/packages/conan/v1/ping') }
it { is_expected.to match('/api/v4/projects/1234/packages/conan/v1/users/authenticate') }
it { is_expected.to match('/api/v4/projects/1234/packages/conan/v1/users/check_credentials') }
it { is_expected.to match('/api/v4/projects/1234/packages/debian/dists/stable/compon/binary-x64/Packages') }
it { is_expected.to match('/api/v4/projects/1234/packages/debian/dists/stable/InRelease') }
it { is_expected.to match('/api/v4/projects/1234/packages/debian/dists/stable/Release') }
it { is_expected.to match('/api/v4/projects/1234/packages/debian/dists/stable/Release.gpg') }
it { is_expected.to match('/api/v4/projects/1234/packages/debian/file.name') }
it { is_expected.to match('/api/v4/projects/1234/packages/debian/file.name/authorize') }
it { is_expected.to match('/api/v4/projects/1234/packages/debian/pool/compon/e/pkg/file.name') }
it { is_expected.to match('/api/v4/projects/1234/packages/generic/pkg_name/1.3.4/myfile.txt') }
it { is_expected.to match('/api/v4/projects/1234/packages/generic/pkg_name/1.3.4/myfile.txt/authorize') }
it { is_expected.to match('/api/v4/projects/1234/packages/go/my_module/@v/11.2.3.info') }
it { is_expected.to match('/api/v4/projects/1234/packages/go/my_module/@v/11.2.3.mod') }
it { is_expected.to match('/api/v4/projects/1234/packages/go/my_module/@v/11.2.3.zip') }
it { is_expected.to match('/api/v4/projects/1234/packages/go/my_module/@v/list') }
it { is_expected.to match('/api/v4/projects/1234/packages/maven/a/path/file.jar') }
it { is_expected.to match('/api/v4/projects/1234/packages/maven/a/path/file.jar/authorize') }
it { is_expected.to match('/api/v4/projects/1234/packages/npm/-/package/pkg_name/dist-tags') }
it { is_expected.to match('/api/v4/projects/1234/packages/npm/-/package/pkg_name/dist-tags/tag') }
it { is_expected.to match('/api/v4/projects/1234/packages/npm/pkg_name') }
it { is_expected.to match('/api/v4/projects/1234/packages/npm/pkg_name/-/tarball.tgz') }
it { is_expected.to match('/api/v4/projects/1234/packages/nuget') }
it { is_expected.to match('/api/v4/projects/1234/packages/nuget/authorize') }
it { is_expected.to match('/api/v4/projects/1234/packages/nuget/download/pkg_name/1.3.4/pkg.npkg') }
it { is_expected.to match('/api/v4/projects/1234/packages/nuget/download/pkg_name/index') }
it { is_expected.to match('/api/v4/projects/1234/packages/nuget/index') }
it { is_expected.to match('/api/v4/projects/1234/packages/nuget/metadata/pkg_name/1.3.4') }
it { is_expected.to match('/api/v4/projects/1234/packages/nuget/metadata/pkg_name/index') }
it { is_expected.to match('/api/v4/projects/1234/packages/nuget/query') }
it { is_expected.to match('/api/v4/projects/1234/packages/pypi') }
it { is_expected.to match('/api/v4/projects/1234/packages/pypi/authorize') }
it { is_expected.to match('/api/v4/projects/1234/packages/pypi/files/1234567890/file.identifier') }
it { is_expected.to match('/api/v4/projects/1234/packages/pypi/simple/pkg_name') }
it { is_expected.to match('/api/v4/projects/1234/packages/rubygems/api/v1/dependencies') }
it { is_expected.to match('/api/v4/projects/1234/packages/rubygems/api/v1/gems') }
it { is_expected.to match('/api/v4/projects/1234/packages/rubygems/api/v1/gems/authorize') }
it { is_expected.to match('/api/v4/projects/1234/packages/rubygems/gems/pkg') }
it { is_expected.to match('/api/v4/projects/1234/packages/rubygems/pkg') }
it { is_expected.to match('/api/v4/projects/1234/packages/rubygems/quick/Marshal.4.8/pkg') }
it { is_expected.not_to match('') }
it { is_expected.not_to match('foo') }
it { is_expected.not_to match('/api/v4') }
it { is_expected.not_to match('/api/v4/version') }
it { is_expected.not_to match('/api/v4/packages') }
it { is_expected.not_to match('/api/v4/packages/') }
it { is_expected.not_to match('/api/v4/group') }
it { is_expected.not_to match('/api/v4/group/12345') }
it { is_expected.not_to match('/api/v4/group/12345/-') }
it { is_expected.not_to match('/api/v4/group/12345/-/packages') }
it { is_expected.not_to match('/api/v4/group/12345/-/packages/') }
it { is_expected.not_to match('/api/v4/group/12345/-/packages/50') }
it { is_expected.not_to match('/api/v4/groups') }
it { is_expected.not_to match('/api/v4/groups/12345') }
it { is_expected.not_to match('/api/v4/groups/12345/-') }
it { is_expected.not_to match('/api/v4/groups/12345/-/packages') }
it { is_expected.not_to match('/api/v4/groups/12345/-/packages/') }
it { is_expected.not_to match('/api/v4/groups/12345/-/packages/50') }
it { is_expected.not_to match('/api/v4/groups/12345/packages') }
it { is_expected.not_to match('/api/v4/groups/12345/packages/') }
it { is_expected.not_to match('/api/v4/groups/12345/badges') }
it { is_expected.not_to match('/api/v4/groups/12345/issues') }
it { is_expected.not_to match('/api/v4/projects') }
it { is_expected.not_to match('/api/v4/projects/1234') }
it { is_expected.not_to match('/api/v4/projects/1234/packages') }
it { is_expected.not_to match('/api/v4/projects/1234/packages/') }
it { is_expected.not_to match('/api/v4/projects/1234/packages/50') }
it { is_expected.not_to match('/api/v4/projects/1234/packages/50/package_files') }
it { is_expected.not_to match('/api/v4/projects/1234/merge_requests') }
it { is_expected.not_to match('/api/v4/projects/1234/registry/repositories') }
it { is_expected.not_to match('/api/v4/projects/1234/issues') }
it { is_expected.not_to match('/api/v4/projects/1234/members') }
it { is_expected.not_to match('/api/v4/projects/1234/milestones') }
# Group level Debian API endpoints are not matched as it's not using the correct prefix (groups/:id/-/packages/)
# TODO: Update Debian group level endpoints urls and adjust this specs: https://gitlab.com/gitlab-org/gitlab/-/issues/326805
it { is_expected.not_to match('/api/v4/groups/1234/packages/debian/dists/stable/compon/binary-compo/Packages') }
it { is_expected.not_to match('/api/v4/groups/1234/packages/debian/dists/stable/InRelease') }
it { is_expected.not_to match('/api/v4/groups/1234/packages/debian/dists/stable/Release') }
it { is_expected.not_to match('/api/v4/groups/1234/packages/debian/dists/stable/Release.gpg') }
it { is_expected.not_to match('/api/v4/groups/1234/packages/debian/pool/compon/a/pkg/file.name') }
end
end end
...@@ -785,6 +785,10 @@ RSpec.describe ApplicationSetting do ...@@ -785,6 +785,10 @@ RSpec.describe ApplicationSetting do
throttle_authenticated_api_period_in_seconds throttle_authenticated_api_period_in_seconds
throttle_authenticated_web_requests_per_period throttle_authenticated_web_requests_per_period
throttle_authenticated_web_period_in_seconds throttle_authenticated_web_period_in_seconds
throttle_unauthenticated_packages_api_requests_per_period
throttle_unauthenticated_packages_api_period_in_seconds
throttle_authenticated_packages_api_requests_per_period
throttle_authenticated_packages_api_period_in_seconds
] ]
end end
......
...@@ -18,7 +18,11 @@ RSpec.describe 'Rack Attack global throttles', :use_clean_rails_memory_store_cac ...@@ -18,7 +18,11 @@ RSpec.describe 'Rack Attack global throttles', :use_clean_rails_memory_store_cac
throttle_authenticated_web_requests_per_period: 100, throttle_authenticated_web_requests_per_period: 100,
throttle_authenticated_web_period_in_seconds: 1, throttle_authenticated_web_period_in_seconds: 1,
throttle_authenticated_protected_paths_request_per_period: 100, throttle_authenticated_protected_paths_request_per_period: 100,
throttle_authenticated_protected_paths_in_seconds: 1 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,
throttle_authenticated_packages_api_period_in_seconds: 1
} }
end end
...@@ -435,6 +439,186 @@ RSpec.describe 'Rack Attack global throttles', :use_clean_rails_memory_store_cac ...@@ -435,6 +439,186 @@ RSpec.describe 'Rack Attack global throttles', :use_clean_rails_memory_store_cac
end end
end end
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
settings_to_set[:throttle_unauthenticated_requests_per_period] = requests_per_period
settings_to_set[:throttle_unauthenticated_period_in_seconds] = period_in_seconds
settings_to_set[:throttle_unauthenticated_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
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
settings_to_set[:throttle_unauthenticated_requests_per_period] = 0
settings_to_set[:throttle_unauthenticated_period_in_seconds] = period_in_seconds
settings_to_set[:throttle_unauthenticated_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_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), {}] }
it_behaves_like 'rate-limited token-authenticated requests'
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)) }
it_behaves_like 'rate-limited token-authenticated requests'
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
end
end
describe 'throttle bypass header' do describe 'throttle bypass header' do
let(:headers) { {} } let(:headers) { {} }
let(:bypass_header) { 'gitlab-bypass-rate-limiting' } let(:bypass_header) { 'gitlab-bypass-rate-limiting' }
......
...@@ -336,6 +336,32 @@ RSpec.describe ApplicationSettings::UpdateService do ...@@ -336,6 +336,32 @@ RSpec.describe ApplicationSettings::UpdateService do
end end
end end
context 'when package registry rate limits are passed' do
let(:params) do
{
throttle_unauthenticated_packages_api_enabled: 1,
throttle_unauthenticated_packages_api_period_in_seconds: 500,
throttle_unauthenticated_packages_api_requests_per_period: 20,
throttle_authenticated_packages_api_enabled: 1,
throttle_authenticated_packages_api_period_in_seconds: 600,
throttle_authenticated_packages_api_requests_per_period: 10
}
end
it 'updates package registry throttle settings' do
subject.execute
application_settings.reload
expect(application_settings.throttle_unauthenticated_packages_api_enabled).to be_truthy
expect(application_settings.throttle_unauthenticated_packages_api_period_in_seconds).to eq(500)
expect(application_settings.throttle_unauthenticated_packages_api_requests_per_period).to eq(20)
expect(application_settings.throttle_authenticated_packages_api_enabled).to be_truthy
expect(application_settings.throttle_authenticated_packages_api_period_in_seconds).to eq(600)
expect(application_settings.throttle_authenticated_packages_api_requests_per_period).to eq(10)
end
end
context 'when issues_create_limit is passed' do context 'when issues_create_limit is passed' do
let(:params) do let(:params) do
{ {
......
# frozen_string_literal: true # frozen_string_literal: true
# #
# Requires let variables: # Requires let variables:
# * throttle_setting_prefix: "throttle_authenticated_api", "throttle_authenticated_web", "throttle_protected_paths" # * throttle_setting_prefix: "throttle_authenticated_api", "throttle_authenticated_web", "throttle_protected_paths", "throttle_authenticated_packages_api"
# * request_method # * request_method
# * request_args # * request_args
# * other_user_request_args # * other_user_request_args
...@@ -13,7 +13,8 @@ RSpec.shared_examples 'rate-limited token-authenticated requests' do ...@@ -13,7 +13,8 @@ RSpec.shared_examples 'rate-limited token-authenticated requests' do
{ {
"throttle_protected_paths" => "throttle_authenticated_protected_paths_api", "throttle_protected_paths" => "throttle_authenticated_protected_paths_api",
"throttle_authenticated_api" => "throttle_authenticated_api", "throttle_authenticated_api" => "throttle_authenticated_api",
"throttle_authenticated_web" => "throttle_authenticated_web" "throttle_authenticated_web" => "throttle_authenticated_web",
"throttle_authenticated_packages_api" => "throttle_authenticated_packages_api"
} }
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