Commit 31a5a03c authored by Rémy Coutable's avatar Rémy Coutable

Merge branch 'fix-lfs-ci-fetch' into 'master'

Fix LFS CI fetch

## What does this MR do?
Fixes LFS fetching for CI objects that were kind of broken.

## Why was this MR needed?
This also refactors LFS tests to be a blackbox tests instead of whitebox tests. Actually testing GrackAuth instead of LFSRouter.

## Related isssues
Resolves: https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/issues/1199

- [ ] [CHANGELOG](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CHANGELOG) entry added
- [ ] [Documentation created/updated](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/development/doc_styleguide.md)
- [ ] API support added
- Tests
  - [ ] Added for this feature/bug
  - [ ] All builds are passing
- [ ] Conform by the [style guides](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#style-guides)
- [ ] Branch has no merge conflicts with `master` (if you do - rebase it please)
- [ ] [Squashed related commits together](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)


See merge request !5270
parents a95a32f0 e300dac2
......@@ -31,6 +31,7 @@ v 8.10.0 (unreleased)
- Support U2F devices in Firefox. !5177
- Fix issue, preventing users w/o push access to sort tags !5105 (redetection)
- Add Spring EmojiOne updates.
- Fix fetching LFS objects for private CI projects
- Add syntax for multiline blockquote using `>>>` fence !3954
- Fix viewing notification settings when a project is pending deletion
- Updated compare dropdown menus to use GL dropdown
......
......@@ -63,7 +63,7 @@ module Grack
def ci_request?(login, password)
matched_login = /(?<s>^[a-zA-Z]*-ci)-token$/.match(login)
if project && matched_login.present? && git_cmd == 'git-upload-pack'
if project && matched_login.present?
underscored_service = matched_login['s'].underscore
if underscored_service == 'gitlab_ci'
......
......@@ -47,6 +47,8 @@ module Gitlab
end
def render_storage_upload_store_response(oid, size, tmp_file_name)
return render_forbidden unless tmp_file_name
render_response_to_push do
render_lfs_upload_ok(oid, size, tmp_file_name)
end
......
......@@ -74,8 +74,6 @@ module Gitlab
lfs.render_storage_upload_authorize_response(oid, size)
else
tmp_file_name = sanitize_tmp_filename(@request.env['HTTP_X_GITLAB_LFS_TMP'])
return nil unless tmp_file_name
lfs.render_storage_upload_store_response(oid, size, tmp_file_name)
end
end
......
require 'spec_helper'
describe Gitlab::Lfs::Router, lib: true do
let(:project) { create(:project) }
let(:public_project) { create(:project, :public) }
let(:forked_project) { fork_project(public_project, user) }
describe Gitlab::Lfs::Router do
let(:user) { create(:user) }
let(:user_two) { create(:user) }
let!(:lfs_object) { create(:lfs_object, :with_file) }
let(:request) { Rack::Request.new(env) }
let(:env) do
let(:headers) do
{
'rack.input' => '',
'REQUEST_METHOD' => 'GET',
}
'Authorization' => authorization,
'X-Sendfile-Type' => sendfile
}.compact
end
let(:authorization) { }
let(:sendfile) { }
let(:lfs_router_auth) { new_lfs_router(project, user: user) }
let(:lfs_router_ci_auth) { new_lfs_router(project, ci: true) }
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\"}"]]}
let(:sample_oid) { lfs_object.oid }
let(:sample_size) { lfs_object.size }
describe 'when lfs is disabled' do
before do
allow(Gitlab.config.lfs).to receive(:enabled).and_return(false)
env['REQUEST_METHOD'] = 'POST'
body = {
let(:project) { create(:empty_project) }
let(:body) do
{
'objects' => [
{ 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
'size' => 1575078
......@@ -46,136 +29,171 @@ describe Gitlab::Lfs::Router, lib: true do
}
],
'operation' => 'upload'
}.to_json
env['rack.input'] = StringIO.new(body)
env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/batch"
}
end
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
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
describe 'when fetching lfs object using deprecated API' do
describe 'deprecated API' do
let(:project) { create(:empty_project) }
before do
enable_lfs
env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/#{sample_oid}"
end
shared_examples 'a deprecated' 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
describe 'when fetching lfs object' do
context 'when fetching lfs object using deprecated API' do
let(:authorization) { authorize_user }
before do
enable_lfs
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}"
get "#{project.http_url_to_repo}/info/lfs/objects/#{sample_oid}", nil, headers
end
describe 'and request comes from gitlab-workhorse' do
context 'without user being authorized' do
it "responds with status 401" do
expect(lfs_router_noauth.try_call.first).to eq(401)
end
it_behaves_like 'a deprecated'
end
context 'with required headers' do
context 'when handling lfs request using deprecated API' do
before do
project.lfs_objects << lfs_object
env['HTTP_X_SENDFILE_TYPE'] = "X-Sendfile"
post_json "#{project.http_url_to_repo}/info/lfs/objects", nil, headers
end
context 'when user does not have project access' do
it "responds with status 403" do
expect(lfs_router_auth.try_call.first).to eq(403)
it_behaves_like 'a deprecated'
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
project.team << [user, :master]
enable_lfs
update_permissions
get "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", nil, headers
end
it "responds with status 200" do
expect(lfs_router_auth.try_call.first).to eq(200)
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
it "responds with the file location" do
expect(lfs_router_auth.try_call[1]['Content-Type']).to eq("application/octet-stream")
expect(lfs_router_auth.try_call[1]['X-Sendfile']).to eq(lfs_object.file.path)
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
context 'when CI is authorized' do
it "responds with status 200" do
expect(lfs_router_ci_auth.try_call.first).to eq(200)
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
it "responds with the file location" do
expect(lfs_router_ci_auth.try_call[1]['Content-Type']).to eq("application/octet-stream")
expect(lfs_router_ci_auth.try_call[1]['X-Sendfile']).to eq(lfs_object.file.path)
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
it 'responds with status 403' do
expect(response).to have_http_status(403)
end
end
context 'without required headers' do
it "responds with status 403" do
expect(lfs_router_auth.try_call.first).to eq(403)
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 CI is authorized' do
let(:authorization) { authorize_ci_project }
let(:update_permissions) do
project.lfs_objects << lfs_object
end
describe 'when handling lfs request using deprecated API' do
before do
enable_lfs
env['REQUEST_METHOD'] = 'POST'
env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects"
it_behaves_like 'responds with a file'
end
end
it 'responds with 501' do
expect(lfs_router_auth.try_call).to match_array(respond_with_deprecated)
context 'without required headers' do
let(:authorization) { authorize_user }
it 'responds with status 403' do
expect(response).to have_http_status(403)
end
end
end
end
describe 'when handling lfs batch request' do
let(:update_lfs_permissions) { }
let(:update_user_permissions) { }
before do
enable_lfs
env['REQUEST_METHOD'] = 'POST'
env['PATH_INFO'] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/batch"
update_lfs_permissions
update_user_permissions
post_json "#{project.http_url_to_repo}/info/lfs/objects/batch", body, headers
end
describe 'download' do
before do
body = { 'operation' => 'download',
let(:project) { create(:empty_project) }
let(:body) do
{ 'operation' => 'download',
'objects' => [
{ 'oid' => sample_oid,
'size' => sample_size
}]
}.to_json
env['rack.input'] = StringIO.new(body)
}
end
shared_examples 'an authorized requests' 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
end
it 'responds with status 200 and href to download' do
response = router.try_call
expect(response.first).to eq(200)
response_body = ActiveSupport::JSON.decode(response.last.first)
it 'responds with status 200' do
expect(response).to have_http_status(200)
end
expect(response_body).to eq('objects' => [
it 'with href to download' do
expect(json_response).to eq('objects' => [
{ 'oid' => sample_oid,
'size' => sample_size,
'actions' => {
'download' => {
'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}",
'header' => { 'Authorization' => auth }
'header' => { 'Authorization' => authorization }
}
}
}])
......@@ -183,16 +201,17 @@ describe Gitlab::Lfs::Router, lib: true do
end
context 'when downloading an lfs object that is assigned to other project' do
before do
public_project.lfs_objects << lfs_object
let(:other_project) { create(:empty_project) }
let(:update_lfs_permissions) do
other_project.lfs_objects << lfs_object
end
it 'responds with status 200 and error message' do
response = router.try_call
expect(response.first).to eq(200)
response_body = ActiveSupport::JSON.decode(response.last.first)
it 'responds with status 200' do
expect(response).to have_http_status(200)
end
expect(response_body).to eq('objects' => [
it 'with href to download' do
expect(json_response).to eq('objects' => [
{ 'oid' => sample_oid,
'size' => sample_size,
'error' => {
......@@ -204,22 +223,21 @@ describe Gitlab::Lfs::Router, lib: true do
end
context 'when downloading a lfs object that does not exist' do
before do
body = { 'operation' => 'download',
let(:body) do
{ 'operation' => 'download',
'objects' => [
{ 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
'size' => 1575078
}]
}.to_json
env['rack.input'] = StringIO.new(body)
}
end
it "responds with status 200 and error message" do
response = router.try_call
expect(response.first).to eq(200)
response_body = ActiveSupport::JSON.decode(response.last.first)
it 'responds with status 200' do
expect(response).to have_http_status(200)
end
expect(response_body).to eq('objects' => [
it 'with an 404 for specific object' do
expect(json_response).to eq('objects' => [
{ 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
'size' => 1575078,
'error' => {
......@@ -231,8 +249,8 @@ describe Gitlab::Lfs::Router, lib: true do
end
context 'when downloading one new and one existing lfs object' do
before do
body = { 'operation' => 'download',
let(:body) do
{ 'operation' => 'download',
'objects' => [
{ 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
'size' => 1575078
......@@ -241,17 +259,19 @@ describe Gitlab::Lfs::Router, lib: true do
'size' => sample_size
}
]
}.to_json
env['rack.input'] = StringIO.new(body)
}
end
let(:update_lfs_permissions) do
project.lfs_objects << lfs_object
end
it "responds with status 200 with upload hypermedia link for the new object" do
response = router.try_call
expect(response.first).to eq(200)
response_body = ActiveSupport::JSON.decode(response.last.first)
it 'responds with status 200' do
expect(response).to have_http_status(200)
end
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',
'size' => 1575078,
'error' => {
......@@ -264,7 +284,7 @@ describe Gitlab::Lfs::Router, lib: true do
'actions' => {
'download' => {
'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}",
'header' => { 'Authorization' => auth }
'header' => { 'Authorization' => authorization }
}
}
}])
......@@ -273,23 +293,21 @@ describe Gitlab::Lfs::Router, lib: true do
end
context 'when user is authenticated' do
let(:auth) { authorize(user) }
let(:authorization) { authorize_user }
before do
env["HTTP_AUTHORIZATION"] = auth
let(:update_user_permissions) do
project.team << [user, role]
end
it_behaves_like 'an authorized requests' do
let(:role) { :reporter }
let(:router) { lfs_router_auth }
end
context 'when user does is not member of the project' do
let(:role) { :guest }
it 'responds with 403' do
expect(lfs_router_auth.try_call.first).to eq(403)
expect(response).to have_http_status(403)
end
end
......@@ -297,40 +315,36 @@ describe Gitlab::Lfs::Router, lib: true do
let(:role) { :guest }
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
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_behaves_like 'an authorized requests' do
let(:router) { lfs_router_ci_auth }
end
it_behaves_like 'an authorized requests'
end
context 'when user is not authenticated' do
describe 'is accessing public project' do
before do
public_project.lfs_objects << lfs_object
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
response = lfs_router_public_noauth.try_call
expect(response.first).to eq(200)
response_body = ActiveSupport::JSON.decode(response.last.first)
expect(response).to have_http_status(200)
end
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,
'size' => sample_size,
'actions' => {
'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' => {}
}
}
......@@ -339,83 +353,83 @@ describe Gitlab::Lfs::Router, lib: true do
end
describe 'is accessing non-public project' do
before do
let(:update_lfs_permissions) do
project.lfs_objects << lfs_object
end
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
describe 'upload' do
before do
body = { 'operation' => 'upload',
let(:project) { create(:project, :public) }
let(:body) do
{ 'operation' => 'upload',
'objects' => [
{ 'oid' => sample_oid,
'size' => sample_size
}]
}.to_json
env['rack.input'] = StringIO.new(body)
}
end
describe 'when request is authenticated' do
describe 'when user has project push access' do
before do
@auth = authorize(user)
env["HTTP_AUTHORIZATION"] = @auth
let(:authorization) { authorize_user }
let(:update_user_permissions) do
project.team << [user, :developer]
end
context 'when pushing an lfs object that already exists' do
before do
public_project.lfs_objects << lfs_object
let(:other_project) { create(:empty_project) }
let(:update_lfs_permissions) do
other_project.lfs_objects << lfs_object
end
it "responds with status 200 and links the object to the project" do
response_body = lfs_router_auth.try_call.last
response = ActiveSupport::JSON.decode(response_body.first)
it 'responds with status 200' do
expect(response).to have_http_status(200)
end
expect(response['objects']).to be_kind_of(Array)
expect(response['objects'].first['oid']).to eq(sample_oid)
expect(response['objects'].first['size']).to eq(sample_size)
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(public_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(response['objects'].first['actions']['upload']['header']).to eq('Authorization' => @auth)
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
before do
body = { 'operation' => 'upload',
let(:body) do
{ 'operation' => 'upload',
'objects' => [
{ 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
'size' => 1575078
}]
}.to_json
env['rack.input'] = StringIO.new(body)
}
end
it "responds with status 200 and upload hypermedia link" do
response = lfs_router_auth.try_call
expect(response.first).to eq(200)
it 'responds with status 200' do
expect(response).to have_http_status(200)
end
response_body = ActiveSupport::JSON.decode(response.last.first)
expect(response_body['objects']).to be_kind_of(Array)
expect(response_body['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897")
expect(response_body['objects'].first['size']).to eq(1575078)
expect(lfs_object.projects.pluck(:id)).not_to include(project.id)
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(response_body['objects'].first['actions']['upload']['header']).to eq('Authorization' => @auth)
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.path_with_namespace}.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
before do
body = { 'operation' => 'upload',
let(:body) do
{ 'operation' => 'upload',
'objects' => [
{ 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
'size' => 1575078
......@@ -424,87 +438,89 @@ describe Gitlab::Lfs::Router, lib: true do
'size' => sample_size
}
]
}.to_json
env['rack.input'] = StringIO.new(body)
}
end
let(:update_lfs_permissions) do
project.lfs_objects << lfs_object
end
it "responds with status 200 with upload hypermedia link for the new object" do
response = lfs_router_auth.try_call
expect(response.first).to eq(200)
it 'responds with status 200' do
expect(response).to have_http_status(200)
end
response_body = ActiveSupport::JSON.decode(response.last.first)
expect(response_body['objects']).to be_kind_of(Array)
it 'responds with upload hypermedia link for the new object' do
expect(json_response['objects']).to be_kind_of(Array)
expect(response_body['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897")
expect(response_body['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(response_body['objects'].first['actions']['upload']['header']).to eq("Authorization" => @auth)
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(response_body['objects'].last['oid']).to eq(sample_oid)
expect(response_body['objects'].last['size']).to eq(sample_size)
expect(response_body['objects'].last).not_to have_key('actions')
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(lfs_router_auth.try_call.first).to eq(403)
expect(response).to have_http_status(403)
end
end
context 'when CI is authorized' do
let(:authorization) { authorize_ci_project }
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
context 'when user is not authenticated' do
context 'when user has push access' do
before do
let(:update_user_permissions) do
project.team << [user, :master]
end
it "responds with status 401" do
expect(lfs_router_public_noauth.try_call.first).to eq(401)
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(lfs_router_public_noauth.try_call.first).to eq(401)
it 'responds with status 401' do
expect(response).to have_http_status(401)
end
end
end
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
expect(lfs_router_public_ci_auth.try_call.first).to eq(401)
it 'responds with status 403' do
expect(response).to have_http_status(401)
end
end
end
describe 'unsupported' do
before do
body = { 'operation' => 'other',
let(:project) { create(:empty_project) }
let(:body) do
{ 'operation' => 'other',
'objects' => [
{ 'oid' => sample_oid,
'size' => sample_size
}]
}.to_json
env['rack.input'] = StringIO.new(body)
}
end
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
......@@ -512,38 +528,36 @@ describe Gitlab::Lfs::Router, lib: true do
describe 'when pushing a lfs object' do
before do
enable_lfs
env['REQUEST_METHOD'] = 'PUT'
end
shared_examples 'unauthorized' do
context 'and request is sent by gitlab-workhorse to authorize the request' do
before do
header_for_upload_authorize(router.project)
put_authorize
end
it 'responds with status 401' do
expect(router.try_call.first).to eq(401)
expect(response).to have_http_status(401)
end
end
context 'and request is sent by gitlab-workhorse to finalize the upload' do
before do
headers_for_upload_finalize(router.project)
put_finalize
end
it 'responds with status 401' do
expect(router.try_call.first).to eq(401)
expect(response).to have_http_status(401)
end
end
context 'and request is sent with a malformed headers' do
before do
env["PATH_INFO"] = "#{router.project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}"
env["HTTP_X_GITLAB_LFS_TMP"] = "cat /etc/passwd"
put_finalize('cat /etc/passwd')
end
it 'does not recognize it as a valid lfs command' do
expect(router.try_call).to eq(nil)
expect(response).to have_http_status(403)
end
end
end
......@@ -551,27 +565,31 @@ describe Gitlab::Lfs::Router, lib: true do
shared_examples 'forbidden' do
context 'and request is sent by gitlab-workhorse to authorize the request' do
before do
header_for_upload_authorize(router.project)
put_authorize
end
it 'responds with 403' do
expect(router.try_call.first).to eq(403)
expect(response).to have_http_status(403)
end
end
context 'and request is sent by gitlab-workhorse to finalize the upload' do
before do
headers_for_upload_finalize(router.project)
put_finalize
end
it 'responds with 403' do
expect(router.try_call.first).to eq(403)
expect(response).to have_http_status(403)
end
end
end
describe 'to one project' do
let(:project) { create(:empty_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]
......@@ -579,13 +597,14 @@ describe Gitlab::Lfs::Router, lib: true do
context 'and request is sent by gitlab-workhorse to authorize the request' do
before do
header_for_upload_authorize(project)
put_authorize
end
it 'responds with status 200, location of lfs store and object details' do
json_response = ActiveSupport::JSON.decode(lfs_router_auth.try_call.last.first)
it 'responds with status 200' do
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['LfsOid']).to eq(sample_oid)
expect(json_response['LfsSize']).to eq(sample_size)
......@@ -594,54 +613,58 @@ describe Gitlab::Lfs::Router, lib: true do
context 'and request is sent by gitlab-workhorse to finalize the upload' do
before do
headers_for_upload_finalize(project)
put_finalize
end
it 'responds with status 200' do
expect(response).to have_http_status(200)
end
it 'responds with status 200 and lfs object is linked to the project' do
expect(lfs_router_auth.try_call.first).to eq(200)
it 'lfs object is linked to the project' do
expect(lfs_object.projects.pluck(:id)).to include(project.id)
end
end
end
describe 'and user does not have push access' do
let(:router) { lfs_router_auth }
it_behaves_like 'forbidden'
end
end
context 'when CI is authenticated' do
let(:router) { lfs_router_ci_auth }
let(:authorization) { authorize_ci_project }
it_behaves_like 'unauthorized'
end
context 'for unauthenticated' do
let(:router) { new_lfs_router(project) }
it_behaves_like 'unauthorized'
end
end
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
let(:authorization) { authorize_user }
describe 'when user has push access to the project' do
before do
forked_project.team << [user_two, :developer]
project.team << [user, :developer]
end
context 'and request is sent by gitlab-workhorse to authorize the request' do
before do
header_for_upload_authorize(forked_project)
put_authorize
end
it 'responds with status 200, location of lfs store and object details' do
json_response = ActiveSupport::JSON.decode(lfs_router_forked_auth.try_call.last.first)
it 'responds with status 200' do
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['LfsOid']).to eq(sample_oid)
expect(json_response['LfsSize']).to eq(sample_size)
......@@ -650,81 +673,96 @@ describe Gitlab::Lfs::Router, lib: true do
context 'and request is sent by gitlab-workhorse to finalize the upload' do
before do
headers_for_upload_finalize(forked_project)
put_finalize
end
it 'responds with status 200' do
expect(response).to have_http_status(200)
end
it 'responds with status 200 and lfs object is linked to the source project' do
expect(lfs_router_forked_auth.try_call.first).to eq(200)
expect(lfs_object.projects.pluck(:id)).to include(public_project.id)
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
let(:router) { lfs_router_forked_auth }
it_behaves_like 'forbidden'
end
end
context 'when CI is authenticated' do
let(:router) { lfs_router_forked_ci_auth }
let(:authorization) { authorize_ci_project }
it_behaves_like 'unauthorized'
end
context 'for unauthenticated' do
let(:router) { lfs_router_forked_noauth }
it_behaves_like 'unauthorized'
end
describe 'and second project not related to fork or a source project' do
let(:second_project) { create(:project) }
let(:lfs_router_second_project) { new_lfs_router(second_project, user: user) }
let(:second_project) { create(:empty_project) }
let(:authorization) { authorize_user }
before do
public_project.lfs_objects << lfs_object
headers_for_upload_finalize(second_project)
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
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
it 'responds with 200 and links the lfs object to the project' do
expect(lfs_router_second_project.try_call.first).to eq(200)
expect(lfs_object.projects.pluck(:id)).to include(second_project.id, public_project.id)
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 enable_lfs
allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
def put_authorize
put "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}/#{sample_size}/authorize", nil, headers
end
def authorize(user)
ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password)
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 new_lfs_router(project, user: nil, ci: false)
Gitlab::Lfs::Router.new(project, user, ci, request)
def lfs_tmp_file
"#{sample_oid}012345678"
end
end
def header_for_upload_authorize(project)
env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}/authorize"
def enable_lfs
allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
end
def authorize_ci_project
ActionController::HttpAuthentication::Basic.encode_credentials('gitlab-ci-token', project.runners_token)
end
def headers_for_upload_finalize(project)
env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}"
env["HTTP_X_GITLAB_LFS_TMP"] = "#{sample_oid}6e561c9d4"
def authorize_user
ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password)
end
def fork_project(project, user, object = nil)
allow(RepositoryForkWorker).to receive(:perform_async).and_return(true)
Projects::ForkService.new(project, user, {}).execute
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
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