Commit 86069a64 authored by Giorgenes Gelatti's avatar Giorgenes Gelatti

Adds job token auth to nuget

- adds job token support
- adds specs
- refactor unauthorized handling to set
  realm header
parent 18acb2d0
---
title: Allows NuGet to authenticate with Job Token
merge_request: 39147
author:
type: added
...@@ -4,6 +4,8 @@ module API ...@@ -4,6 +4,8 @@ module API
module Helpers module Helpers
module Packages module Packages
module BasicAuthHelpers module BasicAuthHelpers
extend ::Gitlab::Utils::Override
module Constants module Constants
AUTHENTICATE_REALM_HEADER = 'Www-Authenticate: Basic realm' AUTHENTICATE_REALM_HEADER = 'Www-Authenticate: Basic realm'
AUTHENTICATE_REALM_NAME = 'GitLab Packages Registry' AUTHENTICATE_REALM_NAME = 'GitLab Packages Registry'
...@@ -44,12 +46,13 @@ module API ...@@ -44,12 +46,13 @@ module API
end end
def unauthorized_or! def unauthorized_or!
current_user ? yield : unauthorized_with_header! current_user ? yield : unauthorized!
end end
def unauthorized_with_header! override :unauthorized!
def unauthorized!
header(AUTHENTICATE_REALM_HEADER, AUTHENTICATE_REALM_NAME) header(AUTHENTICATE_REALM_HEADER, AUTHENTICATE_REALM_NAME)
unauthorized! super
end end
end end
end end
......
...@@ -54,7 +54,9 @@ module API ...@@ -54,7 +54,9 @@ module API
params do params do
requires :id, type: String, desc: 'The ID of a project', regexp: POSITIVE_INTEGER_REGEX requires :id, type: String, desc: 'The ID of a project', regexp: POSITIVE_INTEGER_REGEX
end end
route_setting :authentication, deploy_token_allowed: true
route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
before do before do
authorized_user_project authorized_user_project
...@@ -65,7 +67,9 @@ module API ...@@ -65,7 +67,9 @@ module API
desc 'The NuGet Service Index' do desc 'The NuGet Service Index' do
detail 'This feature was introduced in GitLab 12.6' detail 'This feature was introduced in GitLab 12.6'
end end
route_setting :authentication, deploy_token_allowed: true
route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth
get 'index', format: :json do get 'index', format: :json do
authorize_read_package!(authorized_user_project) authorize_read_package!(authorized_user_project)
...@@ -79,10 +83,13 @@ module API ...@@ -79,10 +83,13 @@ module API
desc 'The NuGet Package Publish endpoint' do desc 'The NuGet Package Publish endpoint' do
detail 'This feature was introduced in GitLab 12.6' detail 'This feature was introduced in GitLab 12.6'
end end
params do params do
requires :package, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)' requires :package, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)'
end end
route_setting :authentication, deploy_token_allowed: true
route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth
put do put do
authorize_upload!(authorized_user_project) authorize_upload!(authorized_user_project)
...@@ -107,7 +114,9 @@ module API ...@@ -107,7 +114,9 @@ module API
forbidden! forbidden!
end end
route_setting :authentication, deploy_token_allowed: true
route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth
put 'authorize' do put 'authorize' do
authorize_workhorse!(subject: authorized_user_project, has_length: false) authorize_workhorse!(subject: authorized_user_project, has_length: false)
end end
...@@ -124,7 +133,9 @@ module API ...@@ -124,7 +133,9 @@ module API
desc 'The NuGet Metadata Service - Package name level' do desc 'The NuGet Metadata Service - Package name level' do
detail 'This feature was introduced in GitLab 12.8' detail 'This feature was introduced in GitLab 12.8'
end end
route_setting :authentication, deploy_token_allowed: true
route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth
get 'index', format: :json do get 'index', format: :json do
present ::Packages::Nuget::PackagesMetadataPresenter.new(find_packages), present ::Packages::Nuget::PackagesMetadataPresenter.new(find_packages),
with: ::API::Entities::Nuget::PackagesMetadata with: ::API::Entities::Nuget::PackagesMetadata
...@@ -136,7 +147,9 @@ module API ...@@ -136,7 +147,9 @@ module API
params do params do
requires :package_version, type: String, desc: 'The NuGet package version', regexp: API::NO_SLASH_URL_PART_REGEX requires :package_version, type: String, desc: 'The NuGet package version', regexp: API::NO_SLASH_URL_PART_REGEX
end end
route_setting :authentication, deploy_token_allowed: true
route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth
get '*package_version', format: :json do get '*package_version', format: :json do
present ::Packages::Nuget::PackageMetadataPresenter.new(find_package), present ::Packages::Nuget::PackageMetadataPresenter.new(find_package),
with: ::API::Entities::Nuget::PackageMetadata with: ::API::Entities::Nuget::PackageMetadata
...@@ -155,7 +168,9 @@ module API ...@@ -155,7 +168,9 @@ module API
desc 'The NuGet Content Service - index request' do desc 'The NuGet Content Service - index request' do
detail 'This feature was introduced in GitLab 12.8' detail 'This feature was introduced in GitLab 12.8'
end end
route_setting :authentication, deploy_token_allowed: true
route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth
get 'index', format: :json do get 'index', format: :json do
present ::Packages::Nuget::PackagesVersionsPresenter.new(find_packages), present ::Packages::Nuget::PackagesVersionsPresenter.new(find_packages),
with: ::API::Entities::Nuget::PackagesVersions with: ::API::Entities::Nuget::PackagesVersions
...@@ -168,7 +183,9 @@ module API ...@@ -168,7 +183,9 @@ module API
requires :package_version, type: String, desc: 'The NuGet package version', regexp: API::NO_SLASH_URL_PART_REGEX 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 requires :package_filename, type: String, desc: 'The NuGet package filename', regexp: API::NO_SLASH_URL_PART_REGEX
end end
route_setting :authentication, deploy_token_allowed: true
route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth
get '*package_version/*package_filename', format: :nupkg do get '*package_version/*package_filename', format: :nupkg do
filename = "#{params[:package_filename]}.#{params[:format]}" filename = "#{params[:package_filename]}.#{params[:format]}"
package_file = ::Packages::PackageFileFinder.new(find_package, filename, with_file_name_like: true) package_file = ::Packages::PackageFileFinder.new(find_package, filename, with_file_name_like: true)
...@@ -198,7 +215,9 @@ module API ...@@ -198,7 +215,9 @@ module API
desc 'The NuGet Search Service' do desc 'The NuGet Search Service' do
detail 'This feature was introduced in GitLab 12.8' detail 'This feature was introduced in GitLab 12.8'
end end
route_setting :authentication, deploy_token_allowed: true
route_setting :authentication, deploy_token_allowed: true, job_token_allowed: :basic_auth
get format: :json do get format: :json do
search_options = { search_options = {
include_prerelease_versions: params[:prerelease], include_prerelease_versions: params[:prerelease],
......
...@@ -4,6 +4,7 @@ require 'spec_helper' ...@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe API::NugetPackages do RSpec.describe API::NugetPackages do
include WorkhorseHelpers include WorkhorseHelpers
include PackagesManagerApiSpecHelpers include PackagesManagerApiSpecHelpers
include HttpBasicAuthHelpers
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be(:project, reload: true) { create(:project, :public) } let_it_be(:project, reload: true) { create(:project, :public) }
...@@ -20,38 +21,76 @@ RSpec.describe API::NugetPackages do ...@@ -20,38 +21,76 @@ RSpec.describe API::NugetPackages do
context 'with valid project' do context 'with valid project' do
using RSpec::Parameterized::TableSyntax using RSpec::Parameterized::TableSyntax
where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do context 'personal token' do
'PUBLIC' | :developer | true | true | 'process nuget service index request' | :success where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
'PUBLIC' | :guest | true | true | 'process nuget service index request' | :success 'PUBLIC' | :developer | true | true | 'process nuget service index request' | :success
'PUBLIC' | :developer | true | false | 'process nuget service index request' | :success 'PUBLIC' | :guest | true | true | 'process nuget service index request' | :success
'PUBLIC' | :guest | true | false | 'process nuget service index request' | :success 'PUBLIC' | :developer | true | false | 'process nuget service index request' | :success
'PUBLIC' | :developer | false | true | 'process nuget service index request' | :success 'PUBLIC' | :guest | true | false | 'process nuget service index request' | :success
'PUBLIC' | :guest | false | true | 'process nuget service index request' | :success 'PUBLIC' | :developer | false | true | 'process nuget service index request' | :success
'PUBLIC' | :developer | false | false | 'process nuget service index request' | :success 'PUBLIC' | :guest | false | true | 'process nuget service index request' | :success
'PUBLIC' | :guest | false | false | 'process nuget service index request' | :success 'PUBLIC' | :developer | false | false | 'process nuget service index request' | :success
'PUBLIC' | :anonymous | false | true | 'process nuget service index request' | :success 'PUBLIC' | :guest | false | false | 'process nuget service index request' | :success
'PRIVATE' | :developer | true | true | 'process nuget service index request' | :success 'PUBLIC' | :anonymous | false | true | 'process nuget service index request' | :success
'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden 'PRIVATE' | :developer | true | true | 'process nuget service index request' | :success
'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized 'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden
'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized 'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found 'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found 'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found
'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized 'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found
'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized 'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
'PRIVATE' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized 'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
end 'PRIVATE' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized
end
with_them do with_them do
let(:token) { user_token ? personal_access_token.token : 'wrong' } let(:token) { user_token ? personal_access_token.token : 'wrong' }
let(:headers) { user_role == :anonymous ? {} : build_basic_auth_header(user.username, token) } let(:headers) { user_role == :anonymous ? {} : build_basic_auth_header(user.username, token) }
subject { get api(url), headers: headers } subject { get api(url), headers: headers }
before do before do
project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false)) project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
end
it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
end end
end
it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member] context 'with job token' do
where(:project_visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
'PUBLIC' | :developer | true | true | 'process nuget service index request' | :success
'PUBLIC' | :guest | true | true | 'process nuget service index request' | :success
'PUBLIC' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
'PUBLIC' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
'PUBLIC' | :developer | false | true | 'process nuget service index request' | :success
'PUBLIC' | :guest | false | true | 'process nuget service index request' | :success
'PUBLIC' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
'PUBLIC' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
'PUBLIC' | :anonymous | false | true | 'process nuget service index request' | :success
'PRIVATE' | :developer | true | true | 'process nuget service index request' | :success
'PRIVATE' | :guest | true | true | 'rejects nuget packages access' | :forbidden
'PRIVATE' | :developer | true | false | 'rejects nuget packages access' | :unauthorized
'PRIVATE' | :guest | true | false | 'rejects nuget packages access' | :unauthorized
'PRIVATE' | :developer | false | true | 'rejects nuget packages access' | :not_found
'PRIVATE' | :guest | false | true | 'rejects nuget packages access' | :not_found
'PRIVATE' | :developer | false | false | 'rejects nuget packages access' | :unauthorized
'PRIVATE' | :guest | false | false | 'rejects nuget packages access' | :unauthorized
'PRIVATE' | :anonymous | false | true | 'rejects nuget packages access' | :unauthorized
end
with_them do
let(:job) { user_token ? create(:ci_build, project: project, user: user) : double(token: 'wrong') }
let(:headers) { user_role == :anonymous ? {} : job_basic_auth_header(job) }
subject { get api(url), headers: headers }
before do
project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
end
it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
end
end end
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