1# frozen_string_literal: true
2
3module Gitlab
4  module RackAttack
5    module Request
6      FILES_PATH_REGEX = %r{^/api/v\d+/projects/[^/]+/repository/files/.+}.freeze
7      GROUP_PATH_REGEX = %r{^/api/v\d+/groups/[^/]+/?$}.freeze
8
9      def unauthenticated?
10        !(authenticated_user_id([:api, :rss, :ics]) || authenticated_runner_id)
11      end
12
13      def throttled_user_id(request_formats)
14        user_id = authenticated_user_id(request_formats)
15
16        if Gitlab::RackAttack.user_allowlist.include?(user_id)
17          Gitlab::Instrumentation::Throttle.safelist = 'throttle_user_allowlist'
18          return
19        end
20
21        user_id
22      end
23
24      def authenticated_runner_id
25        request_authenticator.runner&.id
26      end
27
28      def api_request?
29        path.start_with?('/api')
30      end
31
32      def api_internal_request?
33        path =~ %r{^/api/v\d+/internal/}
34      end
35
36      def health_check_request?
37        path =~ %r{^/-/(health|liveness|readiness|metrics)}
38      end
39
40      def container_registry_event?
41        path =~ %r{^/api/v\d+/container_registry_event/}
42      end
43
44      def product_analytics_collector_request?
45        path.start_with?('/-/collector/i')
46      end
47
48      def should_be_skipped?
49        api_internal_request? || health_check_request? || container_registry_event?
50      end
51
52      def web_request?
53        !api_request? && !health_check_request?
54      end
55
56      def protected_path?
57        !protected_path_regex.nil?
58      end
59
60      def protected_path_regex
61        path =~ protected_paths_regex
62      end
63
64      def throttle?(throttle, authenticated:)
65        fragment = Gitlab::Throttle.throttle_fragment!(throttle, authenticated: authenticated)
66
67        __send__("#{fragment}?") # rubocop:disable GitlabSecurity/PublicSend
68      end
69
70      def throttle_unauthenticated_api?
71        api_request? &&
72        !should_be_skipped? &&
73        !throttle_unauthenticated_packages_api? &&
74        !throttle_unauthenticated_files_api? &&
75        !throttle_unauthenticated_deprecated_api? &&
76        Gitlab::Throttle.settings.throttle_unauthenticated_api_enabled &&
77        unauthenticated?
78      end
79
80      def throttle_unauthenticated_web?
81        web_request? &&
82        !should_be_skipped? &&
83        # TODO: Column will be renamed in https://gitlab.com/gitlab-org/gitlab/-/issues/340031
84        Gitlab::Throttle.settings.throttle_unauthenticated_enabled &&
85        unauthenticated?
86      end
87
88      def throttle_authenticated_api?
89        api_request? &&
90        !throttle_authenticated_packages_api? &&
91        !throttle_authenticated_files_api? &&
92        !throttle_authenticated_deprecated_api? &&
93        Gitlab::Throttle.settings.throttle_authenticated_api_enabled
94      end
95
96      def throttle_authenticated_web?
97        web_request? &&
98        !throttle_authenticated_git_lfs? &&
99        Gitlab::Throttle.settings.throttle_authenticated_web_enabled
100      end
101
102      def throttle_unauthenticated_protected_paths?
103        post? &&
104        !should_be_skipped? &&
105        protected_path? &&
106        Gitlab::Throttle.protected_paths_enabled? &&
107        unauthenticated?
108      end
109
110      def throttle_authenticated_protected_paths_api?
111        post? &&
112        api_request? &&
113        protected_path? &&
114        Gitlab::Throttle.protected_paths_enabled?
115      end
116
117      def throttle_authenticated_protected_paths_web?
118        post? &&
119        web_request? &&
120        protected_path? &&
121        Gitlab::Throttle.protected_paths_enabled?
122      end
123
124      def throttle_unauthenticated_packages_api?
125        packages_api_path? &&
126        Gitlab::Throttle.settings.throttle_unauthenticated_packages_api_enabled &&
127        unauthenticated?
128      end
129
130      def throttle_authenticated_packages_api?
131        packages_api_path? &&
132        Gitlab::Throttle.settings.throttle_authenticated_packages_api_enabled
133      end
134
135      def throttle_authenticated_git_lfs?
136        git_lfs_path? &&
137        Gitlab::Throttle.settings.throttle_authenticated_git_lfs_enabled
138      end
139
140      def throttle_unauthenticated_files_api?
141        files_api_path? &&
142        Gitlab::Throttle.settings.throttle_unauthenticated_files_api_enabled &&
143        unauthenticated?
144      end
145
146      def throttle_authenticated_files_api?
147        files_api_path? &&
148        Gitlab::Throttle.settings.throttle_authenticated_files_api_enabled
149      end
150
151      def throttle_unauthenticated_deprecated_api?
152        deprecated_api_request? &&
153        Gitlab::Throttle.settings.throttle_unauthenticated_deprecated_api_enabled &&
154        unauthenticated?
155      end
156
157      def throttle_authenticated_deprecated_api?
158        deprecated_api_request? &&
159        Gitlab::Throttle.settings.throttle_authenticated_deprecated_api_enabled
160      end
161
162      private
163
164      def authenticated_user_id(request_formats)
165        request_authenticator.user(request_formats)&.id
166      end
167
168      def request_authenticator
169        @request_authenticator ||= Gitlab::Auth::RequestAuthenticator.new(self)
170      end
171
172      def protected_paths
173        Gitlab::CurrentSettings.current_application_settings.protected_paths
174      end
175
176      def protected_paths_regex
177        Regexp.union(protected_paths.map { |path| /\A#{Regexp.escape(path)}/ })
178      end
179
180      def packages_api_path?
181        path =~ ::Gitlab::Regex::Packages::API_PATH_REGEX
182      end
183
184      def git_lfs_path?
185        path =~ Gitlab::PathRegex.repository_git_lfs_route_regex
186      end
187
188      def files_api_path?
189        path =~ FILES_PATH_REGEX
190      end
191
192      def deprecated_api_request?
193        # The projects member of the groups endpoint is deprecated. If left
194        # unspecified, with_projects defaults to true
195        with_projects = params['with_projects']
196        with_projects = true if with_projects.blank?
197
198        path =~ GROUP_PATH_REGEX && Gitlab::Utils.to_boolean(with_projects)
199      end
200    end
201  end
202end
203::Gitlab::RackAttack::Request.prepend_mod_with('Gitlab::RackAttack::Request')
204