Commit 86f39fec authored by Kamil Trzcinski's avatar Kamil Trzcinski

Refactor LFS specs to use requests instead of LfsRouter

parent 7303de91
require 'spec_helper' require 'spec_helper'
describe Gitlab::Lfs::Router, lib: true do describe Gitlab::Lfs::Router do
let(:project) { create(:project) }
let(:public_project) { create(:project, :public) }
let(:forked_project) { fork_project(public_project, user) }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:user_two) { create(:user) }
let!(:lfs_object) { create(:lfs_object, :with_file) } let!(:lfs_object) { create(:lfs_object, :with_file) }
let(:request) { Rack::Request.new(env) } let(:headers) do
let(:env) do
{ {
'rack.input' => '', 'Authorization' => authorization,
'REQUEST_METHOD' => 'GET', 'X-Sendfile-Type' => sendfile
} }.compact
end end
let(:authorization) { }
let(:sendfile) { }
let(:lfs_router_auth) { new_lfs_router(project, user: user) } let(:sample_oid) { lfs_object.oid }
let(:lfs_router_ci_auth) { new_lfs_router(project, ci: true) } let(:sample_size) { lfs_object.size }
let(:lfs_router_noauth) { new_lfs_router(project) }
let(:lfs_router_public_auth) { new_lfs_router(public_project, user: user) }
let(:lfs_router_public_ci_auth) { new_lfs_router(public_project, ci: true) }
let(:lfs_router_public_noauth) { new_lfs_router(public_project) }
let(:lfs_router_forked_noauth) { new_lfs_router(forked_project) }
let(:lfs_router_forked_auth) { new_lfs_router(forked_project, user: user_two) }
let(:lfs_router_forked_ci_auth) { new_lfs_router(forked_project, ci: true) }
let(:sample_oid) { "b68143e6463773b1b6c6fd009a76c32aeec041faff32ba2ed42fd7f708a17f80" }
let(:sample_size) { 499013 }
let(:respond_with_deprecated) {[ 501, { "Content-Type" => "application/json; charset=utf-8" }, ["{\"message\":\"Server supports batch API only, please update your Git LFS client to version 1.0.1 and up.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"]]}
let(:respond_with_disabled) {[ 501, { "Content-Type" => "application/json; charset=utf-8" }, ["{\"message\":\"Git LFS is not enabled on this GitLab server, contact your admin.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"]]}
describe 'when lfs is disabled' do describe 'when lfs is disabled' do
before do let(:project) { create(:empty_project) }
allow(Gitlab.config.lfs).to receive(:enabled).and_return(false) let(:body) do
env['REQUEST_METHOD'] = 'POST' {
body = {
'objects' => [ 'objects' => [
{ 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
'size' => 1575078 'size' => 1575078
...@@ -46,136 +29,166 @@ describe Gitlab::Lfs::Router, lib: true do ...@@ -46,136 +29,166 @@ describe Gitlab::Lfs::Router, lib: true do
} }
], ],
'operation' => 'upload' 'operation' => 'upload'
}.to_json }
env['rack.input'] = StringIO.new(body) end
env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/batch"
before do
allow(Gitlab.config.lfs).to receive(:enabled).and_return(false)
post_json "#{project.http_url_to_repo}/info/lfs/objects/batch", body, headers
end end
it 'responds with 501' do it 'responds with 501' do
expect(lfs_router_auth.try_call).to match_array(respond_with_disabled) 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
end end
describe 'when fetching lfs object using deprecated API' do describe 'deprecated API' do
let(:project) { create(:empty_project) }
before do before do
enable_lfs enable_lfs
env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/#{sample_oid}"
end end
shared_examples 'a deprecated' do
it 'responds with 501' do it 'responds with 501' do
expect(lfs_router_auth.try_call).to match_array(respond_with_deprecated) 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
end end
describe 'when fetching lfs object' do context 'when fetching lfs object using deprecated API' do
let(:authorization) { authorize_user }
before do before do
enable_lfs get "#{project.http_url_to_repo}/info/lfs/objects/#{sample_oid}", nil, headers
env['HTTP_ACCEPT'] = "application/vnd.git-lfs+json; charset=utf-8"
env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}"
end end
describe 'and request comes from gitlab-workhorse' do it_behaves_like 'a deprecated'
context 'without user being authorized' do
it "responds with status 401" do
expect(lfs_router_noauth.try_call.first).to eq(401)
end
end end
context 'with required headers' do context 'when handling lfs request using deprecated API' do
before do before do
project.lfs_objects << lfs_object post_json "#{project.http_url_to_repo}/info/lfs/objects", nil, headers
env['HTTP_X_SENDFILE_TYPE'] = "X-Sendfile"
end end
context 'when user does not have project access' do it_behaves_like 'a deprecated'
it "responds with status 403" do
expect(lfs_router_auth.try_call.first).to eq(403)
end end
end end
context 'when user has project access' do describe 'when fetching lfs object' do
let(:project) { create(:empty_project) }
let(:update_permissions) { }
before do before do
project.team << [user, :master] enable_lfs
update_permissions
get "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", nil, headers
end end
it "responds with status 200" do context 'and request comes from gitlab-workhorse' do
expect(lfs_router_auth.try_call.first).to eq(200) 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
it 'responds with status 200' do
expect(response).to have_http_status(200)
end end
it "responds with the file location" do it 'responds with the file location' do
expect(lfs_router_auth.try_call[1]['Content-Type']).to eq("application/octet-stream") expect(response.headers['Content-Type']).to eq('application/octet-stream')
expect(lfs_router_auth.try_call[1]['X-Sendfile']).to eq(lfs_object.file.path) expect(response.headers['X-Sendfile']).to eq(lfs_object.file.path)
end end
end end
context 'when CI is authorized' do context 'with user is authorized' do
it "responds with status 200" do let(:authorization) { authorize_user }
expect(lfs_router_ci_auth.try_call.first).to eq(200) let(:sendfile) { 'X-Sendfile' }
context 'and does not have project access' do
let(:update_permissions) do
project.lfs_objects << lfs_object
end end
it "responds with the file location" do it 'responds with status 403' do
expect(lfs_router_ci_auth.try_call[1]['Content-Type']).to eq("application/octet-stream") expect(response).to have_http_status(403)
expect(lfs_router_ci_auth.try_call[1]['X-Sendfile']).to eq(lfs_object.file.path)
end end
end end
context 'and does have project access' do
let(:update_permissions) do
project.team << [user, :master]
project.lfs_objects << lfs_object
end end
context 'without required headers' do it_behaves_like 'responds with a file'
it "responds with status 403" do
expect(lfs_router_auth.try_call.first).to eq(403)
end end
end end
context 'when CI is authorized' do
let(:authorization) { authorize_ci_project }
it_behaves_like 'responds with a file'
end end
end end
describe 'when handling lfs request using deprecated API' do context 'without required headers' do
before do let(:authorization) { authorize_user }
enable_lfs
env['REQUEST_METHOD'] = 'POST'
env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects"
end
it 'responds with 501' do it 'responds with status 403' do
expect(lfs_router_auth.try_call).to match_array(respond_with_deprecated) expect(response).to have_http_status(403)
end
end
end end
end end
describe 'when handling lfs batch request' do describe 'when handling lfs batch request' do
let(:update_lfs_permissions) { }
let(:update_user_permissions) { }
before do before do
enable_lfs enable_lfs
env['REQUEST_METHOD'] = 'POST' update_lfs_permissions
env['PATH_INFO'] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/batch" update_user_permissions
post_json "#{project.http_url_to_repo}/info/lfs/objects/batch", body, headers
end end
describe 'download' do describe 'download' do
before do let(:project) { create(:empty_project) }
body = { 'operation' => 'download', let(:body) do
{ 'operation' => 'download',
'objects' => [ 'objects' => [
{ 'oid' => sample_oid, { 'oid' => sample_oid,
'size' => sample_size 'size' => sample_size
}] }]
}.to_json }
env['rack.input'] = StringIO.new(body)
end end
shared_examples 'an authorized requests' do shared_examples 'an authorized requests' do
context 'when downloading an lfs object that is assigned to our project' do context 'when downloading an lfs object that is assigned to our project' do
before do let(:update_lfs_permissions) do
project.lfs_objects << lfs_object project.lfs_objects << lfs_object
end end
it 'responds with status 200 and href to download' do it 'responds with status 200' do
response = router.try_call expect(response).to have_http_status(200)
expect(response.first).to eq(200) end
response_body = ActiveSupport::JSON.decode(response.last.first)
expect(response_body).to eq('objects' => [ it 'with href to download' do
expect(json_response).to eq('objects' => [
{ 'oid' => sample_oid, { 'oid' => sample_oid,
'size' => sample_size, 'size' => sample_size,
'actions' => { 'actions' => {
'download' => { 'download' => {
'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}",
'header' => { 'Authorization' => auth } 'header' => { 'Authorization' => authorization }
} }
} }
}]) }])
...@@ -183,16 +196,17 @@ describe Gitlab::Lfs::Router, lib: true do ...@@ -183,16 +196,17 @@ describe Gitlab::Lfs::Router, lib: true do
end end
context 'when downloading an lfs object that is assigned to other project' do context 'when downloading an lfs object that is assigned to other project' do
before do let(:other_project) { create(:empty_project) }
public_project.lfs_objects << lfs_object let(:update_lfs_permissions) do
other_project.lfs_objects << lfs_object
end end
it 'responds with status 200 and error message' do it 'responds with status 200' do
response = router.try_call expect(response).to have_http_status(200)
expect(response.first).to eq(200) end
response_body = ActiveSupport::JSON.decode(response.last.first)
expect(response_body).to eq('objects' => [ it 'with href to download' do
expect(json_response).to eq('objects' => [
{ 'oid' => sample_oid, { 'oid' => sample_oid,
'size' => sample_size, 'size' => sample_size,
'error' => { 'error' => {
...@@ -204,22 +218,21 @@ describe Gitlab::Lfs::Router, lib: true do ...@@ -204,22 +218,21 @@ describe Gitlab::Lfs::Router, lib: true do
end end
context 'when downloading a lfs object that does not exist' do context 'when downloading a lfs object that does not exist' do
before do let(:body) do
body = { 'operation' => 'download', { 'operation' => 'download',
'objects' => [ 'objects' => [
{ 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
'size' => 1575078 'size' => 1575078
}] }]
}.to_json }
env['rack.input'] = StringIO.new(body)
end end
it "responds with status 200 and error message" do it 'responds with status 200' do
response = router.try_call expect(response).to have_http_status(200)
expect(response.first).to eq(200) end
response_body = ActiveSupport::JSON.decode(response.last.first)
expect(response_body).to eq('objects' => [ it 'with an 404 for specific object' do
expect(json_response).to eq('objects' => [
{ 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
'size' => 1575078, 'size' => 1575078,
'error' => { 'error' => {
...@@ -231,8 +244,8 @@ describe Gitlab::Lfs::Router, lib: true do ...@@ -231,8 +244,8 @@ describe Gitlab::Lfs::Router, lib: true do
end end
context 'when downloading one new and one existing lfs object' do context 'when downloading one new and one existing lfs object' do
before do let(:body) do
body = { 'operation' => 'download', { 'operation' => 'download',
'objects' => [ 'objects' => [
{ 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
'size' => 1575078 'size' => 1575078
...@@ -241,17 +254,19 @@ describe Gitlab::Lfs::Router, lib: true do ...@@ -241,17 +254,19 @@ describe Gitlab::Lfs::Router, lib: true do
'size' => sample_size 'size' => sample_size
} }
] ]
}.to_json }
env['rack.input'] = StringIO.new(body) end
let(:update_lfs_permissions) do
project.lfs_objects << lfs_object project.lfs_objects << lfs_object
end end
it "responds with status 200 with upload hypermedia link for the new object" do it 'responds with status 200' do
response = router.try_call expect(response).to have_http_status(200)
expect(response.first).to eq(200) end
response_body = ActiveSupport::JSON.decode(response.last.first)
expect(response_body).to eq('objects' => [ it 'responds with upload hypermedia link for the new object' do
expect(json_response).to eq('objects' => [
{ 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
'size' => 1575078, 'size' => 1575078,
'error' => { 'error' => {
...@@ -264,7 +279,7 @@ describe Gitlab::Lfs::Router, lib: true do ...@@ -264,7 +279,7 @@ describe Gitlab::Lfs::Router, lib: true do
'actions' => { 'actions' => {
'download' => { 'download' => {
'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}",
'header' => { 'Authorization' => auth } 'header' => { 'Authorization' => authorization }
} }
} }
}]) }])
...@@ -273,23 +288,21 @@ describe Gitlab::Lfs::Router, lib: true do ...@@ -273,23 +288,21 @@ describe Gitlab::Lfs::Router, lib: true do
end end
context 'when user is authenticated' do context 'when user is authenticated' do
let(:auth) { authorize(user) } let(:authorization) { authorize_user }
before do let(:update_user_permissions) do
env["HTTP_AUTHORIZATION"] = auth
project.team << [user, role] project.team << [user, role]
end end
it_behaves_like 'an authorized requests' do it_behaves_like 'an authorized requests' do
let(:role) { :reporter } let(:role) { :reporter }
let(:router) { lfs_router_auth }
end end
context 'when user does is not member of the project' do context 'when user does is not member of the project' do
let(:role) { :guest } let(:role) { :guest }
it 'responds with 403' do it 'responds with 403' do
expect(lfs_router_auth.try_call.first).to eq(403) expect(response).to have_http_status(403)
end end
end end
...@@ -297,40 +310,36 @@ describe Gitlab::Lfs::Router, lib: true do ...@@ -297,40 +310,36 @@ describe Gitlab::Lfs::Router, lib: true do
let(:role) { :guest } let(:role) { :guest }
it 'responds with 403' do it 'responds with 403' do
expect(lfs_router_auth.try_call.first).to eq(403) expect(response).to have_http_status(403)
end end
end end
end end
context 'when CI is authorized' do context 'when CI is authorized' do
let(:auth) { 'gitlab-ci-token:password' } let(:authorization) { authorize_ci_project }
before do it_behaves_like 'an authorized requests'
env["HTTP_AUTHORIZATION"] = auth
end
it_behaves_like 'an authorized requests' do
let(:router) { lfs_router_ci_auth }
end
end end
context 'when user is not authenticated' do context 'when user is not authenticated' do
describe 'is accessing public project' do describe 'is accessing public project' do
before do let(:project) { create(:project, :public) }
public_project.lfs_objects << lfs_object
let(:update_lfs_permissions) do
project.lfs_objects << lfs_object
end end
it 'responds with status 200 and href to download' do it 'responds with status 200 and href to download' do
response = lfs_router_public_noauth.try_call expect(response).to have_http_status(200)
expect(response.first).to eq(200) end
response_body = ActiveSupport::JSON.decode(response.last.first)
expect(response_body).to eq('objects' => [ it 'responds with status 200 and href to download' do
expect(json_response).to eq('objects' => [
{ 'oid' => sample_oid, { 'oid' => sample_oid,
'size' => sample_size, 'size' => sample_size,
'actions' => { 'actions' => {
'download' => { 'download' => {
'href' => "#{public_project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}",
'header' => {} 'header' => {}
} }
} }
...@@ -339,83 +348,83 @@ describe Gitlab::Lfs::Router, lib: true do ...@@ -339,83 +348,83 @@ describe Gitlab::Lfs::Router, lib: true do
end end
describe 'is accessing non-public project' do describe 'is accessing non-public project' do
before do let(:update_lfs_permissions) do
project.lfs_objects << lfs_object project.lfs_objects << lfs_object
end end
it 'responds with authorization required' do it 'responds with authorization required' do
expect(lfs_router_noauth.try_call.first).to eq(401) expect(response).to have_http_status(401)
end end
end end
end end
end end
describe 'upload' do describe 'upload' do
before do let(:project) { create(:project, :public) }
body = { 'operation' => 'upload', let(:body) do
{ 'operation' => 'upload',
'objects' => [ 'objects' => [
{ 'oid' => sample_oid, { 'oid' => sample_oid,
'size' => sample_size 'size' => sample_size
}] }]
}.to_json }
env['rack.input'] = StringIO.new(body)
end end
describe 'when request is authenticated' do describe 'when request is authenticated' do
describe 'when user has project push access' do describe 'when user has project push access' do
before do let(:authorization) { authorize_user }
@auth = authorize(user)
env["HTTP_AUTHORIZATION"] = @auth let(:update_user_permissions) do
project.team << [user, :developer] project.team << [user, :developer]
end end
context 'when pushing an lfs object that already exists' do context 'when pushing an lfs object that already exists' do
before do let(:other_project) { create(:empty_project) }
public_project.lfs_objects << lfs_object let(:update_lfs_permissions) do
other_project.lfs_objects << lfs_object
end end
it "responds with status 200 and links the object to the project" do it 'responds with status 200' do
response_body = lfs_router_auth.try_call.last expect(response).to have_http_status(200)
response = ActiveSupport::JSON.decode(response_body.first) end
expect(response['objects']).to be_kind_of(Array) it 'responds with links the object to the project' do
expect(response['objects'].first['oid']).to eq(sample_oid) expect(json_response['objects']).to be_kind_of(Array)
expect(response['objects'].first['size']).to eq(sample_size) 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)).not_to include(project.id)
expect(lfs_object.projects.pluck(:id)).to include(public_project.id) expect(lfs_object.projects.pluck(:id)).to include(other_project.id)
expect(response['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}") expect(json_response['objects'].first['actions']['upload']['href']).to eq("#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}/#{sample_size}")
expect(response['objects'].first['actions']['upload']['header']).to eq('Authorization' => @auth) expect(json_response['objects'].first['actions']['upload']['header']).to eq('Authorization' => authorization)
end end
end end
context 'when pushing a lfs object that does not exist' do context 'when pushing a lfs object that does not exist' do
before do let(:body) do
body = { 'operation' => 'upload', { 'operation' => 'upload',
'objects' => [ 'objects' => [
{ 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
'size' => 1575078 'size' => 1575078
}] }]
}.to_json }
env['rack.input'] = StringIO.new(body)
end end
it "responds with status 200 and upload hypermedia link" do it 'responds with status 200' do
response = lfs_router_auth.try_call expect(response).to have_http_status(200)
expect(response.first).to eq(200) end
response_body = ActiveSupport::JSON.decode(response.last.first) it 'responds with upload hypermedia link' do
expect(response_body['objects']).to be_kind_of(Array) expect(json_response['objects']).to be_kind_of(Array)
expect(response_body['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897") expect(json_response['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897")
expect(response_body['objects'].first['size']).to eq(1575078) expect(json_response['objects'].first['size']).to eq(1575078)
expect(lfs_object.projects.pluck(:id)).not_to include(project.id) expect(json_response['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078")
expect(response_body['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078") expect(json_response['objects'].first['actions']['upload']['header']).to eq('Authorization' => authorization)
expect(response_body['objects'].first['actions']['upload']['header']).to eq('Authorization' => @auth)
end end
end end
context 'when pushing one new and one existing lfs object' do context 'when pushing one new and one existing lfs object' do
before do let(:body) do
body = { 'operation' => 'upload', { 'operation' => 'upload',
'objects' => [ 'objects' => [
{ 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
'size' => 1575078 'size' => 1575078
...@@ -424,87 +433,89 @@ describe Gitlab::Lfs::Router, lib: true do ...@@ -424,87 +433,89 @@ describe Gitlab::Lfs::Router, lib: true do
'size' => sample_size 'size' => sample_size
} }
] ]
}.to_json }
env['rack.input'] = StringIO.new(body) end
let(:update_lfs_permissions) do
project.lfs_objects << lfs_object project.lfs_objects << lfs_object
end end
it "responds with status 200 with upload hypermedia link for the new object" do it 'responds with status 200' do
response = lfs_router_auth.try_call expect(response).to have_http_status(200)
expect(response.first).to eq(200) end
response_body = ActiveSupport::JSON.decode(response.last.first) it 'responds with upload hypermedia link for the new object' do
expect(response_body['objects']).to be_kind_of(Array) expect(json_response['objects']).to be_kind_of(Array)
expect(response_body['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897") expect(json_response['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897")
expect(response_body['objects'].first['size']).to eq(1575078) expect(json_response['objects'].first['size']).to eq(1575078)
expect(response_body['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078") expect(json_response['objects'].first['actions']['upload']['href']).to eq("#{project.http_url_to_repo}/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078")
expect(response_body['objects'].first['actions']['upload']['header']).to eq("Authorization" => @auth) expect(json_response['objects'].first['actions']['upload']['header']).to eq("Authorization" => authorization)
expect(response_body['objects'].last['oid']).to eq(sample_oid) expect(json_response['objects'].last['oid']).to eq(sample_oid)
expect(response_body['objects'].last['size']).to eq(sample_size) expect(json_response['objects'].last['size']).to eq(sample_size)
expect(response_body['objects'].last).not_to have_key('actions') expect(json_response['objects'].last).not_to have_key('actions')
end end
end end
end end
context 'when user does not have push access' do context 'when user does not have push access' do
let(:authorization) { authorize_user }
it 'responds with 403' do it 'responds with 403' do
expect(lfs_router_auth.try_call.first).to eq(403) expect(response).to have_http_status(403)
end end
end end
context 'when CI is authorized' do context 'when CI is authorized' do
let(:authorization) { authorize_ci_project }
it 'responds with 401' do it 'responds with 401' do
expect(lfs_router_ci_auth.try_call.first).to eq(401) expect(response).to have_http_status(401)
end end
end end
end end
context 'when user is not authenticated' do context 'when user is not authenticated' do
context 'when user has push access' do context 'when user has push access' do
before do let(:update_user_permissions) do
project.team << [user, :master] project.team << [user, :master]
end end
it "responds with status 401" do it 'responds with status 401' do
expect(lfs_router_public_noauth.try_call.first).to eq(401) expect(response).to have_http_status(401)
end end
end end
context 'when user does not have push access' do context 'when user does not have push access' do
it "responds with status 401" do it 'responds with status 401' do
expect(lfs_router_public_noauth.try_call.first).to eq(401) expect(response).to have_http_status(401)
end end
end end
end end
context 'when CI is authorized' do context 'when CI is authorized' do
let(:auth) { 'gitlab-ci-token:password' } let(:authorization) { authorize_ci_project }
before do
env["HTTP_AUTHORIZATION"] = auth
end
it "responds with status 403" do it 'responds with status 403' do
expect(lfs_router_public_ci_auth.try_call.first).to eq(401) expect(response).to have_http_status(401)
end end
end end
end end
describe 'unsupported' do describe 'unsupported' do
before do let(:project) { create(:empty_project) }
body = { 'operation' => 'other', let(:body) do
{ 'operation' => 'other',
'objects' => [ 'objects' => [
{ 'oid' => sample_oid, { 'oid' => sample_oid,
'size' => sample_size 'size' => sample_size
}] }]
}.to_json }
env['rack.input'] = StringIO.new(body)
end end
it 'responds with status 404' do it 'responds with status 404' do
expect(lfs_router_public_noauth.try_call.first).to eq(404) expect(response).to have_http_status(404)
end end
end end
end end
...@@ -512,38 +523,36 @@ describe Gitlab::Lfs::Router, lib: true do ...@@ -512,38 +523,36 @@ describe Gitlab::Lfs::Router, lib: true do
describe 'when pushing a lfs object' do describe 'when pushing a lfs object' do
before do before do
enable_lfs enable_lfs
env['REQUEST_METHOD'] = 'PUT'
end end
shared_examples 'unauthorized' do shared_examples 'unauthorized' do
context 'and request is sent by gitlab-workhorse to authorize the request' do context 'and request is sent by gitlab-workhorse to authorize the request' do
before do before do
header_for_upload_authorize(router.project) put_authorize
end end
it 'responds with status 401' do it 'responds with status 401' do
expect(router.try_call.first).to eq(401) expect(response).to have_http_status(401)
end end
end end
context 'and request is sent by gitlab-workhorse to finalize the upload' do context 'and request is sent by gitlab-workhorse to finalize the upload' do
before do before do
headers_for_upload_finalize(router.project) put_finalize
end end
it 'responds with status 401' do it 'responds with status 401' do
expect(router.try_call.first).to eq(401) expect(response).to have_http_status(401)
end end
end end
context 'and request is sent with a malformed headers' do context 'and request is sent with a malformed headers' do
before do before do
env["PATH_INFO"] = "#{router.project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}" put_finalize('cat /etc/passwd')
env["HTTP_X_GITLAB_LFS_TMP"] = "cat /etc/passwd"
end end
it 'does not recognize it as a valid lfs command' do it 'does not recognize it as a valid lfs command' do
expect(router.try_call).to eq(nil) expect(response).to have_http_status(401)
end end
end end
end end
...@@ -551,27 +560,31 @@ describe Gitlab::Lfs::Router, lib: true do ...@@ -551,27 +560,31 @@ describe Gitlab::Lfs::Router, lib: true do
shared_examples 'forbidden' do shared_examples 'forbidden' do
context 'and request is sent by gitlab-workhorse to authorize the request' do context 'and request is sent by gitlab-workhorse to authorize the request' do
before do before do
header_for_upload_authorize(router.project) put_authorize
end end
it 'responds with 403' do it 'responds with 403' do
expect(router.try_call.first).to eq(403) expect(response).to have_http_status(403)
end end
end end
context 'and request is sent by gitlab-workhorse to finalize the upload' do context 'and request is sent by gitlab-workhorse to finalize the upload' do
before do before do
headers_for_upload_finalize(router.project) put_finalize
end end
it 'responds with 403' do it 'responds with 403' do
expect(router.try_call.first).to eq(403) expect(response).to have_http_status(403)
end end
end end
end end
describe 'to one project' do describe 'to one project' do
let(:project) { create(:empty_project) }
describe 'when user is authenticated' do describe 'when user is authenticated' do
let(:authorization) { authorize_user }
describe 'when user has push access to the project' do describe 'when user has push access to the project' do
before do before do
project.team << [user, :developer] project.team << [user, :developer]
...@@ -579,13 +592,14 @@ describe Gitlab::Lfs::Router, lib: true do ...@@ -579,13 +592,14 @@ describe Gitlab::Lfs::Router, lib: true do
context 'and request is sent by gitlab-workhorse to authorize the request' do context 'and request is sent by gitlab-workhorse to authorize the request' do
before do before do
header_for_upload_authorize(project) put_authorize
end end
it 'responds with status 200, location of lfs store and object details' do it 'responds with status 200' do
json_response = ActiveSupport::JSON.decode(lfs_router_auth.try_call.last.first) expect(response).to have_http_status(200)
end
expect(lfs_router_auth.try_call.first).to eq(200) 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['StoreLFSPath']).to eq("#{Gitlab.config.shared.path}/lfs-objects/tmp/upload")
expect(json_response['LfsOid']).to eq(sample_oid) expect(json_response['LfsOid']).to eq(sample_oid)
expect(json_response['LfsSize']).to eq(sample_size) expect(json_response['LfsSize']).to eq(sample_size)
...@@ -594,54 +608,58 @@ describe Gitlab::Lfs::Router, lib: true do ...@@ -594,54 +608,58 @@ describe Gitlab::Lfs::Router, lib: true do
context 'and request is sent by gitlab-workhorse to finalize the upload' do context 'and request is sent by gitlab-workhorse to finalize the upload' do
before do before do
headers_for_upload_finalize(project) put_finalize
end end
it 'responds with status 200 and lfs object is linked to the project' do it 'responds with status 200' do
expect(lfs_router_auth.try_call.first).to eq(200) 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) expect(lfs_object.projects.pluck(:id)).to include(project.id)
end end
end end
end end
describe 'and user does not have push access' do describe 'and user does not have push access' do
let(:router) { lfs_router_auth }
it_behaves_like 'forbidden' it_behaves_like 'forbidden'
end end
end end
context 'when CI is authenticated' do context 'when CI is authenticated' do
let(:router) { lfs_router_ci_auth } let(:authorization) { authorize_ci_project }
it_behaves_like 'unauthorized' it_behaves_like 'unauthorized'
end end
context 'for unauthenticated' do context 'for unauthenticated' do
let(:router) { new_lfs_router(project) }
it_behaves_like 'unauthorized' it_behaves_like 'unauthorized'
end end
end end
describe 'to a forked project' do describe 'to a forked project' do
let(:forked_project) { fork_project(public_project, user) } 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 describe 'when user is authenticated' do
let(:authorization) { authorize_user }
describe 'when user has push access to the project' do describe 'when user has push access to the project' do
before do before do
forked_project.team << [user_two, :developer] project.team << [user, :developer]
end end
context 'and request is sent by gitlab-workhorse to authorize the request' do context 'and request is sent by gitlab-workhorse to authorize the request' do
before do before do
header_for_upload_authorize(forked_project) put_authorize
end end
it 'responds with status 200, location of lfs store and object details' do it 'responds with status 200' do
json_response = ActiveSupport::JSON.decode(lfs_router_forked_auth.try_call.last.first) expect(response).to have_http_status(200)
end
expect(lfs_router_forked_auth.try_call.first).to eq(200) 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['StoreLFSPath']).to eq("#{Gitlab.config.shared.path}/lfs-objects/tmp/upload")
expect(json_response['LfsOid']).to eq(sample_oid) expect(json_response['LfsOid']).to eq(sample_oid)
expect(json_response['LfsSize']).to eq(sample_size) expect(json_response['LfsSize']).to eq(sample_size)
...@@ -650,81 +668,96 @@ describe Gitlab::Lfs::Router, lib: true do ...@@ -650,81 +668,96 @@ describe Gitlab::Lfs::Router, lib: true do
context 'and request is sent by gitlab-workhorse to finalize the upload' do context 'and request is sent by gitlab-workhorse to finalize the upload' do
before do before do
headers_for_upload_finalize(forked_project) put_finalize
end end
it 'responds with status 200 and lfs object is linked to the source project' do it 'responds with status 200' do
expect(lfs_router_forked_auth.try_call.first).to eq(200) expect(response).to have_http_status(200)
expect(lfs_object.projects.pluck(:id)).to include(public_project.id) 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 end
end end
describe 'and user does not have push access' do describe 'and user does not have push access' do
let(:router) { lfs_router_forked_auth }
it_behaves_like 'forbidden' it_behaves_like 'forbidden'
end end
end end
context 'when CI is authenticated' do context 'when CI is authenticated' do
let(:router) { lfs_router_forked_ci_auth } let(:authorization) { authorize_ci_project }
it_behaves_like 'unauthorized' it_behaves_like 'unauthorized'
end end
context 'for unauthenticated' do context 'for unauthenticated' do
let(:router) { lfs_router_forked_noauth }
it_behaves_like 'unauthorized' it_behaves_like 'unauthorized'
end end
describe 'and second project not related to fork or a source project' do describe 'and second project not related to fork or a source project' do
let(:second_project) { create(:project) } let(:second_project) { create(:empty_project) }
let(:lfs_router_second_project) { new_lfs_router(second_project, user: user) } let(:authorization) { authorize_user }
before do before do
public_project.lfs_objects << lfs_object second_project.team << [user, :master]
headers_for_upload_finalize(second_project) upstream_project.lfs_objects << lfs_object
end end
context 'when pushing the same lfs object to the second project' do context 'when pushing the same lfs object to the second project' do
before do before do
second_project.team << [user, :master] 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 end
it 'responds with 200 and links the lfs object to the project' do it 'responds with status 200' do
expect(lfs_router_second_project.try_call.first).to eq(200) expect(response).to have_http_status(200)
expect(lfs_object.projects.pluck(:id)).to include(second_project.id, public_project.id)
end 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
end end
end end
def enable_lfs def put_authorize
allow(Gitlab.config.lfs).to receive(:enabled).and_return(true) put "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}/#{sample_size}/authorize", nil, headers
end end
def authorize(user) def put_finalize(lfs_tmp = lfs_tmp_file)
ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password) 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 end
def new_lfs_router(project, user: nil, ci: false) def enable_lfs
Gitlab::Lfs::Router.new(project, user, ci, request) allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
end end
def header_for_upload_authorize(project) def authorize_ci_project
env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}/authorize" ActionController::HttpAuthentication::Basic.encode_credentials('gitlab-ci-token', project.runners_token)
end end
def headers_for_upload_finalize(project) def authorize_user
env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}" ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password)
env["HTTP_X_GITLAB_LFS_TMP"] = "#{sample_oid}6e561c9d4"
end end
def fork_project(project, user, object = nil) def fork_project(project, user, object = nil)
allow(RepositoryForkWorker).to receive(:perform_async).and_return(true) allow(RepositoryForkWorker).to receive(:perform_async).and_return(true)
Projects::ForkService.new(project, user, {}).execute Projects::ForkService.new(project, user, {}).execute
end end
def post_json(url, body = nil, headers = nil)
post(url, body.try(:to_json), (headers || {}).merge('Content-Type' => 'application/json'))
end
def json_response
@json_response ||= JSON.parse(response.body)
end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment