203 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Ruby
		
	
	
	
			
		
		
	
	
			203 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Ruby
		
	
	
	
| # frozen_string_literal: true
 | |
| 
 | |
| # NuGet Package Manager Client API
 | |
| #
 | |
| # These API endpoints are not meant to be consumed directly by users. They are
 | |
| # called by the NuGet package manager client when users run commands
 | |
| # like `nuget install` or `nuget push`.
 | |
| #
 | |
| # This is the project level API.
 | |
| module API
 | |
|   class NugetProjectPackages < ::API::Base
 | |
|     helpers ::API::Helpers::PackagesHelpers
 | |
|     helpers ::API::Helpers::Packages::BasicAuthHelpers
 | |
|     include ::API::Helpers::Authentication
 | |
| 
 | |
|     feature_category :package_registry
 | |
| 
 | |
|     PACKAGE_FILENAME = 'package.nupkg'
 | |
|     SYMBOL_PACKAGE_FILENAME = 'package.snupkg'
 | |
| 
 | |
|     default_format :json
 | |
| 
 | |
|     authenticate_with do |accept|
 | |
|       accept.token_types(:personal_access_token_with_username, :deploy_token_with_username, :job_token_with_username)
 | |
|             .sent_through(:http_basic_auth)
 | |
|     end
 | |
| 
 | |
|     rescue_from ArgumentError do |e|
 | |
|       render_api_error!(e.message, 400)
 | |
|     end
 | |
| 
 | |
|     after_validation do
 | |
|       require_packages_enabled!
 | |
|     end
 | |
| 
 | |
|     helpers do
 | |
|       params :file_params do
 | |
|         requires :package, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)'
 | |
|       end
 | |
| 
 | |
|       def project_or_group
 | |
|         authorized_user_project
 | |
|       end
 | |
| 
 | |
|       def snowplow_gitlab_standard_context
 | |
|         { project: authorized_user_project, namespace: authorized_user_project.namespace }
 | |
|       end
 | |
| 
 | |
|       def authorize_nuget_upload
 | |
|         authorize_workhorse!(
 | |
|           subject: project_or_group,
 | |
|           has_length: false,
 | |
|           maximum_size: project_or_group.actual_limits.nuget_max_file_size
 | |
|         )
 | |
|       end
 | |
| 
 | |
|       def temp_file_name(symbol_package)
 | |
|         return ::Packages::Nuget::TEMPORARY_SYMBOL_PACKAGE_NAME if symbol_package
 | |
| 
 | |
|         ::Packages::Nuget::TEMPORARY_PACKAGE_NAME
 | |
|       end
 | |
| 
 | |
|       def file_name(symbol_package)
 | |
|         return SYMBOL_PACKAGE_FILENAME if symbol_package
 | |
| 
 | |
|         PACKAGE_FILENAME
 | |
|       end
 | |
| 
 | |
|       def upload_nuget_package_file(symbol_package: false)
 | |
|         authorize_upload!(project_or_group)
 | |
|         bad_request!('File is too large') if project_or_group.actual_limits.exceeded?(:nuget_max_file_size, params[:package].size)
 | |
| 
 | |
|         file_params = params.merge(
 | |
|           file: params[:package],
 | |
|           file_name: file_name(symbol_package)
 | |
|         )
 | |
| 
 | |
|         package = ::Packages::CreateTemporaryPackageService.new(
 | |
|           project_or_group, current_user, declared_params.merge(build: current_authenticated_job)
 | |
|         ).execute(:nuget, name: temp_file_name(symbol_package))
 | |
| 
 | |
|         package_file = ::Packages::CreatePackageFileService.new(package, file_params.merge(build: current_authenticated_job))
 | |
|                                                             .execute
 | |
| 
 | |
|         yield(package) if block_given?
 | |
| 
 | |
|         ::Packages::Nuget::ExtractionWorker.perform_async(package_file.id) # rubocop:disable CodeReuse/Worker
 | |
| 
 | |
|         created!
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     params do
 | |
|       requires :id, type: String, desc: 'The ID of a project', regexp: ::API::Concerns::Packages::NugetEndpoints::POSITIVE_INTEGER_REGEX
 | |
|     end
 | |
|     resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
 | |
|       namespace ':id/packages/nuget' do
 | |
|         include ::API::Concerns::Packages::NugetEndpoints
 | |
| 
 | |
|         # https://docs.microsoft.com/en-us/nuget/api/package-publish-resource
 | |
|         desc 'The NuGet Package Publish endpoint' do
 | |
