gitlab-org--gitlab-foss/spec/requests/lfs_http_spec.rb

1189 lines
38 KiB
Ruby
Raw Normal View History

require 'spec_helper'
2016-07-22 09:45:38 -04:00
describe 'Git LFS API and storage' do
include WorkhorseHelpers
let(:user) { create(:user) }
let!(:lfs_object) { create(:lfs_object, :with_file) }
let(:headers) do
{
'Authorization' => authorization,
'X-Sendfile-Type' => sendfile
}.compact
end
let(:authorization) { }
let(:sendfile) { }
let(:pipeline) { create(:ci_empty_pipeline, project: project) }
let(:sample_oid) { lfs_object.oid }
let(:sample_size) { lfs_object.size }
describe 'when lfs is disabled' do
let(:project) { create(:project) }
let(:body) do
{
'objects' => [
{ 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
2017-02-22 12:44:44 -05:00
'size' => 1575078 },
{ 'oid' => sample_oid,
2017-02-22 12:44:44 -05:00
'size' => sample_size }
],
'operation' => 'upload'
}
end
2016-07-20 12:41:26 -04:00
let(:authorization) { authorize_user }
before do
allow(Gitlab.config.lfs).to receive(:enabled).and_return(false)
2016-07-22 11:23:35 -04:00
post_lfs_json "#{project.http_url_to_repo}/info/lfs/objects/batch", body, headers
end
it 'responds with 501' do
expect(response).to have_http_status(501)
expect(json_response).to include('message' => 'Git LFS is not enabled on this GitLab server, contact your admin.')
end
end
context 'project specific LFS settings' do
let(:project) { create(:project) }
let(:body) do
{
'objects' => [
{ 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
2017-02-22 12:44:44 -05:00
'size' => 1575078 },
{ 'oid' => sample_oid,
2017-02-22 12:44:44 -05:00
'size' => sample_size }
],
'operation' => 'upload'
}
end
let(:authorization) { authorize_user }
context 'with LFS disabled globally' do
before do
project.team << [user, :master]
allow(Gitlab.config.lfs).to receive(:enabled).and_return(false)
end
describe 'LFS disabled in project' do
before do
project.update_attribute(:lfs_enabled, false)
end
it 'responds with a 501 message on upload' do
post_lfs_json "#{project.http_url_to_repo}/info/lfs/objects/batch", body, headers
expect(response).to have_http_status(501)
end
it 'responds with a 501 message on download' do
get "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", nil, headers
expect(response).to have_http_status(501)
end
end
describe 'LFS enabled in project' do
before do
project.update_attribute(:lfs_enabled, true)
end
it 'responds with a 501 message on upload' do
post_lfs_json "#{project.http_url_to_repo}/info/lfs/objects/batch", body, headers
expect(response).to have_http_status(501)
end
it 'responds with a 501 message on download' do
get "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", nil, headers
expect(response).to have_http_status(501)
end
end
end
context 'with LFS enabled globally' do
before do
project.team << [user, :master]
enable_lfs
end
describe 'LFS disabled in project' do
before do
project.update_attribute(:lfs_enabled, false)
end
it 'responds with a 403 message on upload' do
post_lfs_json "#{project.http_url_to_repo}/info/lfs/objects/batch", body, headers
expect(response).to have_http_status(403)
expect(json_response).to include('message' => 'Access forbidden. Check your access level.')
end
it 'responds with a 403 message on download' do
get "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", nil, headers
expect(response).to have_http_status(403)
expect(json_response).to include('message' => 'Access forbidden. Check your access level.')
end
end
describe 'LFS enabled in project' do
before do
project.update_attribute(:lfs_enabled, true)
end
it 'responds with a 200 message on upload' do
post_lfs_json "#{project.http_url_to_repo}/info/lfs/objects/batch", body, headers
expect(response).to have_http_status(200)
expect(json_response['objects'].first['size']).to eq(1575078)
end
it 'responds with a 200 message on download' do
get "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", nil, headers
expect(response).to have_http_status(200)
end
end
end
end
describe 'deprecated API' do
let(:project) { create(:project) }
before do
enable_lfs
end
shared_examples 'a deprecated' do
it 'responds with 501' do
expect(response).to have_http_status(501)
end
it 'returns deprecated message' do
expect(json_response).to include('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
let(:authorization) { authorize_user }
before do
get "#{project.http_url_to_repo}/info/lfs/objects/#{sample_oid}", nil, headers
end
it_behaves_like 'a deprecated'
end
context 'when handling lfs request using deprecated API' do
2016-07-20 12:41:26 -04:00
let(:authorization) { authorize_user }
before do
2016-07-22 11:23:35 -04:00
post_lfs_json "#{project.http_url_to_repo}/info/lfs/objects", nil, headers
end
it_behaves_like 'a deprecated'
end
end
describe 'when fetching lfs object' do
let(:project) { create(:project) }
let(:update_permissions) { }
before do
enable_lfs
update_permissions
get "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", nil, headers
end
context 'and request comes from gitlab-workhorse' do
context 'without user being authorized' do
it 'responds with status 401' do
expect(response).to have_http_status(401)
end
end
context 'with required headers' do
shared_examples 'responds with a file' do
let(:sendfile) { 'X-Sendfile' }
it 'responds with status 200' do
expect(response).to have_http_status(200)
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
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
2016-07-20 12:41:26 -04:00
it 'responds with status 404' do
expect(response).to have_http_status(404)
end
end
context 'and does have project access' do
let(:update_permissions) do
project.team << [user, :master]
project.lfs_objects << lfs_object
end
it_behaves_like 'responds with a file'
end
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
it_behaves_like 'responds with a file'
end
describe 'when using a user key' do
let(:authorization) { authorize_user_key }
context 'when user allowed' do
let(:update_permissions) do
project.team << [user, :master]
project.lfs_objects << lfs_object
end
it_behaves_like 'responds with a file'
end
context 'when user not allowed' do
let(:update_permissions) do
project.lfs_objects << lfs_object
end
it 'responds with status 404' do
expect(response).to have_http_status(404)
end
end
end
2016-09-16 05:06:57 -04:00
context 'when build is authorized as' do
let(:authorization) { authorize_ci_project }
2016-09-16 05:06:57 -04:00
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
2016-09-16 05:06:57 -04:00
let(:pipeline) { create(:ci_empty_pipeline, project: project) }
let(:update_permissions) do
project.team << [user, :reporter]
project.lfs_objects << lfs_object
end
it_behaves_like 'responds with a file'
end
context 'for other project' do
let(:other_project) { create(:project) }
2016-09-16 05:06:57 -04:00
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_http_status(other_project_status)
end
end
end
2016-09-16 05:06:57 -04:00
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(:user) { create(:user) }
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 }
2016-09-16 05:06:57 -04:00
end
end
end
end
context 'without required headers' do
let(:authorization) { authorize_user }
2016-07-20 12:41:26 -04:00
it 'responds with status 404' do
expect(response).to have_http_status(404)
end
end
end
end
describe 'when handling lfs batch request' do
let(:update_lfs_permissions) { }
let(:update_user_permissions) { }
before do
enable_lfs
update_lfs_permissions
update_user_permissions
2016-07-22 11:23:35 -04:00
post_lfs_json "#{project.http_url_to_repo}/info/lfs/objects/batch", body, headers
end
describe 'download' do
let(:project) { create(:project) }
let(:body) do
2017-02-22 12:35:20 -05:00
{
'operation' => 'download',
'objects' => [
{ 'oid' => sample_oid,
2017-02-22 12:44:44 -05:00
'size' => sample_size }
2017-02-22 12:35:20 -05:00
]
}
end
shared_examples 'an authorized requests' do
context 'when downloading an lfs object that is assigned to our project' do
let(:update_lfs_permissions) do
project.lfs_objects << lfs_object
end
it 'responds with status 200' do
expect(response).to have_http_status(200)
end
it 'with href to download' do
2017-02-22 12:35:20 -05:00
expect(json_response).to eq({
'objects' => [
2017-02-22 12:44:44 -05:00
{
'oid' => sample_oid,
2017-02-22 12:35:20 -05:00
'size' => sample_size,
'actions' => {
'download' => {
'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}",
'header' => { 'Authorization' => authorization }
}
}
}
2017-02-22 12:35:20 -05:00
]
})
end
end
context 'when downloading an lfs object that is assigned to other project' do
let(:other_project) { create(:project) }
let(:update_lfs_permissions) do
other_project.lfs_objects << lfs_object
end
it 'responds with status 200' do
expect(response).to have_http_status(200)
end
it 'with href to download' do
2017-02-22 12:35:20 -05:00
expect(json_response).to eq({
'objects' => [
2017-02-22 12:44:44 -05:00
{
'oid' => sample_oid,
2017-02-22 12:35:20 -05:00
'size' => sample_size,
'error' => {
'code' => 404,
'message' => "Object does not exist on the server or you don't have permissions to access it"
2017-02-22 12:35:20 -05:00
}
}
2017-02-22 12:35:20 -05:00
]
})
end
end
context 'when downloading a lfs object that does not exist' do
let(:body) do
2017-02-22 12:35:20 -05:00
{
'operation' => 'download',
'objects' => [
{ 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
2017-02-22 12:44:44 -05:00
'size' => 1575078 }
2017-02-22 12:35:20 -05:00
]
}
end
it 'responds with status 200' do
expect(response).to have_http_status(200)
end
it 'with an 404 for specific object' do
2017-02-22 12:35:20 -05:00
expect(json_response).to eq({
'objects' => [
2017-02-22 12:44:44 -05:00
{
'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
2017-02-22 12:35:20 -05:00
'size' => 1575078,
'error' => {
'code' => 404,
'message' => "Object does not exist on the server or you don't have permissions to access it"
2017-02-22 12:35:20 -05:00
}
}
2017-02-22 12:35:20 -05:00
]
})
end
end
context 'when downloading one new and one existing lfs object' do
let(:body) do
2017-02-22 12:44:44 -05:00
{
'operation' => 'download',
'objects' => [
{ 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
2017-02-22 12:44:44 -05:00
'size' => 1575078 },
{ 'oid' => sample_oid,
2017-02-22 12:44:44 -05:00
'size' => sample_size }
]
}
end
let(:update_lfs_permissions) do
project.lfs_objects << lfs_object
end
it 'responds with status 200' do
expect(response).to have_http_status(200)
end
it 'responds with upload hypermedia link for the new object' do
2017-02-22 12:35:20 -05:00
expect(json_response).to eq({
'objects' => [
{
'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
'size' => 1575078,
'error' => {
'code' => 404,
'message' => "Object does not exist on the server or you don't have permissions to access it"
2017-02-22 12:35:20 -05:00
}
},
{
'oid' => sample_oid,
'size' => sample_size,
'actions' => {
'download' => {
'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}",
'header' => { 'Authorization' => authorization }
}
}
}
2017-02-22 12:35:20 -05:00
]
})
end
end
end
context 'when user is authenticated' do
let(:authorization) { authorize_user }
let(:update_user_permissions) do
project.team << [user, role]
end
it_behaves_like 'an authorized requests' do
let(:role) { :reporter }
end
context 'when user does is not member of the project' do
2016-07-20 12:41:26 -04:00
let(:update_user_permissions) { nil }
2016-07-20 12:41:26 -04:00
it 'responds with 404' do
expect(response).to have_http_status(404)
end
end
context 'when user does not have download access' do
let(:role) { :guest }
it 'responds with 403' do
expect(response).to have_http_status(403)
end
end
end
2016-09-16 05:06:57 -04:00
context 'when build is authorized as' do
let(:authorization) { authorize_ci_project }
2016-09-16 05:06:57 -04:00
let(:update_lfs_permissions) do
project.lfs_objects << lfs_object
end
shared_examples 'can download LFS only from own projects' do
context 'for own project' do
let(:pipeline) { create(:ci_empty_pipeline, project: project) }
let(:update_user_permissions) do
project.team << [user, :reporter]
end
it_behaves_like 'an authorized requests'
end
context 'for other project' do
let(:other_project) { create(:project) }
2016-09-16 05:06:57 -04:00
let(:pipeline) { create(:ci_empty_pipeline, project: other_project) }
it 'rejects downloading code' do
expect(response).to have_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
end
context 'regular user' do
let(:user) { create(:user) }
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 }
2016-09-16 05:06:57 -04:00
end
end
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
it 'responds with status 200 and href to download' do
expect(response).to have_http_status(200)
end
it 'responds with status 200 and href to download' do
2017-02-22 12:35:20 -05:00
expect(json_response).to eq({
'objects' => [
2017-02-22 12:44:44 -05:00
{
'oid' => sample_oid,
2017-02-22 12:35:20 -05:00
'size' => sample_size,
'authenticated' => true,
'actions' => {
'download' => {
'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}",
'header' => {}
}
}
}
2017-02-22 12:35:20 -05:00
]
})
end
end
describe 'is accessing non-public project' do
let(:update_lfs_permissions) do
project.lfs_objects << lfs_object
end
it 'responds with authorization required' do
expect(response).to have_http_status(401)
end
end
end
end
describe 'upload' do
let(:project) { create(:project, :public) }
let(:body) do
2017-02-22 12:35:20 -05:00
{
'operation' => 'upload',
'objects' => [
{ 'oid' => sample_oid,
2017-02-22 12:44:44 -05:00
'size' => sample_size }
2017-02-22 12:35:20 -05:00
]
}
end
describe 'when request is authenticated' do
describe 'when user has project push access' do
let(:authorization) { authorize_user }
let(:update_user_permissions) do
project.team << [user, :developer]
end
context 'when pushing an lfs object that already exists' do
let(:other_project) { create(:project) }
let(:update_lfs_permissions) do
other_project.lfs_objects << lfs_object
end
it 'responds with status 200' do
expect(response).to have_http_status(200)
end
it 'responds with links the object to the project' do
expect(json_response['objects']).to be_kind_of(Array)
expect(json_response['objects'].first['oid']).to eq(sample_oid)
expect(json_response['objects'].first['size']).to eq(sample_size)
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("#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}/#{sample_size}")
expect(json_response['objects'].first['actions']['upload']['header']).to eq('Authorization' => authorization)
end
end
context 'when pushing a lfs object that does not exist' do
let(:body) do
2017-02-22 12:35:20 -05:00
{
'operation' => 'upload',
'objects' => [
{ 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
2017-02-22 12:44:44 -05:00
'size' => 1575078 }
2017-02-22 12:35:20 -05:00
]
}
end
it 'responds with status 200' do
expect(response).to have_http_status(200)
end
it 'responds with upload hypermedia link' do
expect(json_response['objects']).to be_kind_of(Array)
expect(json_response['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897")
expect(json_response['objects'].first['size']).to eq(1575078)
expect(json_response['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.full_path}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078")
expect(json_response['objects'].first['actions']['upload']['header']).to eq('Authorization' => authorization)
end
end
context 'when pushing one new and one existing lfs object' do
let(:body) do
2017-02-22 12:44:44 -05:00
{
'operation' => 'upload',
'objects' => [
{ 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
2017-02-22 12:44:44 -05:00
'size' => 1575078 },
{ 'oid' => sample_oid,
2017-02-22 12:44:44 -05:00
'size' => sample_size }
]
}
end
let(:update_lfs_permissions) do
project.lfs_objects << lfs_object
end
it 'responds with status 200' do
expect(response).to have_http_status(200)
end
it 'responds with upload hypermedia link for the new object' do
expect(json_response['objects']).to be_kind_of(Array)
expect(json_response['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897")
expect(json_response['objects'].first['size']).to eq(1575078)
expect(json_response['objects'].first['actions']['upload']['href']).to eq("#{project.http_url_to_repo}/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078")
expect(json_response['objects'].first['actions']['upload']['header']).to eq("Authorization" => authorization)
expect(json_response['objects'].last['oid']).to eq(sample_oid)
expect(json_response['objects'].last['size']).to eq(sample_size)
expect(json_response['objects'].last).not_to have_key('actions')
end
end
end
context 'when user does not have push access' do
let(:authorization) { authorize_user }
it 'responds with 403' do
expect(response).to have_http_status(403)
end
end
2016-09-16 05:06:57 -04:00
context 'when build is authorized' do
let(:authorization) { authorize_ci_project }
2016-09-16 05:06:57 -04:00
context 'build has an user' do
let(:user) { create(:user) }
context 'tries to push to own project' do
let(:build) { create(:ci_build, :running, pipeline: pipeline, user: user) }
it 'responds with 403 (not 404 because project is public)' do
expect(response).to have_http_status(403)
2016-09-16 05:06:57 -04:00
end
end
context 'tries to push to other project' do
let(:other_project) { create(:project) }
2016-09-16 05:06:57 -04:00
let(:pipeline) { create(:ci_empty_pipeline, project: other_project) }
let(:build) { create(:ci_build, :running, pipeline: pipeline, user: user) }
# I'm not sure what this tests that is different from the previous test
it 'responds with 403 (not 404 because project is public)' do
expect(response).to have_http_status(403)
2016-09-16 05:06:57 -04:00
end
end
end
context 'does not have user' do
let(:build) { create(:ci_build, :running, pipeline: pipeline) }
it 'responds with 403 (not 404 because project is public)' do
expect(response).to have_http_status(403)
2016-09-16 05:06:57 -04:00
end
end
end
end
context 'when user is not authenticated' do
context 'when user has push access' do
let(:update_user_permissions) do
project.team << [user, :master]
end
it 'responds with status 401' do
expect(response).to have_http_status(401)
end
end
context 'when user does not have push access' do
it 'responds with status 401' do
expect(response).to have_http_status(401)
end
end
end
end
describe 'unsupported' do
let(:project) { create(:project) }
2016-07-20 12:41:26 -04:00
let(:authorization) { authorize_user }
let(:body) do
2017-02-22 12:35:20 -05:00
{
'operation' => 'other',
'objects' => [
{ 'oid' => sample_oid,
2017-02-22 12:44:44 -05:00
'size' => sample_size }
2017-02-22 12:35:20 -05:00
]
}
end
it 'responds with status 404' do
expect(response).to have_http_status(404)
end
end
end
describe 'when pushing a lfs object' do
before do
enable_lfs
end
shared_examples 'unauthorized' do
context 'and request is sent by gitlab-workhorse to authorize the request' do
before do
put_authorize
end
it 'responds with status 401' do
expect(response).to have_http_status(401)
end
end
context 'and request is sent by gitlab-workhorse to finalize the upload' do
before do
put_finalize
end
it 'responds with status 401' do
expect(response).to have_http_status(401)
end
end
context 'and request is sent with a malformed headers' do
before do
put_finalize('/etc/passwd')
end
it 'does not recognize it as a valid lfs command' do
2016-07-20 12:41:26 -04:00
expect(response).to have_http_status(401)
end
end
end
shared_examples 'forbidden' do
context 'and request is sent by gitlab-workhorse to authorize the request' do
before do
put_authorize
end
it 'responds with 403' do
expect(response).to have_http_status(403)
end
end
context 'and request is sent by gitlab-workhorse to finalize the upload' do
before do
put_finalize
end
it 'responds with 403' do
expect(response).to have_http_status(403)
end
end
2016-07-20 12:41:26 -04:00
context 'and request is sent with a malformed headers' do
before do
put_finalize('/etc/passwd')
2016-07-20 12:41:26 -04:00
end
it 'does not recognize it as a valid lfs command' do
expect(response).to have_http_status(403)
end
end
end
describe 'to one project' do
let(:project) { create(:project) }
describe 'when user is authenticated' do
let(:authorization) { authorize_user }
describe 'when user has push access to the project' do
before do
project.team << [user, :developer]
end
context 'and the request bypassed workhorse' do
it 'raises an exception' do
expect { put_authorize(verified: false) }.to raise_error JWT::DecodeError
end
end
context 'and request is sent by gitlab-workhorse to authorize the request' do
before do
put_authorize
end
it 'responds with status 200' do
expect(response).to have_http_status(200)
end
it 'uses the gitlab-workhorse content type' do
expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
end
it 'responds with status 200, location of lfs store and object details' do
expect(json_response['StoreLFSPath']).to eq("#{Gitlab.config.shared.path}/lfs-objects/tmp/upload")
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
it 'responds with status 200' do
expect(response).to have_http_status(200)
end
it 'lfs object is linked to the project' do
expect(lfs_object.projects.pluck(:id)).to include(project.id)
end
2016-08-11 09:05:49 -04:00
end
context 'invalid tempfiles' do
it 'rejects slashes in the tempfile name (path traversal' do
put_finalize('foo/bar')
expect(response).to have_http_status(403)
end
it 'rejects tempfile names that do not start with the oid' do
put_finalize("foo#{sample_oid}")
expect(response).to have_http_status(403)
end
end
end
describe 'and user does not have push access' do
2016-07-20 12:41:26 -04:00
before do
project.team << [user, :reporter]
end
it_behaves_like 'forbidden'
end
end
2016-09-16 05:06:57 -04:00
context 'when build is authorized' do
let(:authorization) { authorize_ci_project }
2016-09-16 05:06:57 -04:00
context 'build has an user' do
let(:user) { create(:user) }
context 'tries to push to own project' do
let(:build) { create(:ci_build, :running, pipeline: pipeline, user: user) }
before do
project.team << [user, :developer]
put_authorize
end
it 'responds with 403 (not 404 because the build user can read the project)' do
expect(response).to have_http_status(403)
2016-09-16 05:06:57 -04:00
end
end
context 'tries to push to other project' do
let(:other_project) { create(:project) }
2016-09-16 05:06:57 -04:00
let(:pipeline) { create(:ci_empty_pipeline, project: other_project) }
let(:build) { create(:ci_build, :running, pipeline: pipeline, user: user) }
before do
put_authorize
end
it 'responds with 404 (do not leak non-public project existence)' do
expect(response).to have_http_status(404)
2016-09-16 05:06:57 -04:00
end
end
end
context 'does not have user' do
let(:build) { create(:ci_build, :running, pipeline: pipeline) }
before do
put_authorize
end
it 'responds with 404 (do not leak non-public project existence)' do
expect(response).to have_http_status(404)
2016-09-16 05:06:57 -04:00
end
end
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
project.team << [user, :developer]
end
context 'and request is sent by gitlab-workhorse to authorize the request' do
before do
put_authorize
end
it 'responds with status 200' do
expect(response).to have_http_status(200)
end
it 'with location of lfs store and object details' do
expect(json_response['StoreLFSPath']).to eq("#{Gitlab.config.shared.path}/lfs-objects/tmp/upload")
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
it 'responds with status 200' do
expect(response).to have_http_status(200)
end
it 'lfs object is linked to the source project' do
expect(lfs_object.projects.pluck(:id)).to include(upstream_project.id)
end
end
end
describe 'and user does not have push access' do
it_behaves_like 'forbidden'
end
end
2016-09-16 05:06:57 -04:00
context 'when build is authorized' do
let(:authorization) { authorize_ci_project }
2016-09-16 05:06:57 -04:00
before do
put_authorize
end
context 'build has an user' do
let(:user) { create(:user) }
context 'tries to push to own project' do
let(:build) { create(:ci_build, :running, pipeline: pipeline, user: user) }
it 'responds with 403 (not 404 because project is public)' do
expect(response).to have_http_status(403)
2016-09-16 05:06:57 -04:00
end
end
context 'tries to push to other project' do
let(:other_project) { create(:project) }
2016-09-16 05:06:57 -04:00
let(:pipeline) { create(:ci_empty_pipeline, project: other_project) }
let(:build) { create(:ci_build, :running, pipeline: pipeline, user: user) }
# I'm not sure what this tests that is different from the previous test
it 'responds with 403 (not 404 because project is public)' do
expect(response).to have_http_status(403)
2016-09-16 05:06:57 -04:00
end
end
end
context 'does not have user' do
let(:build) { create(:ci_build, :running, pipeline: pipeline) }
it 'responds with 403 (not 404 because project is public)' do
expect(response).to have_http_status(403)
2016-09-16 05:06:57 -04:00
end
end
end
context 'for unauthenticated' do
it_behaves_like 'unauthorized'
end
describe 'and second project not related to fork or a source project' do
let(:second_project) { create(:project) }
let(:authorization) { authorize_user }
before do
second_project.team << [user, :master]
upstream_project.lfs_objects << lfs_object
end
context 'when pushing the same lfs object to the second project' do
before do
put "#{second_project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}/#{sample_size}", nil,
headers.merge('X-Gitlab-Lfs-Tmp' => lfs_tmp_file).compact
end
it 'responds with status 200' do
expect(response).to have_http_status(200)
end
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
end
end
def put_authorize(verified: true)
authorize_headers = headers
authorize_headers.merge!(workhorse_internal_api_request_header) if verified
put "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}/#{sample_size}/authorize", nil, authorize_headers
end
def put_finalize(lfs_tmp = lfs_tmp_file)
put "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}/#{sample_size}", nil,
headers.merge('X-Gitlab-Lfs-Tmp' => lfs_tmp).compact
end
def lfs_tmp_file
"#{sample_oid}012345678"
end
end
def enable_lfs
allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
end
def authorize_ci_project
2016-09-15 09:40:53 -04:00
ActionController::HttpAuthentication::Basic.encode_credentials('gitlab-ci-token', build.token)
end
def authorize_user
ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password)
end
def authorize_deploy_key
ActionController::HttpAuthentication::Basic.encode_credentials("lfs+deploy-key-#{key.id}", Gitlab::LfsToken.new(key).token)
end
def authorize_user_key
ActionController::HttpAuthentication::Basic.encode_credentials(user.username, Gitlab::LfsToken.new(user).token)
end
def fork_project(project, user, object = nil)
allow(RepositoryForkWorker).to receive(:perform_async).and_return(true)
Projects::ForkService.new(project, user, {}).execute
end
2016-07-22 11:23:35 -04:00
def post_lfs_json(url, body = nil, headers = nil)
post(url, body.try(:to_json), (headers || {}).merge('Content-Type' => 'application/vnd.git-lfs+json'))
end
def json_response
@json_response ||= JSON.parse(response.body)
end
end