debian-mirror-gitlab/spec/requests/api/generic_packages_spec.rb

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

727 lines
31 KiB
Ruby
Raw Permalink Normal View History

2020-11-24 15:15:51 +05:30
# frozen_string_literal: true
require 'spec_helper'
2023-03-04 22:38:38 +05:30
RSpec.describe API::GenericPackages, feature_category: :package_registry do
2021-03-08 18:12:59 +05:30
include HttpBasicAuthHelpers
2021-03-11 19:13:27 +05:30
using RSpec::Parameterized::TableSyntax
include_context 'workhorse headers'
2021-03-08 18:12:59 +05:30
2020-11-24 15:15:51 +05:30
let_it_be(:personal_access_token) { create(:personal_access_token) }
2021-01-03 14:25:43 +05:30
let_it_be(:project, reload: true) { create(:project) }
2021-03-08 18:12:59 +05:30
let_it_be(:deploy_token_rw) { create(:deploy_token, read_package_registry: true, write_package_registry: true) }
let_it_be(:project_deploy_token_rw) { create(:project_deploy_token, deploy_token: deploy_token_rw, project: project) }
let_it_be(:deploy_token_ro) { create(:deploy_token, read_package_registry: true, write_package_registry: false) }
let_it_be(:project_deploy_token_ro) { create(:project_deploy_token, deploy_token: deploy_token_ro, project: project) }
let_it_be(:deploy_token_wo) { create(:deploy_token, read_package_registry: false, write_package_registry: true) }
let_it_be(:project_deploy_token_wo) { create(:project_deploy_token, deploy_token: deploy_token_wo, project: project) }
2021-04-29 21:17:54 +05:30
2021-01-03 14:25:43 +05:30
let(:user) { personal_access_token.user }
2021-11-11 11:23:49 +05:30
let(:ci_build) { create(:ci_build, :running, user: user, project: project) }
2023-03-04 22:38:38 +05:30
let(:snowplow_gitlab_standard_context) { { user: user, project: project, namespace: project.namespace, property: 'i_package_generic_user' } }
2020-11-24 15:15:51 +05:30
2021-01-03 14:25:43 +05:30
def auth_header
return {} if user_role == :anonymous
2020-11-24 15:15:51 +05:30
2021-01-03 14:25:43 +05:30
case authenticate_with
when :personal_access_token
personal_access_token_header
when :job_token
job_token_header
when :invalid_personal_access_token
personal_access_token_header('wrong token')
when :invalid_job_token
job_token_header('wrong token')
2021-03-08 18:12:59 +05:30
when :user_basic_auth
user_basic_auth_header(user)
when :invalid_user_basic_auth
basic_auth_header('invalid user', 'invalid password')
end
end
def deploy_token_auth_header
case authenticate_with
when :deploy_token_rw
deploy_token_header(deploy_token_rw.token)
when :deploy_token_ro
deploy_token_header(deploy_token_ro.token)
when :deploy_token_wo
deploy_token_header(deploy_token_wo.token)
when :invalid_deploy_token
deploy_token_header('wrong token')
2021-01-03 14:25:43 +05:30
end
end
def personal_access_token_header(value = nil)
{ Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER => value || personal_access_token.token }
end
def job_token_header(value = nil)
{ Gitlab::Auth::AuthFinders::JOB_TOKEN_HEADER => value || ci_build.token }
end
2021-03-08 18:12:59 +05:30
def deploy_token_header(value)
{ Gitlab::Auth::AuthFinders::DEPLOY_TOKEN_HEADER => value }
end
2021-01-03 14:25:43 +05:30
shared_examples 'secure endpoint' do
2020-11-24 15:15:51 +05:30
before do
project.add_developer(user)
end
2021-01-03 14:25:43 +05:30
it 'rejects malicious request' do
subject
2020-11-24 15:15:51 +05:30
2021-01-03 14:25:43 +05:30
expect(response).to have_gitlab_http_status(:bad_request)
end
end
2020-11-24 15:15:51 +05:30
2021-01-03 14:25:43 +05:30
describe 'PUT /api/v4/projects/:id/packages/generic/:package_name/:package_version/:file_name/authorize' do
context 'with valid project' do
where(:project_visibility, :user_role, :member?, :authenticate_with, :expected_status) do
'PUBLIC' | :developer | true | :personal_access_token | :success
'PUBLIC' | :guest | true | :personal_access_token | :forbidden
'PUBLIC' | :developer | true | :invalid_personal_access_token | :unauthorized
'PUBLIC' | :guest | true | :invalid_personal_access_token | :unauthorized
2021-03-08 18:12:59 +05:30
'PUBLIC' | :developer | true | :user_basic_auth | :success
'PUBLIC' | :guest | true | :user_basic_auth | :forbidden
'PUBLIC' | :developer | true | :invalid_user_basic_auth | :unauthorized
'PUBLIC' | :guest | true | :invalid_user_basic_auth | :unauthorized
2021-01-03 14:25:43 +05:30
'PUBLIC' | :developer | false | :personal_access_token | :forbidden
'PUBLIC' | :guest | false | :personal_access_token | :forbidden
'PUBLIC' | :developer | false | :invalid_personal_access_token | :unauthorized
'PUBLIC' | :guest | false | :invalid_personal_access_token | :unauthorized
2021-03-08 18:12:59 +05:30
'PUBLIC' | :developer | false | :user_basic_auth | :forbidden
'PUBLIC' | :guest | false | :user_basic_auth | :forbidden
'PUBLIC' | :developer | false | :invalid_user_basic_auth | :unauthorized
'PUBLIC' | :guest | false | :invalid_user_basic_auth | :unauthorized
2021-01-03 14:25:43 +05:30
'PUBLIC' | :anonymous | false | :none | :unauthorized
'PRIVATE' | :developer | true | :personal_access_token | :success
'PRIVATE' | :guest | true | :personal_access_token | :forbidden
'PRIVATE' | :developer | true | :invalid_personal_access_token | :unauthorized
'PRIVATE' | :guest | true | :invalid_personal_access_token | :unauthorized
2021-03-08 18:12:59 +05:30
'PRIVATE' | :developer | true | :user_basic_auth | :success
'PRIVATE' | :guest | true | :user_basic_auth | :forbidden
'PRIVATE' | :developer | true | :invalid_user_basic_auth | :unauthorized
'PRIVATE' | :guest | true | :invalid_user_basic_auth | :unauthorized
2021-01-03 14:25:43 +05:30
'PRIVATE' | :developer | false | :personal_access_token | :not_found
'PRIVATE' | :guest | false | :personal_access_token | :not_found
'PRIVATE' | :developer | false | :invalid_personal_access_token | :unauthorized
'PRIVATE' | :guest | false | :invalid_personal_access_token | :unauthorized
2021-03-08 18:12:59 +05:30
'PRIVATE' | :developer | false | :user_basic_auth | :not_found
'PRIVATE' | :guest | false | :user_basic_auth | :not_found
'PRIVATE' | :developer | false | :invalid_user_basic_auth | :unauthorized
'PRIVATE' | :guest | false | :invalid_user_basic_auth | :unauthorized
2021-01-03 14:25:43 +05:30
'PRIVATE' | :anonymous | false | :none | :unauthorized
'PUBLIC' | :developer | true | :job_token | :success
'PUBLIC' | :developer | true | :invalid_job_token | :unauthorized
'PUBLIC' | :developer | false | :job_token | :forbidden
'PUBLIC' | :developer | false | :invalid_job_token | :unauthorized
'PRIVATE' | :developer | true | :job_token | :success
'PRIVATE' | :developer | true | :invalid_job_token | :unauthorized
'PRIVATE' | :developer | false | :job_token | :not_found
'PRIVATE' | :developer | false | :invalid_job_token | :unauthorized
end
with_them do
before do
project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility, false))
project.send("add_#{user_role}", user) if member? && user_role != :anonymous
end
it "responds with #{params[:expected_status]}" do
2021-03-11 19:13:27 +05:30
authorize_upload_file(workhorse_headers.merge(auth_header))
2021-01-03 14:25:43 +05:30
expect(response).to have_gitlab_http_status(expected_status)
end
end
2021-03-08 18:12:59 +05:30
where(:authenticate_with, :expected_status) do
:deploy_token_rw | :success
:deploy_token_wo | :success
:deploy_token_ro | :forbidden
:invalid_deploy_token | :unauthorized
end
with_them do
it "responds with #{params[:expected_status]}" do
2021-03-11 19:13:27 +05:30
authorize_upload_file(workhorse_headers.merge(deploy_token_auth_header))
2021-03-08 18:12:59 +05:30
expect(response).to have_gitlab_http_status(expected_status)
end
end
2021-01-03 14:25:43 +05:30
end
context 'application security' do
using RSpec::Parameterized::TableSyntax
where(:param_name, :param_value) do
:package_name | 'my-package/../'
:package_name | 'my-package%2f%2e%2e%2f'
:file_name | '../.ssh%2fauthorized_keys'
:file_name | '%2e%2e%2f.ssh%2fauthorized_keys'
end
with_them do
2021-03-11 19:13:27 +05:30
subject { authorize_upload_file(workhorse_headers.merge(personal_access_token_header), param_name => param_value) }
2021-01-03 14:25:43 +05:30
it_behaves_like 'secure endpoint'
2020-11-24 15:15:51 +05:30
end
end
2021-01-03 14:25:43 +05:30
def authorize_upload_file(request_headers, package_name: 'mypackage', file_name: 'myfile.tar.gz')
url = "/projects/#{project.id}/packages/generic/#{package_name}/0.0.1/#{file_name}/authorize"
put api(url), headers: request_headers
end
end
describe 'PUT /api/v4/projects/:id/packages/generic/:package_name/:package_version/:file_name' do
include WorkhorseHelpers
let(:file_upload) { fixture_file_upload('spec/fixtures/packages/generic/myfile.tar.gz') }
let(:params) { { file: file_upload } }
context 'authentication' do
where(:project_visibility, :user_role, :member?, :authenticate_with, :expected_status) do
'PUBLIC' | :guest | true | :personal_access_token | :forbidden
2021-03-08 18:12:59 +05:30
'PUBLIC' | :guest | true | :user_basic_auth | :forbidden
2021-01-03 14:25:43 +05:30
'PUBLIC' | :developer | true | :invalid_personal_access_token | :unauthorized
'PUBLIC' | :guest | true | :invalid_personal_access_token | :unauthorized
2021-03-08 18:12:59 +05:30
'PUBLIC' | :developer | true | :invalid_user_basic_auth | :unauthorized
'PUBLIC' | :guest | true | :invalid_user_basic_auth | :unauthorized
2021-01-03 14:25:43 +05:30
'PUBLIC' | :developer | false | :personal_access_token | :forbidden
'PUBLIC' | :guest | false | :personal_access_token | :forbidden
2021-03-08 18:12:59 +05:30
'PUBLIC' | :developer | false | :user_basic_auth | :forbidden
'PUBLIC' | :guest | false | :user_basic_auth | :forbidden
2021-01-03 14:25:43 +05:30
'PUBLIC' | :developer | false | :invalid_personal_access_token | :unauthorized
'PUBLIC' | :guest | false | :invalid_personal_access_token | :unauthorized
2021-03-08 18:12:59 +05:30
'PUBLIC' | :developer | false | :invalid_user_basic_auth | :unauthorized
'PUBLIC' | :guest | false | :invalid_user_basic_auth | :unauthorized
2021-01-03 14:25:43 +05:30
'PUBLIC' | :anonymous | false | :none | :unauthorized
'PRIVATE' | :guest | true | :personal_access_token | :forbidden
2021-03-08 18:12:59 +05:30
'PRIVATE' | :guest | true | :user_basic_auth | :forbidden
2021-01-03 14:25:43 +05:30
'PRIVATE' | :developer | true | :invalid_personal_access_token | :unauthorized
'PRIVATE' | :guest | true | :invalid_personal_access_token | :unauthorized
2021-03-08 18:12:59 +05:30
'PRIVATE' | :developer | true | :invalid_user_basic_auth | :unauthorized
'PRIVATE' | :guest | true | :invalid_user_basic_auth | :unauthorized
2021-01-03 14:25:43 +05:30
'PRIVATE' | :developer | false | :personal_access_token | :not_found
'PRIVATE' | :guest | false | :personal_access_token | :not_found
2021-03-08 18:12:59 +05:30
'PRIVATE' | :developer | false | :user_basic_auth | :not_found
'PRIVATE' | :guest | false | :user_basic_auth | :not_found
2021-01-03 14:25:43 +05:30
'PRIVATE' | :developer | false | :invalid_personal_access_token | :unauthorized
'PRIVATE' | :guest | false | :invalid_personal_access_token | :unauthorized
2021-03-08 18:12:59 +05:30
'PRIVATE' | :developer | false | :invalid_user_basic_auth | :unauthorized
'PRIVATE' | :guest | false | :invalid_user_basic_auth | :unauthorized
2021-01-03 14:25:43 +05:30
'PRIVATE' | :anonymous | false | :none | :unauthorized
'PUBLIC' | :developer | true | :invalid_job_token | :unauthorized
'PUBLIC' | :developer | false | :job_token | :forbidden
'PUBLIC' | :developer | false | :invalid_job_token | :unauthorized
'PRIVATE' | :developer | true | :invalid_job_token | :unauthorized
'PRIVATE' | :developer | false | :job_token | :not_found
'PRIVATE' | :developer | false | :invalid_job_token | :unauthorized
end
with_them do
before do
project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility, false))
project.send("add_#{user_role}", user) if member? && user_role != :anonymous
end
it "responds with #{params[:expected_status]}" do
2021-03-11 19:13:27 +05:30
headers = workhorse_headers.merge(auth_header)
2021-01-03 14:25:43 +05:30
upload_file(params, headers)
expect(response).to have_gitlab_http_status(expected_status)
end
end
2021-03-08 18:12:59 +05:30
where(:authenticate_with, :expected_status) do
:deploy_token_ro | :forbidden
:invalid_deploy_token | :unauthorized
end
with_them do
it "responds with #{params[:expected_status]}" do
2021-03-11 19:13:27 +05:30
headers = workhorse_headers.merge(deploy_token_auth_header)
2021-03-08 18:12:59 +05:30
upload_file(params, headers)
expect(response).to have_gitlab_http_status(expected_status)
end
end
2021-01-03 14:25:43 +05:30
end
context 'when user can upload packages and has valid credentials' do
2020-11-24 15:15:51 +05:30
before do
2021-01-03 14:25:43 +05:30
project.add_developer(user)
2020-11-24 15:15:51 +05:30
end
2021-03-08 18:12:59 +05:30
shared_examples 'creates a package and package file' do
it 'creates a package and package file' do
2021-03-11 19:13:27 +05:30
headers = workhorse_headers.merge(auth_header)
2021-01-03 14:25:43 +05:30
2021-03-08 18:12:59 +05:30
expect { upload_file(params, headers) }
.to change { project.packages.generic.count }.by(1)
.and change { Packages::PackageFile.count }.by(1)
2021-01-03 14:25:43 +05:30
2021-03-08 18:12:59 +05:30
aggregate_failures do
expect(response).to have_gitlab_http_status(:created)
2020-11-24 15:15:51 +05:30
2021-03-08 18:12:59 +05:30
package = project.packages.generic.last
expect(package.name).to eq('mypackage')
2021-03-11 19:13:27 +05:30
expect(package.status).to eq('default')
2021-03-08 18:12:59 +05:30
expect(package.version).to eq('0.0.1')
2021-01-03 14:25:43 +05:30
2021-03-08 18:12:59 +05:30
if should_set_build_info
expect(package.original_build_info.pipeline).to eq(ci_build.pipeline)
else
expect(package.original_build_info).to be_nil
end
package_file = package.package_files.last
expect(package_file.file_name).to eq('myfile.tar.gz')
end
2020-11-24 15:15:51 +05:30
end
2021-03-11 19:13:27 +05:30
2021-12-11 22:18:48 +05:30
context 'with select' do
context 'with a valid value' do
context 'package_file' do
let(:params) { super().merge(select: 'package_file') }
it 'returns a package file' do
headers = workhorse_headers.merge(auth_header)
upload_file(params, headers)
aggregate_failures do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to have_key('id')
end
end
end
end
context 'with an invalid value' do
let(:params) { super().merge(select: 'invalid_value') }
it 'returns a package file' do
headers = workhorse_headers.merge(auth_header)
upload_file(params, headers)
expect(response).to have_gitlab_http_status(:bad_request)
end
end
end
2021-03-11 19:13:27 +05:30
context 'with a status' do
context 'valid status' do
let(:params) { super().merge(status: 'hidden') }
it 'assigns the status to the package' do
headers = workhorse_headers.merge(auth_header)
upload_file(params, headers)
aggregate_failures do
expect(response).to have_gitlab_http_status(:created)
package = project.packages.find_by(name: 'mypackage')
expect(package).to be_hidden
end
end
end
context 'invalid status' do
let(:params) { super().merge(status: 'processing') }
it 'rejects the package' do
headers = workhorse_headers.merge(auth_header)
upload_file(params, headers)
aggregate_failures do
expect(response).to have_gitlab_http_status(:bad_request)
end
end
end
2021-04-29 21:17:54 +05:30
context 'different versions' do
where(:version, :expected_status) do
'1.3.350-20201230123456' | :created
'1.2.3' | :created
'1.2.3g' | :created
'1.2' | :created
'1.2.bananas' | :created
'v1.2.4-build' | :created
'd50d836eb3de6177ce6c7a5482f27f9c2c84b672' | :created
'..1.2.3' | :bad_request
'1.2.3-4/../../' | :bad_request
'%2e%2e%2f1.2.3' | :bad_request
end
with_them do
let(:expected_package_diff_count) { expected_status == :created ? 1 : 0 }
let(:headers) { workhorse_headers.merge(auth_header) }
subject { upload_file(params, headers, package_version: version) }
it "returns the #{params[:expected_status]}", :aggregate_failures do
expect { subject }.to change { project.packages.generic.count }.by(expected_package_diff_count)
expect(response).to have_gitlab_http_status(expected_status)
end
end
end
2021-03-11 19:13:27 +05:30
end
2021-01-03 14:25:43 +05:30
end
2021-03-08 18:12:59 +05:30
context 'when valid personal access token is used' do
it_behaves_like 'creates a package and package file' do
let(:auth_header) { personal_access_token_header }
let(:should_set_build_info) { false }
end
end
2020-11-24 15:15:51 +05:30
2021-03-08 18:12:59 +05:30
context 'when valid basic auth is used' do
it_behaves_like 'creates a package and package file' do
let(:auth_header) { user_basic_auth_header(user) }
let(:should_set_build_info) { false }
end
end
2020-11-24 15:15:51 +05:30
2021-03-08 18:12:59 +05:30
context 'when valid deploy token is used' do
it_behaves_like 'creates a package and package file' do
let(:auth_header) { deploy_token_header(deploy_token_wo.token) }
let(:should_set_build_info) { false }
end
end
2021-01-03 14:25:43 +05:30
2021-03-08 18:12:59 +05:30
context 'when valid job token is used' do
it_behaves_like 'creates a package and package file' do
let(:auth_header) { job_token_header }
let(:should_set_build_info) { true }
2020-11-24 15:15:51 +05:30
end
end
2021-01-03 14:25:43 +05:30
context 'event tracking' do
2021-03-11 19:13:27 +05:30
subject { upload_file(params, workhorse_headers.merge(personal_access_token_header)) }
2021-01-03 14:25:43 +05:30
2021-11-11 11:23:49 +05:30
it_behaves_like 'a package tracking event', described_class.name, 'push_package'
2021-01-03 14:25:43 +05:30
end
2022-03-02 08:16:31 +05:30
context 'with existing package' do
let_it_be(:package_name) { 'mypackage' }
let_it_be(:package_version) { '1.2.3' }
let_it_be_with_reload(:existing_package) { create(:generic_package, name: package_name, version: package_version, project: project) }
let(:headers) { workhorse_headers.merge(personal_access_token_header) }
it 'does not create a new package' do
expect { upload_file(params, headers, package_name: package_name, package_version: package_version) }
.to change { project.packages.generic.count }.by(0)
.and change { Packages::PackageFile.count }.by(1)
expect(response).to have_gitlab_http_status(:created)
end
context 'marked as pending_destruction' do
it 'does create a new package' do
existing_package.pending_destruction!
expect { upload_file(params, headers, package_name: package_name, package_version: package_version) }
.to change { project.packages.generic.count }.by(1)
.and change { Packages::PackageFile.count }.by(1)
expect(response).to have_gitlab_http_status(:created)
end
end
end
2021-01-03 14:25:43 +05:30
it 'rejects request without a file from workhorse' do
2021-03-11 19:13:27 +05:30
headers = workhorse_headers.merge(personal_access_token_header)
2021-01-03 14:25:43 +05:30
upload_file({}, headers)
expect(response).to have_gitlab_http_status(:bad_request)
end
it 'rejects request without an auth token' do
2021-03-11 19:13:27 +05:30
upload_file(params, workhorse_headers)
2021-01-03 14:25:43 +05:30
expect(response).to have_gitlab_http_status(:unauthorized)
end
it 'rejects request without workhorse rewritten fields' do
2021-03-11 19:13:27 +05:30
headers = workhorse_headers.merge(personal_access_token_header)
2021-01-03 14:25:43 +05:30
upload_file(params, headers, send_rewritten_field: false)
2020-11-24 15:15:51 +05:30
2021-01-03 14:25:43 +05:30
expect(response).to have_gitlab_http_status(:bad_request)
end
2020-11-24 15:15:51 +05:30
2021-01-03 14:25:43 +05:30
it 'rejects request if file size is too large' do
allow_next_instance_of(UploadedFile) do |uploaded_file|
allow(uploaded_file).to receive(:size).and_return(project.actual_limits.generic_packages_max_file_size + 1)
2020-11-24 15:15:51 +05:30
end
2021-03-11 19:13:27 +05:30
headers = workhorse_headers.merge(personal_access_token_header)
2021-01-03 14:25:43 +05:30
upload_file(params, headers)
expect(response).to have_gitlab_http_status(:bad_request)
end
it 'rejects request without workhorse header' do
expect(Gitlab::ErrorTracking).to receive(:track_exception).once
2020-11-24 15:15:51 +05:30
2021-01-03 14:25:43 +05:30
upload_file(params, personal_access_token_header)
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'application security' do
where(:param_name, :param_value) do
:package_name | 'my-package/../'
:package_name | 'my-package%2f%2e%2e%2f'
:file_name | '../.ssh%2fauthorized_keys'
:file_name | '%2e%2e%2f.ssh%2fauthorized_keys'
end
with_them do
2021-03-11 19:13:27 +05:30
subject { upload_file(params, workhorse_headers.merge(personal_access_token_header), param_name => param_value) }
2021-01-03 14:25:43 +05:30
it_behaves_like 'secure endpoint'
end
end
2021-04-29 21:17:54 +05:30
def upload_file(params, request_headers, send_rewritten_field: true, package_name: 'mypackage', package_version: '0.0.1', file_name: 'myfile.tar.gz')
url = "/projects/#{project.id}/packages/generic/#{package_name}/#{package_version}/#{file_name}"
2021-01-03 14:25:43 +05:30
workhorse_finalize(
api(url),
method: :put,
file_key: :file,
params: params,
headers: request_headers,
send_rewritten_field: send_rewritten_field
)
end
end
describe 'GET /api/v4/projects/:id/packages/generic/:package_name/:package_version/:file_name' do
let_it_be(:package) { create(:generic_package, project: project) }
let_it_be(:package_file) { create(:package_file, :generic, package: package) }
context 'authentication' do
where(:project_visibility, :user_role, :member?, :authenticate_with, :expected_status) do
'PUBLIC' | :developer | true | :personal_access_token | :success
'PUBLIC' | :guest | true | :personal_access_token | :success
2021-03-08 18:12:59 +05:30
'PUBLIC' | :developer | true | :user_basic_auth | :success
'PUBLIC' | :guest | true | :user_basic_auth | :success
2021-01-03 14:25:43 +05:30
'PUBLIC' | :developer | true | :invalid_personal_access_token | :unauthorized
'PUBLIC' | :guest | true | :invalid_personal_access_token | :unauthorized
2021-04-17 20:07:23 +05:30
'PUBLIC' | :developer | true | :invalid_user_basic_auth | :success
'PUBLIC' | :guest | true | :invalid_user_basic_auth | :success
2021-01-03 14:25:43 +05:30
'PUBLIC' | :developer | false | :personal_access_token | :success
'PUBLIC' | :guest | false | :personal_access_token | :success
2021-03-08 18:12:59 +05:30
'PUBLIC' | :developer | false | :user_basic_auth | :success
'PUBLIC' | :guest | false | :user_basic_auth | :success
2021-01-03 14:25:43 +05:30
'PUBLIC' | :developer | false | :invalid_personal_access_token | :unauthorized
'PUBLIC' | :guest | false | :invalid_personal_access_token | :unauthorized
2021-04-17 20:07:23 +05:30
'PUBLIC' | :developer | false | :invalid_user_basic_auth | :success
'PUBLIC' | :guest | false | :invalid_user_basic_auth | :success
'PUBLIC' | :anonymous | false | :none | :success
2021-01-03 14:25:43 +05:30
'PRIVATE' | :developer | true | :personal_access_token | :success
'PRIVATE' | :guest | true | :personal_access_token | :forbidden
2021-03-08 18:12:59 +05:30
'PRIVATE' | :developer | true | :user_basic_auth | :success
'PRIVATE' | :guest | true | :user_basic_auth | :forbidden
2021-01-03 14:25:43 +05:30
'PRIVATE' | :developer | true | :invalid_personal_access_token | :unauthorized
'PRIVATE' | :guest | true | :invalid_personal_access_token | :unauthorized
2021-03-08 18:12:59 +05:30
'PRIVATE' | :developer | true | :invalid_user_basic_auth | :unauthorized
'PRIVATE' | :guest | true | :invalid_user_basic_auth | :unauthorized
2021-01-03 14:25:43 +05:30
'PRIVATE' | :developer | false | :personal_access_token | :not_found
'PRIVATE' | :guest | false | :personal_access_token | :not_found
2021-03-08 18:12:59 +05:30
'PRIVATE' | :developer | false | :user_basic_auth | :not_found
'PRIVATE' | :guest | false | :user_basic_auth | :not_found
2021-01-03 14:25:43 +05:30
'PRIVATE' | :developer | false | :invalid_personal_access_token | :unauthorized
'PRIVATE' | :guest | false | :invalid_personal_access_token | :unauthorized
2021-03-08 18:12:59 +05:30
'PRIVATE' | :developer | false | :invalid_user_basic_auth | :unauthorized
'PRIVATE' | :guest | false | :invalid_user_basic_auth | :unauthorized
2021-01-03 14:25:43 +05:30
'PRIVATE' | :anonymous | false | :none | :unauthorized
'PUBLIC' | :developer | true | :job_token | :success
'PUBLIC' | :developer | true | :invalid_job_token | :unauthorized
'PUBLIC' | :developer | false | :job_token | :success
'PUBLIC' | :developer | false | :invalid_job_token | :unauthorized
'PRIVATE' | :developer | true | :job_token | :success
'PRIVATE' | :developer | true | :invalid_job_token | :unauthorized
'PRIVATE' | :developer | false | :job_token | :not_found
'PRIVATE' | :developer | false | :invalid_job_token | :unauthorized
end
with_them do
before do
project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility, false))
project.send("add_#{user_role}", user) if member? && user_role != :anonymous
end
it "responds with #{params[:expected_status]}" do
download_file(auth_header)
expect(response).to have_gitlab_http_status(expected_status)
2020-11-24 15:15:51 +05:30
end
2022-10-11 01:57:18 +05:30
if params[:expected_status] == :success
it_behaves_like 'bumping the package last downloaded at field' do
subject { download_file(auth_header) }
end
end
2020-11-24 15:15:51 +05:30
end
2021-03-08 18:12:59 +05:30
where(:authenticate_with, :expected_status) do
:deploy_token_rw | :success
:deploy_token_wo | :success
:deploy_token_ro | :success
:invalid_deploy_token | :unauthorized
end
with_them do
it "responds with #{params[:expected_status]}" do
download_file(deploy_token_auth_header)
expect(response).to have_gitlab_http_status(expected_status)
end
2022-10-11 01:57:18 +05:30
if params[:expected_status] == :success
it_behaves_like 'bumping the package last downloaded at field' do
subject { download_file(deploy_token_auth_header) }
end
end
2021-03-08 18:12:59 +05:30
end
2020-11-24 15:15:51 +05:30
end
2022-11-25 23:54:43 +05:30
context 'with access to package registry for everyone' do
let_it_be(:user_role) { :anonymous }
before do
project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
project.project_feature.update!(package_registry_access_level: ProjectFeature::PUBLIC)
end
it 'responds with success' do
download_file(auth_header)
expect(response).to have_gitlab_http_status(:success)
end
end
2022-03-02 08:16:31 +05:30
context 'with package status' do
where(:package_status, :expected_status) do
:default | :success
:hidden | :success
:error | :not_found
end
with_them do
before do
project.add_developer(user)
package.update!(status: package_status)
end
it "responds with #{params[:expected_status]}" do
download_file(personal_access_token_header)
expect(response).to have_gitlab_http_status(expected_status)
end
2022-10-11 01:57:18 +05:30
if params[:expected_status] == :success
it_behaves_like 'bumping the package last downloaded at field' do
subject { download_file(personal_access_token_header) }
end
end
2022-03-02 08:16:31 +05:30
end
end
2021-01-03 14:25:43 +05:30
context 'event tracking' do
before do
project.add_developer(user)
end
subject { download_file(personal_access_token_header) }
2021-11-11 11:23:49 +05:30
it_behaves_like 'a package tracking event', described_class.name, 'pull_package'
2021-01-03 14:25:43 +05:30
end
it 'rejects a malicious file name request' do
project.add_developer(user)
download_file(personal_access_token_header, file_name: '../.ssh%2fauthorized_keys')
expect(response).to have_gitlab_http_status(:bad_request)
end
it 'rejects a malicious file name request' do
project.add_developer(user)
download_file(personal_access_token_header, file_name: '%2e%2e%2f.ssh%2fauthorized_keys')
expect(response).to have_gitlab_http_status(:bad_request)
end
it 'rejects a malicious package name request' do
project.add_developer(user)
download_file(personal_access_token_header, package_name: 'my-package/../')
expect(response).to have_gitlab_http_status(:bad_request)
end
it 'rejects a malicious package name request' do
project.add_developer(user)
download_file(personal_access_token_header, package_name: 'my-package%2f%2e%2e%2f')
expect(response).to have_gitlab_http_status(:bad_request)
end
context 'application security' do
where(:param_name, :param_value) do
:package_name | 'my-package/../'
:package_name | 'my-package%2f%2e%2e%2f'
:file_name | '../.ssh%2fauthorized_keys'
:file_name | '%2e%2e%2f.ssh%2fauthorized_keys'
end
with_them do
subject { download_file(personal_access_token_header, param_name => param_value) }
it_behaves_like 'secure endpoint'
end
end
it 'responds with 404 Not Found for non existing package' do
project.add_developer(user)
download_file(personal_access_token_header, package_name: 'no-such-package')
expect(response).to have_gitlab_http_status(:not_found)
end
it 'responds with 404 Not Found for non existing package file' do
project.add_developer(user)
download_file(personal_access_token_header, file_name: 'no-such-file')
expect(response).to have_gitlab_http_status(:not_found)
end
def download_file(request_headers, package_name: nil, file_name: nil)
package_name ||= package.name
file_name ||= package_file.file_name
url = "/projects/#{project.id}/packages/generic/#{package_name}/#{package.version}/#{file_name}"
2020-11-24 15:15:51 +05:30
2021-01-03 14:25:43 +05:30
get api(url), headers: request_headers
2020-11-24 15:15:51 +05:30
end
end
end