Commit 8b31d696 authored by Rémy Coutable's avatar Rémy Coutable

[EE] Fix Error 500 when pushing LFS objects with a write deploy key

EE backport of https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/15039Signed-off-by: default avatarRémy Coutable <remy@rymai.me>
parent 747646b7
...@@ -74,9 +74,10 @@ module LfsRequest ...@@ -74,9 +74,10 @@ module LfsRequest
def lfs_upload_access? def lfs_upload_access?
return false unless project.lfs_enabled? return false unless project.lfs_enabled?
return false unless has_authentication_ability?(:push_code)
return false if project.above_size_limit? || objects_exceed_repo_limit? return false if project.above_size_limit? || objects_exceed_repo_limit?
has_authentication_ability?(:push_code) && can?(user, :push_code, project) lfs_deploy_token? || can?(user, :push_code, project)
end end
def lfs_deploy_token? def lfs_deploy_token?
......
...@@ -26,7 +26,7 @@ module Gitlab ...@@ -26,7 +26,7 @@ module Gitlab
result = result =
service_request_check(login, password, project) || service_request_check(login, password, project) ||
build_access_token_check(login, password) || build_access_token_check(login, password) ||
lfs_token_check(login, password) || lfs_token_check(login, password, project) ||
oauth_access_token_check(login, password) || oauth_access_token_check(login, password) ||
personal_access_token_check(password) || personal_access_token_check(password) ||
user_with_password_for_git(login, password) || user_with_password_for_git(login, password) ||
...@@ -147,7 +147,7 @@ module Gitlab ...@@ -147,7 +147,7 @@ module Gitlab
end.flatten.uniq end.flatten.uniq
end end
def lfs_token_check(login, password) def lfs_token_check(login, password, project)
deploy_key_matches = login.match(/\Alfs\+deploy-key-(\d+)\z/) deploy_key_matches = login.match(/\Alfs\+deploy-key-(\d+)\z/)
actor = actor =
...@@ -164,6 +164,8 @@ module Gitlab ...@@ -164,6 +164,8 @@ module Gitlab
authentication_abilities = authentication_abilities =
if token_handler.user? if token_handler.user?
full_authentication_abilities full_authentication_abilities
elsif token_handler.deploy_key_pushable?(project)
read_write_authentication_abilities
else else
read_authentication_abilities read_authentication_abilities
end end
...@@ -209,10 +211,15 @@ module Gitlab ...@@ -209,10 +211,15 @@ module Gitlab
] ]
end end
def full_authentication_abilities def read_write_authentication_abilities
read_authentication_abilities + [ read_authentication_abilities + [
:push_code, :push_code,
:create_container_image, :create_container_image
]
end
def full_authentication_abilities
read_write_authentication_abilities + [
:admin_container_image :admin_container_image
] ]
end end
......
...@@ -27,6 +27,10 @@ module Gitlab ...@@ -27,6 +27,10 @@ module Gitlab
end end
end end
def deploy_key_pushable?(project)
actor.is_a?(DeployKey) && actor.can_push_to?(project)
end
def user? def user?
actor.is_a?(User) actor.is_a?(User)
end end
......
...@@ -133,6 +133,25 @@ describe Gitlab::Auth do ...@@ -133,6 +133,25 @@ describe Gitlab::Auth do
gl_auth.find_for_git_client(user.username, token, project: nil, ip: 'ip') gl_auth.find_for_git_client(user.username, token, project: nil, ip: 'ip')
end end
it 'grants deploy key write permissions' do
project = create(:project)
key = create(:deploy_key, can_push: true)
create(:deploy_keys_project, deploy_key: key, project: project)
token = Gitlab::LfsToken.new(key).token
expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: "lfs+deploy-key-#{key.id}")
expect(gl_auth.find_for_git_client("lfs+deploy-key-#{key.id}", token, project: project, ip: 'ip')).to eq(Gitlab::Auth::Result.new(key, nil, :lfs_deploy_token, read_write_authentication_abilities))
end
it 'does not grant deploy key write permissions' do
project = create(:project)
key = create(:deploy_key, can_push: true)
token = Gitlab::LfsToken.new(key).token
expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: "lfs+deploy-key-#{key.id}")
expect(gl_auth.find_for_git_client("lfs+deploy-key-#{key.id}", token, project: project, ip: 'ip')).to eq(Gitlab::Auth::Result.new(key, nil, :lfs_deploy_token, read_authentication_abilities))
end
end end
context 'while using OAuth tokens as passwords' do context 'while using OAuth tokens as passwords' do
...@@ -326,10 +345,15 @@ describe Gitlab::Auth do ...@@ -326,10 +345,15 @@ describe Gitlab::Auth do
] ]
end end
def full_authentication_abilities def read_write_authentication_abilities
read_authentication_abilities + [ read_authentication_abilities + [
:push_code, :push_code,
:create_container_image, :create_container_image
]
end
def full_authentication_abilities
read_write_authentication_abilities + [
:admin_container_image :admin_container_image
] ]
end end
......
...@@ -671,6 +671,48 @@ describe 'Git LFS API and storage' do ...@@ -671,6 +671,48 @@ describe 'Git LFS API and storage' do
} }
end end
shared_examples 'pushes new LFS objects' do
let(:sample_size) { 150.megabytes }
let(:sample_oid) { '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897' }
it 'responds with upload hypermedia link' do
expect(response).to have_gitlab_http_status(200)
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(json_response['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.full_path}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}")
expect(json_response['objects'].first['actions']['upload']['header']).to eq('Authorization' => authorization)
end
## EE-specific context
context 'and project is above the limit' do
let(:update_lfs_permissions) do
allow_any_instance_of(EE::Project).to receive_messages(
repository_and_lfs_size: 100.megabytes,
actual_size_limit: 99.megabytes)
end
it 'responds with status 406' do
expect(response).to have_gitlab_http_status(406)
expect(json_response['message']).to eql('Your push has been rejected, because this repository has exceeded its size limit of 99 MB by 1 MB. Please contact your GitLab administrator for more information.')
end
end
context 'and project will go over the limit' do
let(:update_lfs_permissions) do
allow_any_instance_of(EE::Project).to receive_messages(
repository_and_lfs_size: 200.megabytes,
actual_size_limit: 300.megabytes)
end
it 'responds with status 406' do
expect(response).to have_gitlab_http_status(406)
expect(json_response['documentation_url']).to include('/help')
expect(json_response['message']).to eql('Your push has been rejected, because this repository has exceeded its size limit of 300 MB by 50 MB. Please contact your GitLab administrator for more information.')
end
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
let(:authorization) { authorize_user } let(:authorization) { authorize_user }
...@@ -701,63 +743,7 @@ describe 'Git LFS API and storage' do ...@@ -701,63 +743,7 @@ describe 'Git LFS API and storage' do
end end
context 'when pushing a lfs object that does not exist' do context 'when pushing a lfs object that does not exist' do
let(:sample_size) { 150.megabytes } it_behaves_like 'pushes new LFS objects'
let(:sample_oid) { '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897' }
let(:body) do
{
'operation' => 'upload',
'objects' => [
{ 'oid' => sample_oid,
'size' => sample_size }
]
}
end
it 'responds with status 200' do
expect(response).to have_gitlab_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(sample_oid)
expect(json_response['objects'].first['size']).to eq(sample_size)
expect(json_response['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.full_path}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}")
expect(json_response['objects'].first['actions']['upload']['header']).to eq('Authorization' => authorization)
end
context 'and project is above the limit' do
let(:update_lfs_permissions) do
allow_any_instance_of(EE::Project).to receive_messages(
repository_and_lfs_size: 100.megabytes,
actual_size_limit: 99.megabytes)
end
it 'responds with status 406' do
expect(response).to have_gitlab_http_status(406)
end
it 'show correct error message' do
expect(json_response['message']).to eql('Your push has been rejected, because this repository has exceeded its size limit of 99 MB by 1 MB. Please contact your GitLab administrator for more information.')
end
end
context 'and project will go over the limit' do
let(:update_lfs_permissions) do
allow_any_instance_of(EE::Project).to receive_messages(
repository_and_lfs_size: 200.megabytes,
actual_size_limit: 300.megabytes)
end
it 'responds with status 406' do
expect(response).to have_gitlab_http_status(406)
expect(json_response['documentation_url']).to include('/help')
end
it 'show correct error message' do
expect(json_response['message']).to eql('Your push has been rejected, because this repository has exceeded its size limit of 300 MB by 50 MB. Please contact your GitLab administrator for more information.')
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
...@@ -838,6 +824,17 @@ describe 'Git LFS API and storage' do ...@@ -838,6 +824,17 @@ describe 'Git LFS API and storage' do
end end
end end
end end
context 'when deploy key has project push access' do
let(:key) { create(:deploy_key, can_push: true) }
let(:authorization) { authorize_deploy_key }
let(:update_user_permissions) do
project.deploy_keys << key
end
it_behaves_like 'pushes new LFS objects'
end
end end
context 'when user is not authenticated' do context 'when user is not authenticated' do
......
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