Skip to content

Commit 6c7f7b4

Browse files
author
Douwe Maan
committed
Merge branch 'lfs-authenticate-support' into 'master'
Added LFS support to SSH Required changes to GitLab Shell include the actual handling of the `git-lfs-authenticate` command and the retrieval of the correct credentials. Needed for gitlab-org/gitlab-ce!6043 Related to gitlab-org/gitlab-ce#3589 > **Note:** gitlab-org/gitlab-ce!6043 needs to be merged before this one. cc @jacobvosmaer-gitlab @marin @DouweM See merge request !86
2 parents c6d8af5 + 3c9ef9e commit 6c7f7b4

File tree

8 files changed

+191
-5
lines changed

8 files changed

+191
-5
lines changed

CHANGELOG

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
v3.5.0
22
- Add option to recover 2FA via SSH
3+
- Added full support for `git-lfs-authenticate` to properly handle LFS requests and pass them on to Workhorse
34

45
v3.4.0
56
- Redis Sentinel support

lib/gitlab_lfs_authentication.rb

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
require 'base64'
2+
require 'json'
3+
4+
class GitlabLfsAuthentication
5+
attr_accessor :username, :lfs_token, :repository_http_path
6+
7+
def initialize(username, lfs_token, repository_http_path)
8+
@username = username
9+
@lfs_token = lfs_token
10+
@repository_http_path = repository_http_path
11+
end
12+
13+
def self.build_from_json(json)
14+
begin
15+
values = JSON.parse(json)
16+
self.new(values['username'], values['lfs_token'], values['repository_http_path'])
17+
rescue
18+
nil
19+
end
20+
end
21+
22+
def authentication_payload
23+
authorization = {
24+
header: {
25+
Authorization: "Basic #{Base64.strict_encode64("#{username}:#{lfs_token}")}"
26+
},
27+
href: "#{repository_http_path}/info/lfs/"
28+
}
29+
30+
JSON.generate(authorization)
31+
end
32+
end

lib/gitlab_net.rb

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
require_relative 'gitlab_logger'
77
require_relative 'gitlab_access'
88
require_relative 'gitlab_redis'
9+
require_relative 'gitlab_lfs_authentication'
910
require_relative 'httpunix'
1011

1112
class GitlabNet
@@ -15,15 +16,12 @@ class ApiUnreachableError < StandardError; end
1516
READ_TIMEOUT = 300
1617

