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
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
|
# frozen_string_literal: true
module API
class API < ::API::Base
include APIGuard
include Helpers::OpenApi
LOG_FILENAME = Rails.root.join("log", "api_json.log")
NO_SLASH_URL_PART_REGEX = %r{[^/]+}.freeze
NAMESPACE_OR_PROJECT_REQUIREMENTS = { id: NO_SLASH_URL_PART_REGEX }.freeze
COMMIT_ENDPOINT_REQUIREMENTS = NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(sha: NO_SLASH_URL_PART_REGEX).freeze
USER_REQUIREMENTS = { user_id: NO_SLASH_URL_PART_REGEX }.freeze
LOG_FILTERS = ::Rails.application.config.filter_parameters + [/^output$/]
LOG_FORMATTER = Gitlab::GrapeLogging::Formatters::LogrageWithTimestamp.new
LOGGER = Logger.new(LOG_FILENAME)
insert_before Grape::Middleware::Error,
GrapeLogging::Middleware::RequestLogger,
logger: LOGGER,
formatter: LOG_FORMATTER,
include: [
Gitlab::GrapeLogging::Loggers::FilterParameters.new(LOG_FILTERS),
Gitlab::GrapeLogging::Loggers::ClientEnvLogger.new,
Gitlab::GrapeLogging::Loggers::RouteLogger.new,
Gitlab::GrapeLogging::Loggers::UserLogger.new,
Gitlab::GrapeLogging::Loggers::TokenLogger.new,
Gitlab::GrapeLogging::Loggers::ExceptionLogger.new,
Gitlab::GrapeLogging::Loggers::QueueDurationLogger.new,
Gitlab::GrapeLogging::Loggers::PerfLogger.new,
Gitlab::GrapeLogging::Loggers::CorrelationIdLogger.new,
Gitlab::GrapeLogging::Loggers::ContextLogger.new,
Gitlab::GrapeLogging::Loggers::ContentLogger.new,
Gitlab::GrapeLogging::Loggers::UrgencyLogger.new,
Gitlab::GrapeLogging::Loggers::ResponseLogger.new
]
allow_access_with_scope :api
allow_access_with_scope :read_api, if: -> (request) { request.get? || request.head? }
prefix :api
version 'v3', using: :path do
route :any, '*path' do
error!('API V3 is no longer supported. Use API V4 instead.', 410)
end
end
version 'v4', using: :path
before do
header['X-Frame-Options'] = 'SAMEORIGIN'
header['X-Content-Type-Options'] = 'nosniff'
if Rails.application.config.content_security_policy && !Rails.application.config.content_security_policy_report_only
policy = ActionDispatch::ContentSecurityPolicy.new { |p| p.default_src :none }
end
request.env[ActionDispatch::ContentSecurityPolicy::Request::POLICY] = policy
end
before do
coerce_nil_params_to_array!
api_endpoint = request.env[Grape::Env::API_ENDPOINT]
feature_category = api_endpoint.options[:for].try(:feature_category_for_app, api_endpoint).to_s
# remote_ip is added here and the ContextLogger so that the
# client_id field is set correctly, as the user object does not
# survive between multiple context pushes.
Gitlab::ApplicationContext.push(
user: -> { @current_user },
project: -> { @project },
namespace: -> { @group },
runner: -> { @current_runner || @runner },
remote_ip: request.ip,
caller_id: api_endpoint.endpoint_id,
feature_category: feature_category
)
end
before do
set_peek_enabled_for_current_request
end
after do
Gitlab::UsageDataCounters::VSCodeExtensionActivityUniqueCounter.track_api_request_when_trackable(user_agent: request&.user_agent, user: @current_user)
end
after do
Gitlab::UsageDataCounters::JetBrainsPluginActivityUniqueCounter.track_api_request_when_trackable(user_agent: request&.user_agent, user: @current_user)
end
after do
Gitlab::UsageDataCounters::GitLabCliActivityUniqueCounter.track_api_request_when_trackable(user_agent: request&.user_agent, user: @current_user)
end
# The locale is set to the current user's locale when `current_user` is loaded
after { Gitlab::I18n.use_default_locale }
rescue_from Gitlab::Access::AccessDeniedError do
rack_response({ 'message' => '403 Forbidden' }.to_json, 403)
end
rescue_from ActiveRecord::RecordNotFound do
rack_response({ 'message' => '404 Not found' }.to_json, 404)
end
rescue_from(
::ActiveRecord::StaleObjectError,
::Gitlab::ExclusiveLeaseHelpers::FailedToObtainLockError
) do
rack_response({ 'message' => '409 Conflict: Resource lock' }.to_json, 409)
end
rescue_from UploadedFile::InvalidPathError do |e|
rack_response({ 'message' => e.message }.to_json, 400)
end
rescue_from ObjectStorage::RemoteStoreError do |e|
rack_response({ 'message' => e.message }.to_json, 500)
end
# Retain 405 error rather than a 500 error for Grape 0.15.0+.
# https://github.com/ruby-grape/grape/blob/a3a28f5b5dfbb2797442e006dbffd750b27f2a76/UPGRADING.md#changes-to-method-not-allowed-routes
rescue_from Grape::Exceptions::MethodNotAllowed do |e|
error! e.message, e.status, e.headers
end
rescue_from Grape::Exceptions::Base do |e|
error! e.message, e.status, e.headers
end
rescue_from Gitlab::Auth::TooManyIps do |e|
rack_response({ 'message' => '403 Forbidden' }.to_json, 403)
end
rescue_from :all do |exception|
handle_api_exception(exception)
end
# This is a specific exception raised by `rack-timeout` gem when Puma
# requests surpass its timeout. Given it inherits from Exception, we
# should rescue it separately. For more info, see:
# - https://github.com/zombocom/rack-timeout/blob/master/doc/exceptions.md
# - https://github.com/ruby-grape/grape#exception-handling
rescue_from Rack::Timeout::RequestTimeoutException do |exception|
handle_api_exception(exception)
end
rescue_from RateLimitedService::RateLimitedError do |exception|
exception.log_request(context.request, context.current_user)
rack_response({ 'message' => { 'error' => exception.message } }.to_json, 429, exception.headers)
end
format :json
formatter :json, Gitlab::Json::GrapeFormatter
content_type :json, 'application/json'
# Ensure the namespace is right, otherwise we might load Grape::API::Helpers
helpers ::API::Helpers
helpers ::API::Helpers::CommonHelpers
helpers ::API::Helpers::PerformanceBarHelpers
helpers ::API::Helpers::RateLimiter
namespace do
after do
::Users::ActivityService.new(@current_user).execute
end
# Mount endpoints to include in the OpenAPI V2 documentation here
namespace do
# Keep in alphabetical order
mount ::API::AccessRequests
mount ::API::Admin::Ci::Variables
mount ::API::Admin::InstanceClusters
mount ::API::Appearance
mount ::API::Applications
mount ::API::BroadcastMessages
mount ::API::BulkImports
mount ::API::Ci::Jobs
mount ::API::Ci::ResourceGroups
mount ::API::Ci::Runner
mount ::API::Ci::Runners
mount ::API::Ci::Variables
mount ::API::Clusters::AgentTokens
mount ::API::Clusters::Agents
mount ::API::Commits
mount ::API::CommitStatuses
mount ::API::DeployKeys
mount ::API::DeployTokens
mount ::API::Deployments
mount ::API::Environments
mount ::API::FeatureFlags
mount ::API::FeatureFlagsUserLists
mount ::API::Features
mount ::API::FreezePeriods
mount ::API::GroupClusters
mount ::API::GroupExport
mount ::API::GroupVariables
mount ::API::ImportBitbucketServer
mount ::API::ImportGithub
mount ::API::Invitations
mount ::API::Keys
mount ::API::Lint
mount ::API::MergeRequestDiffs
mount ::API::Metadata
mount ::API::PersonalAccessTokens::SelfInformation
mount ::API::PersonalAccessTokens
mount ::API::ProjectClusters
mount ::API::ProjectEvents
mount ::API::ProjectExport
mount ::API::ProjectHooks
mount ::API::ProjectImport
mount ::API::ProjectRepositoryStorageMoves
mount ::API::ProjectSnippets
mount ::API::ProjectSnapshots
mount ::API::ProjectTemplates
mount ::API::ProtectedBranches
mount ::API::ProtectedTags
mount ::API::Releases
mount ::API::Release::Links
mount ::API::RemoteMirrors
mount ::API::Repositories
mount ::API::ResourceAccessTokens
mount ::API::Snippets
mount ::API::SnippetRepositoryStorageMoves
mount ::API::Statistics
mount ::API::Submodules
mount ::API::Suggestions
mount ::API::SystemHooks
mount ::API::Tags
mount ::API::Unleash
mount ::API::UserCounts
add_open_api_documentation!
end
# Keep in alphabetical order
mount ::API::Admin::BatchedBackgroundMigrations
mount ::API::Admin::PlanLimits
mount ::API::Admin::Sidekiq
mount ::API::AlertManagementAlerts
mount ::API::Avatar
mount ::API::AwardEmoji
mount ::API::Badges
mount ::API::Boards
mount ::API::Branches
mount ::API::Ci::JobArtifacts
mount ::API::Ci::PipelineSchedules
mount ::API::Ci::Pipelines
mount ::API::Ci::SecureFiles
mount ::API::Ci::Triggers
mount ::API::CommitStatuses
mount ::API::ComposerPackages
mount ::API::ConanInstancePackages
mount ::API::ConanProjectPackages
mount ::API::ContainerRegistryEvent
mount ::API::ContainerRepositories
mount ::API::DebianGroupPackages
mount ::API::DebianProjectPackages
mount ::API::DependencyProxy
mount ::API::Discussions
mount ::API::ErrorTracking::ClientKeys
mount ::API::ErrorTracking::Collector
mount ::API::ErrorTracking::ProjectSettings
mount ::API::Events
mount ::API::Files
mount ::API::GenericPackages
mount ::API::Geo
mount ::API::GoProxy
mount ::API::GroupAvatar
mount ::API::GroupBoards
mount ::API::GroupContainerRepositories
mount ::API::GroupDebianDistributions
mount ::API::GroupImport
mount ::API::GroupLabels
mount ::API::GroupMilestones
mount ::API::GroupPackages
mount ::API::Groups
mount ::API::HelmPackages
mount ::API::Integrations
mount ::API::Integrations::JiraConnect::Subscriptions
mount ::API::IssueLinks
mount ::API::Issues
mount ::API::Labels
mount ::API::Markdown
mount ::API::MavenPackages
mount ::API::Members
mount ::API::MergeRequestApprovals
mount ::API::MergeRequests
mount ::API::Metrics::Dashboard::Annotations
mount ::API::Metrics::UserStarredDashboards
mount ::API::Namespaces
mount ::API::Notes
mount ::API::NotificationSettings
mount ::API::NpmInstancePackages
mount ::API::NpmProjectPackages
mount ::API::NugetGroupPackages
mount ::API::NugetProjectPackages
mount ::API::PackageFiles
mount ::API::Pages
mount ::API::PagesDomains
mount ::API::ProjectContainerRepositories
mount ::API::ProjectDebianDistributions
mount ::API::ProjectEvents
mount ::API::ProjectMilestones
mount ::API::ProjectPackages
mount ::API::ProjectStatistics
mount ::API::Projects
mount ::API::ProtectedTags
mount ::API::PypiPackages
mount ::API::ResourceLabelEvents
mount ::API::ResourceMilestoneEvents
mount ::API::ResourceStateEvents
mount ::API::RpmProjectPackages
mount ::API::RubygemPackages
mount ::API::Search
mount ::API::Settings
mount ::API::SidekiqMetrics
mount ::API::Subscriptions
mount ::API::Tags
mount ::API::Templates
mount ::API::Terraform::Modules::V1::Packages
mount ::API::Terraform::State
mount ::API::Terraform::StateVersion
mount ::API::Todos
mount ::API::Topics
mount ::API::UsageData
mount ::API::UsageDataNonSqlMetrics
mount ::API::UsageDataQueries
mount ::API::Users
mount ::API::Wikis
mount ::API::Ml::Mlflow
end
mount ::API::Internal::Base
mount ::API::Internal::Lfs
mount ::API::Internal::Pages
mount ::API::Internal::Kubernetes
mount ::API::Internal::ErrorTracking
mount ::API::Internal::MailRoom
mount ::API::Internal::ContainerRegistry::Migration
mount ::API::Internal::Workhorse
version 'v3', using: :path do
# Although the following endpoints are kept behind V3 namespace,
# they're not deprecated neither should be removed when V3 get
# removed. They're needed as a layer to integrate with Jira
# Development Panel.
namespace '/', requirements: ::API::V3::Github::ENDPOINT_REQUIREMENTS do
mount ::API::V3::Github
end
end
route :any, '*path', feature_category: :not_owned do # rubocop:todo Gitlab/AvoidFeatureCategoryNotOwned
error!('404 Not Found', 404)
end
end
end
API::API.prepend_mod
|