summaryrefslogtreecommitdiff
path: root/lib/api/api.rb
blob: de9a3120d9026663f44b430d05f3ff755973ac43 (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
# frozen_string_literal: true

module API
  class API < Grape::API
    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
                  ]

    allow_access_with_scope :api
    allow_access_with_scope :read_api, if: -> (request) { request.get? }
    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
      Gitlab::ApplicationContext.push(
        user: -> { @current_user },
        project: -> { @project },
        namespace: -> { @group },
        caller_id: route.origin
      )
    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

    format :json
    content_type :txt, "text/plain"

    # Ensure the namespace is right, otherwise we might load Grape::API::Helpers
    helpers ::API::Helpers
    helpers ::API::Helpers::CommonHelpers

    namespace do
      after do
        ::Users::ActivityService.new(@current_user).execute
      end

      # Keep in alphabetical order
      mount ::API::AccessRequests
      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::Commits
      mount ::API::CommitStatuses
      mount ::API::ContainerRegistryEvent
      mount ::API::DeployKeys
      mount ::API::DeployTokens
      mount ::API::Deployments
      mount ::API::Environments
      mount ::API::ErrorTracking
      mount ::API::Events
      mount ::API::Features
      mount ::API::Files
      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::ImportGithub
      mount ::API::Issues
      mount ::API::JobArtifacts
      mount ::API::Jobs
      mount ::API::Keys
      mount ::API::Labels
      mount ::API::Lint
      mount ::API::LsifData
      mount ::API::Markdown
      mount ::API::Members
      mount ::API::MergeRequestDiffs
      mount ::API::MergeRequests
      mount ::API::Metrics::Dashboard::Annotations
      mount ::API::Namespaces
      mount ::API::Notes
      mount ::API::Discussions
      mount ::API::ResourceLabelEvents
      mount ::API::NotificationSettings
      mount ::API::Pages
      mount ::API::PagesDomains
      mount ::API::Pipelines
      mount ::API::PipelineSchedules
      mount ::API::ProjectClusters
      mount ::API::ProjectContainerRepositories
      mount ::API::ProjectEvents
      mount ::API::ProjectExport
      mount ::API::ProjectImport
      mount ::API::ProjectHooks
      mount ::API::ProjectMilestones
      mount ::API::Projects
      mount ::API::ProjectSnapshots
      mount ::API::ProjectSnippets
      mount ::API::ProjectStatistics
      mount ::API::ProjectTemplates
      mount ::API::Terraform::State
      mount ::API::ProtectedBranches
      mount ::API::ProtectedTags
      mount ::API::Releases
      mount ::API::Release::Links
      mount ::API::RemoteMirrors
      mount ::API::Repositories
      mount ::API::Runner
      mount ::API::Runners
      mount ::API::Search
      mount ::API::Services
      mount ::API::Settings
      mount ::API::SidekiqMetrics
      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::UserCounts
      mount ::API::Users
      mount ::API::Variables
      mount ::API::Version
      mount ::API::Wikis
    end

    mount ::API::Internal::Base
    mount ::API::Internal::Pages

    route :any, '*path' do
      error!('404 Not Found', 404)
    end
  end
end

API::API.prepend_if_ee('::EE::API::API')