Commit 67f59293 authored by Steve Abrams's avatar Steve Abrams

Gem download route

Gem download route for rubygems package registry
parent 0f600368
......@@ -30,6 +30,10 @@ class Packages::PackageFile < ApplicationRecord
scope :preload_conan_file_metadata, -> { preload(:conan_file_metadatum) }
scope :preload_debian_file_metadata, -> { preload(:debian_file_metadatum) }
scope :for_rubygem_with_file_name, ->(project, file_name) do
joins(:package).merge(project.packages.rubygems).with_file_name(file_name)
end
scope :with_conan_file_type, ->(file_type) do
joins(:conan_file_metadatum)
.where(packages_conan_file_metadata: { conan_file_type: ::Packages::Conan::FileMetadatum.conan_file_types[file_type] })
......
......@@ -64,8 +64,15 @@ module API
requires :file_name, type: String, desc: 'Package file name'
end
get "gems/:file_name", requirements: FILE_NAME_REQUIREMENTS do
# To be implemented in https://gitlab.com/gitlab-org/gitlab/-/issues/299283
not_found!
authorize!(:read_package, user_project)
package_file = ::Packages::PackageFile.for_rubygem_with_file_name(
user_project, params[:file_name]
).last!
track_package_event('pull_package', :rubygems)
present_carrierwave_file!(package_file.file)
end
namespace 'api/v1' do
......
......@@ -62,6 +62,21 @@ RSpec.describe Packages::PackageFile, type: :model do
end
end
describe '.for_rubygem_with_file_name' do
let_it_be(:project) { create(:project) }
let_it_be(:non_ruby_package) { create(:nuget_package, project: project, package_type: :nuget) }
let_it_be(:ruby_package) { create(:rubygems_package, project: project, package_type: :rubygems) }
let_it_be(:file_name) { 'other.gem' }
let_it_be(:non_ruby_file) { create(:package_file, :nuget, package: non_ruby_package, file_name: file_name) }
let_it_be(:gem_file1) { create(:package_file, :gem, package: ruby_package) }
let_it_be(:gem_file2) { create(:package_file, :gem, package: ruby_package, file_name: file_name) }
it 'returns the matching gem file only for ruby packages' do
expect(described_class.for_rubygem_with_file_name(project, file_name)).to contain_exactly(gem_file2)
end
end
describe '#update_file_store callback' do
let_it_be(:package_file) { build(:package_file, :nuget, size: nil) }
......
......@@ -108,11 +108,68 @@ RSpec.describe API::RubygemPackages do
end
describe 'GET /api/v4/projects/:project_id/packages/rubygems/gems/:file_name' do
let(:url) { api("/projects/#{project.id}/packages/rubygems/gems/my_gem-1.0.0.gem") }
let_it_be(:package_name) { 'package' }
let_it_be(:version) { '0.0.1' }
let_it_be(:package) { create(:rubygems_package, project: project, name: package_name, version: version) }
let_it_be(:file_name) { "#{package_name}-#{version}.gem" }
let(:url) { api("/projects/#{project.id}/packages/rubygems/gems/#{file_name}") }
subject { get(url, headers: headers) }
it_behaves_like 'an unimplemented route'
context 'with valid project' do
where(:visibility, :user_role, :member, :token_type, :valid_token, :shared_examples_name, :expected_status) do
:public | :developer | true | :personal_access_token | true | 'Rubygems gem download' | :success
:public | :guest | true | :personal_access_token | true | 'Rubygems gem download' | :success
:public | :developer | true | :personal_access_token | false | 'rejects rubygems packages access' | :unauthorized
:public | :guest | true | :personal_access_token | false | 'rejects rubygems packages access' | :unauthorized
:public | :developer | false | :personal_access_token | true | 'Rubygems gem download' | :success
:public | :guest | false | :personal_access_token | true | 'Rubygems gem download' | :success
:public | :developer | false | :personal_access_token | false | 'rejects rubygems packages access' | :unauthorized
:public | :guest | false | :personal_access_token | false | 'rejects rubygems packages access' | :unauthorized
:public | :anonymous | false | :personal_access_token | true | 'Rubygems gem download' | :success
:private | :developer | true | :personal_access_token | true | 'Rubygems gem download' | :success
:private | :guest | true | :personal_access_token | true | 'rejects rubygems packages access' | :forbidden
:private | :developer | true | :personal_access_token | false | 'rejects rubygems packages access' | :unauthorized
:private | :guest | true | :personal_access_token | false | 'rejects rubygems packages access' | :unauthorized
:private | :developer | false | :personal_access_token | true | 'rejects rubygems packages access' | :not_found
:private | :guest | false | :personal_access_token | true | 'rejects rubygems packages access' | :not_found
:private | :developer | false | :personal_access_token | false | 'rejects rubygems packages access' | :unauthorized
:private | :guest | false | :personal_access_token | false | 'rejects rubygems packages access' | :unauthorized
:private | :anonymous | false | :personal_access_token | true | 'rejects rubygems packages access' | :not_found
:public | :developer | true | :job_token | true | 'Rubygems gem download' | :success
:public | :guest | true | :job_token | true | 'Rubygems gem download' | :success
:public | :developer | true | :job_token | false | 'rejects rubygems packages access' | :unauthorized
:public | :guest | true | :job_token | false | 'rejects rubygems packages access' | :unauthorized
:public | :developer | false | :job_token | true | 'Rubygems gem download' | :success
:public | :guest | false | :job_token | true | 'Rubygems gem download' | :success
:public | :developer | false | :job_token | false | 'rejects rubygems packages access' | :unauthorized
:public | :guest | false | :job_token | false | 'rejects rubygems packages access' | :unauthorized
:private | :developer | true | :job_token | true | 'Rubygems gem download' | :success
:private | :guest | true | :job_token | true | 'rejects rubygems packages access' | :forbidden
:private | :developer | true | :job_token | false | 'rejects rubygems packages access' | :unauthorized
:private | :guest | true | :job_token | false | 'rejects rubygems packages access' | :unauthorized
:private | :developer | false | :job_token | true | 'rejects rubygems packages access' | :not_found
:private | :guest | false | :job_token | true | 'rejects rubygems packages access' | :not_found
:private | :developer | false | :job_token | false | 'rejects rubygems packages access' | :unauthorized
:private | :guest | false | :job_token | false | 'rejects rubygems packages access' | :unauthorized
:public | :developer | true | :deploy_token | true | 'Rubygems gem download' | :success
:public | :developer | true | :deploy_token | false | 'rejects rubygems packages access' | :unauthorized
:private | :developer | true | :deploy_token | true | 'Rubygems gem download' | :success
:private | :developer | true | :deploy_token | false | 'rejects rubygems packages access' | :unauthorized
end
with_them do
let(:token) { valid_token ? tokens[token_type] : 'invalid-token123' }
let(:headers) { user_role == :anonymous ? {} : { 'HTTP_AUTHORIZATION' => token } }
before do
project.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value(visibility.to_s))
end
it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
end
end
end
describe 'POST /api/v4/projects/:project_id/packages/rubygems/api/v1/gems/authorize' do
......@@ -171,7 +228,7 @@ RSpec.describe API::RubygemPackages do
let(:headers) { user_headers.merge(workhorse_headers) }
before do
project.update!(visibility: visibility.to_s)
project.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value(visibility.to_s))
end
it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
......@@ -249,7 +306,7 @@ RSpec.describe API::RubygemPackages do
let(:headers) { user_headers.merge(workhorse_headers) }
before do
project.update!(visibility: visibility.to_s)
project.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value(visibility.to_s))
end
it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
......
......@@ -175,3 +175,20 @@ RSpec.shared_examples 'dependency endpoint success' do |user_type, status, add_m
end
end
end
RSpec.shared_examples 'Rubygems gem download' do |user_type, status, add_member = true|
context "for user type #{user_type}" do
before do
project.send("add_#{user_type}", user) if add_member && user_type != :anonymous
end
it 'returns the gem', :aggregate_failures do
subject
expect(response.media_type).to eq('application/octet-stream')
expect(response).to have_gitlab_http_status(status)
end
it_behaves_like 'a package tracking event', described_class.name, 'pull_package'
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment