Commit 1a675dc9 authored by Ash McKenzie's avatar Ash McKenzie

Allow upload_code ability for auth'd Geo request

parent 3bcb345e
...@@ -6,11 +6,24 @@ module EE ...@@ -6,11 +6,24 @@ module EE
override :render_ok override :render_ok
def render_ok def render_ok
set_workhorse_internal_api_content_type set_workhorse_internal_api_content_type
render json: ::Gitlab::Workhorse.git_http_ok(repository, wiki?, user, action_name, show_all_refs: geo_request?) render json: ::Gitlab::Workhorse.git_http_ok(repository, wiki?, user, action_name, show_all_refs: geo_request?)
end end
private private
def user
super || geo_push_user&.user
end
def geo_push_user
@geo_push_user ||= ::Geo::PushUser.new_from_headers(request.headers)
end
def geo_push_user_headers_provided?
::Geo::PushUser.needed_headers_provided?(request.headers)
end
def geo_request? def geo_request?
::Gitlab::Geo::JwtRequestDecoder.geo_auth_attempt?(request.headers['Authorization']) ::Gitlab::Geo::JwtRequestDecoder.geo_auth_attempt?(request.headers['Authorization'])
end end
...@@ -21,9 +34,11 @@ module EE ...@@ -21,9 +34,11 @@ module EE
override :access_actor override :access_actor
def access_actor def access_actor
return :geo if geo? return super unless geo?
return :geo unless geo_push_user_headers_provided?
return geo_push_user.user if geo_push_user.user
super raise ::Gitlab::GitAccess::UnauthorizedError, 'Geo push user is invalid.'
end end
override :authenticate_user override :authenticate_user
...@@ -32,7 +47,7 @@ module EE ...@@ -32,7 +47,7 @@ module EE
payload = ::Gitlab::Geo::JwtRequestDecoder.new(request.headers['Authorization']).decode payload = ::Gitlab::Geo::JwtRequestDecoder.new(request.headers['Authorization']).decode
if payload if payload
@authentication_result = ::Gitlab::Auth::Result.new(nil, project, :geo, [:download_code]) # rubocop:disable Gitlab/ModuleWithInstanceVariables @authentication_result = ::Gitlab::Auth::Result.new(nil, project, :geo, [:download_code, :push_code]) # rubocop:disable Gitlab/ModuleWithInstanceVariables
return # grant access return # grant access
end end
......
...@@ -52,6 +52,11 @@ module EE ...@@ -52,6 +52,11 @@ module EE
def geo? def geo?
actor == :geo actor == :geo
end end
override :authed_via_jwt?
def authed_via_jwt?
geo?
end
end end
end end
end end
...@@ -268,7 +268,7 @@ For more information: #{EE::Gitlab::GeoGitAccess::GEO_SERVER_DOCS_URL}" ...@@ -268,7 +268,7 @@ For more information: #{EE::Gitlab::GeoGitAccess::GEO_SERVER_DOCS_URL}"
let(:actor) { :geo } let(:actor) { :geo }
it { expect { pull_changes }.not_to raise_error } it { expect { pull_changes }.not_to raise_error }
it { expect { push_changes }.to raise_unauthorized(Gitlab::GitAccess::ERROR_MESSAGES[:upload]) } it { expect { push_changes }.to raise_unauthorized(Gitlab::GitAccess::ERROR_MESSAGES[:push_code]) }
end end
private private
......
...@@ -13,15 +13,24 @@ describe "Git HTTP requests (Geo)" do ...@@ -13,15 +13,24 @@ describe "Git HTTP requests (Geo)" do
# Ensure the token always comes from the real time of the request # Ensure the token always comes from the real time of the request
let!(:auth_token) { Gitlab::Geo::BaseRequest.new.authorization } let!(:auth_token) { Gitlab::Geo::BaseRequest.new.authorization }
let!(:user) { create(:user) }
let!(:user_without_any_access) { create(:user) }
let!(:user_without_push_access) { create(:user) }
let!(:key) { create(:key, user: user) }
let!(:key_for_user_without_any_access) { create(:key, user: user_without_any_access) }
let!(:key_for_user_without_push_access) { create(:key, user: user_without_push_access) }
let(:env) { valid_geo_env } let(:env) { valid_geo_env }
before do before do
project.add_maintainer(user)
project.add_guest(user_without_push_access)
stub_licensed_features(geo: true) stub_licensed_features(geo: true)
stub_current_geo_node(secondary) stub_current_geo_node(current_node)
end end
shared_examples_for 'Geo sync request' do shared_examples_for 'Geo request' do
subject do subject do
make_request make_request
response response
...@@ -64,180 +73,291 @@ describe "Git HTTP requests (Geo)" do ...@@ -64,180 +73,291 @@ describe "Git HTTP requests (Geo)" do
end end
end end
describe 'GET info_refs' do context 'when current node is a secondary' do
context 'git pull' do let(:current_node) { secondary }
def make_request
get "/#{project.full_path}.git/info/refs", { service: 'git-upload-pack' }, env set(:project) { create(:project, :repository, :private) }
describe 'GET info_refs' do
context 'git pull' do
def make_request
get "/#{project.full_path}.git/info/refs", { service: 'git-upload-pack' }, env
end
it_behaves_like 'Geo request'
context 'when terms are enforced' do
before do
enforce_terms
end
it_behaves_like 'Geo request'
end
end end
it_behaves_like 'Geo sync request' context 'git push' do
def make_request
get url, { service: 'git-receive-pack' }, env
end
context 'when terms are enforced' do let(:url) { "/#{project.full_path}.git/info/refs" }
before do
enforce_terms subject do
make_request
response
end end
it_behaves_like 'Geo sync request' it 'redirects to the primary' do
is_expected.to have_gitlab_http_status(:redirect)
redirect_location = "#{primary.url.chomp('/')}#{url}?service=git-receive-pack"
expect(subject.header['Location']).to eq(redirect_location)
end
end end
end end
context 'git push' do describe 'POST git_upload_pack' do
def make_request def make_request
get url, { service: 'git-receive-pack' }, env post "/#{project.full_path}.git/git-upload-pack", {}, env
end end
let(:url) { "/#{project.full_path}.git/info/refs" } it_behaves_like 'Geo request'
subject do context 'when terms are enforced' do
make_request before do
response enforce_terms
end end
it 'redirects to the primary' do it_behaves_like 'Geo request'
is_expected.to have_gitlab_http_status(:redirect)
redirect_location = "#{primary.url.chomp('/')}#{url}?service=git-receive-pack"
expect(subject.header['Location']).to eq(redirect_location)
end end
end end
end
describe 'POST upload_pack' do context 'git-lfs' do
def make_request context 'API' do
post "/#{project.full_path}.git/git-upload-pack", {}, env describe 'POST batch' do
end def make_request
post url, args, env
end
it_behaves_like 'Geo sync request' let(:args) { {} }
let(:url) { "/#{project.full_path}.git/info/lfs/objects/batch" }
context 'when terms are enforced' do subject do
before do make_request
enforce_terms response
end end
it_behaves_like 'Geo sync request' before do
end allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
end project.update_attribute(:lfs_enabled, true)
env['Content-Type'] = LfsRequest::CONTENT_TYPE
end
context 'git-lfs' do context 'operation upload' do
context 'API' do let(:args) { { 'operation' => 'upload' }.to_json }
describe 'POST batch' do
def make_request
post url, args, env
end
let(:args) { {} } context 'with the correct git-lfs version' do
let(:url) { "/#{project.full_path}.git/info/lfs/objects/batch" } before do
env['User-Agent'] = 'git-lfs/2.4.2 (GitHub; darwin amd64; go 1.10.2)'
end
subject do it 'redirects to the primary' do
make_request is_expected.to have_gitlab_http_status(:redirect)
response redirect_location = "#{primary.url.chomp('/')}#{url}"
end expect(subject.header['Location']).to eq(redirect_location)
end
end
before do context 'with an incorrect git-lfs version' do
allow(Gitlab.config.lfs).to receive(:enabled).and_return(true) where(:description, :version) do
project.update_attribute(:lfs_enabled, true) 'outdated' | 'git-lfs/2.4.1'
env['Content-Type'] = LfsRequest::CONTENT_TYPE 'unknown' | 'git-lfs'
end end
with_them do
context "that is #{description}" do
before do
env['User-Agent'] = "#{version} (GitHub; darwin amd64; go 1.10.2)"
end
it 'is forbidden' do
is_expected.to have_gitlab_http_status(:forbidden)
expect(json_response['message']).to match(/You need git-lfs version 2.4.2/)
end
end
end
end
end
context 'operation upload' do context 'operation download' do
let(:args) { { 'operation' => 'upload' }.to_json } let(:user) { create(:user) }
let(:authorization) { ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password) }
let(:lfs_object) { create(:lfs_object, :with_file) }
let(:args) do
{
'operation' => 'download',
'objects' => [{ 'oid' => lfs_object.oid, 'size' => lfs_object.size }]
}.to_json
end
context 'with the correct git-lfs version' do
before do before do
env['User-Agent'] = 'git-lfs/2.4.2 (GitHub; darwin amd64; go 1.10.2)' project.add_maintainer(user)
env['Authorization'] = authorization
end end
it 'redirects to the primary' do it 'is handled by the secondary' do
is_expected.to have_gitlab_http_status(:redirect) is_expected.to have_gitlab_http_status(:ok)
redirect_location = "#{primary.url.chomp('/')}#{url}"
expect(subject.header['Location']).to eq(redirect_location)
end end
end
context 'with an incorrect git-lfs version' do
where(:description, :version) do where(:description, :version) do
'outdated' | 'git-lfs/2.4.1' 'outdated' | 'git-lfs/2.4.1'
'unknown' | 'git-lfs' 'unknown' | 'git-lfs'
end end
with_them do with_them do
context "that is #{description}" do context "with an #{description} git-lfs version" do
before do before do
env['User-Agent'] = "#{version} (GitHub; darwin amd64; go 1.10.2)" env['User-Agent'] = "#{version} (GitHub; darwin amd64; go 1.10.2)"
end end
it 'is forbidden' do it 'is handled by the secondary' do
is_expected.to have_gitlab_http_status(:forbidden) is_expected.to have_gitlab_http_status(:ok)
expect(json_response['message']).to match(/You need git-lfs version 2.4.2/)
end end
end end
end end
end end
end end
end
context 'Locks API' do
where(:description, :path, :args) do
'create' | 'info/lfs/locks' | {}
'verify' | 'info/lfs/locks/verify' | {}
'unlock' | 'info/lfs/locks/1/unlock' | { id: 1 }
end
with_them do
describe "POST #{description}" do
def make_request
post url, args, env
end
context 'operation download' do let(:url) { "/#{project.full_path}.git/#{path}" }
let(:user) { create(:user) }
let(:authorization) { ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password) } subject do
let(:lfs_object) { create(:lfs_object, :with_file) } make_request
let(:args) do response
{ end
'operation' => 'download',
'objects' => [{ 'oid' => lfs_object.oid, 'size' => lfs_object.size }] it 'redirects to the primary' do
}.to_json is_expected.to have_gitlab_http_status(:redirect)
redirect_location = "#{primary.url.chomp('/')}#{url}"
expect(subject.header['Location']).to eq(redirect_location)
end
end end
end
end
end
end
before do context 'when current node is the primary' do
project.add_maintainer(user) let(:current_node) { primary }
env['Authorization'] = authorization
describe 'POST git_receive_pack' do
def make_request
post url, {}, env
end
let(:url) { "/#{project.full_path}.git/git-receive-pack" }
before do
env['Geo-GL-Id'] = geo_gl_id
end
subject do
make_request
response
end
context 'when gl_id is not correctly provided via HTTP headers' do
context "as it's empty" do
where(:geo_gl_id) do
[
nil,
''
]
end end
it 'is handled by the secondary' do with_them do
is_expected.to have_gitlab_http_status(:ok) it 'returns a 403' do
is_expected.to have_gitlab_http_status(:forbidden)
expect(response.body).to eql('You are not allowed to push code to this project.')
end
end end
end
where(:description, :version) do context "as it's junk" do
'outdated' | 'git-lfs/2.4.1' where(:geo_gl_id) do
'unknown' | 'git-lfs' [
'junk',
'junk-1',
'kkey-1'
]
end end
with_them do with_them do
context "with an #{description} git-lfs version" do it 'returns a 403' do
before do is_expected.to have_gitlab_http_status(:forbidden)
env['User-Agent'] = "#{version} (GitHub; darwin amd64; go 1.10.2)" expect(response.body).to eql('Geo push user is invalid.')
end
it 'is handled by the secondary' do
is_expected.to have_gitlab_http_status(:ok)
end
end end
end end
end end
end end
end
context 'Locks API' do context 'when gl_id is provided via HTTP headers' do
where(:description, :path, :args) do context 'but is invalid' do
'create' | 'info/lfs/locks' | {} where(:geo_gl_id) do
'verify' | 'info/lfs/locks/verify' | {} [
'unlock' | 'info/lfs/locks/1/unlock' | { id: 1 } 'key-999',
end 'key-1',
'key-999'
]
end
with_them do with_them do
describe "POST #{description}" do it 'returns a 403' do
def make_request is_expected.to have_gitlab_http_status(:forbidden)
post url, args, env expect(response.body).to eql('Geo push user is invalid.')
end
end end
end
let(:url) { "/#{project.full_path}.git/#{path}" } context 'and is valid' do
context 'but the user has no access' do
let(:geo_gl_id) { "key-#{key_for_user_without_any_access.id}" }
subject do it 'returns a 404' do
make_request is_expected.to have_gitlab_http_status(:not_found)
response expect(response.body).to eql('The project you were looking for could not be found.')
end
end end
it 'redirects to the primary' do context 'but the user does not have push access' do
is_expected.to have_gitlab_http_status(:redirect) let(:geo_gl_id) { "key-#{key_for_user_without_push_access.id}" }
redirect_location = "#{primary.url.chomp('/')}#{url}"
expect(subject.header['Location']).to eq(redirect_location) it 'returns a 403' do
is_expected.to have_gitlab_http_status(:forbidden)
expect(response.body).to eql('You are not allowed to push code to this project.')
end
end
context 'and the user has push access' do
let(:geo_gl_id) { "key-#{key.id}" }
it 'returns a 200' do
is_expected.to have_gitlab_http_status(:ok)
expect(json_response['GL_ID']).to match("user-#{user.id}")
expect(json_response['GL_REPOSITORY']).to match(Gitlab::GlRepository.gl_repository(project, false))
end
end end
end 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