Module: API::Helpers::Packages::Conan::ApiHelpers

Includes:
Gitlab::Utils::StrongMemoize
Included in:
Packages::Conan::PackagePresenter
Defined in:
lib/api/helpers/packages/conan/api_helpers.rb

Instance Method Summary collapse

Instance Method Details

#access_token_from_requestObject



231
232
233
234
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 231

def access_token_from_request
  find_personal_access_token_from_conan_jwt ||
    find_password_from_basic_auth
end

#build_package_file_upload_url(file_name) ⇒ Object



69
70
71
72
73
74
75
76
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 69

def build_package_file_upload_url(file_name)
  options = url_options(file_name).merge(
    conan_package_reference: params[:conan_package_reference],
    package_revision: ::Packages::Conan::FileMetadatum::DEFAULT_PACKAGE_REVISION
  )

  package_file_url(options)
end

#build_recipe_file_upload_url(file_name) ⇒ Object



78
79
80
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 78

def build_recipe_file_upload_url(file_name)
  recipe_file_url(url_options(file_name))
end

#check_username_channelObject



10
11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 10

def check_username_channel
  username = declared(params)[:package_username]
  channel = declared(params)[:package_channel]

  if username == ::Packages::Conan::Metadatum::NONE_VALUE && package_scope == :instance
    # at the instance level, username must not be empty (naming convention)
    # don't try to process the empty username and eagerly return not found.
    not_found!
  end

  ::Packages::Conan::Metadatum.validate_username_and_channel(username, channel) do |none_field|
    bad_request!("#{none_field} can't be solely blank")
  end
end

#create_package_file_with_type(file_type, current_package) ⇒ Object



198
199
200
201
202
203
204
205
206
207
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 198

def create_package_file_with_type(file_type, current_package)
  unless params[:file].size == 0 # rubocop: disable Style/ZeroLengthPredicate
    # conan sends two upload requests, the first has no file, so we skip record creation if file.size == 0
    ::Packages::Conan::CreatePackageFileService.new(
      current_package,
      params[:file],
      params.merge(conan_file_type: file_type, build: current_authenticated_job)
    ).execute
  end
end

#decode_oauth_token_from_jwtObject



295
296
297
298
299
300
301
302
303
304
305
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 295

def decode_oauth_token_from_jwt
  jwt = Doorkeeper::OAuth::Token.from_bearer_authorization(current_request)

  return unless jwt

  token = ::Gitlab::ConanToken.decode(jwt)

  return unless token && token.access_token_id && token.user_id

  token
end

#deploy_token_from_requestObject



254
255
256
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 254

def deploy_token_from_request
  find_deploy_token_from_conan_jwt || find_deploy_token_from_http_basic_auth
end

#download_package_file(file_type) ⇒ Object



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 160

def download_package_file(file_type)
  authorize_read_package!(project)

  package_file = ::Packages::Conan::PackageFileFinder
    .new(
      package,
      params[:file_name].to_s,
      conan_file_type: file_type,
      conan_package_reference: params[:conan_package_reference]
    ).execute!

  track_package_event('pull_package', :conan, category: 'API::ConanPackages', project: project, namespace: project.namespace) if params[:file_name] == ::Packages::Conan::FileMetadatum::PACKAGE_BINARY

  present_package_file!(package_file)
end

#file_namesObject



190
191
192
193
194
195
196
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 190

def file_names
  json_payload = Gitlab::Json.parse(request.body.read)

  bad_request!(nil) unless json_payload.is_a?(Hash)

  json_payload.keys
end

#find_deploy_token_from_conan_jwtObject



275
276
277
278
279
280
281
282
283
284
285
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 275

def find_deploy_token_from_conan_jwt
  token = decode_oauth_token_from_jwt

  return unless token

  deploy_token = DeployToken.active.find_by_token(token.access_token_id.to_s)
  # note: uesr_id is not a user record id, but is the attribute set on ConanToken
  return if token.user_id != deploy_token&.username

  deploy_token
end

#find_job_from_conan_jwtObject



287
288
289
290
291
292
293
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 287

def find_job_from_conan_jwt
  token = decode_oauth_token_from_jwt

  return unless token

  ::Ci::AuthJobFinder.new(token: token.access_token_id.to_s).execute
end

#find_job_from_tokenObject



258
259
260
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 258

def find_job_from_token
  find_job_from_conan_jwt || find_job_from_http_basic_auth
end

#find_oauth_access_tokenObject

We need to override this one because it looks into Bearer authorization header



264
265
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 264

def find_oauth_access_token
end

#find_or_create_packageObject



176
177
178
179
180
181
182
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 176

def find_or_create_package
  package || ::Packages::Conan::CreatePackageService.new(
    project,
    current_user,
    params.merge(build: current_authenticated_job)
  ).execute
end

#find_password_from_basic_authObject



237
238
239
240
241
242
243
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 237

def find_password_from_basic_auth
  return unless route_authentication_setting[:basic_auth_personal_access_token]
  return unless has_basic_credentials?(current_request)

  _username, password = user_name_and_password(current_request)
  password
end

#find_personal_access_tokenObject

We override this method from auth_finders because we need to extract the token from the Conan JWT which is specific to the Conan API



226
227
228
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 226

def find_personal_access_token
  PersonalAccessToken.find_by_token(access_token_from_request)
end

#find_personal_access_token_from_conan_jwtObject



267
268
269
270
271
272
273
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 267

def find_personal_access_token_from_conan_jwt
  token = decode_oauth_token_from_jwt

  return unless token

  token.access_token_id
end

