2019-12-21 20:55:43 +05:30
# frozen_string_literal: true
2016-08-24 12:49:21 +05:30
require 'spec_helper'
2020-07-28 23:09:34 +05:30
RSpec . describe 'Git LFS API and storage' do
2019-12-21 20:55:43 +05:30
include LfsHttpHelpers
2018-03-17 18:26:18 +05:30
include ProjectForksHelper
2019-12-21 20:55:43 +05:30
include WorkhorseHelpers
2016-09-29 09:46:39 +05:30
2020-04-22 19:07:51 +05:30
let_it_be ( :project , reload : true ) { create ( :project , :repository ) }
let_it_be ( :other_project ) { create ( :project , :repository ) }
let_it_be ( :user ) { create ( :user ) }
2016-08-24 12:49:21 +05:30
let! ( :lfs_object ) { create ( :lfs_object , :with_file ) }
let ( :headers ) do
{
'Authorization' = > authorization ,
'X-Sendfile-Type' = > sendfile
} . compact
end
2020-10-24 23:57:45 +05:30
let ( :include_workhorse_jwt_header ) { true }
2016-08-24 12:49:21 +05:30
let ( :authorization ) { }
let ( :sendfile ) { }
2016-09-29 09:46:39 +05:30
let ( :pipeline ) { create ( :ci_empty_pipeline , project : project ) }
2016-08-24 12:49:21 +05:30
let ( :sample_oid ) { lfs_object . oid }
let ( :sample_size ) { lfs_object . size }
2019-12-21 20:55:43 +05:30
let ( :sample_object ) { { 'oid' = > sample_oid , 'size' = > sample_size } }
let ( :non_existing_object_oid ) { '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897' }
let ( :non_existing_object_size ) { 1575078 }
let ( :non_existing_object ) { { 'oid' = > non_existing_object_oid , 'size' = > non_existing_object_size } }
let ( :multiple_objects ) { [ sample_object , non_existing_object ] }
2016-08-24 12:49:21 +05:30
2019-12-21 20:55:43 +05:30
let ( :lfs_enabled ) { true }
before do
stub_lfs_setting ( enabled : lfs_enabled )
end
describe 'when LFS is disabled' do
let ( :lfs_enabled ) { false }
let ( :body ) { upload_body ( multiple_objects ) }
2016-09-13 17:45:13 +05:30
let ( :authorization ) { authorize_user }
2016-08-24 12:49:21 +05:30
before do
2019-12-21 20:55:43 +05:30
post_lfs_json batch_url ( project ) , body , headers
2016-08-24 12:49:21 +05:30
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 501 response'
2016-08-24 12:49:21 +05:30
end
2016-09-29 09:46:39 +05:30
context 'project specific LFS settings' do
2019-12-21 20:55:43 +05:30
let ( :body ) { upload_body ( sample_object ) }
2016-09-29 09:46:39 +05:30
let ( :authorization ) { authorize_user }
2019-12-21 20:55:43 +05:30
before do
project . add_maintainer ( user )
project . update_attribute ( :lfs_enabled , project_lfs_enabled )
subject
end
2016-09-29 09:46:39 +05:30
context 'with LFS disabled globally' do
2019-12-21 20:55:43 +05:30
let ( :lfs_enabled ) { false }
2016-09-29 09:46:39 +05:30
describe 'LFS disabled in project' do
2019-12-21 20:55:43 +05:30
let ( :project_lfs_enabled ) { false }
2016-09-29 09:46:39 +05:30
2019-12-21 20:55:43 +05:30
context 'when uploading' do
subject { post_lfs_json ( batch_url ( project ) , body , headers ) }
2016-09-29 09:46:39 +05:30
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 501 response'
2016-09-29 09:46:39 +05:30
end
2019-12-21 20:55:43 +05:30
context 'when downloading' do
subject { get ( objects_url ( project , sample_oid ) , params : { } , headers : headers ) }
2016-09-29 09:46:39 +05:30
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 501 response'
2016-09-29 09:46:39 +05:30
end
end
describe 'LFS enabled in project' do
2019-12-21 20:55:43 +05:30
let ( :project_lfs_enabled ) { true }
2016-09-29 09:46:39 +05:30
2019-12-21 20:55:43 +05:30
context 'when uploading' do
subject { post_lfs_json ( batch_url ( project ) , body , headers ) }
2016-09-29 09:46:39 +05:30
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 501 response'
2016-09-29 09:46:39 +05:30
end
2019-12-21 20:55:43 +05:30
context 'when downloading' do
subject { get ( objects_url ( project , sample_oid ) , params : { } , headers : headers ) }
2016-09-29 09:46:39 +05:30
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 501 response'
2016-09-29 09:46:39 +05:30
end
end
end
context 'with LFS enabled globally' do
describe 'LFS disabled in project' do
2019-12-21 20:55:43 +05:30
let ( :project_lfs_enabled ) { false }
2016-09-29 09:46:39 +05:30
2019-12-21 20:55:43 +05:30
context 'when uploading' do
subject { post_lfs_json ( batch_url ( project ) , body , headers ) }
2016-09-29 09:46:39 +05:30
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 403 response'
2016-09-29 09:46:39 +05:30
end
2019-12-21 20:55:43 +05:30
context 'when downloading' do
subject { get ( objects_url ( project , sample_oid ) , params : { } , headers : headers ) }
2016-09-29 09:46:39 +05:30
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 403 response'
2016-09-29 09:46:39 +05:30
end
end
describe 'LFS enabled in project' do
2019-12-21 20:55:43 +05:30
let ( :project_lfs_enabled ) { true }
2016-09-29 09:46:39 +05:30
2019-12-21 20:55:43 +05:30
context 'when uploading' do
subject { post_lfs_json ( batch_url ( project ) , body , headers ) }
2016-09-29 09:46:39 +05:30
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 200 response'
2016-09-29 09:46:39 +05:30
end
2019-12-21 20:55:43 +05:30
context 'when downloading' do
subject { get ( objects_url ( project , sample_oid ) , params : { } , headers : headers ) }
2016-09-29 09:46:39 +05:30
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 200 response'
2016-09-29 09:46:39 +05:30
end
end
end
end
2016-08-24 12:49:21 +05:30
describe 'deprecated API' do
2019-12-21 20:55:43 +05:30
let ( :authorization ) { authorize_user }
2016-08-24 12:49:21 +05:30
2019-12-21 20:55:43 +05:30
shared_examples 'deprecated request' do
before do
subject
2016-08-24 12:49:21 +05:30
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http expected response code and message' do
let ( :response_code ) { 501 }
let ( :message ) { 'Server supports batch API only, please update your Git LFS client to version 1.0.1 and up.' }
2016-08-24 12:49:21 +05:30
end
end
2019-12-21 20:55:43 +05:30
context 'when fetching LFS object using deprecated API' do
subject { get ( deprecated_objects_url ( project , sample_oid ) , params : { } , headers : headers ) }
2016-08-24 12:49:21 +05:30
2019-12-21 20:55:43 +05:30
it_behaves_like 'deprecated request'
2016-08-24 12:49:21 +05:30
end
2019-12-21 20:55:43 +05:30
context 'when handling LFS request using deprecated API' do
subject { post_lfs_json ( deprecated_objects_url ( project ) , nil , headers ) }
it_behaves_like 'deprecated request'
end
2016-08-24 12:49:21 +05:30
2019-12-21 20:55:43 +05:30
def deprecated_objects_url ( project , oid = nil )
File . join ( [ " #{ project . http_url_to_repo } /info/lfs/objects/ " , oid ] . compact )
2016-08-24 12:49:21 +05:30
end
end
2019-12-21 20:55:43 +05:30
describe 'when fetching LFS object' do
2016-08-24 12:49:21 +05:30
let ( :update_permissions ) { }
2018-05-09 12:01:36 +05:30
let ( :before_get ) { }
2016-08-24 12:49:21 +05:30
before do
update_permissions
2018-05-09 12:01:36 +05:30
before_get
2019-12-21 20:55:43 +05:30
get objects_url ( project , sample_oid ) , params : { } , headers : headers
2016-08-24 12:49:21 +05:30
end
context 'and request comes from gitlab-workhorse' do
context 'without user being authorized' do
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 401 response'
2016-08-24 12:49:21 +05:30
end
context 'with required headers' do
shared_examples 'responds with a file' do
let ( :sendfile ) { 'X-Sendfile' }
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 200 response'
2016-08-24 12:49:21 +05:30
it 'responds with the file location' do
expect ( response . headers [ 'Content-Type' ] ) . to eq ( 'application/octet-stream' )
expect ( response . headers [ 'X-Sendfile' ] ) . to eq ( lfs_object . file . path )
end
end
context 'with user is authorized' do
let ( :authorization ) { authorize_user }
context 'and does not have project access' do
let ( :update_permissions ) do
project . lfs_objects << lfs_object
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 404 response'
2016-08-24 12:49:21 +05:30
end
context 'and does have project access' do
let ( :update_permissions ) do
2018-11-18 11:00:15 +05:30
project . add_maintainer ( user )
2016-08-24 12:49:21 +05:30
project . lfs_objects << lfs_object
end
it_behaves_like 'responds with a file'
2018-05-09 12:01:36 +05:30
context 'when LFS uses object storage' do
context 'when proxy download is enabled' do
let ( :before_get ) do
stub_lfs_object_storage ( proxy_download : true )
lfs_object . file . migrate! ( LfsObjectUploader :: Store :: REMOTE )
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 200 response'
2018-05-09 12:01:36 +05:30
it 'responds with the workhorse send-url' do
expect ( response . headers [ Gitlab :: Workhorse :: SEND_DATA_HEADER ] ) . to start_with ( " send-url: " )
end
end
context 'when proxy download is disabled' do
let ( :before_get ) do
stub_lfs_object_storage ( proxy_download : false )
lfs_object . file . migrate! ( LfsObjectUploader :: Store :: REMOTE )
end
it 'responds with redirect' do
2020-03-13 15:44:24 +05:30
expect ( response ) . to have_gitlab_http_status ( :found )
2018-05-09 12:01:36 +05:30
end
it 'responds with the file location' do
expect ( response . location ) . to include ( lfs_object . reload . file . path )
end
end
end
2016-08-24 12:49:21 +05:30
end
end
2016-09-29 09:46:39 +05:30
context 'when deploy key is authorized' do
let ( :key ) { create ( :deploy_key ) }
let ( :authorization ) { authorize_deploy_key }
2016-08-24 12:49:21 +05:30
let ( :update_permissions ) do
2016-09-29 09:46:39 +05:30
project . deploy_keys << key
2016-08-24 12:49:21 +05:30
project . lfs_objects << lfs_object
end
it_behaves_like 'responds with a file'
end
2016-09-29 09:46:39 +05:30
2019-12-21 20:55:43 +05:30
describe 'when using a user key (LFSToken)' do
2016-10-01 15:18:49 +05:30
let ( :authorization ) { authorize_user_key }
context 'when user allowed' do
let ( :update_permissions ) do
2018-11-18 11:00:15 +05:30
project . add_maintainer ( user )
2016-10-01 15:18:49 +05:30
project . lfs_objects << lfs_object
end
it_behaves_like 'responds with a file'
2019-12-21 20:55:43 +05:30
context 'when user password is expired' do
let ( :user ) { create ( :user , password_expires_at : 1 . minute . ago ) }
it_behaves_like 'LFS http 401 response'
end
context 'when user is blocked' do
let ( :user ) { create ( :user , :blocked ) }
it_behaves_like 'LFS http 401 response'
end
2016-10-01 15:18:49 +05:30
end
context 'when user not allowed' do
let ( :update_permissions ) do
project . lfs_objects << lfs_object
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 404 response'
2016-10-01 15:18:49 +05:30
end
end
2016-09-29 09:46:39 +05:30
context 'when build is authorized as' do
let ( :authorization ) { authorize_ci_project }
shared_examples 'can download LFS only from own projects' do
2016-11-24 13:41:30 +05:30
context 'for owned project' do
2017-09-10 17:25:29 +05:30
let ( :project ) { create ( :project , namespace : user . namespace ) }
2016-11-24 13:41:30 +05:30
let ( :update_permissions ) do
project . lfs_objects << lfs_object
end
it_behaves_like 'responds with a file'
end
context 'for member of project' do
2016-09-29 09:46:39 +05:30
let ( :pipeline ) { create ( :ci_empty_pipeline , project : project ) }
let ( :update_permissions ) do
2018-03-17 18:26:18 +05:30
project . add_reporter ( user )
2016-09-29 09:46:39 +05:30
project . lfs_objects << lfs_object
end
it_behaves_like 'responds with a file'
end
context 'for other project' do
let ( :pipeline ) { create ( :ci_empty_pipeline , project : other_project ) }
let ( :update_permissions ) do
project . lfs_objects << lfs_object
end
it 'rejects downloading code' do
2018-03-17 18:26:18 +05:30
expect ( response ) . to have_gitlab_http_status ( other_project_status )
2016-09-29 09:46:39 +05:30
end
end
end
context 'administrator' do
let ( :user ) { create ( :admin ) }
let ( :build ) { create ( :ci_build , :running , pipeline : pipeline , user : user ) }
it_behaves_like 'can download LFS only from own projects' do
# We render 403, because administrator does have normally access
let ( :other_project_status ) { 403 }
end
end
context 'regular user' do
let ( :build ) { create ( :ci_build , :running , pipeline : pipeline , user : user ) }
it_behaves_like 'can download LFS only from own projects' do
# We render 404, to prevent data leakage about existence of the project
let ( :other_project_status ) { 404 }
end
end
context 'does not have user' do
let ( :build ) { create ( :ci_build , :running , pipeline : pipeline ) }
it_behaves_like 'can download LFS only from own projects' do
# We render 404, to prevent data leakage about existence of the project
let ( :other_project_status ) { 404 }
end
end
end
2016-08-24 12:49:21 +05:30
end
context 'without required headers' do
let ( :authorization ) { authorize_user }
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 404 response'
2016-08-24 12:49:21 +05:30
end
end
end
2019-12-21 20:55:43 +05:30
describe 'when handling LFS batch request' do
2016-08-24 12:49:21 +05:30
let ( :update_lfs_permissions ) { }
let ( :update_user_permissions ) { }
before do
update_lfs_permissions
update_user_permissions
2019-12-21 20:55:43 +05:30
post_lfs_json batch_url ( project ) , body , headers
2016-08-24 12:49:21 +05:30
end
2019-12-21 20:55:43 +05:30
shared_examples 'process authorization header' do | renew_authorization : |
let ( :response_authorization ) do
authorization_in_action ( lfs_actions . first )
2016-08-24 12:49:21 +05:30
end
2019-12-21 20:55:43 +05:30
if renew_authorization
context 'when the authorization comes from a user' do
it 'returns a new valid LFS token authorization' do
expect ( response_authorization ) . not_to eq ( authorization )
2016-08-24 12:49:21 +05:30
end
2019-12-21 20:55:43 +05:30
it 'returns a a valid token' do
username , token = :: Base64 . decode64 ( response_authorization . split ( ' ' , 2 ) . last ) . split ( ':' , 2 )
expect ( username ) . to eq ( user . username )
expect ( Gitlab :: LfsToken . new ( user ) . token_valid? ( token ) ) . to be_truthy
2016-08-24 12:49:21 +05:30
end
2019-12-21 20:55:43 +05:30
it 'generates only one new token per each request' do
authorizations = lfs_actions . map do | action |
authorization_in_action ( action )
end . compact
expect ( authorizations . uniq . count ) . to eq 1
2016-08-24 12:49:21 +05:30
end
end
2019-12-21 20:55:43 +05:30
else
context 'when the authorization comes from a token' do
it 'returns the same authorization header' do
expect ( response_authorization ) . to eq ( authorization )
end
end
end
def lfs_actions
json_response [ 'objects' ] . map { | a | a [ 'actions' ] } . compact
end
def authorization_in_action ( action )
( action [ 'upload' ] || action [ 'download' ] ) . dig ( 'header' , 'Authorization' )
end
end
2016-08-24 12:49:21 +05:30
2019-12-21 20:55:43 +05:30
describe 'download' do
let ( :body ) { download_body ( sample_object ) }
shared_examples 'an authorized request' do | renew_authorization : |
context 'when downloading an LFS object that is assigned to our project' do
2016-08-24 12:49:21 +05:30
let ( :update_lfs_permissions ) do
2019-12-21 20:55:43 +05:30
project . lfs_objects << lfs_object
2016-08-24 12:49:21 +05:30
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 200 response'
2016-08-24 12:49:21 +05:30
it 'with href to download' do
2019-12-21 20:55:43 +05:30
expect ( json_response [ 'objects' ] . first ) . to include ( sample_object )
expect ( json_response [ 'objects' ] . first [ 'actions' ] [ 'download' ] [ 'href' ] ) . to eq ( objects_url ( project , sample_oid ) )
2016-08-24 12:49:21 +05:30
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'process authorization header' , renew_authorization : renew_authorization
2016-08-24 12:49:21 +05:30
end
2019-12-21 20:55:43 +05:30
context 'when downloading an LFS object that is assigned to other project' do
let ( :update_lfs_permissions ) do
other_project . lfs_objects << lfs_object
2016-08-24 12:49:21 +05:30
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 200 response'
2016-08-24 12:49:21 +05:30
it 'with an 404 for specific object' do
2019-12-21 20:55:43 +05:30
expect ( json_response [ 'objects' ] . first ) . to include ( sample_object )
expect ( json_response [ 'objects' ] . first [ 'error' ] ) . to include ( 'code' = > 404 , 'message' = > " Object does not exist on the server or you don't have permissions to access it " )
2016-08-24 12:49:21 +05:30
end
end
2019-12-21 20:55:43 +05:30
context 'when downloading a LFS object that does not exist' do
let ( :body ) { download_body ( non_existing_object ) }
it_behaves_like 'LFS http 200 response'
it 'with an 404 for specific object' do
expect ( json_response [ 'objects' ] . first ) . to include ( non_existing_object )
expect ( json_response [ 'objects' ] . first [ 'error' ] ) . to include ( 'code' = > 404 , 'message' = > " Object does not exist on the server or you don't have permissions to access it " )
2016-08-24 12:49:21 +05:30
end
2019-12-21 20:55:43 +05:30
end
2016-08-24 12:49:21 +05:30
2019-12-21 20:55:43 +05:30
context 'when downloading one new and one existing LFS object' do
let ( :body ) { download_body ( multiple_objects ) }
2016-08-24 12:49:21 +05:30
let ( :update_lfs_permissions ) do
project . lfs_objects << lfs_object
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 200 response'
2016-08-24 12:49:21 +05:30
2019-12-21 20:55:43 +05:30
it 'responds with download hypermedia link for the new object' do
expect ( json_response [ 'objects' ] . first ) . to include ( sample_object )
expect ( json_response [ 'objects' ] . first [ 'actions' ] [ 'download' ] ) . to include ( 'href' = > objects_url ( project , sample_oid ) )
expect ( json_response [ 'objects' ] . last ) . to eq ( {
'oid' = > non_existing_object_oid ,
'size' = > non_existing_object_size ,
'error' = > {
'code' = > 404 ,
'message' = > " Object does not exist on the server or you don't have permissions to access it "
}
2017-08-17 22:00:37 +05:30
} )
2016-08-24 12:49:21 +05:30
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'process authorization header' , renew_authorization : renew_authorization
end
context 'when downloading two existing LFS objects' do
let ( :body ) { download_body ( multiple_objects ) }
let ( :other_object ) { create ( :lfs_object , :with_file , oid : non_existing_object_oid , size : non_existing_object_size ) }
let ( :update_lfs_permissions ) do
project . lfs_objects << [ lfs_object , other_object ]
end
it 'responds with the download hypermedia link for each object' do
expect ( json_response [ 'objects' ] . first ) . to include ( sample_object )
expect ( json_response [ 'objects' ] . first [ 'actions' ] [ 'download' ] ) . to include ( 'href' = > objects_url ( project , sample_oid ) )
expect ( json_response [ 'objects' ] . last ) . to include ( non_existing_object )
expect ( json_response [ 'objects' ] . last [ 'actions' ] [ 'download' ] ) . to include ( 'href' = > objects_url ( project , non_existing_object_oid ) )
end
it_behaves_like 'process authorization header' , renew_authorization : renew_authorization
2016-08-24 12:49:21 +05:30
end
end
context 'when user is authenticated' do
let ( :authorization ) { authorize_user }
let ( :update_user_permissions ) do
2018-03-17 18:26:18 +05:30
project . add_role ( user , role )
2016-08-24 12:49:21 +05:30
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'an authorized request' , renew_authorization : true do
2016-08-24 12:49:21 +05:30
let ( :role ) { :reporter }
end
context 'when user does is not member of the project' do
2016-09-13 17:45:13 +05:30
let ( :update_user_permissions ) { nil }
2016-08-24 12:49:21 +05:30
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 404 response'
2016-08-24 12:49:21 +05:30
end
context 'when user does not have download access' do
let ( :role ) { :guest }
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 403 response'
end
context 'when user password is expired' do
let ( :role ) { :reporter }
let ( :user ) { create ( :user , password_expires_at : 1 . minute . ago ) }
it 'with an 404 for specific object' do
expect ( json_response [ 'objects' ] . first ) . to include ( sample_object )
expect ( json_response [ 'objects' ] . first [ 'error' ] ) . to include ( 'code' = > 404 , 'message' = > " Object does not exist on the server or you don't have permissions to access it " )
2016-08-24 12:49:21 +05:30
end
end
2019-12-21 20:55:43 +05:30
context 'when user is blocked' do
let ( :role ) { :reporter }
let ( :user ) { create ( :user , :blocked ) }
it_behaves_like 'LFS http 401 response'
end
2016-08-24 12:49:21 +05:30
end
2018-11-18 11:00:15 +05:30
context 'when using Deploy Tokens' do
let ( :authorization ) { authorize_deploy_token }
let ( :update_user_permissions ) { nil }
let ( :role ) { nil }
let ( :update_lfs_permissions ) do
project . lfs_objects << lfs_object
end
context 'when Deploy Token is not valid' do
let ( :deploy_token ) { create ( :deploy_token , projects : [ project ] , read_repository : false ) }
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 401 response'
2018-11-18 11:00:15 +05:30
end
context 'when Deploy Token is not related to the project' do
2019-12-21 20:55:43 +05:30
let ( :deploy_token ) { create ( :deploy_token , projects : [ other_project ] ) }
2018-11-18 11:00:15 +05:30
2020-08-19 02:56:42 +05:30
it_behaves_like 'LFS http 401 response'
end
# TODO: We should fix this test case that causes flakyness by alternating the result of the above test cases.
context 'when Deploy Token is valid' do
let ( :deploy_token ) { create ( :deploy_token , projects : [ project ] ) }
it_behaves_like 'an authorized request' , renew_authorization : false
2018-11-18 11:00:15 +05:30
end
end
2016-09-29 09:46:39 +05:30
context 'when build is authorized as' do
2016-08-24 12:49:21 +05:30
let ( :authorization ) { authorize_ci_project }
2016-09-29 09:46:39 +05:30
let ( :update_lfs_permissions ) do
project . lfs_objects << lfs_object
end
2019-12-21 20:55:43 +05:30
shared_examples 'can download LFS only from own projects' do | renew_authorization : |
2016-09-29 09:46:39 +05:30
context 'for own project' do
let ( :pipeline ) { create ( :ci_empty_pipeline , project : project ) }
let ( :update_user_permissions ) do
2018-03-17 18:26:18 +05:30
project . add_reporter ( user )
2016-09-29 09:46:39 +05:30
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'an authorized request' , renew_authorization : renew_authorization
2016-09-29 09:46:39 +05:30
end
context 'for other project' do
let ( :pipeline ) { create ( :ci_empty_pipeline , project : other_project ) }
it 'rejects downloading code' do
2018-03-17 18:26:18 +05:30
expect ( response ) . to have_gitlab_http_status ( other_project_status )
2016-09-29 09:46:39 +05:30
end
end
end
context 'administrator' do
let ( :user ) { create ( :admin ) }
let ( :build ) { create ( :ci_build , :running , pipeline : pipeline , user : user ) }
2019-12-21 20:55:43 +05:30
it_behaves_like 'can download LFS only from own projects' , renew_authorization : true do
2016-09-29 09:46:39 +05:30
# We render 403, because administrator does have normally access
let ( :other_project_status ) { 403 }
end
end
context 'regular user' do
let ( :build ) { create ( :ci_build , :running , pipeline : pipeline , user : user ) }
2019-12-21 20:55:43 +05:30
it_behaves_like 'can download LFS only from own projects' , renew_authorization : true do
2016-09-29 09:46:39 +05:30
# We render 404, to prevent data leakage about existence of the project
let ( :other_project_status ) { 404 }
end
end
context 'does not have user' do
let ( :build ) { create ( :ci_build , :running , pipeline : pipeline ) }
2019-12-21 20:55:43 +05:30
it_behaves_like 'can download LFS only from own projects' , renew_authorization : false do
2016-09-29 09:46:39 +05:30
# We render 404, to prevent data leakage about existence of the project
let ( :other_project_status ) { 404 }
end
end
2016-08-24 12:49:21 +05:30
end
context 'when user is not authenticated' do
describe 'is accessing public project' do
let ( :project ) { create ( :project , :public ) }
let ( :update_lfs_permissions ) do
project . lfs_objects << lfs_object
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 200 response'
2016-08-24 12:49:21 +05:30
2019-12-21 20:55:43 +05:30
it 'returns href to download' do
2017-08-17 22:00:37 +05:30
expect ( json_response ) . to eq ( {
'objects' = > [
{
'oid' = > sample_oid ,
'size' = > sample_size ,
'authenticated' = > true ,
'actions' = > {
'download' = > {
2019-12-21 20:55:43 +05:30
'href' = > objects_url ( project , sample_oid ) ,
2017-08-17 22:00:37 +05:30
'header' = > { }
}
2016-08-24 12:49:21 +05:30
}
}
2017-08-17 22:00:37 +05:30
]
} )
2016-08-24 12:49:21 +05:30
end
end
describe 'is accessing non-public project' do
let ( :update_lfs_permissions ) do
project . lfs_objects << lfs_object
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 401 response'
2016-08-24 12:49:21 +05:30
end
end
end
describe 'upload' do
let ( :project ) { create ( :project , :public ) }
2019-12-21 20:55:43 +05:30
let ( :body ) { upload_body ( sample_object ) }
2016-08-24 12:49:21 +05:30
2019-12-21 20:55:43 +05:30
shared_examples 'pushes new LFS objects' do | renew_authorization : |
2018-03-17 18:26:18 +05:30
let ( :sample_size ) { 150 . megabytes }
2019-12-21 20:55:43 +05:30
let ( :sample_oid ) { non_existing_object_oid }
it_behaves_like 'LFS http 200 response'
2018-03-17 18:26:18 +05:30
it 'responds with upload hypermedia link' do
expect ( json_response [ 'objects' ] ) . to be_kind_of ( Array )
2019-12-21 20:55:43 +05:30
expect ( json_response [ 'objects' ] . first ) . to include ( sample_object )
expect ( json_response [ 'objects' ] . first [ 'actions' ] [ 'upload' ] [ 'href' ] ) . to eq ( objects_url ( project , sample_oid , sample_size ) )
expect ( json_response [ 'objects' ] . first [ 'actions' ] [ 'upload' ] [ 'header' ] ) . to include ( 'Content-Type' = > 'application/octet-stream' )
2018-03-17 18:26:18 +05:30
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'process authorization header' , renew_authorization : renew_authorization
2018-03-17 18:26:18 +05:30
end
2016-08-24 12:49:21 +05:30
describe 'when request is authenticated' do
describe 'when user has project push access' do
let ( :authorization ) { authorize_user }
let ( :update_user_permissions ) do
2018-03-17 18:26:18 +05:30
project . add_developer ( user )
2016-08-24 12:49:21 +05:30
end
2019-12-21 20:55:43 +05:30
context 'when pushing an LFS object that already exists' do
2020-04-08 14:13:33 +05:30
shared_examples_for 'batch upload with existing LFS object' do
it_behaves_like 'LFS http 200 response'
it 'responds with links the object to the project' do
expect ( json_response [ 'objects' ] ) . to be_kind_of ( Array )
expect ( json_response [ 'objects' ] . first ) . to include ( sample_object )
expect ( lfs_object . projects . pluck ( :id ) ) . not_to include ( project . id )
expect ( lfs_object . projects . pluck ( :id ) ) . to include ( other_project . id )
expect ( json_response [ 'objects' ] . first [ 'actions' ] [ 'upload' ] [ 'href' ] ) . to eq ( objects_url ( project , sample_oid , sample_size ) )
expect ( json_response [ 'objects' ] . first [ 'actions' ] [ 'upload' ] [ 'header' ] ) . to include ( 'Content-Type' = > 'application/octet-stream' )
end
it_behaves_like 'process authorization header' , renew_authorization : true
end
2016-08-24 12:49:21 +05:30
let ( :update_lfs_permissions ) do
other_project . lfs_objects << lfs_object
end
2020-04-08 14:13:33 +05:30
context 'in another project' do
it_behaves_like 'batch upload with existing LFS object'
2016-08-24 12:49:21 +05:30
end
2020-04-08 14:13:33 +05:30
context 'in source of fork project' do
let ( :project ) { fork_project ( other_project ) }
it_behaves_like 'batch upload with existing LFS object'
end
2016-08-24 12:49:21 +05:30
end
2019-12-21 20:55:43 +05:30
context 'when pushing a LFS object that does not exist' do
it_behaves_like 'pushes new LFS objects' , renew_authorization : true
end
2016-08-24 12:49:21 +05:30
2019-12-21 20:55:43 +05:30
context 'when pushing one new and one existing LFS object' do
let ( :body ) { upload_body ( multiple_objects ) }
2016-08-24 12:49:21 +05:30
let ( :update_lfs_permissions ) do
project . lfs_objects << lfs_object
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 200 response'
2016-08-24 12:49:21 +05:30
it 'responds with upload hypermedia link for the new object' do
expect ( json_response [ 'objects' ] ) . to be_kind_of ( Array )
2019-12-21 20:55:43 +05:30
expect ( json_response [ 'objects' ] . first ) . to include ( sample_object )
expect ( json_response [ 'objects' ] . first ) . not_to have_key ( 'actions' )
2016-08-24 12:49:21 +05:30
2019-12-21 20:55:43 +05:30
expect ( json_response [ 'objects' ] . last ) . to include ( non_existing_object )
expect ( json_response [ 'objects' ] . last [ 'actions' ] [ 'upload' ] [ 'href' ] ) . to eq ( objects_url ( project , non_existing_object_oid , non_existing_object_size ) )
expect ( json_response [ 'objects' ] . last [ 'actions' ] [ 'upload' ] [ 'header' ] ) . to include ( 'Content-Type' = > 'application/octet-stream' )
2016-08-24 12:49:21 +05:30
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'process authorization header' , renew_authorization : true
2016-08-24 12:49:21 +05:30
end
end
context 'when user does not have push access' do
let ( :authorization ) { authorize_user }
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 403 response'
2016-08-24 12:49:21 +05:30
end
2016-09-29 09:46:39 +05:30
context 'when build is authorized' do
2016-08-24 12:49:21 +05:30
let ( :authorization ) { authorize_ci_project }
2016-09-29 09:46:39 +05:30
context 'build has an user' do
2019-12-21 20:55:43 +05:30
let ( :build ) { create ( :ci_build , :running , pipeline : pipeline , user : user ) }
2016-09-29 09:46:39 +05:30
context 'tries to push to own project' do
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 403 response'
2016-09-29 09:46:39 +05:30
end
context 'tries to push to other project' do
let ( :pipeline ) { create ( :ci_empty_pipeline , project : other_project ) }
2017-09-10 17:25:29 +05:30
# I'm not sure what this tests that is different from the previous test
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 403 response'
2016-09-29 09:46:39 +05:30
end
end
context 'does not have user' do
let ( :build ) { create ( :ci_build , :running , pipeline : pipeline ) }
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 403 response'
2016-08-24 12:49:21 +05:30
end
end
2018-03-17 18:26:18 +05:30
context 'when deploy key has project push access' do
let ( :key ) { create ( :deploy_key ) }
let ( :authorization ) { authorize_deploy_key }
let ( :update_user_permissions ) do
2020-11-24 15:15:51 +05:30
project . deploy_keys_projects . create! ( deploy_key : key , can_push : true )
2018-03-17 18:26:18 +05:30
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'pushes new LFS objects' , renew_authorization : false
2018-03-17 18:26:18 +05:30
end
2016-08-24 12:49:21 +05:30
end
context 'when user is not authenticated' do
context 'when user has push access' do
let ( :update_user_permissions ) do
2018-11-18 11:00:15 +05:30
project . add_maintainer ( user )
2016-08-24 12:49:21 +05:30
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 401 response'
2016-08-24 12:49:21 +05:30
end
context 'when user does not have push access' do
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 401 response'
2016-08-24 12:49:21 +05:30
end
end
end
describe 'unsupported' do
2016-09-13 17:45:13 +05:30
let ( :authorization ) { authorize_user }
2019-12-21 20:55:43 +05:30
let ( :body ) { request_body ( 'other' , sample_object ) }
2016-08-24 12:49:21 +05:30
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 404 response'
2016-08-24 12:49:21 +05:30
end
end
2019-12-21 20:55:43 +05:30
describe 'when handling LFS batch request on a read-only GitLab instance' do
2018-03-17 18:26:18 +05:30
let ( :authorization ) { authorize_user }
2019-12-21 20:55:43 +05:30
subject { post_lfs_json ( batch_url ( project ) , body , headers ) }
2018-03-17 18:26:18 +05:30
before do
allow ( Gitlab :: Database ) . to receive ( :read_only? ) { true }
2019-12-21 20:55:43 +05:30
2018-11-18 11:00:15 +05:30
project . add_maintainer ( user )
2019-12-21 20:55:43 +05:30
subject
2018-03-17 18:26:18 +05:30
end
2019-12-21 20:55:43 +05:30
context 'when downloading' do
let ( :body ) { download_body ( sample_object ) }
2018-03-17 18:26:18 +05:30
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 200 response'
2018-03-17 18:26:18 +05:30
end
2019-12-21 20:55:43 +05:30
context 'when uploading' do
let ( :body ) { upload_body ( sample_object ) }
2018-03-17 18:26:18 +05:30
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http expected response code and message' do
let ( :response_code ) { 403 }
let ( :message ) { 'You cannot write to this read-only GitLab instance.' }
end
2018-03-17 18:26:18 +05:30
end
end
2019-12-21 20:55:43 +05:30
describe 'when pushing a LFS object' do
2016-08-24 12:49:21 +05:30
shared_examples 'unauthorized' do
context 'and request is sent by gitlab-workhorse to authorize the request' do
before do
put_authorize
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 401 response'
2016-08-24 12:49:21 +05:30
end
context 'and request is sent by gitlab-workhorse to finalize the upload' do
before do
put_finalize
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 401 response'
2016-08-24 12:49:21 +05:30
end
context 'and request is sent with a malformed headers' do
before do
2016-09-13 17:45:13 +05:30
put_finalize ( '/etc/passwd' )
2016-08-24 12:49:21 +05:30
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 401 response'
2016-08-24 12:49:21 +05:30
end
end
shared_examples 'forbidden' do
context 'and request is sent by gitlab-workhorse to authorize the request' do
before do
put_authorize
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 403 response'
2016-08-24 12:49:21 +05:30
end
context 'and request is sent by gitlab-workhorse to finalize the upload' do
before do
put_finalize
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 403 response'
2016-08-24 12:49:21 +05:30
end
2016-09-13 17:45:13 +05:30
context 'and request is sent with a malformed headers' do
before do
put_finalize ( '/etc/passwd' )
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 403 response'
2016-09-13 17:45:13 +05:30
end
2016-08-24 12:49:21 +05:30
end
describe 'to one project' do
describe 'when user is authenticated' do
let ( :authorization ) { authorize_user }
describe 'when user has push access to the project' do
before do
2018-03-17 18:26:18 +05:30
project . add_developer ( user )
2016-08-24 12:49:21 +05:30
end
2016-09-29 09:46:39 +05:30
context 'and the request bypassed workhorse' do
it 'raises an exception' do
expect { put_authorize ( verified : false ) } . to raise_error JWT :: DecodeError
end
end
2016-08-24 12:49:21 +05:30
context 'and request is sent by gitlab-workhorse to authorize the request' do
2018-05-09 12:01:36 +05:30
shared_examples 'a valid response' do
before do
put_authorize
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 200 response'
2018-05-09 12:01:36 +05:30
it 'uses the gitlab-workhorse content type' do
2020-03-13 15:44:24 +05:30
expect ( response . media_type ) . to eq ( Gitlab :: Workhorse :: INTERNAL_API_CONTENT_TYPE )
2018-05-09 12:01:36 +05:30
end
2016-08-24 12:49:21 +05:30
end
2018-05-09 12:01:36 +05:30
shared_examples 'a local file' do
it_behaves_like 'a valid response' do
2019-12-21 20:55:43 +05:30
it 'responds with status 200, location of LFS store and object details' do
2018-05-09 12:01:36 +05:30
expect ( json_response [ 'TempPath' ] ) . to eq ( LfsObjectUploader . workhorse_local_upload_path )
expect ( json_response [ 'RemoteObject' ] ) . to be_nil
expect ( json_response [ 'LfsOid' ] ) . to eq ( sample_oid )
expect ( json_response [ 'LfsSize' ] ) . to eq ( sample_size )
end
end
2016-08-24 12:49:21 +05:30
end
2018-05-09 12:01:36 +05:30
context 'when using local storage' do
it_behaves_like 'a local file'
2016-09-29 09:46:39 +05:30
end
2018-05-09 12:01:36 +05:30
context 'when using remote storage' do
context 'when direct upload is enabled' do
before do
stub_lfs_object_storage ( enabled : true , direct_upload : true )
end
it_behaves_like 'a valid response' do
2019-12-21 20:55:43 +05:30
it 'responds with status 200, location of LFS remote store and object details' do
expect ( json_response ) . not_to have_key ( 'TempPath' )
2018-05-09 12:01:36 +05:30
expect ( json_response [ 'RemoteObject' ] ) . to have_key ( 'ID' )
expect ( json_response [ 'RemoteObject' ] ) . to have_key ( 'GetURL' )
expect ( json_response [ 'RemoteObject' ] ) . to have_key ( 'StoreURL' )
expect ( json_response [ 'RemoteObject' ] ) . to have_key ( 'DeleteURL' )
2018-11-08 19:23:39 +05:30
expect ( json_response [ 'RemoteObject' ] ) . not_to have_key ( 'MultipartUpload' )
2018-05-09 12:01:36 +05:30
expect ( json_response [ 'LfsOid' ] ) . to eq ( sample_oid )
expect ( json_response [ 'LfsSize' ] ) . to eq ( sample_size )
end
end
end
context 'when direct upload is disabled' do
before do
stub_lfs_object_storage ( enabled : true , direct_upload : false )
end
it_behaves_like 'a local file'
end
2016-08-24 12:49:21 +05:30
end
end
context 'and request is sent by gitlab-workhorse to finalize the upload' do
before do
put_finalize
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 200 response'
2016-08-24 12:49:21 +05:30
2019-12-21 20:55:43 +05:30
it 'LFS object is linked to the project' do
2016-08-24 12:49:21 +05:30
expect ( lfs_object . projects . pluck ( :id ) ) . to include ( project . id )
end
end
2016-09-13 17:45:13 +05:30
2019-02-02 18:00:53 +05:30
context 'and request to finalize the upload is not sent by gitlab-workhorse' do
it 'fails with a JWT decode error' do
expect { put_finalize ( lfs_tmp_file , verified : false ) } . to raise_error ( JWT :: DecodeError )
end
end
2019-12-21 20:55:43 +05:30
context 'and workhorse requests upload finalize for a new LFS object' do
2018-05-09 12:01:36 +05:30
before do
2020-11-24 15:15:51 +05:30
lfs_object . destroy!
2018-05-09 12:01:36 +05:30
end
context 'with object storage disabled' do
it " doesn't attempt to migrate file to object storage " do
expect ( ObjectStorage :: BackgroundMoveWorker ) . not_to receive ( :perform_async )
put_finalize ( with_tempfile : true )
end
end
context 'with object storage enabled' do
context 'and direct upload enabled' do
let! ( :fog_connection ) do
stub_lfs_object_storage ( direct_upload : true )
end
2019-12-21 20:55:43 +05:30
let ( :tmp_object ) do
2020-11-24 15:15:51 +05:30
fog_connection . directories . new ( key : 'lfs-objects' ) . files . create ( # rubocop: disable Rails/SaveBang
2019-12-21 20:55:43 +05:30
key : 'tmp/uploads/12312300' ,
body : 'content'
)
end
2018-05-09 12:01:36 +05:30
[ '123123' , '../../123123' ] . each do | remote_id |
context " with invalid remote_id: #{ remote_id } " do
subject do
2019-12-21 20:55:43 +05:30
put_finalize ( remote_object : tmp_object , args : {
2018-05-09 12:01:36 +05:30
'file.remote_id' = > remote_id
} )
end
it 'responds with status 403' do
subject
2020-03-13 15:44:24 +05:30
expect ( response ) . to have_gitlab_http_status ( :forbidden )
2018-05-09 12:01:36 +05:30
end
end
end
context 'with valid remote_id' do
subject do
2019-12-21 20:55:43 +05:30
put_finalize ( remote_object : tmp_object , args : {
2018-05-09 12:01:36 +05:30
'file.remote_id' = > '12312300' ,
'file.name' = > 'name'
} )
end
it 'responds with status 200' do
subject
2020-03-13 15:44:24 +05:30
expect ( response ) . to have_gitlab_http_status ( :ok )
2019-12-21 20:55:43 +05:30
object = LfsObject . find_by_oid ( sample_oid )
expect ( object ) . to be_present
expect ( object . file . read ) . to eq ( tmp_object . body )
2018-05-09 12:01:36 +05:30
end
it 'schedules migration of file to object storage' do
subject
expect ( LfsObject . last . projects ) . to include ( project )
end
it 'have valid file' do
subject
expect ( LfsObject . last . file_store ) . to eq ( ObjectStorage :: Store :: REMOTE )
expect ( LfsObject . last . file ) . to be_exists
end
end
end
context 'and background upload enabled' do
before do
stub_lfs_object_storage ( background_upload : true )
end
it 'schedules migration of file to object storage' do
expect ( ObjectStorage :: BackgroundMoveWorker ) . to receive ( :perform_async ) . with ( 'LfsObjectUploader' , 'LfsObject' , :file , kind_of ( Numeric ) )
put_finalize ( with_tempfile : true )
end
end
end
end
2020-10-24 23:57:45 +05:30
context 'without the lfs object' do
2018-05-09 12:01:36 +05:30
before do
2020-11-24 15:15:51 +05:30
lfs_object . destroy!
2016-09-13 17:45:13 +05:30
end
2018-05-09 12:01:36 +05:30
it 'rejects slashes in the tempfile name (path traversal)' do
put_finalize ( '../bar' , with_tempfile : true )
2020-10-24 23:57:45 +05:30
expect ( response ) . to have_gitlab_http_status ( :bad_request )
end
context 'not sending the workhorse jwt header' do
let ( :include_workhorse_jwt_header ) { false }
it 'rejects the request' do
put_finalize ( with_tempfile : true )
expect ( response ) . to have_gitlab_http_status ( :unprocessable_entity )
end
2016-09-13 17:45:13 +05:30
end
end
2016-08-24 12:49:21 +05:30
end
describe 'and user does not have push access' do
2016-09-13 17:45:13 +05:30
before do
2018-03-17 18:26:18 +05:30
project . add_reporter ( user )
2016-09-13 17:45:13 +05:30
end
2016-08-24 12:49:21 +05:30
it_behaves_like 'forbidden'
end
end
2016-09-29 09:46:39 +05:30
context 'when build is authorized' do
2016-08-24 12:49:21 +05:30
let ( :authorization ) { authorize_ci_project }
2016-09-29 09:46:39 +05:30
context 'build has an user' do
2019-12-21 20:55:43 +05:30
let ( :build ) { create ( :ci_build , :running , pipeline : pipeline , user : user ) }
2016-09-29 09:46:39 +05:30
context 'tries to push to own project' do
before do
2018-03-17 18:26:18 +05:30
project . add_developer ( user )
2016-09-29 09:46:39 +05:30
put_authorize
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 403 response'
2016-09-29 09:46:39 +05:30
end
context 'tries to push to other project' do
let ( :pipeline ) { create ( :ci_empty_pipeline , project : other_project ) }
before do
put_authorize
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 404 response'
2016-09-29 09:46:39 +05:30
end
end
context 'does not have user' do
let ( :build ) { create ( :ci_build , :running , pipeline : pipeline ) }
before do
put_authorize
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 404 response'
end
end
describe 'when using a user key (LFSToken)' do
let ( :authorization ) { authorize_user_key }
context 'when user allowed' do
before do
project . add_developer ( user )
put_authorize
end
it_behaves_like 'LFS http 200 response'
context 'when user password is expired' do
let ( :user ) { create ( :user , password_expires_at : 1 . minute . ago ) }
it_behaves_like 'LFS http 401 response'
end
context 'when user is blocked' do
let ( :user ) { create ( :user , :blocked ) }
it_behaves_like 'LFS http 401 response'
2016-09-29 09:46:39 +05:30
end
end
2019-12-21 20:55:43 +05:30
context 'when user not allowed' do
before do
put_authorize
end
it_behaves_like 'LFS http 404 response'
end
2016-08-24 12:49:21 +05:30
end
context 'for unauthenticated' do
it_behaves_like 'unauthorized'
end
end
describe 'to a forked project' do
let ( :upstream_project ) { create ( :project , :public ) }
let ( :project_owner ) { create ( :user ) }
let ( :project ) { fork_project ( upstream_project , project_owner ) }
describe 'when user is authenticated' do
let ( :authorization ) { authorize_user }
describe 'when user has push access to the project' do
before do
2018-03-17 18:26:18 +05:30
project . add_developer ( user )
2016-08-24 12:49:21 +05:30
end
context 'and request is sent by gitlab-workhorse to authorize the request' do
before do
put_authorize
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 200 response'
2016-08-24 12:49:21 +05:30
2019-12-21 20:55:43 +05:30
it 'with location of LFS store and object details' do
2018-05-09 12:01:36 +05:30
expect ( json_response [ 'TempPath' ] ) . to eq ( LfsObjectUploader . workhorse_local_upload_path )
2016-08-24 12:49:21 +05:30
expect ( json_response [ 'LfsOid' ] ) . to eq ( sample_oid )
expect ( json_response [ 'LfsSize' ] ) . to eq ( sample_size )
end
end
context 'and request is sent by gitlab-workhorse to finalize the upload' do
before do
put_finalize
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 200 response'
2016-08-24 12:49:21 +05:30
2020-03-13 15:44:24 +05:30
it 'LFS object is linked to the forked project' do
expect ( lfs_object . projects . pluck ( :id ) ) . to include ( project . id )
2016-08-24 12:49:21 +05:30
end
end
end
describe 'and user does not have push access' do
it_behaves_like 'forbidden'
end
end
2016-09-29 09:46:39 +05:30
context 'when build is authorized' do
2016-08-24 12:49:21 +05:30
let ( :authorization ) { authorize_ci_project }
2016-09-29 09:46:39 +05:30
before do
put_authorize
end
context 'build has an user' do
2019-12-21 20:55:43 +05:30
let ( :build ) { create ( :ci_build , :running , pipeline : pipeline , user : user ) }
2016-09-29 09:46:39 +05:30
context 'tries to push to own project' do
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 403 response'
2016-09-29 09:46:39 +05:30
end
context 'tries to push to other project' do
let ( :pipeline ) { create ( :ci_empty_pipeline , project : other_project ) }
2017-09-10 17:25:29 +05:30
# I'm not sure what this tests that is different from the previous test
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 403 response'
2016-09-29 09:46:39 +05:30
end
end
context 'does not have user' do
let ( :build ) { create ( :ci_build , :running , pipeline : pipeline ) }
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 403 response'
2016-09-29 09:46:39 +05:30
end
2016-08-24 12:49:21 +05:30
end
context 'for unauthenticated' do
it_behaves_like 'unauthorized'
end
describe 'and second project not related to fork or a source project' do
2017-09-10 17:25:29 +05:30
let ( :second_project ) { create ( :project ) }
2016-08-24 12:49:21 +05:30
let ( :authorization ) { authorize_user }
before do
2018-11-18 11:00:15 +05:30
second_project . add_maintainer ( user )
2016-08-24 12:49:21 +05:30
upstream_project . lfs_objects << lfs_object
end
2019-12-21 20:55:43 +05:30
context 'when pushing the same LFS object to the second project' do
2016-08-24 12:49:21 +05:30
before do
2019-02-02 18:00:53 +05:30
finalize_headers = headers
. merge ( 'X-Gitlab-Lfs-Tmp' = > lfs_tmp_file )
. merge ( workhorse_internal_api_request_header )
2019-12-21 20:55:43 +05:30
put objects_url ( second_project , sample_oid , sample_size ) ,
params : { } ,
headers : finalize_headers
2016-08-24 12:49:21 +05:30
end
2019-12-21 20:55:43 +05:30
it_behaves_like 'LFS http 200 response'
2016-08-24 12:49:21 +05:30
2019-12-21 20:55:43 +05:30
it 'links the LFS object to the project' do
2016-08-24 12:49:21 +05:30
expect ( lfs_object . projects . pluck ( :id ) ) . to include ( second_project . id , upstream_project . id )
end
end
end
end
2016-09-29 09:46:39 +05:30
def put_authorize ( verified : true )
authorize_headers = headers
authorize_headers . merge! ( workhorse_internal_api_request_header ) if verified
2019-12-21 20:55:43 +05:30
put authorize_url ( project , sample_oid , sample_size ) , params : { } , headers : authorize_headers
2016-08-24 12:49:21 +05:30
end
2019-12-21 20:55:43 +05:30
def put_finalize ( lfs_tmp = lfs_tmp_file , with_tempfile : false , verified : true , remote_object : nil , args : { } )
uploaded_file = nil
2018-05-09 12:01:36 +05:30
if with_tempfile
2019-12-21 20:55:43 +05:30
upload_path = LfsObjectUploader . workhorse_local_upload_path
file_path = upload_path + '/' + lfs_tmp if lfs_tmp
2018-05-09 12:01:36 +05:30
FileUtils . mkdir_p ( upload_path )
FileUtils . touch ( file_path )
2019-12-21 20:55:43 +05:30
uploaded_file = UploadedFile . new ( file_path , filename : File . basename ( file_path ) )
elsif remote_object
uploaded_file = fog_to_uploaded_file ( remote_object )
end
2018-05-09 12:01:36 +05:30
2019-02-02 18:00:53 +05:30
finalize_headers = headers
finalize_headers . merge! ( workhorse_internal_api_request_header ) if verified
2019-12-21 20:55:43 +05:30
workhorse_finalize (
objects_url ( project , sample_oid , sample_size ) ,
method : :put ,
file_key : :file ,
params : args . merge ( file : uploaded_file ) ,
2020-10-24 23:57:45 +05:30
headers : finalize_headers ,
send_rewritten_field : include_workhorse_jwt_header
2019-12-21 20:55:43 +05:30
)
2016-08-24 12:49:21 +05:30
end
def lfs_tmp_file
" #{ sample_oid } 012345678 "
end
end
end