debian-mirror-gitlab/app/services/concerns/rate_limited_service.rb

85 lines
2.3 KiB
Ruby
Raw Normal View History

2021-11-18 22:05:49 +05:30
# frozen_string_literal: true
module RateLimitedService
extend ActiveSupport::Concern
RateLimitedNotSetupError = Class.new(StandardError)
class RateLimitedError < StandardError
def initialize(key:, rate_limiter:)
@key = key
@rate_limiter = rate_limiter
end
def headers
# TODO: This will be fleshed out in https://gitlab.com/gitlab-org/gitlab/-/issues/342370
{}
end
def log_request(request, current_user)
2022-04-04 11:22:00 +05:30
rate_limiter.log_request(request, "#{key}_request_limit".to_sym, current_user)
2021-11-18 22:05:49 +05:30
end
private
attr_reader :key, :rate_limiter
end
class RateLimiterScopedAndKeyed
2022-04-04 11:22:00 +05:30
attr_reader :key, :opts, :rate_limiter
2021-11-18 22:05:49 +05:30
2022-04-04 11:22:00 +05:30
def initialize(key:, opts:, rate_limiter:)
2021-11-18 22:05:49 +05:30
@key = key
@opts = opts
2022-04-04 11:22:00 +05:30
@rate_limiter = rate_limiter
2021-11-18 22:05:49 +05:30
end
def rate_limit!(service)
evaluated_scope = evaluated_scope_for(service)
2022-04-04 11:22:00 +05:30
if rate_limiter.throttled?(key, **opts.merge(scope: evaluated_scope.values, users_allowlist: users_allowlist))
2021-11-18 22:05:49 +05:30
raise RateLimitedError.new(key: key, rate_limiter: rate_limiter), _('This endpoint has been requested too many times. Try again later.')
end
end
private
def users_allowlist
@users_allowlist ||= opts[:users_allowlist] ? opts[:users_allowlist].call : []
end
def evaluated_scope_for(service)
2023-03-04 22:38:38 +05:30
opts[:scope].index_with do |var|
service.public_send(var) # rubocop: disable GitlabSecurity/PublicSend
2021-11-18 22:05:49 +05:30
end
end
end
prepended do
attr_accessor :rate_limiter_bypassed
2022-05-07 20:08:51 +05:30
2021-11-18 22:05:49 +05:30
cattr_accessor :rate_limiter_scoped_and_keyed
2022-04-04 11:22:00 +05:30
def self.rate_limit(key:, opts:, rate_limiter: ::Gitlab::ApplicationRateLimiter)
2021-11-18 22:05:49 +05:30
self.rate_limiter_scoped_and_keyed = RateLimiterScopedAndKeyed.new(key: key,
opts: opts,
2022-04-04 11:22:00 +05:30
rate_limiter: rate_limiter)
2021-11-18 22:05:49 +05:30
end
end
def execute_without_rate_limiting(*args, **kwargs)
self.rate_limiter_bypassed = true
execute(*args, **kwargs)
ensure
self.rate_limiter_bypassed = false
end
def execute(*args, **kwargs)
raise RateLimitedNotSetupError if rate_limiter_scoped_and_keyed.nil?
rate_limiter_scoped_and_keyed.rate_limit!(self) unless rate_limiter_bypassed
super
end
end