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

request.rb « rack_attack « gitlab « lib - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: e45782b8be0b8b3d2f1857439bd37b1048d25d4a (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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# frozen_string_literal: true

module Gitlab
  module RackAttack
    module Request
      include ::Gitlab::Utils::StrongMemoize

      API_PATH_REGEX = %r{^/api/|/oauth/}
      FILES_PATH_REGEX = %r{^/api/v\d+/projects/[^/]+/repository/files/.+}
      GROUP_PATH_REGEX = %r{^/api/v\d+/groups/[^/]+/?$}

      def unauthenticated?
        !(authenticated_identifier([:api, :rss, :ics]) || authenticated_runner_id)
      end

      def throttled_identifer(request_formats)
        identifier = authenticated_identifier(request_formats)
        return unless identifier

        identifier_type = identifier[:identifier_type]
        identifier_id = identifier[:identifier_id]

        if identifier_type == :user && Gitlab::RackAttack.user_allowlist.include?(identifier_id)
          Gitlab::Instrumentation::Throttle.safelist = 'throttle_user_allowlist'
          return
        end

        "#{identifier_type}:#{identifier_id}"
      end

      def authenticated_runner_id
        request_authenticator.runner&.id
      end

      def api_request?
        if ::Feature.enabled?(:rate_limit_oauth_api, ::Feature.current_request)
          matches?(API_PATH_REGEX)
        else
          logical_path.start_with?('/api')
        end
      end

      def logical_path
        @logical_path ||= path.delete_prefix(Gitlab.config.gitlab.relative_url_root)
      end

      def matches?(regex)
        logical_path.match?(regex)
      end

      def api_internal_request?
        matches?(%r{^/api/v\d+/internal/})
      end

      def health_check_request?
        matches?(%r{^/-/(health|liveness|readiness|metrics)})
      end

      def container_registry_event?
        matches?(%r{^/api/v\d+/container_registry_event/})
      end

      def product_analytics_collector_request?
        logical_path.start_with?('/-/collector/i')
      end

      def should_be_skipped?
        api_internal_request? || health_check_request? || container_registry_event?
      end

      def web_request?
        !api_request? && !health_check_request?
      end

      def protected_path?
        matches?(protected_paths_regex)
      end

      def get_request_protected_path?
        matches?(protected_paths_for_get_request_regex)
      end

      def throttle?(throttle, authenticated:)
        fragment = Gitlab::Throttle.throttle_fragment!(throttle, authenticated: authenticated)

        __send__("#{fragment}?") # rubocop:disable GitlabSecurity/PublicSend
      end

      def throttle_unauthenticated_api?
        api_request? &&
          !should_be_skipped? &&
          !frontend_request? &&
          !throttle_unauthenticated_packages_api? &&
          !throttle_unauthenticated_files_api? &&
          !throttle_unauthenticated_deprecated_api? &&
          Gitlab::Throttle.settings.throttle_unauthenticated_api_enabled &&
          unauthenticated?
      end

      def throttle_unauthenticated_web?
        (web_request? || frontend_request?) &&
          !should_be_skipped? &&
          # TODO: Column will be renamed in https://gitlab.com/gitlab-org/gitlab/-/issues/340031
          Gitlab::Throttle.settings.throttle_unauthenticated_enabled &&
          unauthenticated?
      end

      def throttle_authenticated_api?
        api_request? &&
          !frontend_request? &&
          !throttle_authenticated_packages_api? &&
          !throttle_authenticated_files_api? &&
          !throttle_authenticated_deprecated_api? &&
          Gitlab::Throttle.settings.throttle_authenticated_api_enabled
      end

      def throttle_authenticated_web?
        (web_request? || frontend_request?) &&
          !throttle_authenticated_git_lfs? &&
          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_get_protected_paths?
        get? &&
          !should_be_skipped? &&
          get_request_protected_path? &&
          Gitlab::Throttle.protected_paths_enabled? &&
          unauthenticated?
      end

      def throttle_authenticated_get_protected_paths_api?
        get? &&
          api_request? &&
          get_request_protected_path? &&
          Gitlab::Throttle.protected_paths_enabled?
      end

      def throttle_authenticated_get_protected_paths_web?
        get? &&
          web_request? &&
          get_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

      def throttle_authenticated_git_lfs?
        git_lfs_path? &&
          Gitlab::Throttle.settings.throttle_authenticated_git_lfs_enabled
      end

      def throttle_unauthenticated_files_api?
        files_api_path? &&
          Gitlab::Throttle.settings.throttle_unauthenticated_files_api_enabled &&
          unauthenticated?
      end

      def throttle_authenticated_files_api?
        files_api_path? &&
          Gitlab::Throttle.settings.throttle_authenticated_files_api_enabled
      end

      def throttle_unauthenticated_deprecated_api?
        deprecated_api_request? &&
          Gitlab::Throttle.settings.throttle_unauthenticated_deprecated_api_enabled &&
          unauthenticated?
      end

      def throttle_authenticated_deprecated_api?
        deprecated_api_request? &&
          Gitlab::Throttle.settings.throttle_authenticated_deprecated_api_enabled
      end

      private

      def authenticated_identifier(request_formats)
        requester = request_authenticator.find_authenticated_requester(request_formats)

        return unless requester

        identifier_type = if requester.is_a?(DeployToken)
                            :deploy_token
                          else
                            :user
                          end

        { identifier_type: identifier_type, identifier_id: requester.id }
      end

      def request_authenticator
        @request_authenticator ||= Gitlab::Auth::RequestAuthenticator.new(self)
      end

      def protected_paths
        Gitlab::CurrentSettings.current_application_settings.protected_paths
      end

      def protected_paths_regex
        Regexp.union(protected_paths.map { |path| /\A#{Regexp.escape(path)}/ })
      end

      def protected_paths_for_get_request
        Gitlab::CurrentSettings.current_application_settings.protected_paths_for_get_request
      end

      def protected_paths_for_get_request_regex
        Regexp.union(protected_paths_for_get_request.map { |path| /\A#{Regexp.escape(path)}/ })
      end

      def packages_api_path?
        matches?(::Gitlab::Regex::Packages::API_PATH_REGEX)
      end

      def git_lfs_path?
        matches?(::Gitlab::PathRegex.repository_git_lfs_route_regex)
      end

      def files_api_path?
        matches?(FILES_PATH_REGEX)
      end

      def frontend_request?
        strong_memoize(:frontend_request) do
          next false unless env.include?('HTTP_X_CSRF_TOKEN') && session.include?(:_csrf_token)

          # CSRF tokens are not verified for GET/HEAD requests, so we pretend that we always have a POST request.
          Gitlab::RequestForgeryProtection.verified?(env.merge('REQUEST_METHOD' => 'POST'))
        end
      end

      def deprecated_api_request?
        # The projects member of the groups endpoint is deprecated. If left
        # unspecified, with_projects defaults to true
        with_projects = params['with_projects']
        with_projects = true if with_projects.blank?

        matches?(GROUP_PATH_REGEX) && Gitlab::Utils.to_boolean(with_projects)
      end
    end
  end
end
::Gitlab::RackAttack::Request.prepend_mod_with('Gitlab::RackAttack::Request')