1718
def check_access(cmd, repo, actor, changes, protocol)
18-
project_name = repo.gsub("'", "")
19-
project_name = project_name.gsub(/\.git\Z/, "")
20-
project_name = project_name.gsub(/\A\//, "")
2119
changes = changes.join("\n") unless changes.kind_of?(String)
2220

2321
params = {
2422
action: cmd,
2523
changes: changes,
26-
project: project_name,
24+
project: project_name(repo),
2725
protocol: protocol
2826
}
2927

@@ -49,6 +47,19 @@ def discover(key)
4947
JSON.parse(resp.body) rescue nil
5048
end
5149

50+
def lfs_authenticate(key, repo)
51+
params = {
52+
project: project_name(repo),
53+
key_id: key.gsub('key-', '')
54+
}
55+
56+
resp = post("#{host}/lfs_authenticate", params)
57+
58+
if resp.code == '200'
59+
GitlabLfsAuthentication.build_from_json(resp.body)
60+
end
61+
end
62+
5263
def broadcast_message
5364
resp = get("#{host}/broadcast_message")
5465
JSON.parse(resp.body) rescue {}
@@ -107,6 +118,12 @@ def redis_client
107118

108119
protected
109120

121+
def project_name(repo)
122+
project_name = repo.gsub("'", "")
123+
project_name = project_name.gsub(/\.git\Z/, "")
124+
project_name.gsub(/\A\//, "")
125+
end
126+
110127
def config
111128
@config ||= GitlabConfig.new
112129
end

lib/gitlab_shell.rb

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class InvalidRepositoryPathError < StandardError; end
1111
API_COMMANDS = %w(2fa_recovery_codes)
1212
GL_PROTOCOL = 'ssh'.freeze
1313

14-
attr_accessor :key_id, :repo_name, :command
14+
attr_accessor :key_id, :repo_name, :command, :git_access
1515
attr_reader :repo_path
1616

1717
def initialize(key_id)
@@ -117,6 +117,11 @@ def process_cmd(args)
117117

118118
$logger.info "gitlab-shell: executing git-annex command <#{parsed_args.join(' ')}> for #{log_username}."
119119
exec_cmd(*parsed_args)
120+
121+
elsif @command == 'git-lfs-authenticate'
122+
$logger.info "gitlab-shell: Processing LFS authentication for #{log_username}."
123+
lfs_authenticate
124+
120125
else
121126
$logger.info "gitlab-shell: executing git command <#{@command} #{repo_path}> for #{log_username}."
122127
exec_cmd(@command, repo_path)
@@ -184,6 +189,14 @@ def gcryptsetup?(args)
184189
non_dashed[0, 2] == %w{git-annex-shell gcryptsetup}
185190
end
186191

192+
def lfs_authenticate
193+
lfs_access = api.lfs_authenticate(@key_id, @repo_name)
194+
195+
return unless lfs_access
196+
197+
puts lfs_access.authentication_payload
198+
end
199+
187200
private
188201

189202
def continue?(question)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
require 'spec_helper'
2+
require 'gitlab_lfs_authentication'
3+
require 'json'
4+
5+
describe GitlabLfsAuthentication do
6+
subject do
7+
GitlabLfsAuthentication.build_from_json(
8+
JSON.generate(
9+
{
10+
username: 'dzaporozhets',
11+
lfs_token: 'wsnys8Zm8Jn7zyhHTAAK',
12+
repository_http_path: 'http://gitlab.dev/repo'
13+
}
14+
)
15+
)
16+
end
17+
18+
describe '#build_from_json' do
19+
it { subject.username.should == 'dzaporozhets' }
20+
it { subject.lfs_token.should == 'wsnys8Zm8Jn7zyhHTAAK' }
21+
it { subject.repository_http_path.should == 'http://gitlab.dev/repo' }
22+
end
23+
24+
describe '#authentication_payload' do
25+
result = "{\"header\":{\"Authorization\":\"Basic ZHphcG9yb3poZXRzOndzbnlzOFptOEpuN3p5aEhUQUFL\"},\"href\":\"http://gitlab.dev/repo/info/lfs/\"}"
26+
27+
it { subject.authentication_payload.should eq(result) }
28+
29+
it 'should be a proper JSON' do
30+
payload = subject.authentication_payload
31+
json_payload = JSON.parse(payload)
32+
33+
json_payload['header']['Authorization'].should eq('Basic ZHphcG9yb3poZXRzOndzbnlzOFptOEpuN3p5aEhUQUFL')
34+
json_payload['href'].should eq('http://gitlab.dev/repo/info/lfs/')
35+
end
36+
end
37+
end

spec/gitlab_net_spec.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
VCR.use_cassette("discover-ok") do
3939
user = gitlab_net.discover('key-126')
4040
user['name'].should == 'Dmitriy Zaporozhets'
41+
user['username'].should == 'dzaporozhets'
4142
end
4243
end
4344

@@ -56,6 +57,19 @@
5657
end
5758
end
5859

60+
describe '#lfs_authenticate' do
61+
context 'lfs authentication succeeded' do
62+
it 'should return the correct data' do
63+
VCR.use_cassette('lfs-authenticate-ok') do
64+
lfs_access = gitlab_net.lfs_authenticate('key-126', 'gitlab/gitlabhq.git')
65+
lfs_access.username.should == 'dzaporozhets'
66+
lfs_access.lfs_token.should == 'wsnys8Zm8Jn7zyhHTAAK'
67+
lfs_access.repository_http_path.should == 'http://gitlab.dev/gitlab/gitlabhq.git'
68+
end
69+
end
70+
end
71+
end
72+
5973
describe :broadcast_message do
6074
context "broadcast message exists" do
6175
it 'should return message' do

spec/gitlab_shell_spec.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,32 @@
112112
its(:repo_name) { should == 'dzaporozhets/gitlab.git' }
113113
its(:command) { should == 'git-annex-shell' }
114114
end
115+
116+
describe 'git-lfs' do
117+
let(:repo_name) { 'dzaporozhets/gitlab.git' }
118+
let(:ssh_args) { %W(git-lfs-authenticate dzaporozhets/gitlab.git download) }
119+
120+
before do
121+
subject.send :parse_cmd, ssh_args
122+
end
123+
124+
its(:repo_name) { should == 'dzaporozhets/gitlab.git' }
125+
its(:command) { should == 'git-lfs-authenticate' }
126+
its(:git_access) { should == 'git-upload-pack' }
127+
end
128+
129+
describe 'git-lfs old clients' do
130+
let(:repo_name) { 'dzaporozhets/gitlab.git' }
131+
let(:ssh_args) { %W(git-lfs-authenticate dzaporozhets/gitlab.git download long_oid) }
132+
133+
before do
134+
subject.send :parse_cmd, ssh_args
135+
end
136+
137+
its(:repo_name) { should == 'dzaporozhets/gitlab.git' }
138+
its(:command) { should == 'git-lfs-authenticate' }
139+
its(:git_access) { should == 'git-upload-pack' }
140+
end
115141
end
116142

117143
describe :exec do

spec/vcr_cassettes/lfs-authenticate-ok.yml

Lines changed: 46 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)