Commit 5addac5e authored by Steve Abrams's avatar Steve Abrams

Merge branch 'fix-unsecured-archive-endpoint' into 'master'

Add authorization to composer package archive download

See merge request gitlab-org/gitlab!77950
parents 8edb4bf8 cae37bcd
......@@ -262,6 +262,8 @@ Example response:
## Download a package archive
> Authorization for this endpoint was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/331601) in GitLab 14.10.
Download a Composer package. This URL is provided in the [v1](#v1-package-metadata)
or [v2 package metadata](#v2-package-metadata)
response. A `.zip` file extension must be in the request.
......@@ -287,3 +289,6 @@ curl --user <username>:<personal_access_token> "https://gitlab.example.com/api/v
```
This writes the downloaded file to `package.tar.gz` in the current directory.
NOTE:
This endpoint requires authorization in GitLab 14.10 and later. In GitLab 14.9 and earlier, it was publicly accessible.
......@@ -171,6 +171,8 @@ When you publish:
## Install a Composer package
> Authorization to [download a package archive](../../../api/packages/composer.md#download-a-package-archive) was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/331601) in GitLab 14.10.
Install a package from the Package Registry so you can use it as a dependency.
Prerequisites:
......@@ -354,6 +356,8 @@ used to access them:
## Troubleshooting
### Caching
To improve performance, Composer caches files related to a package. Note that Composer doesn't remove data by
itself. The cache grows as new packages are installed. If you encounter issues, clear the cache with
this command:
......@@ -362,6 +366,14 @@ this command:
composer clearcache
```
### Authorization requirement when using `composer install`
In GitLab 14.9 and earlier, you did not require authorization to use `composer install` if you already had a generated `composer.lock`.
If you committed your `composer.lock`, you could do a `composer install` in CI without setting up credentials.
In GitLab 14.10 and later, authorization is required for the [downloading a package archive](../../../api/packages/composer.md#download-a-package-archive) endpoint.
If you encounter a credentials prompt when you are using `composer install`, follow the instructions in the [install a composer package](#install-a-composer-package) section to create an `auth.json` file.
## Supported CLI commands
The GitLab Composer repository supports the following Composer CLI commands:
......
......@@ -113,10 +113,6 @@ module API
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
before do
unauthorized_user_project!
end
desc 'Composer packages endpoint for registering packages'
namespace ':id/packages/composer' do
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true, deploy_token_allowed: true
......@@ -150,8 +146,11 @@ module API
requires :sha, type: String, desc: 'Shasum of current json'
requires :package_name, type: String, file_path: true, desc: 'The Composer package name'
end
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true, deploy_token_allowed: true
get 'archives/*package_name' do
metadata = unauthorized_user_project
authorize_read_package!(authorized_user_project)
metadata = authorized_user_project
.packages
.composer
.with_name(params[:package_name])
......@@ -161,9 +160,9 @@ module API
not_found! unless metadata
track_package_event('pull_package', :composer, project: unauthorized_user_project, namespace: unauthorized_user_project.namespace)
track_package_event('pull_package', :composer, project: authorized_user_project, namespace: authorized_user_project.namespace)
send_git_archive unauthorized_user_project.repository, ref: metadata.target_sha, format: 'zip', append_sha: true
send_git_archive authorized_user_project.repository, ref: metadata.target_sha, format: 'zip', append_sha: true
end
end
end
......
......@@ -430,11 +430,23 @@ RSpec.describe API::ComposerPackages do
context 'with valid project' do
let!(:package) { create(:composer_package, :with_metadatum, name: package_name, project: project) }
let(:headers) { basic_auth_header(user.username, personal_access_token.token) }
before do
project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
end
context 'when the sha does not match the package name' do
let(:sha) { '123' }
let(:headers) { basic_auth_header(user.username, personal_access_token.token) }
it_behaves_like 'process Composer api request', :anonymous, :not_found
context 'anonymous' do
let(:headers) { {} }
it_behaves_like 'process Composer api request', :anonymous, :unauthorized
end
it_behaves_like 'process Composer api request', :developer, :not_found
end
context 'when the package name does not match the sha' do
......@@ -442,7 +454,13 @@ RSpec.describe API::ComposerPackages do
let(:sha) { branch.target }
let(:url) { "/projects/#{project.id}/packages/composer/archives/unexisting-package-name.zip" }
it_behaves_like 'process Composer api request', :anonymous, :not_found
context 'anonymous' do
let(:headers) { {} }
it_behaves_like 'process Composer api request', :anonymous, :unauthorized
end
it_behaves_like 'process Composer api request', :developer, :not_found
end
context 'with a match package name and sha' do
......@@ -460,14 +478,14 @@ RSpec.describe API::ComposerPackages do
'PUBLIC' | :guest | false | false | :success
'PUBLIC' | :anonymous | false | true | :success
'PRIVATE' | :developer | true | true | :success
'PRIVATE' | :developer | true | false | :success
'PRIVATE' | :developer | false | true | :success
'PRIVATE' | :developer | false | false | :success
'PRIVATE' | :guest | true | true | :success
'PRIVATE' | :guest | true | false | :success
'PRIVATE' | :guest | false | true | :success
'PRIVATE' | :guest | false | false | :success
'PRIVATE' | :anonymous | false | true | :success
'PRIVATE' | :developer | true | false | :unauthorized
'PRIVATE' | :developer | false | true | :not_found
'PRIVATE' | :developer | false | false | :unauthorized
'PRIVATE' | :guest | true | true | :forbidden
'PRIVATE' | :guest | true | false | :unauthorized
'PRIVATE' | :guest | false | true | :not_found
'PRIVATE' | :guest | false | false | :unauthorized
'PRIVATE' | :anonymous | false | true | :unauthorized
end
with_them do
......@@ -480,8 +498,17 @@ RSpec.describe API::ComposerPackages do
end
it_behaves_like 'process Composer api request', params[:user_role], params[:expected_status], params[:member]
it_behaves_like 'a package tracking event', described_class.name, 'pull_package'
include_context 'Composer user type', params[:user_role], params[:member] do
if params[:expected_status] == :success
it_behaves_like 'a package tracking event', described_class.name, 'pull_package'
else
it_behaves_like 'not a package tracking event'
end
end
end
it_behaves_like 'Composer publish with deploy tokens'
end
end
......
......@@ -163,11 +163,11 @@ RSpec.shared_examples 'rejects Composer access with unknown project id' do
let(:project) { double(id: non_existing_record_id) }
context 'as anonymous' do
it_behaves_like 'process Composer api request', :anonymous, :not_found
it_behaves_like 'process Composer api request', :anonymous, :unauthorized
end
context 'as authenticated user' do
subject { get api(url), headers: basic_auth_header(user.username, personal_access_token.token) }
subject { get api(url), params: params, headers: basic_auth_header(user.username, personal_access_token.token) }
it_behaves_like 'process Composer api request', :anonymous, :not_found
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