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
|
# frozen_string_literal: true
module API
class API < ::API::Base
include APIGuard
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$/]
insert_before Grape::Middleware::Error,
GrapeLogging::Middleware::RequestLogger,
logger: Logger.new(LOG_FILENAME),
formatter: Gitlab::GrapeLogging::Formatters::LogrageWithTimestamp.new,
include: [
GrapeLogging::Loggers::FilterParameters.new(LOG_FILTERS),
Gitlab::GrapeLogging::Loggers::ClientEnvLogger.new,
Gitlab::GrapeLogging::Loggers::RouteLogger.new,
Gitlab::GrapeLogging::Loggers::UserLogger.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
]
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'
end
before do
coerce_nil_params_to_array!
api_endpoint = env['api.endpoint']
feature_category = api_endpoint.options[:for].try(:feature_category_for_app, api_endpoint).to_s
header[Gitlab::Metrics::RequestsRackMiddleware::FEATURE_CATEGORY_HEADER] = feature_category
Gitlab::ApplicationContext.push(
user: -> { @current_user },
project: -> { @project },
namespace: -> { @group },
caller_id: route.origin,
remote_ip: request.ip,
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
# 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/sharpstone/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
format :json
formatter :json, Gitlab::Json::GrapeFormatter
content_type :json, 'application/json'
# Remove the `text/plain+deprecated` with `api_always_use_application_json` feature flag
# There is a small chance some users depend on the old behavior.
# We this change under a feature flag to see if affects GitLab.com users.
# The `+deprecated` is added to distinguish content type
# as defined by `API::API` vs ex. `API::Repositories`
content_type :txt, 'text/plain+deprecated'
before do
# the feature flag workaround is only for `.txt`
api_format = env[Grape::Env::API_FORMAT]
next unless api_format == :txt
# get all defined content-types for the endpoint
api_endpoint = env[Grape::Env::API_ENDPOINT]
content_types = api_endpoint&.namespace_stackable_with_hash(:content_types).to_h
# Only overwrite `text/plain+deprecated`
if content_types[api_format] == 'text/plain+deprecated'
if Feature.enabled?(:api_always_use_application_json)
content_type 'application/json'
else
content_type 'text/plain'
end
end
end
# Ensure the namespace is right, otherwise we might load Grape::API::Helpers
helpers ::API::Helpers
helpers ::API::Helpers::CommonHelpers
helpers ::API::Helpers::PerformanceBarHelpers
namespace do
after do
::Users::ActivityService.new(@current_user).execute
end
# Keep in alphabetical order
mount ::API::AccessRequests
mount ::API::Admin::Ci::Variables
mount ::API::Admin::InstanceClusters
mount ::API::Admin::Sidekiq
mount ::API::Appearance
mount ::API::Applications
mount ::API::Avatar
mount ::API::AwardEmoji
mount ::API::Badges
mount ::API::Boards
mount ::API::Branches
mount ::API::BroadcastMessages
mount ::API::Ci::Pipelines
mount ::API::Ci::PipelineSchedules
mount ::API::Ci::Runner
mount ::API::Ci::Runners
mount ::API::Commits
mount ::API::CommitStatuses
mount ::API::ContainerRegistryEvent
mount ::API::ContainerRepositories
mount ::API::DependencyProxy
mount ::API::DeployKeys
mount ::API::DeployTokens
mount ::API::Deployments
mount ::API::Environments
mount ::API::ErrorTracking
mount ::API::Events
mount ::API::FeatureFlags
mount ::API::FeatureFlagScopes
mount ::API::FeatureFlagsUserLists
mount ::API::Features
mount ::API::Files
mount ::API::FreezePeriods
mount ::API::GroupBoards
mount ::API::GroupClusters
mount ::API::GroupExport
mount ::API::GroupImport
mount ::API::GroupLabels
mount ::API::GroupMilestones
mount ::API::Groups
mount ::API::GroupContainerRepositories
mount ::API::GroupVariables
mount ::API::ImportBitbucketServer
mount ::API::ImportGithub
mount ::API::IssueLinks
mount ::API::Invitations
mount ::API::Issues
mount ::API::JobArtifacts
mount ::API::Jobs
mount ::API::Keys
mount ::API::Labels
mount ::API::Lint
mount ::API::Markdown
mount ::API::Members
mount ::API::MergeRequestDiffs
mount ::API::MergeRequests
mount ::API::MergeRequestApprovals
mount ::API::Metrics::Dashboard::Annotations
mount ::API::Metrics::UserStarredDashboards
mount ::API::Namespaces
mount ::API::Notes
mount ::API::Discussions
mount ::API::ResourceLabelEvents
mount ::API::ResourceMilestoneEvents
mount ::API::ResourceStateEvents
mount ::API::NotificationSettings
mount ::API::ProjectPackages
mount ::API::GroupPackages
mount ::API::PackageFiles
mount ::API::NugetProjectPackages
mount ::API::NugetGroupPackages
mount ::API::PypiPackages
mount ::API::ComposerPackages
mount ::API::ConanProjectPackages
mount ::API::ConanInstancePackages
mount ::API::DebianGroupPackages
mount ::API::DebianProjectPackages
mount ::API::MavenPackages
mount ::API::NpmProjectPackages
mount ::API::NpmInstancePackages
mount ::API::GenericPackages
mount ::API::GoProxy
mount ::API::Pages
mount ::API::PagesDomains
mount ::API::ProjectClusters
mount ::API::ProjectContainerRepositories
mount ::API::ProjectEvents
mount ::API::ProjectExport
mount ::API::ProjectImport
mount ::API::ProjectHooks
mount ::API::ProjectMilestones
mount ::API::ProjectRepositoryStorageMoves
mount ::API::Projects
mount ::API::ProjectSnapshots
mount ::API::ProjectSnippets
mount ::API::ProjectStatistics
mount ::API::ProjectTemplates
mount ::API::Terraform::State
mount ::API::Terraform::StateVersion
mount ::API::PersonalAccessTokens
mount ::API::ProtectedBranches
mount ::API::ProtectedTags
mount ::API::Releases
mount ::API::Release::Links
mount ::API::RemoteMirrors
mount ::API::Repositories
mount ::API::ResourceAccessTokens
mount ::API::RubygemPackages
mount ::API::Search
mount ::API::Services
mount ::API::Settings
mount ::API::SidekiqMetrics
mount ::API::SnippetRepositoryStorageMoves
mount ::API::Snippets
mount ::API::Statistics
mount ::API::Submodules
mount ::API::Subscriptions
mount ::API::Suggestions
mount ::API::SystemHooks
mount ::API::Tags
mount ::API::Templates
mount ::API::Todos
mount ::API::Triggers
mount ::API::Unleash
mount ::API::UsageData
mount ::API::UserCounts
mount ::API::Users
mount ::API::Variables
mount ::API::Version
mount ::API::Wikis
end
mount ::API::Internal::Base
mount ::API::Internal::Lfs
mount ::API::Internal::Pages
mount ::API::Internal::Kubernetes
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
error!('404 Not Found', 404)
end
end
end
API::API.prepend_ee_mod
|