Commit dbf374e1 authored by Patricio Cano's avatar Patricio Cano

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.
parent c6d8af59
v3.5.0 v3.5.0
- Add option to recover 2FA via SSH - Add option to recover 2FA via SSH
- Added full support for `git-lfs-authenticate` to properly handle LFS requests and pass them on to Workhorse
v3.4.0 v3.4.0
- Redis Sentinel support - Redis Sentinel support
......
require 'json' require 'json'
class GitAccessStatus class GitAccessStatus
attr_reader :message, :repository_path attr_reader :message, :repository_path, :repository_http_path
def initialize(status, message, repository_path) def initialize(status, message, repository_path, repository_http_path)
@status = status @status = status
@message = message @message = message
@repository_path = repository_path @repository_path = repository_path
@repository_http_path = repository_http_path
end end
def self.create_from_json(json) def self.create_from_json(json)
values = JSON.parse(json) values = JSON.parse(json)
self.new(values["status"], values["message"], values["repository_path"]) self.new(values["status"], values["message"], values["repository_path"], values["repository_http_path"])
end end
def allowed? def allowed?
......
...@@ -39,7 +39,7 @@ class GitlabNet ...@@ -39,7 +39,7 @@ class GitlabNet
if resp.code == '200' if resp.code == '200'
GitAccessStatus.create_from_json(resp.body) GitAccessStatus.create_from_json(resp.body)
else else
GitAccessStatus.new(false, 'API is not accessible', nil) GitAccessStatus.new(false, 'API is not accessible', nil, nil)
end end
end end
......
require 'shellwords' require 'shellwords'
require 'base64'
require 'json'
require_relative 'gitlab_net' require_relative 'gitlab_net'
...@@ -11,7 +13,7 @@ class GitlabShell ...@@ -11,7 +13,7 @@ class GitlabShell
API_COMMANDS = %w(2fa_recovery_codes) API_COMMANDS = %w(2fa_recovery_codes)
GL_PROTOCOL = 'ssh'.freeze GL_PROTOCOL = 'ssh'.freeze
attr_accessor :key_id, :repo_name, :command attr_accessor :key_id, :repo_name, :command, :git_access, :repository_http_path
attr_reader :repo_path attr_reader :repo_path
def initialize(key_id) def initialize(key_id)
...@@ -94,6 +96,7 @@ class GitlabShell ...@@ -94,6 +96,7 @@ class GitlabShell
raise AccessDeniedError, status.message unless status.allowed? raise AccessDeniedError, status.message unless status.allowed?
self.repo_path = status.repository_path self.repo_path = status.repository_path
@repository_http_path = status.repository_http_path
end end
def process_cmd(args) def process_cmd(args)
...@@ -117,6 +120,11 @@ class GitlabShell ...@@ -117,6 +120,11 @@ class GitlabShell
$logger.info "gitlab-shell: executing git-annex command <#{parsed_args.join(' ')}> for #{log_username}." $logger.info "gitlab-shell: executing git-annex command <#{parsed_args.join(' ')}> for #{log_username}."
exec_cmd(*parsed_args) exec_cmd(*parsed_args)
elsif @command == 'git-lfs-authenticate'
$logger.info "gitlab-shell: Processing LFS authentication for #{log_username}."
lfs_authenticate
else else
$logger.info "gitlab-shell: executing git command <#{@command} #{repo_path}> for #{log_username}." $logger.info "gitlab-shell: executing git command <#{@command} #{repo_path}> for #{log_username}."
exec_cmd(@command, repo_path) exec_cmd(@command, repo_path)
...@@ -184,6 +192,19 @@ class GitlabShell ...@@ -184,6 +192,19 @@ class GitlabShell
non_dashed[0, 2] == %w{git-annex-shell gcryptsetup} non_dashed[0, 2] == %w{git-annex-shell gcryptsetup}
end end
def lfs_authenticate
return unless user
authorization = {
header: {
Authorization: "Basic #{Base64.strict_encode64("#{user['username']}:#{user['lfs_token']}")}"
},
href: "#{repository_http_path}/info/lfs/"
}
puts JSON.generate(authorization)
end
private private
def continue?(question) def continue?(question)
......
...@@ -7,7 +7,7 @@ describe GitlabAccess do ...@@ -7,7 +7,7 @@ describe GitlabAccess do
let(:repo_path) { File.join(repository_path, repo_name) + ".git" } let(:repo_path) { File.join(repository_path, repo_name) + ".git" }
let(:api) do let(:api) do
double(GitlabNet).tap do |api| double(GitlabNet).tap do |api|
api.stub(check_access: GitAccessStatus.new(true, 'ok', '/home/git/repositories')) api.stub(check_access: GitAccessStatus.new(true, 'ok', '/home/git/repositories', 'http://gitlab.dev/repo'))
end end
end end
subject do subject do
...@@ -39,7 +39,7 @@ describe GitlabAccess do ...@@ -39,7 +39,7 @@ describe GitlabAccess do
context "access is denied" do context "access is denied" do
before do before do
api.stub(check_access: GitAccessStatus.new(false, 'denied', nil)) api.stub(check_access: GitAccessStatus.new(false, 'denied', nil, nil))
end end
it "returns false" do it "returns false" do
......
...@@ -38,6 +38,8 @@ describe GitlabNet, vcr: true do ...@@ -38,6 +38,8 @@ describe GitlabNet, vcr: true do
VCR.use_cassette("discover-ok") do VCR.use_cassette("discover-ok") do
user = gitlab_net.discover('key-126') user = gitlab_net.discover('key-126')
user['name'].should == 'Dmitriy Zaporozhets' user['name'].should == 'Dmitriy Zaporozhets'
user['lfs_token'].should == 'wsnys8Zm8Jn7zyhHTAAK'
user['username'].should == 'dzaporozhets'
end end
end end
...@@ -130,6 +132,7 @@ describe GitlabNet, vcr: true do ...@@ -130,6 +132,7 @@ describe GitlabNet, vcr: true do
VCR.use_cassette("allowed-pull") do VCR.use_cassette("allowed-pull") do
access = gitlab_net.check_access('git-receive-pack', 'gitlab/gitlabhq.git', 'key-126', changes, 'ssh') access = gitlab_net.check_access('git-receive-pack', 'gitlab/gitlabhq.git', 'key-126', changes, 'ssh')
access.allowed?.should be_true access.allowed?.should be_true
access.repository_http_path.should == 'http://gitlab.dev/gitlab/gitlabhq.git'
end end
end end
......
...@@ -36,6 +36,7 @@ describe GitlabShell do ...@@ -36,6 +36,7 @@ describe GitlabShell do
let(:repo_name) { 'gitlab-ci.git' } let(:repo_name) { 'gitlab-ci.git' }
let(:repo_path) { File.join(tmp_repos_path, repo_name) } let(:repo_path) { File.join(tmp_repos_path, repo_name) }
let(:repo_http_path) { 'http://gitlab.dev/dzaporozhets/gitlab.git' }
before do before do
GitlabConfig.any_instance.stub(audit_usernames: false) GitlabConfig.any_instance.stub(audit_usernames: false)
...@@ -112,6 +113,19 @@ describe GitlabShell do ...@@ -112,6 +113,19 @@ describe GitlabShell do
its(:repo_name) { should == 'dzaporozhets/gitlab.git' } its(:repo_name) { should == 'dzaporozhets/gitlab.git' }
its(:command) { should == 'git-annex-shell' } its(:command) { should == 'git-annex-shell' }
end end
describe 'git-lfs' do
let(:repo_name) { 'dzaporozhets/gitlab.git' }
let(:ssh_args) { %W(git-lfs-authenticate dzaporozhets/gitlab.git download) }
before do
subject.send :parse_cmd, ssh_args
end
its(:repo_name) { should == 'dzaporozhets/gitlab.git' }
its(:command) { should == 'git-lfs-authenticate' }
its(:git_access) { should == 'git-upload-pack' }
end
end end
describe :exec do describe :exec do
...@@ -306,7 +320,7 @@ describe GitlabShell do ...@@ -306,7 +320,7 @@ describe GitlabShell do
end end
it "should disallow access and log the attempt if check_access returns false status" do it "should disallow access and log the attempt if check_access returns false status" do
api.stub(check_access: GitAccessStatus.new(false, 'denied', nil)) api.stub(check_access: GitAccessStatus.new(false, 'denied', nil, nil))
message = "gitlab-shell: Access denied for git command <git-upload-pack gitlab-ci.git> " message = "gitlab-shell: Access denied for git command <git-upload-pack gitlab-ci.git> "
message << "by user with key #{key_id}." message << "by user with key #{key_id}."
$logger.should_receive(:warn).with(message) $logger.should_receive(:warn).with(message)
......
...@@ -42,7 +42,7 @@ http_interactions: ...@@ -42,7 +42,7 @@ http_interactions:
- '0.089741' - '0.089741'
body: body:
encoding: UTF-8 encoding: UTF-8
string: '{"status": "true"}' string: '{"status": "true","repository_http_path": "http://gitlab.dev/gitlab/gitlabhq.git"}'
http_version: http_version:
recorded_at: Wed, 03 Sep 2014 11:27:36 GMT recorded_at: Wed, 03 Sep 2014 11:27:36 GMT
recorded_with: VCR 2.4.0 recorded_with: VCR 2.4.0
...@@ -40,7 +40,7 @@ http_interactions: ...@@ -40,7 +40,7 @@ http_interactions:
- '0.016934' - '0.016934'
body: body:
encoding: UTF-8 encoding: UTF-8
string: '{"name":"Dmitriy Zaporozhets","username":"dzaporozhets"}' string: '{"name":"Dmitriy Zaporozhets","username":"dzaporozhets","lfs_token":"wsnys8Zm8Jn7zyhHTAAK"}'
http_version: http_version:
recorded_at: Wed, 03 Sep 2014 11:27:35 GMT recorded_at: Wed, 03 Sep 2014 11:27:35 GMT
recorded_with: VCR 2.4.0 recorded_with: VCR 2.4.0
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