diff options
author | Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com> | 2018-11-07 18:47:59 +0300 |
---|---|---|
committer | Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com> | 2018-11-28 17:15:15 +0300 |
commit | d2f55f9a7ab37242e66f4ae90b95e976682e92a6 (patch) | |
tree | 1fdc6d7405908bae754d83a8cd9a7eae060ae274 | |
parent | b3bb56d4f4d168b1f85afb0170608e53e7f790f4 (diff) |
Add instance-level maven endpoint for download
Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
-rw-r--r-- | doc/user/project/packages/maven_repository.md | 25 | ||||
-rw-r--r-- | ee/app/finders/packages/maven_package_finder.rb | 16 | ||||
-rw-r--r-- | ee/app/services/packages/find_or_create_maven_package_service.rb | 2 | ||||
-rw-r--r-- | ee/changelogs/unreleased/7769-instance-level-maven-endpoint.yml | 5 | ||||
-rw-r--r-- | ee/lib/api/maven_packages.rb | 37 | ||||
-rw-r--r-- | ee/spec/finders/packages/maven_package_finder_spec.rb | 28 | ||||
-rw-r--r-- | ee/spec/requests/api/maven_packages_spec.rb | 100 |
7 files changed, 200 insertions, 13 deletions
diff --git a/doc/user/project/packages/maven_repository.md b/doc/user/project/packages/maven_repository.md index a84761006ea..0e369131c64 100644 --- a/doc/user/project/packages/maven_repository.md +++ b/doc/user/project/packages/maven_repository.md @@ -119,6 +119,31 @@ on the home page of your project. If you have a self-hosted GitLab installation, replace `gitlab.com` with your domain name. +## Instance level Maven endpoint + +> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/8274) in GitLab Premium 11.6. + +If you rely on many packages, it might be inefficient to include the `repository` section +with a unique URL for each package. Instead, you can use the instance level endpoint for +all maven packages stored in GitLab. Only packages you have access to +will be available for download. Here's how the relevant `repository` section of +your `pom.xml` would look like: + +```xml +<repositories> + <repository> + <id>gitlab-maven</id> + <url>https://gitlab.com/api/v4/packages/maven</url> + </repository> +</repositories> +``` + +If you have a self-hosted GitLab installation, replace `gitlab.com` with your +domain name. + +You still need a project specific URL for uploading a package +in the `distributionManagement` section. + ## Uploading packages Once you have set up the [authorization](#authorizing-with-the-gitlab-maven-repository) diff --git a/ee/app/finders/packages/maven_package_finder.rb b/ee/app/finders/packages/maven_package_finder.rb index 7c970a262b8..6297b5325a4 100644 --- a/ee/app/finders/packages/maven_package_finder.rb +++ b/ee/app/finders/packages/maven_package_finder.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true class Packages::MavenPackageFinder - attr_reader :project, :path + attr_reader :path, :project - def initialize(project, path) - @project = project + def initialize(path, project = nil) @path = path + @project = project end def execute @@ -17,9 +17,17 @@ class Packages::MavenPackageFinder private + def scope + if project + project.packages + else + ::Packages::Package.all + end + end + # rubocop: disable CodeReuse/ActiveRecord def packages - project.packages.joins(:maven_metadatum) + scope.joins(:maven_metadatum) .where(packages_maven_metadata: { path: path }) end # rubocop: enable CodeReuse/ActiveRecord diff --git a/ee/app/services/packages/find_or_create_maven_package_service.rb b/ee/app/services/packages/find_or_create_maven_package_service.rb index 141f31a2169..1e43bf863b5 100644 --- a/ee/app/services/packages/find_or_create_maven_package_service.rb +++ b/ee/app/services/packages/find_or_create_maven_package_service.rb @@ -5,7 +5,7 @@ module Packages def execute package = ::Packages::MavenPackageFinder - .new(project, params[:path]).execute + .new(params[:path], project).execute unless package if params[:file_name] == MAVEN_METADATA_FILE diff --git a/ee/changelogs/unreleased/7769-instance-level-maven-endpoint.yml b/ee/changelogs/unreleased/7769-instance-level-maven-endpoint.yml new file mode 100644 index 00000000000..8c7a8b74be7 --- /dev/null +++ b/ee/changelogs/unreleased/7769-instance-level-maven-endpoint.yml @@ -0,0 +1,5 @@ +--- +title: Add an instance-level endpoint for downloading maven packages +merge_request: 8274 +author: +type: added diff --git a/ee/lib/api/maven_packages.rb b/ee/lib/api/maven_packages.rb index c386e6d7243..070b484f21a 100644 --- a/ee/lib/api/maven_packages.rb +++ b/ee/lib/api/maven_packages.rb @@ -12,7 +12,6 @@ module API before do require_packages_enabled! authenticate_non_get! - authorize_packages_feature! end helpers do @@ -54,10 +53,44 @@ module API end end + desc 'Download the maven package file at instance level' do + detail 'This feature was introduced in GitLab 11.6' + end + params do + requires :path, type: String, desc: 'Package path' + requires :file_name, type: String, desc: 'Package file name' + end + route_setting :authentication, job_token_allowed: true + get 'packages/maven/*path/:file_name', requirements: MAVEN_ENDPOINT_REQUIREMENTS do + file_name, format = extract_format(params[:file_name]) + + package = ::Packages::MavenPackageFinder.new(params[:path]).execute! + + forbidden! unless package.project.feature_available?(:packages) + + authorize!(:read_package, package.project) + + package_file = ::Packages::PackageFileFinder + .new(package, file_name).execute! + + case format + when 'md5' + package_file.file_md5 + when 'sha1' + package_file.file_sha1 + when nil + present_carrierwave_file!(package_file.file) + end + end + params do requires :id, type: String, desc: 'The ID of a project' end resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do + before do + authorize_packages_feature! + end + desc 'Download the maven package file' do detail 'This feature was introduced in GitLab 11.3' end @@ -72,7 +105,7 @@ module API file_name, format = extract_format(params[:file_name]) package = ::Packages::MavenPackageFinder - .new(user_project, params[:path]).execute! + .new(params[:path], user_project).execute! package_file = ::Packages::PackageFileFinder .new(package, file_name).execute! diff --git a/ee/spec/finders/packages/maven_package_finder_spec.rb b/ee/spec/finders/packages/maven_package_finder_spec.rb index eb198f527c2..0e1792e58ef 100644 --- a/ee/spec/finders/packages/maven_package_finder_spec.rb +++ b/ee/spec/finders/packages/maven_package_finder_spec.rb @@ -6,16 +6,32 @@ describe Packages::MavenPackageFinder do let(:package) { create(:maven_package, project: project) } describe '#execute!' do - it 'returns a package' do - finder = described_class.new(project, package.maven_metadatum.path) + context 'within the project' do + it 'returns a package' do + finder = described_class.new(package.maven_metadatum.path, project) - expect(finder.execute!).to eq(package) + expect(finder.execute!).to eq(package) + end + + it 'raises an error' do + finder = described_class.new('com/example/my-app/1.0-SNAPSHOT', project) + + expect { finder.execute! }.to raise_error(ActiveRecord::RecordNotFound) + end end - it 'raises an error' do - finder = described_class.new(project, 'com/example/my-app/1.0-SNAPSHOT') + context 'across all projects' do + it 'returns a package' do + finder = described_class.new(package.maven_metadatum.path) + + expect(finder.execute!).to eq(package) + end + + it 'raises an error' do + finder = described_class.new('com/example/my-app/1.0-SNAPSHOT') - expect { finder.execute! }.to raise_error(ActiveRecord::RecordNotFound) + expect { finder.execute! }.to raise_error(ActiveRecord::RecordNotFound) + end end end end diff --git a/ee/spec/requests/api/maven_packages_spec.rb b/ee/spec/requests/api/maven_packages_spec.rb index b59f5a2dec4..582bfbd8435 100644 --- a/ee/spec/requests/api/maven_packages_spec.rb +++ b/ee/spec/requests/api/maven_packages_spec.rb @@ -15,6 +15,106 @@ describe API::MavenPackages do stub_licensed_features(packages: true) end + describe 'GET /api/v4/packages/maven/*path/:file_name' do + let(:package) { create(:maven_package, project: project) } + let(:maven_metadatum) { package.maven_metadatum } + let(:package_file_xml) { package.package_files.find_by(file_type: 'xml') } + + context 'a public project' do + it 'returns the file' do + download_file(package_file_xml.file_name) + + expect(response).to have_gitlab_http_status(200) + expect(response.content_type.to_s).to eq('application/octet-stream') + end + + it 'returns sha1 of the file' do + download_file(package_file_xml.file_name + '.sha1') + + expect(response).to have_gitlab_http_status(200) + expect(response.content_type.to_s).to eq('text/plain') + expect(response.body).to eq(package_file_xml.file_sha1) + end + end + + context 'internal project' do + before do + project.team.truncate + project.update!(visibility_level: Gitlab::VisibilityLevel::INTERNAL) + end + + it 'returns the file' do + download_file_with_token(package_file_xml.file_name) + + expect(response).to have_gitlab_http_status(200) + expect(response.content_type.to_s).to eq('application/octet-stream') + end + + it 'denies download when no private token' do + download_file(package_file_xml.file_name) + + expect(response).to have_gitlab_http_status(403) + end + + it 'allows download with job token' do + download_file(package_file_xml.file_name, job_token: job.token) + + expect(response).to have_gitlab_http_status(200) + expect(response.content_type.to_s).to eq('application/octet-stream') + end + end + + context 'private project' do + before do + project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE) + end + + it 'returns the file' do + download_file_with_token(package_file_xml.file_name) + + expect(response).to have_gitlab_http_status(200) + expect(response.content_type.to_s).to eq('application/octet-stream') + end + + it 'denies download when not enough permissions' do + project.add_guest(user) + + download_file_with_token(package_file_xml.file_name) + + expect(response).to have_gitlab_http_status(403) + end + + it 'denies download when no private token' do + download_file(package_file_xml.file_name) + + expect(response).to have_gitlab_http_status(403) + end + + it 'allows download with job token' do + download_file(package_file_xml.file_name, job_token: job.token) + + expect(response).to have_gitlab_http_status(200) + expect(response.content_type.to_s).to eq('application/octet-stream') + end + end + + it 'rejects request if feature is not in the license' do + stub_licensed_features(packages: false) + + download_file(package_file_xml.file_name) + + expect(response).to have_gitlab_http_status(403) + end + + def download_file(file_name, params = {}, request_headers = headers) + get api("/packages/maven/#{maven_metadatum.path}/#{file_name}"), params, request_headers + end + + def download_file_with_token(file_name, params = {}, request_headers = headers_with_token) + download_file(file_name, params, request_headers) + end + end + describe 'GET /api/v4/projects/:id/packages/maven/*path/:file_name' do let(:package) { create(:maven_package, project: project) } let(:maven_metadatum) { package.maven_metadatum } |