#find_user_from_job_tokenObject



245
246
247
248
249
250
251
252
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 245

def find_user_from_job_token
  return unless route_authentication_setting[:job_token_allowed]

  job = find_job_from_token || return
  @current_authenticated_job = job # rubocop:disable Gitlab/ModuleWithInstanceVariables

  job.user
end

#packageObject



138
139
140
141
142
143
144
145
146
147
148
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 138

def package
  project.packages
      .conan
      .with_name(params[:package_name])
      .with_version(params[:package_version])
      .with_conan_username(params[:package_username])
      .with_conan_channel(params[:package_channel])
      .order_created
      .not_pending_destruction
      .last
end

#package_file?(file_name) ⇒ Boolean

Returns:

  • (Boolean)


65
66
67
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 65

def package_file?(file_name)
  file_name.in?(::Packages::Conan::FileMetadatum::PACKAGE_FILES)
end

#package_file_url(options) ⇒ Object



93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 93

def package_file_url(options)
  case package_scope
  when :project
    expose_url(
      api_v4_projects_packages_conan_v1_files_package_path(
        options.merge(id: project.id)
      )
    )
  when :instance
    expose_url(
      api_v4_packages_conan_v1_files_package_path(options)
    )
  end
end

#package_scopeObject



307
308
309
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 307

def package_scope
  params[:id].present? ? :project : :instance
end

#package_upload_urlsObject



55
56
57
58
59
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 55

def package_upload_urls
  { upload_urls: file_names.select(&method(:package_file?)).index_with do |file_name|
                   build_package_file_upload_url(file_name)
                 end }
end

#present_download_urls(entity) ⇒ Object



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 25

def present_download_urls(entity)
  authorize_read_package!(project)

  presenter = ::Packages::Conan::PackagePresenter.new(
    package,
    current_user,
    project,
    conan_package_reference: params[:conan_package_reference],
    id: params[:id]
  )

  render_api_error!("No recipe manifest found", 404) if yield(presenter).empty?

  present presenter, with: entity
end

#present_package_download_urlsObject



41
42
43
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 41

def present_package_download_urls
  present_download_urls(::API::Entities::ConanPackage::ConanPackageManifest, &:package_urls)
end

#present_recipe_download_urlsObject



45
46
47
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 45

def present_recipe_download_urls
  present_download_urls(::API::Entities::ConanPackage::ConanRecipeManifest, &:recipe_urls)
end

#projectObject



127
128
129
130
131
132
133
134
135
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 127

def project
  case package_scope
  when :project
    user_project(action: :read_package)
  when :instance
    full_path = ::Packages::Conan::Metadatum.full_path_from(package_username: params[:package_username])
    find_project!(full_path)
  end
end

#recipeObject



123
124
125
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 123

def recipe
  "%{package_name}/%{package_version}@%{package_username}/%{package_channel}" % params.symbolize_keys
end

#recipe_file?(file_name) ⇒ Boolean

Returns:

  • (Boolean)


61
62
63
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 61

def recipe_file?(file_name)
  file_name.in?(::Packages::Conan::FileMetadatum::RECIPE_FILES)
end

#recipe_file_url(options) ⇒ Object



108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 108

def recipe_file_url(options)
  case package_scope
  when :project
    expose_url(
      api_v4_projects_packages_conan_v1_files_export_path(
        options.merge(id: project.id)
      )
    )
  when :instance
    expose_url(
      api_v4_packages_conan_v1_files_export_path(options)
    )
  end
end

#recipe_upload_urlsObject



49
50
51
52
53
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 49

def recipe_upload_urls
  { upload_urls: file_names.select(&method(:recipe_file?)).index_with do |file_name|
                   build_recipe_file_upload_url(file_name)
                 end }
end

#tokenObject



151
152
153
154
155
156
157
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 151

def token
  token = nil
  token = ::Gitlab::ConanToken.from_personal_access_token(find_personal_access_token.user_id, access_token_from_request) if find_personal_access_token
  token = ::Gitlab::ConanToken.from_deploy_token(deploy_token_from_request) if deploy_token_from_request
  token = ::Gitlab::ConanToken.from_job(find_job_from_token) if find_job_from_token
  token
end

#track_push_package_eventObject



184
185
186
187
188
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 184

def track_push_package_event
  if params[:file_name] == ::Packages::Conan::FileMetadatum::PACKAGE_BINARY && params[:file].size > 0 # rubocop: disable Style/ZeroLengthPredicate
    track_package_event('push_package', :conan, category: 'API::ConanPackages', project: project, namespace: project.namespace)
  end
end

#upload_package_file(file_type) ⇒ Object



209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 209

def upload_package_file(file_type)
  authorize_upload!(project)
  bad_request!('File is too large') if project.actual_limits.exceeded?(:conan_max_file_size, params['file.size'].to_i)

  current_package = find_or_create_package

  track_push_package_event

  create_package_file_with_type(file_type, current_package)
rescue ObjectStorage::RemoteStoreError => e
  Gitlab::ErrorTracking.track_exception(e, file_name: params[:file_name], project_id: project.id)

  forbidden!
end

#url_options(file_name) ⇒ Object



82
83
84
85
86
87
88
89
90
91
# File 'lib/api/helpers/packages/conan/api_helpers.rb', line 82

def url_options(file_name)
  {
    package_name: params[:package_name],
    package_version: params[:package_version],
    package_username: params[:package_username],
    package_channel: params[:package_channel],
    file_name: file_name,
    recipe_revision: ::Packages::Conan::FileMetadatum::DEFAULT_RECIPE_REVISION
  }
end