summaryrefslogtreecommitdiff
path: root/spec/requests/lfs_http_spec.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/requests/lfs_http_spec.rb')
-rw-r--r--spec/requests/lfs_http_spec.rb428
1 files changed, 158 insertions, 270 deletions
diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb
index 31bb0586e9f..48d125a37c3 100644
--- a/spec/requests/lfs_http_spec.rb
+++ b/spec/requests/lfs_http_spec.rb
@@ -9,18 +9,17 @@ RSpec.describe 'Git LFS API and storage' do
let_it_be(:project, reload: true) { create(:project, :repository) }
let_it_be(:other_project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
- let!(:lfs_object) { create(:lfs_object, :with_file) }
+ let(:lfs_object) { create(:lfs_object, :with_file) }
let(:headers) do
{
'Authorization' => authorization,
- 'X-Sendfile-Type' => sendfile
+ 'X-Sendfile-Type' => 'X-Sendfile'
}.compact
end
let(:include_workhorse_jwt_header) { true }
let(:authorization) { }
- let(:sendfile) { }
let(:pipeline) { create(:ci_empty_pipeline, project: project) }
let(:sample_oid) { lfs_object.oid }
@@ -37,18 +36,6 @@ RSpec.describe 'Git LFS API and storage' do
stub_lfs_setting(enabled: lfs_enabled)
end
- describe 'when LFS is disabled' do
- let(:lfs_enabled) { false }
- let(:body) { upload_body(multiple_objects) }
- let(:authorization) { authorize_user }
-
- before do
- post_lfs_json batch_url(project), body, headers
- end
-
- it_behaves_like 'LFS http 501 response'
- end
-
context 'project specific LFS settings' do
let(:body) { upload_body(sample_object) }
let(:authorization) { authorize_user }
@@ -60,105 +47,36 @@ RSpec.describe 'Git LFS API and storage' do
subject
end
- context 'with LFS disabled globally' do
- let(:lfs_enabled) { false }
-
- describe 'LFS disabled in project' do
- let(:project_lfs_enabled) { false }
-
- context 'when uploading' do
- subject { post_lfs_json(batch_url(project), body, headers) }
-
- it_behaves_like 'LFS http 501 response'
- end
+ describe 'LFS disabled in project' do
+ let(:project_lfs_enabled) { false }
- context 'when downloading' do
- subject { get(objects_url(project, sample_oid), params: {}, headers: headers) }
+ context 'when uploading' do
+ subject { post_lfs_json(batch_url(project), body, headers) }
- it_behaves_like 'LFS http 501 response'
- end
+ it_behaves_like 'LFS http 404 response'
end
- describe 'LFS enabled in project' do
- let(:project_lfs_enabled) { true }
-
- context 'when uploading' do
- subject { post_lfs_json(batch_url(project), body, headers) }
-
- it_behaves_like 'LFS http 501 response'
- end
+ context 'when downloading' do
+ subject { get(objects_url(project, sample_oid), params: {}, headers: headers) }
- context 'when downloading' do
- subject { get(objects_url(project, sample_oid), params: {}, headers: headers) }
-
- it_behaves_like 'LFS http 501 response'
- end
+ it_behaves_like 'LFS http 404 response'
end
end
- context 'with LFS enabled globally' do
- describe 'LFS disabled in project' do
- let(:project_lfs_enabled) { false }
-
- context 'when uploading' do
- subject { post_lfs_json(batch_url(project), body, headers) }
-
- it_behaves_like 'LFS http 403 response'
- end
-
- context 'when downloading' do
- subject { get(objects_url(project, sample_oid), params: {}, headers: headers) }
-
- it_behaves_like 'LFS http 403 response'
- end
- end
-
- describe 'LFS enabled in project' do
- let(:project_lfs_enabled) { true }
-
- context 'when uploading' do
- subject { post_lfs_json(batch_url(project), body, headers) }
-
- it_behaves_like 'LFS http 200 response'
- end
+ describe 'LFS enabled in project' do
+ let(:project_lfs_enabled) { true }
- context 'when downloading' do
- subject { get(objects_url(project, sample_oid), params: {}, headers: headers) }
+ context 'when uploading' do
+ subject { post_lfs_json(batch_url(project), body, headers) }
- it_behaves_like 'LFS http 200 response'
- end
+ it_behaves_like 'LFS http 200 response'
end
- end
- end
- describe 'deprecated API' do
- let(:authorization) { authorize_user }
+ context 'when downloading' do
+ subject { get(objects_url(project, sample_oid), params: {}, headers: headers) }
- shared_examples 'deprecated request' do
- before do
- subject
+ it_behaves_like 'LFS http 200 blob response'
end
-
- 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.' }
- end
- end
-
- context 'when fetching LFS object using deprecated API' do
- subject { get(deprecated_objects_url(project, sample_oid), params: {}, headers: headers) }
-
- it_behaves_like 'deprecated request'
- end
-
- 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
-
- def deprecated_objects_url(project, oid = nil)
- File.join(["#{project.http_url_to_repo}/info/lfs/objects/", oid].compact)
end
end
@@ -167,196 +85,133 @@ RSpec.describe 'Git LFS API and storage' do
let(:before_get) { }
before do
+ project.lfs_objects << lfs_object
update_permissions
before_get
+
get objects_url(project, sample_oid), params: {}, headers: headers
end
- context 'and request comes from gitlab-workhorse' do
- context 'without user being authorized' do
- it_behaves_like 'LFS http 401 response'
- end
+ context 'when LFS uses object storage' do
+ let(:authorization) { authorize_user }
- context 'with required headers' do
- shared_examples 'responds with a file' do
- let(:sendfile) { 'X-Sendfile' }
+ let(:update_permissions) do
+ project.add_maintainer(user)
+ end
- it_behaves_like 'LFS http 200 response'
+ 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
- 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
+ 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
- context 'with user is authorized' do
- let(:authorization) { authorize_user }
+ 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
- context 'and does not have project access' do
- let(:update_permissions) do
- project.lfs_objects << lfs_object
- end
+ it 'responds with redirect' do
+ expect(response).to have_gitlab_http_status(:found)
+ end
- it_behaves_like 'LFS http 404 response'
- end
+ it 'responds with the file location' do
+ expect(response.location).to include(lfs_object.reload.file.path)
+ end
+ end
+ end
- context 'and does have project access' do
- let(:update_permissions) do
- project.add_maintainer(user)
- project.lfs_objects << lfs_object
- end
+ context 'when deploy key is authorized' do
+ let(:key) { create(:deploy_key) }
+ let(:authorization) { authorize_deploy_key }
- it_behaves_like 'responds with a file'
+ let(:update_permissions) do
+ project.deploy_keys << key
+ end
- 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
+ it_behaves_like 'LFS http 200 blob response'
+ end
- it_behaves_like 'LFS http 200 response'
+ context 'when using a user key (LFSToken)' do
+ let(:authorization) { authorize_user_key }
- 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 user allowed' do
+ let(:update_permissions) do
+ project.add_maintainer(user)
+ 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_behaves_like 'LFS http 200 blob response'
- it 'responds with redirect' do
- expect(response).to have_gitlab_http_status(:found)
- end
+ context 'when user password is expired' do
+ let(:user) { create(:user, password_expires_at: 1.minute.ago)}
- it 'responds with the file location' do
- expect(response.location).to include(lfs_object.reload.file.path)
- end
- end
- end
- end
+ it_behaves_like 'LFS http 401 response'
end
- context 'when deploy key is authorized' do
- let(:key) { create(:deploy_key) }
- let(:authorization) { authorize_deploy_key }
-
- let(:update_permissions) do
- project.deploy_keys << key
- project.lfs_objects << lfs_object
- end
+ context 'when user is blocked' do
+ let(:user) { create(:user, :blocked)}
- it_behaves_like 'responds with a file'
+ it_behaves_like 'LFS http 401 response'
end
+ end
- describe 'when using a user key (LFSToken)' do
- let(:authorization) { authorize_user_key }
-
- context 'when user allowed' do
- let(:update_permissions) do
- project.add_maintainer(user)
- project.lfs_objects << lfs_object
- end
+ context 'when user not allowed' do
+ it_behaves_like 'LFS http 404 response'
+ end
+ end
- it_behaves_like 'responds with a file'
+ context 'when build is authorized as' do
+ let(:authorization) { authorize_ci_project }
- context 'when user password is expired' do
- let(:user) { create(:user, password_expires_at: 1.minute.ago)}
+ shared_examples 'can download LFS only from own projects' do
+ context 'for owned project' do
+ let(:project) { create(:project, namespace: user.namespace) }
- it_behaves_like 'LFS http 401 response'
- end
+ it_behaves_like 'LFS http 200 blob response'
+ end
- context 'when user is blocked' do
- let(:user) { create(:user, :blocked)}
+ context 'for member of project' do
+ let(:pipeline) { create(:ci_empty_pipeline, project: project) }
- it_behaves_like 'LFS http 401 response'
- end
+ let(:update_permissions) do
+ project.add_reporter(user)
end
- context 'when user not allowed' do
- let(:update_permissions) do
- project.lfs_objects << lfs_object
- end
-
- it_behaves_like 'LFS http 404 response'
- end
+ it_behaves_like 'LFS http 200 blob response'
end
- context 'when build is authorized as' do
- let(:authorization) { authorize_ci_project }
-
- shared_examples 'can download LFS only from own projects' do
- context 'for owned project' do
- let(:project) { create(:project, namespace: user.namespace) }
-
- let(:update_permissions) do
- project.lfs_objects << lfs_object
- end
-
- it_behaves_like 'responds with a file'
- end
-
- context 'for member of project' do
- let(:pipeline) { create(:ci_empty_pipeline, project: project) }
-
- let(:update_permissions) do
- project.add_reporter(user)
- 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) }
- 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
- expect(response).to have_gitlab_http_status(other_project_status)
- 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
+ it 'rejects downloading code' do
+ expect(response).to have_gitlab_http_status(:not_found)
end
+ end
+ end
- context 'regular user' do
- let(:build) { create(:ci_build, :running, pipeline: pipeline, user: user) }
+ 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 404, to prevent data leakage about existence of the project
- let(:other_project_status) { 404 }
- end
- end
+ it_behaves_like 'can download LFS only from own projects'
+ end
- context 'does not have user' do
- let(:build) { create(:ci_build, :running, pipeline: pipeline) }
+ 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
- end
+ it_behaves_like 'can download LFS only from own projects'
end
- context 'without required headers' do
- let(:authorization) { authorize_user }
+ context 'does not have user' do
+ let(:build) { create(:ci_build, :running, pipeline: pipeline) }
- it_behaves_like 'LFS http 404 response'
+ it_behaves_like 'can download LFS only from own projects'
end
end
end
@@ -511,7 +366,7 @@ RSpec.describe 'Git LFS API and storage' do
let(:role) { :reporter }
end
- context 'when user does is not member of the project' do
+ context 'when user is not a member of the project' do
let(:update_user_permissions) { nil }
it_behaves_like 'LFS http 404 response'
@@ -520,7 +375,7 @@ RSpec.describe 'Git LFS API and storage' do
context 'when user does not have download access' do
let(:role) { :guest }
- it_behaves_like 'LFS http 403 response'
+ it_behaves_like 'LFS http 404 response'
end
context 'when user password is expired' do
@@ -591,7 +446,7 @@ RSpec.describe 'Git LFS API and storage' do
let(:pipeline) { create(:ci_empty_pipeline, project: other_project) }
it 'rejects downloading code' do
- expect(response).to have_gitlab_http_status(other_project_status)
+ expect(response).to have_gitlab_http_status(:not_found)
end
end
end
@@ -600,28 +455,19 @@ RSpec.describe 'Git LFS API and storage' 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', renew_authorization: true do
- # We render 403, because administrator does have normally access
- let(:other_project_status) { 403 }
- end
+ it_behaves_like 'can download LFS only from own projects', renew_authorization: true
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', renew_authorization: true do
- # We render 404, to prevent data leakage about existence of the project
- let(:other_project_status) { 404 }
- end
+ it_behaves_like 'can download LFS only from own projects', renew_authorization: true
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', renew_authorization: false do
- # We render 404, to prevent data leakage about existence of the project
- let(:other_project_status) { 404 }
- end
+ it_behaves_like 'can download LFS only from own projects', renew_authorization: false
end
end
@@ -919,11 +765,7 @@ RSpec.describe 'Git LFS API and storage' do
put_authorize
end
- it_behaves_like 'LFS http 200 response'
-
- it 'uses the gitlab-workhorse content type' do
- expect(response.media_type).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
- end
+ it_behaves_like 'LFS http 200 workhorse response'
end
shared_examples 'a local file' do
@@ -1142,7 +984,7 @@ RSpec.describe 'Git LFS API and storage' do
put_authorize
end
- it_behaves_like 'LFS http 404 response'
+ it_behaves_like 'LFS http 403 response'
end
end
@@ -1155,7 +997,7 @@ RSpec.describe 'Git LFS API and storage' do
put_authorize
end
- it_behaves_like 'LFS http 200 response'
+ it_behaves_like 'LFS http 200 workhorse response'
context 'when user password is expired' do
let(:user) { create(:user, password_expires_at: 1.minute.ago)}
@@ -1202,7 +1044,7 @@ RSpec.describe 'Git LFS API and storage' do
put_authorize
end
- it_behaves_like 'LFS http 200 response'
+ it_behaves_like 'LFS http 200 workhorse response'
it 'with location of LFS store and object details' do
expect(json_response['TempPath']).to eq(LfsObjectUploader.workhorse_local_upload_path)
@@ -1330,4 +1172,50 @@ RSpec.describe 'Git LFS API and storage' do
"#{sample_oid}012345678"
end
end
+
+ context 'with projects' do
+ it_behaves_like 'LFS http requests' do
+ let(:container) { 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 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
end