|           detail 'This feature was introduced in GitLab 12.6'
 | |
|         end
 | |
| 
 | |
|         params do
 | |
|           use :file_params
 | |
|         end
 | |
|         put do
 | |
|           upload_nuget_package_file do |package|
 | |
|             track_package_event(
 | |
|               'push_package',
 | |
|               :nuget,
 | |
|               category: 'API::NugetPackages',
 | |
|               user: current_user,
 | |
|               project: package.project,
 | |
|               namespace: package.project.namespace
 | |
|             )
 | |
|           end
 | |
|         rescue ObjectStorage::RemoteStoreError => e
 | |
|           Gitlab::ErrorTracking.track_exception(e, extra: { file_name: params[:file_name], project_id: project_or_group.id })
 | |
| 
 | |
|           forbidden!
 | |
|         end
 | |
|         put 'authorize' do
 | |
|           authorize_nuget_upload
 | |
|         end
 | |
| 
 | |
|         # https://docs.microsoft.com/en-us/nuget/api/symbol-package-publish-resource
 | |
|         desc 'The NuGet Symbol Package Publish endpoint' do
 | |
|           detail 'This feature was introduced in GitLab 14.1'
 | |
|         end
 | |
| 
 | |
|         params do
 | |
|           use :file_params
 | |
|         end
 | |
|         put 'symbolpackage' do
 | |
|           upload_nuget_package_file(symbol_package: true) do |package|
 | |
|             track_package_event(
 | |
|               'push_symbol_package',
 | |
|               :nuget,
 | |
|               category: 'API::NugetPackages',
 | |
|               user: current_user,
 | |
|               project: package.project,
 | |
|               namespace: package.project.namespace
 | |
|             )
 | |
|           end
 | |
|         rescue ObjectStorage::RemoteStoreError => e
 | |
|           Gitlab::ErrorTracking.track_exception(e, extra: { file_name: params[:file_name], project_id: project_or_group.id })
 | |
| 
 | |
|           forbidden!
 | |
|         end
 | |
|         put 'symbolpackage/authorize' do
 | |
|           authorize_nuget_upload
 | |
|         end
 | |
| 
 | |
|         # https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource
 | |
|         params do
 | |
|           requires :package_name, type: String, desc: 'The NuGet package name', regexp: API::NO_SLASH_URL_PART_REGEX
 | |
|         end
 | |
|         namespace '/download/*package_name' do
 | |
|           after_validation do
 | |
|             authorize_read_package!(project_or_group)
 | |
|           end
 | |
| 
 | |
|           desc 'The NuGet Content Service - index request' do
 | |
|             detail 'This feature was introduced in GitLab 12.8'
 | |
|           end
 | |
|           get 'index', format: :json do
 | |
|             present ::Packages::Nuget::PackagesVersionsPresenter.new(find_packages(params[:package_name])),
 | |
|                     with: ::API::Entities::Nuget::PackagesVersions
 | |
|           end
 | |
| 
 | |
|           desc 'The NuGet Content Service - content request' do
 | |
|             detail 'This feature was introduced in GitLab 12.8'
 | |
|           end
 | |
|           params do
 | |
|             requires :package_version, type: String, desc: 'The NuGet package version', regexp: API::NO_SLASH_URL_PART_REGEX
 | |
|             requires :package_filename, type: String, desc: 'The NuGet package filename', regexp: API::NO_SLASH_URL_PART_REGEX
 | |
|           end
 | |
|           get '*package_version/*package_filename', format: [:nupkg, :snupkg] do
 | |
|             filename = "#{params[:package_filename]}.#{params[:format]}"
 | |
|             package_file = ::Packages::PackageFileFinder.new(find_package(params[:package_name], params[:package_version]), filename, with_file_name_like: true)
 | |
|                                                         .execute
 | |
| 
 | |
|             not_found!('Package') unless package_file
 | |
| 
 | |
|             track_package_event(
 | |
|               params[:format] == 'snupkg' ? 'pull_symbol_package' : 'pull_package',
 | |
|               :nuget,
 | |
|               category: 'API::NugetPackages',
 | |
|               project: package_file.project,
 | |
|               namespace: package_file.project.namespace
 | |
|             )
 | |
| 
 | |
|             # nuget and dotnet don't support 302 Moved status codes, supports_direct_download has to be set to false
 | |
|             present_carrierwave_file!(package_file.file, supports_direct_download: false)
 | |
|           end
 | |
|         end
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| end
 |