Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-07-20 18:40:28 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-07-20 18:40:28 +0300
commitb595cb0c1dec83de5bdee18284abe86614bed33b (patch)
tree8c3d4540f193c5ff98019352f554e921b3a41a72 /lib/gitlab/application_rate_limiter.rb
parent2f9104a328fc8a4bddeaa4627b595166d24671d0 (diff)
Add latest changes from gitlab-org/gitlab@15-2-stable-eev15.2.0-rc42
Diffstat (limited to 'lib/gitlab/application_rate_limiter.rb')
-rw-r--r--lib/gitlab/application_rate_limiter.rb72
1 files changed, 39 insertions, 33 deletions
diff --git a/lib/gitlab/application_rate_limiter.rb b/lib/gitlab/application_rate_limiter.rb
index 722ee061eba..0c52ce8aba4 100644
--- a/lib/gitlab/application_rate_limiter.rb
+++ b/lib/gitlab/application_rate_limiter.rb
@@ -37,6 +37,7 @@ module Gitlab
users_get_by_id: { threshold: -> { application_settings.users_get_by_id_limit }, interval: 10.minutes },
username_exists: { threshold: 20, interval: 1.minute },
user_sign_up: { threshold: 20, interval: 1.minute },
+ user_sign_in: { threshold: 5, interval: 10.minutes },
profile_resend_email_confirmation: { threshold: 5, interval: 1.minute },
profile_update_username: { threshold: 10, interval: 1.minute },
update_environment_canary_ingress: { threshold: 1, interval: 1.minute },
@@ -45,7 +46,10 @@ module Gitlab
search_rate_limit_unauthenticated: { threshold: -> { application_settings.search_rate_limit_unauthenticated }, interval: 1.minute },
gitlab_shell_operation: { threshold: 600, interval: 1.minute },
pipelines_create: { threshold: -> { application_settings.pipeline_limit_per_project_user_sha }, interval: 1.minute },
- temporary_email_failure: { threshold: 50, interval: 1.day }
+ temporary_email_failure: { threshold: 50, interval: 1.day },
+ project_testing_integration: { threshold: 5, interval: 1.minute },
+ email_verification: { threshold: 10, interval: 10.minutes },
+ email_verification_code_send: { threshold: 10, interval: 1.hour }
}.freeze
end
@@ -53,15 +57,26 @@ module Gitlab
# be throttled.
#
# @param key [Symbol] Key attribute registered in `.rate_limits`
- # @param scope [Array<ActiveRecord>] Array of ActiveRecord models, Strings or Symbols to scope throttling to a specific request (e.g. per user per project)
- # @param threshold [Integer] Optional threshold value to override default one registered in `.rate_limits`
- # @param users_allowlist [Array<String>] Optional list of usernames to exclude from the limit. This param will only be functional if Scope includes a current user.
- # @param peek [Boolean] Optional. When true the key will not be incremented but the current throttled state will be returned.
+ # @param scope [Array<ActiveRecord>] Array of ActiveRecord models, Strings
+ # or Symbols to scope throttling to a specific request (e.g. per user
+ # per project)
+ # @param resource [ActiveRecord] An ActiveRecord model to count an action
+ # for (e.g. limit unique project (resource) downloads (action) to five
+ # per user (scope))
+ # @param threshold [Integer] Optional threshold value to override default
+ # one registered in `.rate_limits`
+ # @param users_allowlist [Array<String>] Optional list of usernames to
+ # exclude from the limit. This param will only be functional if Scope
+ # includes a current user.
+ # @param peek [Boolean] Optional. When true the key will not be
+ # incremented but the current throttled state will be returned.
#
# @return [Boolean] Whether or not a request should be throttled
- def throttled?(key, scope:, threshold: nil, users_allowlist: nil, peek: false)
+ def throttled?(key, scope:, resource: nil, threshold: nil, users_allowlist: nil, peek: false)
raise InvalidKeyError unless rate_limits[key]
+ strategy = resource.present? ? IncrementPerActionedResource.new(resource.id) : IncrementPerAction.new
+
::Gitlab::Instrumentation::RateLimitingGates.track(key)
return false if scoped_user_in_allowlist?(scope, users_allowlist)
@@ -71,6 +86,9 @@ module Gitlab
return false if threshold_value == 0
interval_value = interval(key)
+
+ return false if interval_value == 0
+
# `period_key` is based on the current time and interval so when time passes to the next interval
# the key changes and the rate limit count starts again from 0.
# Based on https://github.com/rack/rack-attack/blob/886ba3a18d13c6484cd511a4dc9b76c0d14e5e96/lib/rack/attack/cache.rb#L63-L68
@@ -78,9 +96,12 @@ module Gitlab
cache_key = cache_key(key, scope, period_key)
value = if peek
- read(cache_key)
+ strategy.read(cache_key)
else
- increment(cache_key, interval_value, time_elapsed_in_period)
+ # We add a 1 second buffer to avoid timing issues when we're at the end of a period
+ expiry = interval_value - time_elapsed_in_period + 1
+
+ strategy.increment(cache_key, expiry)
end
value > threshold_value
@@ -128,40 +149,25 @@ module Gitlab
def threshold(key)
value = rate_limit_value_by_key(key, :threshold)
- return value.call if value.is_a?(Proc)
-
- value.to_i
+ rate_limit_value(value)
end
def interval(key)
- rate_limit_value_by_key(key, :interval).to_i
- end
-
- def rate_limit_value_by_key(key, setting)
- action = rate_limits[key]
+ value = rate_limit_value_by_key(key, :interval)
- action[setting] if action
+ rate_limit_value(value)
end
- # Increments the rate limit count and returns the new count value.
- def increment(cache_key, interval_value, time_elapsed_in_period)
- # We add a 1 second buffer to avoid timing issues when we're at the end of a period
- expiry = interval_value - time_elapsed_in_period + 1
+ def rate_limit_value(value)
+ value = value.call if value.is_a?(Proc)
- ::Gitlab::Redis::RateLimiting.with do |redis|
- redis.pipelined do
- redis.incr(cache_key)
- redis.expire(cache_key, expiry)
- end.first
- end
+ value.to_i
end
- # Returns the rate limit count.
- # Will be 0 if there is no data in the cache.
- def read(cache_key)
- ::Gitlab::Redis::RateLimiting.with do |redis|
- redis.get(cache_key).to_i
- end
+ def rate_limit_value_by_key(key, setting)
+ action = rate_limits[key]
+
+ action[setting] if action
end
def cache_key(key, scope, period_key)