summaryrefslogtreecommitdiff
path: root/lib/gitlab/throttle.rb
blob: 622dc7d9ed013887c96d288d0075f165b063ec9e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# frozen_string_literal: true

module Gitlab
  class Throttle
    DEFAULT_RATE_LIMITING_RESPONSE_TEXT = 'Retry later'

    # Each of these settings follows the same pattern of specifying separate
    # authenticated and unauthenticated rates via settings. New throttles should
    # ideally be regular as well.
    REGULAR_THROTTLES = [:api, :packages_api, :files_api].freeze

    def self.settings
      Gitlab::CurrentSettings.current_application_settings
    end

    # Returns true if we should use the Admin Area protected paths throttle
    def self.protected_paths_enabled?
      self.settings.throttle_protected_paths_enabled?
    end

    def self.omnibus_protected_paths_present?
      Rack::Attack.throttles.key?('protected paths')
    end

    def self.bypass_header
      env_value = ENV['GITLAB_THROTTLE_BYPASS_HEADER']
      return unless env_value.present?

      "HTTP_#{env_value.upcase.tr('-', '_')}"
    end

    class << self
      def options(throttle, authenticated:)
        fragment = throttle_fragment!(throttle, authenticated: authenticated)

        # rubocop:disable GitlabSecurity/PublicSend
        limit_proc = proc { |req| settings.public_send("#{fragment}_requests_per_period") }
        period_proc = proc { |req| settings.public_send("#{fragment}_period_in_seconds").seconds }
        # rubocop:enable GitlabSecurity/PublicSend

        { limit: limit_proc, period: period_proc }
      end

      def throttle_fragment!(throttle, authenticated:)
        raise("Unknown throttle: #{throttle}") unless REGULAR_THROTTLES.include?(throttle)

        "throttle_#{'un' unless authenticated}authenticated_#{throttle}"
      end
    end

    def self.unauthenticated_web_options
      # TODO: Columns will be renamed in https://gitlab.com/gitlab-org/gitlab/-/issues/340031
      # Once this is done, web can be made into a regular throttle
      limit_proc = proc { |req| settings.throttle_unauthenticated_requests_per_period }
      period_proc = proc { |req| settings.throttle_unauthenticated_period_in_seconds.seconds }

      { limit: limit_proc, period: period_proc }
    end

    def self.authenticated_web_options
      limit_proc = proc { |req| settings.throttle_authenticated_web_requests_per_period }
      period_proc = proc { |req| settings.throttle_authenticated_web_period_in_seconds.seconds }

      { limit: limit_proc, period: period_proc }
    end

    def self.protected_paths_options
      limit_proc = proc { |req| settings.throttle_protected_paths_requests_per_period }
      period_proc = proc { |req| settings.throttle_protected_paths_period_in_seconds.seconds }

      { limit: limit_proc, period: period_proc }
    end

    def self.throttle_authenticated_git_lfs_options
      limit_proc = proc { |req| settings.throttle_authenticated_git_lfs_requests_per_period }
      period_proc = proc { |req| settings.throttle_authenticated_git_lfs_period_in_seconds.seconds }

      { limit: limit_proc, period: period_proc }
    end

    def self.rate_limiting_response_text
      (settings.rate_limiting_response_text.presence || DEFAULT_RATE_LIMITING_RESPONSE_TEXT) + "\n"
    end
  end
end