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
- 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
- Redis Sentinel support
......
require 'json'
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
@message = message
@repository_path = repository_path
@repository_http_path = repository_http_path
end
def self.create_from_json(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
def allowed?
......
......@@ -39,7 +39,7 @@ class GitlabNet
if resp.code == '200'
GitAccessStatus.create_from_json(resp.body)
else
GitAccessStatus.new(false, 'API is not accessible', nil)
GitAccessStatus.new(false, 'API is not accessible', nil, nil)
end
end
......
require 'shellwords'
require 'base64'
require 'json'
require_relative 'gitlab_net'
......@@ -11,7 +13,7 @@ class GitlabShell
API_COMMANDS = %w(2fa_recovery_codes)
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
def initialize(key_id)
......@@ -94,6 +96,7 @@ class GitlabShell
raise AccessDeniedError, status.message unless status.allowed?
self.repo_path = status.repository_path
@repository_http_path = status.repository_http_path
end
def process_cmd(args)
......@@ -117,6 +120,11 @@ class GitlabShell
$logger.info "gitlab-shell: executing git-annex command <#{parsed_args.join(' ')}> for #{log_username}."
exec_cmd(*parsed_args)
elsif @command == 'git-lfs-authenticate'
$logger.info "gitlab-shell: Processing LFS authentication for #{log_username}."
lfs_authenticate
else
$logger.info "gitlab-shell: executing git command <#{@command} #{repo_path}> for #{log_username}."
exec_cmd(@command, repo_path)
......@@ -184,6 +192,19 @@ class GitlabShell
non_dashed[0, 2] == %w{git-annex-shell gcryptsetup}
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
def continue?(question)
......
......@@ -7,7 +7,7 @@ describe GitlabAccess do
let(:repo_path) { File.join(repository_path, repo_name) + ".git" }
let(:api) do
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
subject do
......@@ -39,7 +39,7 @@ describe GitlabAccess do
context "access is denied" do
before do
api.stub(check_access: GitAccessStatus.new(false, 'denied', nil))
api.stub(check_access: GitAccessStatus.new(false, 'denied', nil, nil))
end
it "returns false" do
......
......@@ -38,6 +38,8 @@ describe GitlabNet, vcr: true do
VCR.use_cassette("discover-ok") do
user = gitlab_net.discover('key-126')
user['name'].should == 'Dmitriy Zaporozhets'
user['lfs_token'].should == 'wsnys8Zm8Jn7zyhHTAAK'
user['username'].should == 'dzaporozhets'
end
end
......@@ -130,6 +132,7 @@ describe GitlabNet, vcr: true do
VCR.use_cassette("allowed-pull") do
access = gitlab_net.check_access('git-receive-pack', 'gitlab/gitlabhq.git', 'key-126', changes, 'ssh')
access.allowed?.should be_true
access.repository_http_path.should == 'http://gitlab.dev/gitlab/gitlabhq.git'
end
end
......
......@@ -36,6 +36,7 @@ describe GitlabShell do
let(:repo_name) { 'gitlab-ci.git' }
let(:repo_path) { File.join(tmp_repos_path, repo_name) }
let(:repo_http_path) { 'http://gitlab.dev/dzaporozhets/gitlab.git' }
before do
GitlabConfig.any_instance.stub(audit_usernames: false)
......@@ -112,6 +113,19 @@ describe GitlabShell do
its(:repo_name) { should == 'dzaporozhets/gitlab.git' }
its(:command) { should == 'git-annex-shell' }
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
describe :exec do
......@@ -306,7 +320,7 @@ describe GitlabShell do
end
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 << "by user with key #{key_id}."
$logger.should_receive(:warn).with(message)
......
......@@ -42,7 +42,7 @@ http_interactions:
- '0.089741'
body:
encoding: UTF-8
string: '{"status": "true"}'
string: '{"status": "true","repository_http_path": "http://gitlab.dev/gitlab/gitlabhq.git"}'
http_version:
recorded_at: Wed, 03 Sep 2014 11:27:36 GMT
recorded_with: VCR 2.4.0
......@@ -40,7 +40,7 @@ http_interactions:
- '0.016934'
body:
encoding: UTF-8
string: '{"name":"Dmitriy Zaporozhets","username":"dzaporozhets"}'
string: '{"name":"Dmitriy Zaporozhets","username":"dzaporozhets","lfs_token":"wsnys8Zm8Jn7zyhHTAAK"}'
http_version:
recorded_at: Wed, 03 Sep 2014 11:27:35 GMT
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