2019-12-21 20:55:43 +05:30
# frozen_string_literal: true
2016-08-24 12:49:21 +05:30
require 'spec_helper'
2023-03-04 22:38:38 +05:30
RSpec . describe 'Git LFS API and storage' , feature_category : :source_code_management do
2022-07-16 23:28:13 +05:30
using RSpec :: Parameterized :: TableSyntax
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
2022-07-16 23:28:13 +05:30
include WorkhorseLfsHelpers
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
let_it_be ( :project , reload : true ) { create ( :project , :empty_repo ) }
2020-04-22 19:07:51 +05:30
let_it_be ( :user ) { create ( :user ) }
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
context 'with projects' do
it_behaves_like 'LFS http requests' do
let_it_be ( :other_project , reload : true ) { create ( :project , :empty_repo ) }
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
let ( :container ) { project }
let ( :authorize_guest ) { project . add_guest ( user ) }
let ( :authorize_download ) { project . add_reporter ( user ) }
let ( :authorize_upload ) { project . add_developer ( user ) }
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
context 'project specific LFS settings' do
let ( :body ) { upload_body ( sample_object ) }
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
before do
authorize_upload
project . update_attribute ( :lfs_enabled , project_lfs_enabled )
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
subject
end
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
describe 'LFS disabled in project' do
let ( :project_lfs_enabled ) { false }
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
context 'when uploading' do
subject ( :request ) { post_lfs_json ( batch_url ( project ) , body , headers ) }
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 404 response'
end
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
context 'when downloading' do
subject ( :request ) { get ( objects_url ( project , sample_oid ) , params : { } , headers : headers ) }
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 404 response'
end
end
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
describe 'LFS enabled in project' do
let ( :project_lfs_enabled ) { true }
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
context 'when uploading' do
subject ( :request ) { post_lfs_json ( batch_url ( project ) , body , headers ) }
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 200 response'
end
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
context 'when downloading' do
subject ( :request ) { get ( objects_url ( project , sample_oid ) , params : { } , headers : headers ) }
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 200 blob response'
end
end
2016-09-29 09:46:39 +05:30
end
2021-02-22 17:27:13 +05:30
describe 'when fetching LFS object' do
subject ( :request ) { get objects_url ( project , sample_oid ) , params : { } , headers : headers }
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
let ( :response ) { request && super ( ) }
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
before do
project . lfs_objects << lfs_object
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
context 'when LFS uses object storage' do
before do
authorize_download
end
2021-01-29 00:20:46 +05:30
2021-02-22 17:27:13 +05:30
context 'when proxy download is enabled' do
before do
stub_lfs_object_storage ( proxy_download : true )
lfs_object . file . migrate! ( LfsObjectUploader :: Store :: REMOTE )
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it 'responds with the workhorse send-url' do
expect ( response ) . to have_gitlab_http_status ( :ok )
expect ( response . headers [ Gitlab :: Workhorse :: SEND_DATA_HEADER ] ) . to start_with ( " send-url: " )
end
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
context 'when proxy download is disabled' do
before do
stub_lfs_object_storage ( proxy_download : false )
lfs_object . file . migrate! ( LfsObjectUploader :: Store :: REMOTE )
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it 'responds with redirect' do
expect ( response ) . to have_gitlab_http_status ( :found )
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it 'responds with the file location' do
expect ( response . location ) . to include ( lfs_object . reload . file . path )
end
end
2016-08-24 12:49:21 +05:30
end
2021-02-22 17:27:13 +05:30
context 'when deploy key is authorized' do
let_it_be ( :key ) { create ( :deploy_key ) }
2021-09-30 23:02:18 +05:30
2021-02-22 17:27:13 +05:30
let ( :authorization ) { authorize_deploy_key }
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
before do
project . deploy_keys << key
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 200 blob response'
2021-01-29 00:20:46 +05:30
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
context 'when using a user key (LFSToken)' do
let ( :authorization ) { authorize_user_key }
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
context 'when user allowed' do
before do
authorize_download
end
2018-05-09 12:01:36 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 200 blob response'
2018-05-09 12:01:36 +05:30
2021-02-22 17:27:13 +05:30
context 'when user password is expired' do
2022-08-27 11:52:29 +05:30
let_it_be ( :user ) { create ( :user , password_expires_at : 1 . minute . ago ) }
2018-05-09 12:01:36 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 401 response'
end
2018-05-09 12:01:36 +05:30
2021-02-22 17:27:13 +05:30
context 'when user is blocked' do
2022-08-27 11:52:29 +05:30
let_it_be ( :user ) { create ( :user , :blocked ) }
2018-05-09 12:01:36 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 401 response'
end
end
2018-05-09 12:01:36 +05:30
2021-02-22 17:27:13 +05:30
context 'when user not allowed' do
it_behaves_like 'LFS http 404 response'
end
2016-08-24 12:49:21 +05:30
end
2021-02-22 17:27:13 +05:30
context 'when build is authorized as' do
let ( :authorization ) { authorize_ci_project }
let ( :pipeline ) { create ( :ci_empty_pipeline , project : project ) }
let ( :build ) { create ( :ci_build , :running , pipeline : pipeline , user : user ) }
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
shared_examples 'can download LFS only from own projects' do
context 'for owned project' do
let_it_be ( :project ) { create ( :project , namespace : user . namespace ) }
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 200 blob response'
end
2016-10-01 15:18:49 +05:30
2021-02-22 17:27:13 +05:30
context 'for member of project' do
before do
authorize_download
end
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 200 blob response'
end
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
context 'for other project' do
let ( :pipeline ) { create ( :ci_empty_pipeline , project : other_project ) }
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
it 'rejects downloading code' do
expect ( response ) . to have_gitlab_http_status ( :not_found )
end
end
end
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
context 'administrator' , :enable_admin_mode do
let_it_be ( :user ) { create ( :admin ) }
it_behaves_like 'can download LFS only from own projects'
2016-10-01 15:18:49 +05:30
end
2021-02-22 17:27:13 +05:30
context 'regular user' do
it_behaves_like 'can download LFS only from own projects'
end
2016-10-01 15:18:49 +05:30
2021-02-22 17:27:13 +05:30
context 'does not have user' do
let ( :build ) { create ( :ci_build , :running , pipeline : pipeline ) }
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'can download LFS only from own projects'
2016-09-29 09:46:39 +05:30
end
2021-01-29 00:20:46 +05:30
end
end
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
describe 'when handling LFS batch request' do
subject ( :request ) { post_lfs_json batch_url ( project ) , body , headers }
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
let ( :response ) { request && super ( ) }
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
before do
project . lfs_objects << lfs_object
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
shared_examples 'process authorization header' do | renew_authorization : |
let ( :response_authorization ) do
authorization_in_action ( lfs_actions . first )
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +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 )
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it 'returns a valid token' do
username , token = :: Base64 . decode64 ( response_authorization . split ( ' ' , 2 ) . last ) . split ( ':' , 2 )
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
expect ( username ) . to eq ( user . username )
expect ( Gitlab :: LfsToken . new ( user ) . token_valid? ( token ) ) . to be_truthy
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it 'generates only one new token per each request' do
authorizations = lfs_actions . map do | action |
authorization_in_action ( action )
end . compact
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
expect ( authorizations . uniq . count ) . to eq 1
end
end
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
2016-08-24 12:49:21 +05:30
end
2021-02-22 17:27:13 +05:30
def lfs_actions
json_response [ 'objects' ] . map { | a | a [ 'actions' ] } . compact
2016-08-24 12:49:21 +05:30
end
2021-02-22 17:27:13 +05:30
def authorization_in_action ( action )
( action [ 'upload' ] || action [ 'download' ] ) . dig ( 'header' , 'Authorization' )
2019-12-21 20:55:43 +05:30
end
end
2021-02-22 17:27:13 +05:30
describe 'download' do
let ( :body ) { download_body ( sample_object ) }
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
shared_examples 'an authorized request' do | renew_authorization : |
context 'when downloading an LFS object that is assigned to our project' do
it_behaves_like 'LFS http 200 response'
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
it 'with href to download' do
expect ( json_response [ 'objects' ] . first ) . to include ( sample_object )
expect ( json_response [ 'objects' ] . first [ 'actions' ] [ 'download' ] [ 'href' ] ) . to eq ( objects_url ( project , sample_oid ) )
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'process authorization header' , renew_authorization : renew_authorization
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
context 'when downloading an LFS object that is assigned to other project' do
before do
lfs_object . update! ( projects : [ other_project ] )
end
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 200 response'
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
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 " )
end
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
context 'when downloading a LFS object that does not exist' do
let ( :body ) { download_body ( non_existing_object ) }
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 200 response'
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
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 " )
end
end
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
context 'when downloading one existing and one missing LFS object' do
let ( :body ) { download_body ( multiple_objects ) }
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 200 response'
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it 'responds with download hypermedia link for the existing 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 "
}
} )
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'process authorization header' , renew_authorization : renew_authorization
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
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 ) }
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
before do
project . lfs_objects << other_object
end
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
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 ) )
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
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
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'process authorization header' , renew_authorization : renew_authorization
end
2023-07-07 10:43:13 +05:30
context 'when downloading an LFS object that is stored on object storage' do
before do
stub_lfs_object_storage
lfs_object . file . migrate! ( LfsObjectUploader :: Store :: REMOTE )
end
context 'when lfs.object_store.proxy_download=true' do
before do
stub_lfs_object_storage ( proxy_download : true )
end
it_behaves_like 'LFS http 200 response'
it 'does return proxied address URL' do
expect ( json_response [ 'objects' ] . first ) . to include ( sample_object )
expect ( json_response [ 'objects' ] . first [ 'actions' ] [ 'download' ] [ 'href' ] ) . to eq ( objects_url ( project , sample_oid ) )
end
end
context 'when "lfs.object_store.proxy_download" is "false"' do
before do
stub_lfs_object_storage ( proxy_download : false )
end
it_behaves_like 'LFS http 200 response'
it 'does return direct object storage URL' do
expect ( json_response [ 'objects' ] . first ) . to include ( sample_object )
expect ( json_response [ 'objects' ] . first [ 'actions' ] [ 'download' ] [ 'href' ] ) . to start_with ( " https://lfs-objects.s3.amazonaws.com/ " )
expect ( json_response [ 'objects' ] . first [ 'actions' ] [ 'download' ] [ 'href' ] ) . to include ( " X-Amz-Expires=3600& " )
end
context 'when feature flag "lfs_batch_direct_downloads" is "false"' do
before do
stub_feature_flags ( lfs_batch_direct_downloads : false )
end
it_behaves_like 'LFS http 200 response'
it 'does return proxied address URL' do
expect ( json_response [ 'objects' ] . first ) . to include ( sample_object )
expect ( json_response [ 'objects' ] . first [ 'actions' ] [ 'download' ] [ 'href' ] ) . to eq ( objects_url ( project , sample_oid ) )
end
end
end
end
context 'when sending objects=[]' do
let ( :body ) { download_body ( [ ] ) }
it_behaves_like 'LFS http expected response code and message' do
let ( :response_code ) { 404 }
let ( :message ) { 'Not found.' }
end
end
2019-12-21 20:55:43 +05:30
end
2021-02-22 17:27:13 +05:30
context 'when user is authenticated' do
before do
project . add_role ( user , role ) if role
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'an authorized request' , renew_authorization : true do
let ( :role ) { :reporter }
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
context 'when user is not a member of the project' do
let ( :role ) { nil }
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 404 response'
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
context 'when user does not have download access' do
let ( :role ) { :guest }
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 404 response'
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
context 'when user password is expired' do
2022-08-27 11:52:29 +05:30
let_it_be ( :user ) { create ( :user , password_expires_at : 1 . minute . ago ) }
2021-09-30 23:02:18 +05:30
2022-08-27 11:52:29 +05:30
let ( :role ) { :reporter }
2016-08-24 12:49:21 +05:30
2021-06-02 17:11:27 +05:30
it_behaves_like 'LFS http 401 response'
2021-02-22 17:27:13 +05:30
end
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
context 'when user is blocked' do
2022-08-27 11:52:29 +05:30
let_it_be ( :user ) { create ( :user , :blocked ) }
2021-09-30 23:02:18 +05:30
2022-08-27 11:52:29 +05:30
let ( :role ) { :reporter }
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 401 response'
end
2016-08-24 12:49:21 +05:30
end
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
context 'when using Deploy Tokens' do
let ( :authorization ) { authorize_deploy_token }
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
context 'when Deploy Token is not valid' do
let ( :deploy_token ) { create ( :deploy_token , projects : [ project ] , read_repository : false ) }
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 401 response'
end
2018-11-18 11:00:15 +05:30
2021-02-22 17:27:13 +05:30
context 'when Deploy Token is not related to the project' do
let ( :deploy_token ) { create ( :deploy_token , projects : [ other_project ] ) }
2018-11-18 11:00:15 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 401 response'
end
2018-11-18 11:00:15 +05:30
2023-03-04 22:38:38 +05:30
context 'when deploy token is from an unrelated group to the project' do
let ( :group ) { create ( :group ) }
let ( :deploy_token ) { create ( :deploy_token , :group , groups : [ group ] ) }
it_behaves_like 'LFS http 401 response'
end
context 'when deploy token is from a parent group of the project and valid' do
let ( :group ) { create ( :group ) }
let ( :project ) { create ( :project , group : group ) }
let ( :deploy_token ) { create ( :deploy_token , :group , groups : [ group ] ) }
it_behaves_like 'an authorized request' , renew_authorization : false
end
2021-02-22 17:27:13 +05:30
# 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 ] ) }
2018-11-18 11:00:15 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'an authorized request' , renew_authorization : false
end
end
2020-08-19 02:56:42 +05:30
2021-02-22 17:27:13 +05:30
context 'when build is authorized as' do
let ( :authorization ) { authorize_ci_project }
2020-08-19 02:56:42 +05:30
2021-02-22 17:27:13 +05:30
shared_examples 'can download LFS only from own projects' do | renew_authorization : |
context 'for own project' do
let ( :pipeline ) { create ( :ci_empty_pipeline , project : project ) }
2018-11-18 11:00:15 +05:30
2021-02-22 17:27:13 +05:30
before do
authorize_download
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'an authorized request' , renew_authorization : renew_authorization
end
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
context 'for other project' do
let ( :pipeline ) { create ( :ci_empty_pipeline , project : other_project ) }
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
it 'rejects downloading code' do
expect ( response ) . to have_gitlab_http_status ( :not_found )
end
end
2016-09-29 09:46:39 +05:30
end
2021-02-22 17:27:13 +05:30
context 'administrator' , :enable_admin_mode do
let_it_be ( :user ) { create ( :admin ) }
2021-09-30 23:02:18 +05:30
2021-02-22 17:27:13 +05:30
let ( :build ) { create ( :ci_build , :running , pipeline : pipeline , user : user ) }
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'can download LFS only from own projects' , renew_authorization : true
2016-09-29 09:46:39 +05:30
end
2021-02-22 17:27:13 +05:30
context 'regular user' do
let ( :build ) { create ( :ci_build , :running , pipeline : pipeline , user : user ) }
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'can download LFS only from own projects' , renew_authorization : true
end
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
context 'does not have user' do
let ( :build ) { create ( :ci_build , :running , pipeline : pipeline ) }
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'can download LFS only from own projects' , renew_authorization : false
end
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
context 'when user is not authenticated' do
let ( :authorization ) { nil }
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
describe 'is accessing public project' do
let_it_be ( :project ) { create ( :project , :public ) }
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 200 response'
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it 'returns href to download' do
expect ( json_response ) . to eq ( {
'objects' = > [
{
'oid' = > sample_oid ,
'size' = > sample_size ,
'authenticated' = > true ,
'actions' = > {
'download' = > {
'href' = > objects_url ( project , sample_oid ) ,
'header' = > { }
}
}
2017-08-17 22:00:37 +05:30
}
2021-02-22 17:27:13 +05:30
]
} )
end
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
describe 'is accessing non-public project' do
it_behaves_like 'LFS http 401 response'
end
2016-08-24 12:49:21 +05:30
end
end
2021-02-22 17:27:13 +05:30
describe 'upload' do
let_it_be ( :project ) { create ( :project , :public ) }
2021-09-30 23:02:18 +05:30
2021-02-22 17:27:13 +05:30
let ( :body ) { upload_body ( sample_object ) }
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
shared_examples 'pushes new LFS objects' do | renew_authorization : |
let ( :sample_size ) { 150 . megabytes }
let ( :sample_oid ) { non_existing_object_oid }
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 200 response'
2018-03-17 18:26:18 +05:30
2021-02-22 17:27:13 +05:30
it 'responds with upload hypermedia link' do
expect ( json_response [ 'objects' ] ) . to be_kind_of ( Array )
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 ) )
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
headers = json_response [ 'objects' ] . first [ 'actions' ] [ 'upload' ] [ 'header' ]
expect ( headers [ 'Content-Type' ] ) . to eq ( 'application/octet-stream' )
expect ( headers [ 'Transfer-Encoding' ] ) . to eq ( 'chunked' )
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'process authorization header' , renew_authorization : renew_authorization
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
describe 'when request is authenticated' do
describe 'when user has project push access' do
before do
authorize_upload
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
context 'when pushing an LFS object that already exists' do
shared_examples_for 'batch upload with existing LFS object' do
it_behaves_like 'LFS http 200 response'
2020-04-08 14:13:33 +05:30
2021-02-22 17:27:13 +05:30
it 'responds with links to the object in 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 ) )
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
headers = json_response [ 'objects' ] . first [ 'actions' ] [ 'upload' ] [ 'header' ]
expect ( headers [ 'Content-Type' ] ) . to eq ( 'application/octet-stream' )
expect ( headers [ 'Transfer-Encoding' ] ) . to eq ( 'chunked' )
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'process authorization header' , renew_authorization : true
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
context 'in another project' do
before do
lfs_object . update! ( projects : [ other_project ] )
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'batch upload with existing LFS object'
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
context 'in source of fork project' do
2022-01-26 12:08:38 +05:30
let ( :other_project ) { create ( :project , :empty_repo ) }
2021-02-22 17:27:13 +05:30
let ( :project ) { fork_project ( other_project ) }
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
before do
lfs_object . update! ( projects : [ other_project ] )
end
2019-12-21 20:55:43 +05:30
2022-01-26 12:08:38 +05:30
context 'when user has access to both the parent and fork' do
before do
project . add_developer ( user )
other_project . add_developer ( user )
end
it 'links existing LFS objects to other project' do
expect ( Gitlab :: AppJsonLogger ) . to receive ( :info ) . with (
message : " LFS object auto-linked to forked project " ,
lfs_object_oid : lfs_object . oid ,
lfs_object_size : lfs_object . size ,
source_project_id : other_project . id ,
source_project_path : other_project . full_path ,
target_project_id : project . id ,
target_project_path : project . full_path ) . and_call_original
expect ( json_response [ 'objects' ] ) . to be_kind_of ( Array )
expect ( json_response [ 'objects' ] . first ) . to include ( sample_object )
expect ( json_response [ 'objects' ] . first ) . not_to have_key ( 'actions' )
expect ( lfs_object . reload . projects . pluck ( :id ) ) . to match_array ( [ other_project . id , project . id ] )
end
end
context 'when user does not have access to parent' do
before do
project . add_developer ( user )
end
it_behaves_like 'batch upload with existing LFS object'
end
2021-02-22 17:27:13 +05:30
end
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +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
2021-02-22 17:27:13 +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
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 200 response'
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it 'responds with upload hypermedia link for the new object' do
expect ( json_response [ 'objects' ] ) . to be_kind_of ( Array )
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
expect ( json_response [ 'objects' ] . first ) . to include ( sample_object )
expect ( json_response [ 'objects' ] . first ) . not_to have_key ( 'actions' )
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +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 ) )
headers = json_response [ 'objects' ] . last [ 'actions' ] [ 'upload' ] [ 'header' ]
expect ( headers [ 'Content-Type' ] ) . to eq ( 'application/octet-stream' )
expect ( headers [ 'Transfer-Encoding' ] ) . to eq ( 'chunked' )
end
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'process authorization header' , renew_authorization : true
end
end
context 'when user does not have push access' 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
2021-02-22 17:27:13 +05:30
context 'when build is authorized' do
let ( :authorization ) { authorize_ci_project }
let ( :pipeline ) { create ( :ci_empty_pipeline , project : project ) }
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
context 'build has an user' do
let ( :build ) { create ( :ci_build , :running , pipeline : pipeline , user : user ) }
2018-03-17 18:26:18 +05:30
2021-02-22 17:27:13 +05:30
context 'tries to push to own project' do
it_behaves_like 'LFS http 403 response'
end
2018-03-17 18:26:18 +05:30
2021-02-22 17:27:13 +05:30
context 'tries to push to other project' do
let ( :pipeline ) { create ( :ci_empty_pipeline , project : other_project ) }
2018-03-17 18:26:18 +05:30
2021-02-22 17:27:13 +05:30
# I'm not sure what this tests that is different from the previous test
2021-09-30 23:02:18 +05:30
it_behaves_like 'LFS http 403 response'
2021-02-22 17:27:13 +05:30
end
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
context 'does not have user' do
let ( :build ) { create ( :ci_build , :running , pipeline : pipeline ) }
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 403 response'
end
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
context 'when deploy key has project push access' do
let ( :key ) { create ( :deploy_key ) }
let ( :authorization ) { authorize_deploy_key }
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
before do
project . deploy_keys_projects . create! ( deploy_key : key , can_push : true )
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'pushes new LFS objects' , renew_authorization : false
end
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
context 'when user is not authenticated' do
let ( :authorization ) { nil }
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
context 'when user has push access' do
before do
authorize_upload
end
2018-03-17 18:26:18 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 401 response'
end
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
context 'when user does not have push access' do
it_behaves_like 'LFS http 401 response'
end
end
end
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
describe 'unsupported' do
let ( :body ) { request_body ( 'other' , sample_object ) }
2018-03-17 18:26:18 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 404 response'
end
end
2018-03-17 18:26:18 +05:30
2021-02-22 17:27:13 +05:30
describe 'when handling LFS batch request on a read-only GitLab instance' do
subject { post_lfs_json ( batch_url ( project ) , body , headers ) }
2018-03-17 18:26:18 +05:30
2021-02-22 17:27:13 +05:30
before do
allow ( Gitlab :: Database ) . to receive ( :read_only? ) { true }
2018-03-17 18:26:18 +05:30
2021-02-22 17:27:13 +05:30
project . add_maintainer ( user )
2018-03-17 18:26:18 +05:30
2021-02-22 17:27:13 +05:30
subject
2016-08-24 12:49:21 +05:30
end
2021-02-22 17:27:13 +05:30
context 'when downloading' do
let ( :body ) { download_body ( sample_object ) }
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 200 response'
2016-08-24 12:49:21 +05:30
end
2021-02-22 17:27:13 +05:30
context 'when uploading' do
let ( :body ) { upload_body ( sample_object ) }
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +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
2016-08-24 12:49:21 +05:30
end
end
2021-02-22 17:27:13 +05:30
describe 'when pushing a LFS object' do
let ( :include_workhorse_jwt_header ) { true }
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
shared_examples 'unauthorized' do
context 'and request is sent by gitlab-workhorse to authorize the request' do
before do
put_authorize
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 401 response'
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
context 'and request is sent by gitlab-workhorse to finalize the upload' do
before do
put_finalize
end
2016-09-13 17:45:13 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 401 response'
end
context 'and request is sent with a malformed headers' do
before do
put_finalize ( '/etc/passwd' )
end
it_behaves_like 'LFS http 401 response'
end
2016-09-13 17:45:13 +05:30
end
2021-02-22 17:27:13 +05:30
shared_examples 'forbidden' do
context 'and request is sent by gitlab-workhorse to authorize the request' do
before do
put_authorize
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 403 response'
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
context 'and request is sent by gitlab-workhorse to finalize the upload' do
before do
put_finalize
end
it_behaves_like 'LFS http 403 response'
2016-08-24 12:49:21 +05:30
end
2021-02-22 17:27:13 +05:30
context 'and request is sent with a malformed headers' do
before do
put_finalize ( '/etc/passwd' )
2016-09-29 09:46:39 +05:30
end
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 403 response'
2016-09-29 09:46:39 +05:30
end
2021-02-22 17:27:13 +05:30
end
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
describe 'to one project' do
describe 'when user is authenticated' do
describe 'when user has push access to the project' do
2018-05-09 12:01:36 +05:30
before do
2021-02-22 17:27:13 +05:30
project . add_developer ( user )
2018-05-09 12:01:36 +05:30
end
2021-02-22 17:27:13 +05:30
context 'and the request bypassed workhorse' do
it 'raises an exception' do
expect { put_authorize ( verified : false ) } . to raise_error JWT :: DecodeError
2018-05-09 12:01:36 +05:30
end
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
context 'and request is sent by gitlab-workhorse to authorize the request' do
shared_examples 'a valid response' do
before do
put_authorize
end
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 200 workhorse response'
2018-05-09 12:01:36 +05:30
end
2021-02-22 17:27:13 +05:30
shared_examples 'a local file' do
it_behaves_like 'a valid response' do
it 'responds with status 200, location of LFS store and object details' do
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
2018-05-09 12:01:36 +05:30
end
end
2021-02-22 17:27:13 +05:30
context 'when using local storage' do
it_behaves_like 'a local file'
2018-05-09 12:01:36 +05:30
end
2021-02-22 17:27:13 +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
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'a valid response' do
it 'responds with status 200, location of LFS remote store and object details' do
expect ( json_response ) . not_to have_key ( 'TempPath' )
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' )
expect ( json_response [ 'RemoteObject' ] ) . to have_key ( 'MultipartUpload' )
expect ( json_response [ 'LfsOid' ] ) . to eq ( sample_oid )
expect ( json_response [ 'LfsSize' ] ) . to eq ( sample_size )
end
end
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
context 'when direct upload is disabled' do
before do
stub_lfs_object_storage ( enabled : true , direct_upload : false )
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'a local file'
end
end
end
2016-09-13 17:45:13 +05:30
2021-02-22 17:27:13 +05:30
context 'and request is sent by gitlab-workhorse to finalize the upload' do
before do
put_finalize
end
2019-02-02 18:00:53 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 200 response'
2018-05-09 12:01:36 +05:30
2021-02-22 17:27:13 +05:30
it 'LFS object is linked to the project' do
expect ( lfs_object . projects . pluck ( :id ) ) . to include ( project . id )
end
end
2018-05-09 12:01:36 +05:30
2021-02-22 17:27:13 +05:30
context 'and request to finalize the upload is not sent by gitlab-workhorse' do
it 'fails with a JWT decode error' do
2022-07-16 23:28:13 +05:30
expect { put_finalize ( verified : false ) } . to raise_error ( JWT :: DecodeError )
end
end
context 'and the uploaded file is invalid' do
where ( :size , :sha256 , :status ) do
nil | nil | :ok # Test setup sanity check
0 | nil | :bad_request
nil | 'a' * 64 | :bad_request
end
with_them do
it 'validates the upload size and SHA256' do
put_finalize ( size : size , sha256 : sha256 )
expect ( response ) . to have_gitlab_http_status ( status )
end
2021-02-22 17:27:13 +05:30
end
2018-05-09 12:01:36 +05:30
end
2021-02-22 17:27:13 +05:30
context 'and workhorse requests upload finalize for a new LFS object' do
before do
lfs_object . destroy!
2018-05-09 12:01:36 +05:30
end
2021-02-22 17:27:13 +05:30
context 'with object storage enabled' do
context 'and direct upload enabled' do
let! ( :fog_connection ) do
stub_lfs_object_storage ( direct_upload : true )
2018-05-09 12:01:36 +05:30
end
2021-02-22 17:27:13 +05:30
let ( :tmp_object ) do
fog_connection . directories . new ( key : 'lfs-objects' ) . files . create ( # rubocop: disable Rails/SaveBang
key : 'tmp/uploads/12312300' ,
2022-07-16 23:28:13 +05:30
body : 'x' * sample_size
2021-02-22 17:27:13 +05:30
)
end
[ '123123' , '../../123123' ] . each do | remote_id |
context " with invalid remote_id: #{ remote_id } " do
subject do
put_finalize ( remote_object : tmp_object , args : {
'file.remote_id' = > remote_id
} )
end
2018-05-09 12:01:36 +05:30
2021-02-22 17:27:13 +05:30
it 'responds with status 403' do
subject
expect ( response ) . to have_gitlab_http_status ( :forbidden )
end
end
2018-05-09 12:01:36 +05:30
end
2021-02-22 17:27:13 +05:30
context 'with valid remote_id' do
subject do
put_finalize ( remote_object : tmp_object , args : {
'file.remote_id' = > '12312300' ,
'file.name' = > 'name'
} )
end
2018-05-09 12:01:36 +05:30
2021-02-22 17:27:13 +05:30
it 'responds with status 200' do
subject
2018-05-09 12:01:36 +05:30
2021-02-22 17:27:13 +05:30
expect ( response ) . to have_gitlab_http_status ( :ok )
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
object = LfsObject . find_by_oid ( sample_oid )
expect ( object ) . to be_present
expect ( object . file . read ) . to eq ( tmp_object . body )
end
it 'schedules migration of file to object storage' do
subject
expect ( LfsObject . last . projects ) . to include ( project )
end
2018-05-09 12:01:36 +05:30
2021-02-22 17:27:13 +05:30
it 'have valid file' do
subject
2018-05-09 12:01:36 +05:30
2021-02-22 17:27:13 +05:30
expect ( LfsObject . last . file_store ) . to eq ( ObjectStorage :: Store :: REMOTE )
expect ( LfsObject . last . file ) . to be_exists
end
end
2018-05-09 12:01:36 +05:30
end
end
end
2021-02-22 17:27:13 +05:30
context 'without the lfs object' do
2018-05-09 12:01:36 +05:30
before do
2021-02-22 17:27:13 +05:30
lfs_object . destroy!
end
it 'rejects slashes in the tempfile name (path traversal)' do
put_finalize ( '../bar' , with_tempfile : true )
expect ( response ) . to have_gitlab_http_status ( :bad_request )
2018-05-09 12:01:36 +05:30
end
2021-02-22 17:27:13 +05:30
context 'not sending the workhorse jwt header' do
let ( :include_workhorse_jwt_header ) { false }
it 'rejects the request' do
put_finalize ( with_tempfile : true )
2018-05-09 12:01:36 +05:30
2021-02-22 17:27:13 +05:30
expect ( response ) . to have_gitlab_http_status ( :unprocessable_entity )
end
2018-05-09 12:01:36 +05:30
end
end
end
2021-02-22 17:27:13 +05:30
describe 'and user does not have push access' do
before do
project . add_reporter ( user )
end
2016-09-13 17:45:13 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'forbidden'
2020-10-24 23:57:45 +05:30
end
2021-02-22 17:27:13 +05:30
end
2020-10-24 23:57:45 +05:30
2021-02-22 17:27:13 +05:30
context 'when build is authorized' do
let ( :authorization ) { authorize_ci_project }
let ( :pipeline ) { create ( :ci_empty_pipeline , project : project ) }
2020-10-24 23:57:45 +05:30
2021-02-22 17:27:13 +05:30
context 'build has an user' do
let ( :build ) { create ( :ci_build , :running , pipeline : pipeline , user : user ) }
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
context 'tries to push to own project' do
before do
project . add_developer ( user )
put_authorize
end
2016-09-13 17:45:13 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 403 response'
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
context 'tries to push to other project' do
let ( :pipeline ) { create ( :ci_empty_pipeline , project : other_project ) }
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
before do
put_authorize
end
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 404 response'
end
2016-09-29 09:46:39 +05:30
end
2021-02-22 17:27:13 +05:30
context 'does not have user' do
let ( :build ) { create ( :ci_build , :running , pipeline : pipeline ) }
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
before do
put_authorize
end
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 403 response'
2016-09-29 09:46:39 +05:30
end
end
2021-02-22 17:27:13 +05:30
describe 'when using a user key (LFSToken)' do
let ( :authorization ) { authorize_user_key }
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
context 'when user allowed' do
before do
project . add_developer ( user )
put_authorize
end
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 200 workhorse response'
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
context 'when user password is expired' do
2021-09-30 23:02:18 +05:30
let_it_be ( :user ) { create ( :user , password_expires_at : 1 . minute . ago ) }
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 401 response'
end
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
context 'when user is blocked' do
2022-08-27 11:52:29 +05:30
let_it_be ( :user ) { create ( :user , :blocked ) }
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 401 response'
end
end
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
context 'when user not allowed' do
before do
put_authorize
end
it_behaves_like 'LFS http 404 response'
end
2019-12-21 20:55:43 +05:30
end
2021-02-22 17:27:13 +05:30
context 'for unauthenticated' do
let ( :authorization ) { nil }
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'unauthorized'
2016-09-29 09:46:39 +05:30
end
end
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
describe 'to a forked project' do
2023-03-04 22:38:38 +05:30
let_it_be_with_reload ( :upstream_project ) { create ( :project , :public ) }
2021-02-22 17:27:13 +05:30
let_it_be ( :project_owner ) { create ( :user ) }
2021-09-30 23:02:18 +05:30
2021-02-22 17:27:13 +05:30
let ( :project ) { fork_project ( upstream_project , project_owner ) }
2019-12-21 20:55:43 +05:30
2021-02-22 17:27:13 +05:30
describe 'when user is authenticated' do
describe 'when user has push access to the project' do
before do
project . add_developer ( user )
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
context 'and request is sent by gitlab-workhorse to authorize the request' do
before do
put_authorize
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 200 workhorse response'
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it 'with location of LFS store and object details' do
expect ( json_response [ 'TempPath' ] ) . to eq ( LfsObjectUploader . workhorse_local_upload_path )
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
2021-02-22 17:27:13 +05:30
context 'and request is sent by gitlab-workhorse to finalize the upload' do
before do
put_finalize
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 200 response'
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it 'LFS object is linked to the forked project' do
expect ( lfs_object . projects . pluck ( :id ) ) . to include ( project . id )
end
end
end
2016-08-24 12:49:21 +05:30
2023-03-04 22:38:38 +05:30
describe 'when user has push access to upstream project' do
before do
upstream_project . add_maintainer ( user )
end
context 'an MR exists on target forked project' do
let ( :allow_collaboration ) { true }
let ( :merge_request ) do
create ( :merge_request ,
target_project : upstream_project ,
source_project : project ,
allow_collaboration : allow_collaboration )
end
before do
merge_request
end
context 'with allow_collaboration option set to true' do
context 'and request is sent by gitlab-workhorse to authorize the request' do
before do
put_authorize
end
it_behaves_like 'LFS http 200 workhorse response'
end
context 'and request is sent by gitlab-workhorse to finalize the upload' do
before do
put_finalize
end
it_behaves_like 'LFS http 200 response'
end
end
context 'with allow_collaboration option set to false' do
context 'request is sent by gitlab-workhorse to authorize the request' do
let ( :allow_collaboration ) { false }
before do
put_authorize
end
it_behaves_like 'forbidden'
end
end
end
end
2021-02-22 17:27:13 +05:30
describe 'and user does not have push access' do
it_behaves_like 'forbidden'
2016-08-24 12:49:21 +05:30
end
end
2021-02-22 17:27:13 +05:30
context 'when build is authorized' do
let ( :authorization ) { authorize_ci_project }
let ( :pipeline ) { create ( :ci_empty_pipeline , project : project ) }
2016-08-24 12:49:21 +05:30
before do
2021-02-22 17:27:13 +05:30
put_authorize
2016-08-24 12:49:21 +05:30
end
2021-02-22 17:27:13 +05:30
context 'build has an user' do
let ( :build ) { create ( :ci_build , :running , pipeline : pipeline , user : user ) }
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
context 'tries to push to own project' do
it_behaves_like 'LFS http 403 response'
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
context 'tries to push to other project' do
let ( :pipeline ) { create ( :ci_empty_pipeline , project : other_project ) }
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
# I'm not sure what this tests that is different from the previous test
2021-09-30 23:02:18 +05:30
it_behaves_like 'LFS http 403 response'
2021-02-22 17:27:13 +05:30
end
end
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
context 'does not have user' do
let ( :build ) { create ( :ci_build , :running , pipeline : pipeline ) }
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 403 response'
end
2016-09-29 09:46:39 +05:30
end
2021-02-22 17:27:13 +05:30
context 'for unauthenticated' do
let ( :authorization ) { nil }
2016-09-29 09:46:39 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'unauthorized'
2016-09-29 09:46:39 +05:30
end
2021-02-22 17:27:13 +05:30
describe 'and second project not related to fork or a source project' do
let_it_be ( :second_project ) { create ( :project ) }
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
before do
second_project . add_maintainer ( user )
upstream_project . lfs_objects << lfs_object
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
context 'when pushing the same LFS object to the second project' do
before do
2022-07-16 23:28:13 +05:30
put_finalize ( with_tempfile : true , to_project : second_project )
2021-02-22 17:27:13 +05:30
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
it_behaves_like 'LFS http 200 response'
2019-02-02 18:00:53 +05:30
2021-02-22 17:27:13 +05:30
it 'links the LFS object to the project' do
expect ( lfs_object . projects . pluck ( :id ) ) . to include ( second_project . id , upstream_project . id )
end
end
2016-08-24 12:49:21 +05:30
end
2021-02-22 17:27:13 +05:30
end
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +05:30
def put_authorize ( verified : true )
authorize_headers = headers
authorize_headers . merge! ( workhorse_internal_api_request_header ) if verified
2016-08-24 12:49:21 +05:30
2021-02-22 17:27:13 +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
end
2021-01-29 00:20:46 +05:30
end
end
context 'with project wikis' do
it_behaves_like 'LFS http requests' do
let ( :container ) { create ( :project_wiki , :empty_repo , project : project ) }
let ( :authorize_guest ) { project . add_guest ( user ) }
let ( :authorize_download ) { project . add_reporter ( user ) }
let ( :authorize_upload ) { project . add_developer ( user ) }
end
end
context 'with snippets' do
# LFS is not supported on snippets, so we override the shared examples
# to expect 404 responses instead.
[
'LFS http 200 response' ,
'LFS http 200 blob response' ,
'LFS http 403 response'
] . each do | examples |
shared_examples_for ( examples ) { it_behaves_like 'LFS http 404 response' }
end
context 'with project snippets' do
it_behaves_like 'LFS http requests' do
let ( :container ) { create ( :project_snippet , :empty_repo , project : project ) }
let ( :authorize_guest ) { project . add_guest ( user ) }
let ( :authorize_download ) { project . add_reporter ( user ) }
let ( :authorize_upload ) { project . add_developer ( user ) }
end
end
context 'with personal snippets' do
it_behaves_like 'LFS http requests' do
let ( :container ) { create ( :personal_snippet , :empty_repo ) }
let ( :authorize_upload ) { container . update! ( author : user ) }
end
end
end
2016-08-24 12:49:21 +05:30
end