gitlab-org--gitlab-foss/lib/gitlab/action_rate_limiter.rb
Mayra Cabrera 3cefc5d7df Add RateLimiter to RawController
* Limits raw requests to 300 per minute and per raw path.
* Add a new attribute to ApplicationSettings so user can change this
value on their instance.
* Uses Gitlab::ActionRateLimiter to limit the raw requests.
* Add a new method into ActionRateLimiter to log the event into auth.log

Related to https://gitlab.com/gitlab-org/gitlab-ce/issues/48717
2019-07-24 19:49:31 +00:00

81 lines
2.3 KiB
Ruby

# frozen_string_literal: true
module Gitlab
# This class implements a simple rate limiter that can be used to throttle
# certain actions. Unlike Rack Attack and Rack::Throttle, which operate at
# the middleware level, this can be used at the controller level.
class ActionRateLimiter
TIME_TO_EXPIRE = 60 # 1 min
attr_accessor :action, :expiry_time
def initialize(action:, expiry_time: TIME_TO_EXPIRE)
@action = action
@expiry_time = expiry_time
end
# Increments the given cache key and increments the value by 1 with the
# given expiration time. Returns the incremented value.
#
# key - An array of ActiveRecord instances
def increment(key)
value = 0
Gitlab::Redis::Cache.with do |redis|
cache_key = action_key(key)
value = redis.incr(cache_key)
redis.expire(cache_key, expiry_time) if value == 1
end
value
end
# Increments the given key and returns true if the action should
# be throttled.
#
# key - An array of ActiveRecord instances or strings
# threshold_value - The maximum number of times this action should occur in the given time interval. If number is zero is considered disabled.
def throttled?(key, threshold_value)
threshold_value > 0 &&
self.increment(key) > threshold_value
end
# Logs request into auth.log
#
# request - Web request to be logged
# type - A symbol key that represents the request.
# current_user - Current user of the request, it can be nil.
def log_request(request, type, current_user)
request_information = {
message: 'Action_Rate_Limiter_Request',
env: type,
ip: request.ip,
request_method: request.request_method,
fullpath: request.fullpath
}
if current_user
request_information.merge!({
user_id: current_user.id,
username: current_user.username
})
end
Gitlab::AuthLogger.error(request_information)
end
private
def action_key(key)
serialized = key.map do |obj|
if obj.is_a?(String)
"#{obj}"
else
"#{obj.class.model_name.to_s.underscore}:#{obj.id}"
end
end.join(":")
"action_rate_limiter:#{action}:#{serialized}"
end
end
end