summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAsh McKenzie <amckenzie@gitlab.com>2018-07-25 23:53:24 +1000
committerAsh McKenzie <amckenzie@gitlab.com>2018-07-25 23:53:24 +1000
commit5e04d2613f6cecd41407e5037d8e2a36fe241151 (patch)
treed9250aa2c5492bd7e3373e8cff5bc6f7122ba4db
parentcf27364efc75388de805adc368eb6124c180b1b4 (diff)
downloadgitlab-shell-ash.mckenzie/srp-refactor.tar.gz
-rw-r--r--lib/errors.rb1
-rw-r--r--lib/gitlab_shell.rb17
-rw-r--r--spec/gitlab_shell_spec.rb1106
3 files changed, 628 insertions, 496 deletions
diff --git a/lib/errors.rb b/lib/errors.rb
index f504f56..9e4d27b 100644
--- a/lib/errors.rb
+++ b/lib/errors.rb
@@ -1 +1,2 @@
class AccessDeniedError < StandardError; end
+class InvalidRepositoryPathError < StandardError; end
diff --git a/lib/gitlab_shell.rb b/lib/gitlab_shell.rb
index 10a9256..0a73e04 100644
--- a/lib/gitlab_shell.rb
+++ b/lib/gitlab_shell.rb
@@ -14,7 +14,6 @@ class GitlabShell
include LogHelper
class DisallowedCommandError < StandardError; end
- class InvalidRepositoryPathError < StandardError; end
GIT_COMMANDS = %w(git-upload-pack git-receive-pack git-upload-archive git-lfs-authenticate).freeze
API_COMMANDS = %w(2fa_recovery_codes).freeze
@@ -28,7 +27,7 @@ class GitlabShell
# ssh git@gitlab.example.com 'evil command', then origin_cmd contains
# 'evil command'.
def exec(origin_cmd)
- unless origin_cmd
+ if !origin_cmd || origin_cmd.empty?
puts "Welcome to GitLab, #{username}!"
return true
end
@@ -49,10 +48,10 @@ class GitlabShell
false
rescue DisallowedCommandError
$logger.warn('Denied disallowed command', command: origin_cmd, user: log_username)
- $stderr.puts "GitLab: Disallowed command"
+ $stderr.puts 'GitLab: Disallowed command'
false
rescue InvalidRepositoryPathError
- $stderr.puts "GitLab: Invalid repository path"
+ $stderr.puts 'GitLab: Invalid repository path'
false
end
@@ -61,6 +60,10 @@ class GitlabShell
attr_accessor :repo_name, :command, :git_access
attr_reader :config, :key_id, :repo_path
+ def api
+ @api ||= GitlabNet.new
+ end
+
def parse_cmd(args)
# Handle Git for Windows 2.14 using "git upload-pack" instead of git-upload-pack
if args.length == 3 && args.first == 'git'
@@ -89,7 +92,7 @@ class GitlabShell
raise DisallowedCommandError
end
else
- raise DisallowedCommandError unless args.count == 2
+ raise DisallowedCommandError, 'Expected two arguments' unless args.count == 2
@repo_name = args.last
end
@@ -112,10 +115,6 @@ class GitlabShell
action.execute(command, args)
end
- def api
- GitlabNet.new
- end
-
def lfs_authenticate
lfs_access = api.lfs_authenticate(key_id, repo_name)
return unless lfs_access
diff --git a/spec/gitlab_shell_spec.rb b/spec/gitlab_shell_spec.rb
index e02e452..4da5c52 100644
--- a/spec/gitlab_shell_spec.rb
+++ b/spec/gitlab_shell_spec.rb
@@ -13,41 +13,44 @@ describe GitlabShell do
end
subject do
- ARGV[0] = key_id
- GitlabShell.new(key_id).tap do |shell|
- shell.stub(exec_cmd: :exec_called)
- shell.stub(api: api)
- end
- end
-
- let(:gitaly_check_access) { GitAccessStatus.new(
- true,
- 'ok',
- gl_repository: gl_repository,
- gl_username: gl_username,
- repository_path: repo_path,
- gitaly: { 'repository' => { 'relative_path' => repo_name, 'storage_name' => 'default'} , 'address' => 'unix:gitaly.socket' }
- )
- }
-
- let(:api) do
- double(GitlabNet).tap do |api|
- api.stub(discover: { 'name' => 'John Doe', 'username' => 'testuser' })
- api.stub(check_access: GitAccessStatus.new(
- true,
- 'ok',
- gl_repository: gl_repository,
- gl_username: gl_username,
- repository_path: repo_path,
- gitaly: nil))
- api.stub(two_factor_recovery_codes: {
- 'success' => true,
- 'recovery_codes' => ['f67c514de60c4953', '41278385fc00c1e0']
- })
- end
+ # ARGV[0] = key_id
+ # GitlabShell.new(key_id).tap do |shell|
+ # shell.stub(exec_cmd: :exec_called)
+ # shell.stub(api: api)
+ # end
+ described_class.new(key_id)
end
let(:key_id) { "key-#{rand(100) + 100}" }
+ let(:gitaly_action) { Action::Gitaly.new(
+ key_id,
+ gl_repository,
+ gl_username,
+ repo_path,
+ { 'repository' => { 'relative_path' => repo_name, 'storage_name' => 'default'} , 'address' => 'unix:gitaly.socket' })
+ }
+
+ # let(:gitaly_nil_action) { Action::Gitaly.new(
+ # key_id,
+ # gl_repository,
+ # gl_username,
+ # repo_path,
+ # nil)
+ # }
+
+ let(:api) { double(GitlabNet) }
+
+ # let(:api) do
+ # double(GitlabNet).tap do |api|
+ # api.stub(discover: { 'name' => 'John Doe', 'username' => 'testuser' })
+ # api.stub(check_access: gitaly_nil_action) # FIXME: need two different?
+ # api.stub(two_factor_recovery_codes: {
+ # 'success' => true,
+ # 'recovery_codes' => ['f67c514de60c4953', '41278385fc00c1e0']
+ # })
+ # end
+ # end
+
let(:ssh_cmd) { nil }
let(:tmp_repos_path) { File.join(ROOT_PATH, 'tmp', 'repositories') }
@@ -57,517 +60,646 @@ describe GitlabShell do
let(:gl_username) { 'testuser' }
before do
- GitlabConfig.any_instance.stub(audit_usernames: false)
- end
-
- describe :initialize do
- let(:ssh_cmd) { 'git-receive-pack' }
-
- its(:key_id) { should == key_id }
- end
-
- describe :parse_cmd do
- describe 'git' do
- context 'w/o namespace' do
- let(:ssh_args) { %W(git-upload-pack gitlab-ci.git) }
-
- before do
- subject.send :parse_cmd, ssh_args
- end
-
- its(:repo_name) { should == 'gitlab-ci.git' }
- its(:command) { should == 'git-upload-pack' }
- end
-
- context 'namespace' do
- let(:repo_name) { 'dmitriy.zaporozhets/gitlab-ci.git' }
- let(:ssh_args) { %W(git-upload-pack dmitriy.zaporozhets/gitlab-ci.git) }
-
- before do
- subject.send :parse_cmd, ssh_args
- end
-
- its(:repo_name) { should == 'dmitriy.zaporozhets/gitlab-ci.git' }
- its(:command) { should == 'git-upload-pack' }
- end
-
- context 'with an invalid number of arguments' do
- let(:ssh_args) { %W(foobar) }
-
- it "should raise an DisallowedCommandError" do
- expect { subject.send :parse_cmd, ssh_args }.to raise_error(GitlabShell::DisallowedCommandError)
- end
- end
-
- context 'with an API command' do
- before do
- subject.send :parse_cmd, ssh_args
- end
-
- context 'when generating recovery codes' do
- let(:ssh_args) { %w(2fa_recovery_codes) }
-
- it 'sets the correct command' do
- expect(subject.send(:command)).to eq('2fa_recovery_codes') # FIXME: don't access private instance variables
- end
-
- it 'does not set repo name' do
- expect(subject.send(:repo_name)).to be_nil # FIXME: don't access private instance variables
- end
- end
- 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
-
- describe 'git-lfs old clients' do
- let(:repo_name) { 'dzaporozhets/gitlab.git' }
- let(:ssh_args) { %W(git-lfs-authenticate dzaporozhets/gitlab.git download long_oid) }
-
- 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
+ # GitlabConfig.any_instance.stub(audit_usernames: false)
+ allow(GitlabNet).to receive(:new).and_return(api)
+ allow(api).to receive(:discover).with(key_id).and_return('username' => gl_username)
end
- describe :exec do
- let(:gitaly_message) { JSON.dump({ 'repository' => { 'relative_path' => repo_name, 'storage_name' => 'default' }, 'gl_repository' => gl_repository, 'gl_id' => key_id, 'gl_username' => gl_username}) }
-
- shared_examples_for 'upload-pack' do |command|
- let(:ssh_cmd) { "#{command} gitlab-ci.git" }
- after { subject.exec(ssh_cmd) }
-
- it "should process the command" do
- subject.should_receive(:exec_cmd_for_gitaly).with(instance_of(GitAccessStatus), %W(git-upload-pack gitlab-ci.git))
+ describe '#exec' do
+ context 'when origin_cmd is nil' do
+ it 'prints Welcome.. and returns true' do
+ expect($stdout).to receive(:puts).with('Welcome to GitLab, testuser!')
+ expect(subject.exec(nil)).to be_truthy
end
-
- it "should execute the command" do
- subject.should_receive(:exec_cmd).with('git-upload-pack', repo_path)
- end
-
- it "should log the command execution" do
- message = "executing git command"
- user_string = "user with key #{key_id}"
- $logger.should_receive(:info).with(message, command: "git-upload-pack #{repo_path}", user: user_string)
- end
-
- it "should use usernames if configured to do so" do
- GitlabConfig.any_instance.stub(audit_usernames: true)
- $logger.should_receive(:info).with("executing git command", hash_including(user: 'testuser'))
- end
- end
-
- context 'git-upload-pack' do
- it_behaves_like 'upload-pack', 'git-upload-pack'
end
- context 'git upload-pack' do
- it_behaves_like 'upload-pack', 'git upload-pack'
- end
-
- context 'gitaly-upload-pack' do
- let(:ssh_cmd) { "git-upload-pack gitlab-ci.git" }
- before {
- api.stub(check_access: gitaly_check_access)
- }
- after { subject.exec(ssh_cmd) }
-
- it "should process the command" do
- subject.should_receive(:exec_cmd_for_gitaly).with(instance_of(GitAccessStatus), %W(git-upload-pack gitlab-ci.git))
- end
-
- it "should execute the command" do
- subject.should_receive(:exec_cmd).with(File.join(ROOT_PATH, "bin/gitaly-upload-pack"), 'unix:gitaly.socket', gitaly_message)
- end
-
- it "should log the command execution" do
- message = "executing git command"
- user_string = "user with key #{key_id}"
- $logger.should_receive(:info).with(message, command: "gitaly-upload-pack unix:gitaly.socket #{gitaly_message}", user: user_string)
- end
-
- it "should use usernames if configured to do so" do
- GitlabConfig.any_instance.stub(audit_usernames: true)
- $logger.should_receive(:info).with("executing git command", hash_including(user: 'testuser'))
+ context 'when origin_cmd is empty' do
+ it 'prints Welcome.. and returns true' do
+ expect($stdout).to receive(:puts).with('Welcome to GitLab, testuser!')
+ expect(subject.exec('')).to be_truthy
end
end
- context 'git-receive-pack' do
- let(:ssh_cmd) { "git-receive-pack gitlab-ci.git" }
- after { subject.exec(ssh_cmd) }
-
- it "should process the command" do
- subject.should_receive(:exec_cmd_for_gitaly).with(instance_of(GitAccessStatus), %W(git-receive-pack gitlab-ci.git))
- end
-
- it "should execute the command" do
- subject.should_receive(:exec_cmd).with('git-receive-pack', repo_path)
- end
-
- it "should log the command execution" do
- message = "executing git command"
- user_string = "user with key #{key_id}"
- $logger.should_receive(:info).with(message, command: "git-receive-pack #{repo_path}", user: user_string)
+ context 'when origin_cmd is invalid' do
+ it 'prints a message to stderr and returns false' do
+ expect($stderr).to receive(:puts).with('GitLab: Disallowed command')
+ expect(subject.exec("git-invalid-command #{repo_name}")).to be_falsey
end
end
- context 'gitaly-receive-pack' do
- let(:ssh_cmd) { "git-receive-pack gitlab-ci.git" }
- before {
- api.stub(check_access: gitaly_check_access)
- }
- after { subject.exec(ssh_cmd) }
-
- it "should process the command" do
- subject.should_receive(:exec_cmd_for_gitaly).with(instance_of(GitAccessStatus), %W(git-receive-pack gitlab-ci.git))
- end
-
- it "should execute the command" do
- subject.should_receive(:exec_cmd).with(File.join(ROOT_PATH, "bin/gitaly-receive-pack"), 'unix:gitaly.socket', gitaly_message)
- end
-
- it "should log the command execution" do
- message = "executing git command"
- user_string = "user with key #{key_id}"
- $logger.should_receive(:info).with(message, command: "gitaly-receive-pack unix:gitaly.socket #{gitaly_message}", user: user_string)
- end
-
- it "should use usernames if configured to do so" do
- GitlabConfig.any_instance.stub(audit_usernames: true)
- $logger.should_receive(:info).with("executing git command", hash_including(user: 'testuser'))
+ context 'when origin_cmd is valid, but incomplete' do
+ it 'prints a message to stderr and returns false' do
+ expect($stderr).to receive(:puts).with('GitLab: Disallowed command')
+ expect(subject.exec('git-upload-pack')).to be_falsey
end
end
- context 'custom action' do
- let(:ssh_cmd) { "git-receive-pack gitlab-ci.git" }
- let(:custom_action_access_result) { Action::Custom.create_from_json('{"status":true,"message":"Attempting to proxy to primary..","payload":{"api_endpoints":["fake/info_refs","fake/push"],"data":{"username":"user1","primary_repo":"http://localhost:3001/user1/repo1.git"}}}') }
- let(:http_success) { double(Net::HTTPOK, code: '200', body: 'OK') }
- let(:http_error) { double(Net::HTTPInternalServerError, code: '500', body: 'Internal Server Error') }
-
- before { api.stub(check_access: custom_action_access_result) }
- after { subject.exec(ssh_cmd) }
-
- it "should attempt execute the custom action" do
- subject.should_receive(:exec_custom_action).with(custom_action_access_result)
- end
-
- it "should execute the custom action" do
- custom_action_access_result.should_receive(:execute).with(key_id).and_return(http_success)
- end
-
- it "should raise an exception when the custom action is unsuccessful" do
- custom_action_access_result.should_receive(:execute).with(key_id).and_return(http_error)
- end
- end
-
- shared_examples_for 'upload-archive' do |command|
- let(:ssh_cmd) { "#{command} gitlab-ci.git" }
- let(:exec_cmd_params) { ['git-upload-archive', repo_path] }
- let(:exec_cmd_log_params) { exec_cmd_params }
-
- after { subject.exec(ssh_cmd) }
-
- it "should process the command" do
- subject.should_receive(:exec_cmd_for_gitaly).with(instance_of(GitAccessStatus), %W(git-upload-archive gitlab-ci.git))
- end
-
- it "should execute the command" do
- subject.should_receive(:exec_cmd).with(*exec_cmd_params)
- end
-
- it "should log the command execution" do
- message = "executing git command"
- user_string = "user with key #{key_id}"
- $logger.should_receive(:info).with(message, command: exec_cmd_log_params.join(' '), user: user_string)
- end
-
- it "should use usernames if configured to do so" do
- GitlabConfig.any_instance.stub(audit_usernames: true)
- $logger.should_receive(:info).with("executing git command", hash_including(user: 'testuser'))
- end
- end
-
- context 'git-upload-archive' do
- it_behaves_like 'upload-archive', 'git-upload-archive'
- end
-
- context 'git upload-archive' do
- it_behaves_like 'upload-archive', 'git upload-archive'
- end
-
- context 'gitaly-upload-archive' do
- before do
- api.stub(check_access: gitaly_check_access)
+ context 'when origin_cmd is git-lfs-authenticate' do
+ context 'but incomplete' do
+ it 'prints a message to stderr and returns false' do
+ expect($stderr).to receive(:puts).with('GitLab: Disallowed command')
+ expect(subject.exec('git-lfs-authenticate')).to be_falsey
+ end
end
- it_behaves_like 'upload-archive', 'git-upload-archive' do
- let(:gitaly_executable) { "gitaly-upload-archive" }
- let(:exec_cmd_params) do
- [
- File.join(ROOT_PATH, "bin", gitaly_executable),
- 'unix:gitaly.socket',
- gitaly_message
- ]
- end
- let(:exec_cmd_log_params) do
- [gitaly_executable, 'unix:gitaly.socket', gitaly_message]
+ context 'but invalid' do
+ it 'prints a message to stderr and returns false' do
+ expect($stderr).to receive(:puts).with('GitLab: Disallowed command')
+ expect(subject.exec("git-lfs-authenticate #{repo_name} invalid")).to be_falsey
end
end
end
- context 'arbitrary command' do
- let(:ssh_cmd) { 'arbitrary command' }
- after { subject.exec(ssh_cmd) }
-
- it "should not process the command" do
- subject.should_not_receive(:process_cmd)
- end
-
- it "should not execute the command" do
- subject.should_not_receive(:exec_cmd)
- end
-
- it "should log the attempt" do
- message = 'Denied disallowed command'
- user_string = "user with key #{key_id}"
- $logger.should_receive(:warn).with(message, command: 'arbitrary command', user: user_string)
+ context 'when the API is unavailable' do
+ before do
+ expect(api).to receive(:check_access).with('git-upload-pack', nil, repo_name, key_id, '_any').and_raise(GitlabNet::ApiUnreachableError)
end
- end
-
- context 'no command' do
- after { subject.exec(nil) }
- it "should call api.discover" do
- api.should_receive(:discover).with(key_id)
+ it 'prints a message to stderr and returns false' do
+ expect($stderr).to receive(:puts).with('GitLab: Failed to authorize your Git request: internal API unreachable')
+ expect(subject.exec("git-upload-pack #{repo_name}")).to be_falsey
end
end
- context "failed connection" do
- let(:ssh_cmd) { 'git-upload-pack gitlab-ci.git' }
-
- before {
- api.stub(:check_access).and_raise(GitlabNet::ApiUnreachableError)
- }
- after { subject.exec(ssh_cmd) }
-
- it "should not process the command" do
- subject.should_not_receive(:process_cmd)
+ context 'when access to the repo is denied' do
+ before do
+ expect(api).to receive(:check_access).with('git-upload-pack', nil, repo_name, key_id, '_any').and_raise(AccessDeniedError, 'Sorry, access denied')
end
- it "should not execute the command" do
- subject.should_not_receive(:exec_cmd)
+ it 'prints a message to stderr and returns false' do
+ expect($stderr).to receive(:puts).with('GitLab: Sorry, access denied')
+ expect(subject.exec("git-upload-pack #{repo_name}")).to be_falsey
end
end
- context 'with an API command' do
+ context 'when access has been verified OK' do
before do
- allow(subject).to receive(:continue?).and_return(true)
+ expect(api).to receive(:check_access).with(git_access, nil, repo_name, key_id, '_any').and_return(gitaly_action)
end
- context 'when generating recovery codes' do
- let(:ssh_cmd) { '2fa_recovery_codes' }
- after do
- subject.exec(ssh_cmd)
- end
-
- it 'does not call verify_access' do
- expect(subject).not_to receive(:verify_access)
- end
-
- it 'calls the corresponding method' do
- expect(subject).to receive(:api_2fa_recovery_codes)
- end
+ context 'when origin_cmd is git-upload-pack' do
+ let(:git_access) { 'git-upload-pack'}
- it 'outputs recovery codes' do
- expect($stdout).to receive(:puts)
- .with(/f67c514de60c4953\n41278385fc00c1e0/)
+ it 'returns true' do
+ expect(gitaly_action).to receive(:execute).with('git-upload-pack', %W{git-upload-pack #{repo_name}}).and_return(true)
+ expect(subject.exec("git-upload-pack #{repo_name}")).to be_truthy
end
- context 'when the process is unsuccessful' do
- it 'displays the error to the user' do
- api.stub(two_factor_recovery_codes: {
- 'success' => false,
- 'message' => 'Could not find the given key'
- })
-
- expect($stdout).to receive(:puts)
- .with(/Could not find the given key/)
+ context 'but repo path is invalid' do
+ it 'prints a message to stderr and returns false' do
+ expect(gitaly_action).to receive(:execute).with('git-upload-pack', %W{git-upload-pack #{repo_name}}).and_raise(InvalidRepositoryPathError)
+ expect($stderr).to receive(:puts).with('GitLab: Invalid repository path')
+ expect(subject.exec("git-upload-pack #{repo_name}")).to be_falsey
end
end
end
- end
- end
-
- describe :validate_access do
- let(:ssh_cmd) { "git-upload-pack gitlab-ci.git" }
- describe 'check access with api' do
- after { subject.exec(ssh_cmd) }
+ context 'when origin_cmd is git upload-pack (git for Windows 2.14' do
+ let(:git_access) { 'git-upload-pack'}
- it "should call api.check_access" do
- api.should_receive(:check_access).with('git-upload-pack', nil, 'gitlab-ci.git', key_id, '_any', 'ssh')
- end
-
- it "should disallow access and log the attempt if check_access returns false status" do
- api.stub(check_access: GitAccessStatus.new(
- false,
- 'denied',
- gl_repository: nil,
- gl_username: nil,
- repository_path: nil,
- gitaly: nil))
- message = 'Access denied'
- user_string = "user with key #{key_id}"
- $logger.should_receive(:warn).with(message, command: 'git-upload-pack gitlab-ci.git', user: user_string)
- end
- end
-
- describe 'set the repository path' do
- context 'with a correct path' do
- before { subject.exec(ssh_cmd) }
-
- its(:repo_path) { should == repo_path }
- end
-
- context "with a path that doesn't match an absolute path" do
- before do
- File.stub(:absolute_path) { 'y/gitlab-ci.git' }
- end
-
- it "refuses to assign the path" do
- $stderr.should_receive(:puts).with("GitLab: Invalid repository path")
- expect(subject.exec(ssh_cmd)).to be_falsey
+ it 'returns true' do
+ expect(gitaly_action).to receive(:execute).with('git-upload-pack', %W{git-upload-pack #{repo_name}}).and_return(true)
+ expect(subject.exec("git upload-pack #{repo_name}")).to be_truthy
end
end
- end
- end
-
- describe :exec_cmd do
- let(:shell) { GitlabShell.new(key_id) }
- let(:env) do
- {
- 'HOME' => ENV['HOME'],
- 'PATH' => ENV['PATH'],
- 'LD_LIBRARY_PATH' => ENV['LD_LIBRARY_PATH'],
- 'LANG' => ENV['LANG'],
- 'GL_ID' => key_id,
- 'GL_PROTOCOL' => 'ssh',
- 'GL_REPOSITORY' => gl_repository,
- 'GL_USERNAME' => 'testuser'
- }
- end
- let(:exec_options) { { unsetenv_others: true, chdir: ROOT_PATH } }
- before do
- Kernel.stub(:exec)
- shell.instance_variable_set(:@gl_repository, gl_repository)
- shell.instance_variable_set(:@username, gl_username)
- end
- it "uses Kernel::exec method" do
- Kernel.should_receive(:exec).with(env, 1, 2, exec_options).once
- shell.send :exec_cmd, 1, 2
- end
-
- it "refuses to execute a lone non-array argument" do
- expect { shell.send :exec_cmd, 1 }.to raise_error(GitlabShell::DisallowedCommandError)
- end
-
- it "allows one argument if it is an array" do
- Kernel.should_receive(:exec).with(env, [1, 2], exec_options).once
- shell.send :exec_cmd, [1, 2]
- end
+ context 'when origin_cmd is git-lfs-authenticate' do
+ let(:fake_payload) { 'FAKE PAYLOAD' }
+ let(:lfs_access) { double(GitlabLfsAuthentication, authentication_payload: fake_payload)}
- context "when specifying a git_tracing log file" do
- let(:git_trace_log_file) { '/tmp/git_trace_performance.log' }
-
- before do
- GitlabConfig.any_instance.stub(git_trace_log_file: git_trace_log_file)
- shell
- end
-
- it "uses GIT_TRACE_PERFORMANCE" do
- expected_hash = hash_including(
- 'GIT_TRACE' => git_trace_log_file,
- 'GIT_TRACE_PACKET' => git_trace_log_file,
- 'GIT_TRACE_PERFORMANCE' => git_trace_log_file
- )
- Kernel.should_receive(:exec).with(expected_hash, [1, 2], exec_options).once
-
- shell.send :exec_cmd, [1, 2]
- end
-
- context "when provides a relative path" do
- let(:git_trace_log_file) { 'git_trace_performance.log' }
-
- it "does not uses GIT_TRACE*" do
- # If we try to use it we'll show a warning to the users
- expected_hash = hash_excluding(
- 'GIT_TRACE', 'GIT_TRACE_PACKET', 'GIT_TRACE_PERFORMANCE'
- )
- Kernel.should_receive(:exec).with(expected_hash, [1, 2], exec_options).once
-
- shell.send :exec_cmd, [1, 2]
- end
-
- it "writes an entry on the log" do
- message = 'git trace log path must be absolute, ignoring'
-
- expect($logger).to receive(:warn).
- with(message, git_trace_log_file: git_trace_log_file)
-
- Kernel.should_receive(:exec).with(env, [1, 2], exec_options).once
- shell.send :exec_cmd, [1, 2]
- end
- end
-
- context "when provides a file not writable" do
before do
- expect(File).to receive(:open).with(git_trace_log_file, 'a').and_raise(Errno::EACCES)
+ expect(api).to receive(:lfs_authenticate).with(key_id, repo_name).and_return(lfs_access)
end
- it "does not uses GIT_TRACE*" do
- # If we try to use it we'll show a warning to the users
- expected_hash = hash_excluding(
- 'GIT_TRACE', 'GIT_TRACE_PACKET', 'GIT_TRACE_PERFORMANCE'
- )
- Kernel.should_receive(:exec).with(expected_hash, [1, 2], exec_options).once
+ context 'upload' do
+ let(:git_access) { 'git-receive-pack'}
- shell.send :exec_cmd, [1, 2]
+ it 'returns true' do
+ expect($stdout).to receive(:puts).with(fake_payload)
+ expect(subject.exec("git-lfs-authenticate #{repo_name} upload")).to be_truthy
+ end
end
- it "writes an entry on the log" do
- message = 'Failed to open git trace log file'
- error = 'Permission denied'
-
- expect($logger).to receive(:warn).
- with(message, git_trace_log_file: git_trace_log_file, error: error)
+ context 'download' do
+ let(:git_access) { 'git-upload-pack'}
- Kernel.should_receive(:exec).with(env, [1, 2], exec_options).once
- shell.send :exec_cmd, [1, 2]
+ it 'returns true' do
+ expect($stdout).to receive(:puts).with(fake_payload)
+ expect(subject.exec("git-lfs-authenticate #{repo_name} download")).to be_truthy
+ end
end
end
end
end
- describe :api do
- let(:shell) { GitlabShell.new(key_id) }
- subject { shell.send :api }
+ # describe :initialize do
+ # let(:ssh_cmd) { 'git-receive-pack' }
+
+ # its(:key_id) { should == key_id }
+ # end
- it { should be_a(GitlabNet) }
- end
+ # describe '#parse_cmd' do
+ # describe 'git' do
+ # context 'w/o namespace' do
+ # let(:ssh_args) { %W(git-upload-pack gitlab-ci.git) }
+
+ # before do
+ # subject.send(:parse_cmd, ssh_args) # FIXME: don't access private methods
+ # end
+
+ # its(:repo_name) { should == 'gitlab-ci.git' }
+ # its(:command) { should == 'git-upload-pack' }
+ # end
+
+ # context 'namespace' do
+ # let(:repo_name) { 'dmitriy.zaporozhets/gitlab-ci.git' }
+ # let(:ssh_args) { %W(git-upload-pack dmitriy.zaporozhets/gitlab-ci.git) }
+
+ # before do
+ # subject.send(:parse_cmd, ssh_args) # FIXME: don't access private methods
+ # end
+
+ # its(:repo_name) { should == 'dmitriy.zaporozhets/gitlab-ci.git' }
+ # its(:command) { should == 'git-upload-pack' }
+ # end
+
+ # context 'with an invalid number of arguments' do
+ # let(:ssh_args) { %W(foobar) }
+
+ # it "should raise an DisallowedCommandError" do
+ # expect { subject.send :parse_cmd, ssh_args }.to raise_error(GitlabShell::DisallowedCommandError)
+ # end
+ # end
+
+ # context 'with an API command' do
+ # before do
+ # subject.send(:parse_cmd, ssh_args) # FIXME: don't access private methods
+ # end
+
+ # context 'when generating recovery codes' do
+ # let(:ssh_args) { %w(2fa_recovery_codes) }
+
+ # it 'sets the correct command' do
+ # expect(subject.send(:command)).to eq('2fa_recovery_codes') # FIXME: don't access private instance variables
+ # end
+
+ # it 'does not set repo name' do
+ # expect(subject.send(:repo_name)).to be_nil # FIXME: don't access private instance variables
+ # end
+ # end
+ # 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) # FIXME: don't access private methods
+ # end
+
+ # its(:repo_name) { should == 'dzaporozhets/gitlab.git' }
+ # its(:command) { should == 'git-lfs-authenticate' }
+ # its(:git_access) { should == 'git-upload-pack' }
+ # end
+
+ # describe 'git-lfs old clients' do
+ # let(:repo_name) { 'dzaporozhets/gitlab.git' }
+ # let(:ssh_args) { %W(git-lfs-authenticate dzaporozhets/gitlab.git download long_oid) }
+
+ # before do
+ # subject.send(:parse_cmd, ssh_args) # FIXME: don't access private methods
+ # 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
+ # let(:gitaly_message) { JSON.dump({ 'repository' => { 'relative_path' => repo_name, 'storage_name' => 'default' }, 'gl_repository' => gl_repository, 'gl_id' => key_id, 'gl_username' => gl_username}) }
+
+ # shared_examples_for 'upload-pack' do |command|
+ # let(:ssh_cmd) { "#{command} gitlab-ci.git" }
+ # after { subject.exec(ssh_cmd) }
+
+ # it "should process the command" do
+ # subject.should_receive(:exec_cmd_for_gitaly).with(instance_of(GitAccessStatus), %W(git-upload-pack gitlab-ci.git))
+ # end
+
+ # it "should execute the command" do
+ # subject.should_receive(:exec_cmd).with('git-upload-pack', repo_path)
+ # end
+
+ # it "should log the command execution" do
+ # message = "executing git command"
+ # user_string = "user with key #{key_id}"
+ # $logger.should_receive(:info).with(message, command: "git-upload-pack #{repo_path}", user: user_string)
+ # end
+
+ # it "should use usernames if configured to do so" do
+ # GitlabConfig.any_instance.stub(audit_usernames: true)
+ # $logger.should_receive(:info).with("executing git command", hash_including(user: 'testuser'))
+ # end
+ # end
+
+ # context 'git-upload-pack' do
+ # it_behaves_like 'upload-pack', 'git-upload-pack'
+ # end
+
+ # context 'git upload-pack' do
+ # it_behaves_like 'upload-pack', 'git upload-pack'
+ # end
+
+ # context 'gitaly-upload-pack' do
+ # let(:ssh_cmd) { "git-upload-pack gitlab-ci.git" }
+ # before {
+ # api.stub(check_access: gitaly_action)
+ # }
+ # after { subject.exec(ssh_cmd) }
+
+ # it "should process the command" do
+ # subject.should_receive(:exec_cmd_for_gitaly).with(instance_of(GitAccessStatus), %W(git-upload-pack gitlab-ci.git))
+ # end
+
+ # it "should execute the command" do
+ # subject.should_receive(:exec_cmd).with(File.join(ROOT_PATH, "bin/gitaly-upload-pack"), 'unix:gitaly.socket', gitaly_message)
+ # end
+
+ # it "should log the command execution" do
+ # message = "executing git command"
+ # user_string = "user with key #{key_id}"
+ # $logger.should_receive(:info).with(message, command: "gitaly-upload-pack unix:gitaly.socket #{gitaly_message}", user: user_string)
+ # end
+
+ # it "should use usernames if configured to do so" do
+ # GitlabConfig.any_instance.stub(audit_usernames: true)
+ # $logger.should_receive(:info).with("executing git command", hash_including(user: 'testuser'))
+ # end
+ # end
+
+ # context 'git-receive-pack' do
+ # let(:ssh_cmd) { "git-receive-pack gitlab-ci.git" }
+ # after { subject.exec(ssh_cmd) }
+
+ # it "should process the command" do
+ # subject.should_receive(:exec_cmd_for_gitaly).with(instance_of(GitAccessStatus), %W(git-receive-pack gitlab-ci.git))
+ # end
+
+ # it "should execute the command" do
+ # subject.should_receive(:exec_cmd).with('git-receive-pack', repo_path)
+ # end
+
+ # it "should log the command execution" do
+ # message = "executing git command"
+ # user_string = "user with key #{key_id}"
+ # $logger.should_receive(:info).with(message, command: "git-receive-pack #{repo_path}", user: user_string)
+ # end
+ # end
+
+ # context 'gitaly-receive-pack' do
+ # let(:ssh_cmd) { "git-receive-pack gitlab-ci.git" }
+ # before {
+ # api.stub(check_access: gitaly_action)
+ # }
+ # after { subject.exec(ssh_cmd) }
+
+ # it "should process the command" do
+ # subject.should_receive(:exec_cmd_for_gitaly).with(instance_of(GitAccessStatus), %W(git-receive-pack gitlab-ci.git))
+ # end
+
+ # it "should execute the command" do
+ # subject.should_receive(:exec_cmd).with(File.join(ROOT_PATH, "bin/gitaly-receive-pack"), 'unix:gitaly.socket', gitaly_message)
+ # end
+
+ # it "should log the command execution" do
+ # message = "executing git command"
+ # user_string = "user with key #{key_id}"
+ # $logger.should_receive(:info).with(message, command: "gitaly-receive-pack unix:gitaly.socket #{gitaly_message}", user: user_string)
+ # end
+
+ # it "should use usernames if configured to do so" do
+ # GitlabConfig.any_instance.stub(audit_usernames: true)
+ # $logger.should_receive(:info).with("executing git command", hash_including(user: 'testuser'))
+ # end
+ # end
+
+ # context 'custom action' do
+ # let(:ssh_cmd) { "git-receive-pack gitlab-ci.git" }
+ # let(:custom_action_access_result) { Action::Custom.create_from_json('{"status":true,"message":"Attempting to proxy to primary..","payload":{"api_endpoints":["fake/info_refs","fake/push"],"data":{"username":"user1","primary_repo":"http://localhost:3001/user1/repo1.git"}}}') }
+ # let(:http_success) { double(Net::HTTPOK, code: '200', body: 'OK') }
+ # let(:http_error) { double(Net::HTTPInternalServerError, code: '500', body: 'Internal Server Error') }
+
+ # before { api.stub(check_access: custom_action_access_result) }
+ # after { subject.exec(ssh_cmd) }
+
+ # it "should attempt execute the custom action" do
+ # subject.should_receive(:exec_custom_action).with(custom_action_access_result)
+ # end
+
+ # it "should execute the custom action" do
+ # custom_action_access_result.should_receive(:execute).with(key_id).and_return(http_success)
+ # end
+
+ # it "should raise an exception when the custom action is unsuccessful" do
+ # custom_action_access_result.should_receive(:execute).with(key_id).and_return(http_error)
+ # end
+ # end
+
+ # shared_examples_for 'upload-archive' do |command|
+ # let(:ssh_cmd) { "#{command} gitlab-ci.git" }
+ # let(:exec_cmd_params) { ['git-upload-archive', repo_path] }
+ # let(:exec_cmd_log_params) { exec_cmd_params }
+
+ # after { subject.exec(ssh_cmd) }
+
+ # it "should process the command" do
+ # subject.should_receive(:exec_cmd_for_gitaly).with(instance_of(GitAccessStatus), %W(git-upload-archive gitlab-ci.git))
+ # end
+
+ # it "should execute the command" do
+ # subject.should_receive(:exec_cmd).with(*exec_cmd_params)
+ # end
+
+ # it "should log the command execution" do
+ # message = "executing git command"
+ # user_string = "user with key #{key_id}"
+ # $logger.should_receive(:info).with(message, command: exec_cmd_log_params.join(' '), user: user_string)
+ # end
+
+ # it "should use usernames if configured to do so" do
+ # GitlabConfig.any_instance.stub(audit_usernames: true)
+ # $logger.should_receive(:info).with("executing git command", hash_including(user: 'testuser'))
+ # end
+ # end
+
+ # context 'git-upload-archive' do
+ # it_behaves_like 'upload-archive', 'git-upload-archive'
+ # end
+
+ # context 'git upload-archive' do
+ # it_behaves_like 'upload-archive', 'git upload-archive'
+ # end
+
+ # context 'gitaly-upload-archive' do
+ # before do
+ # api.stub(check_access: gitaly_action)
+ # end
+
+ # it_behaves_like 'upload-archive', 'git-upload-archive' do
+ # let(:gitaly_executable) { "gitaly-upload-archive" }
+ # let(:exec_cmd_params) do
+ # [
+ # File.join(ROOT_PATH, "bin", gitaly_executable),
+ # 'unix:gitaly.socket',
+ # gitaly_message
+ # ]
+ # end
+ # let(:exec_cmd_log_params) do
+ # [gitaly_executable, 'unix:gitaly.socket', gitaly_message]
+ # end
+ # end
+ # end
+
+ # context 'arbitrary command' do
+ # let(:ssh_cmd) { 'arbitrary command' }
+ # after { subject.exec(ssh_cmd) }
+
+ # it "should not process the command" do
+ # subject.should_not_receive(:process_cmd)
+ # end
+
+ # it "should not execute the command" do
+ # subject.should_not_receive(:exec_cmd)
+ # end
+
+ # it "should log the attempt" do
+ # message = 'Denied disallowed command'
+ # user_string = "user with key #{key_id}"
+ # $logger.should_receive(:warn).with(message, command: 'arbitrary command', user: user_string)
+ # end
+ # end
+
+ # context 'no command' do
+ # after { subject.exec(nil) }
+
+ # it "should call api.discover" do
+ # api.should_receive(:discover).with(key_id)
+ # end
+ # end
+
+ # context "failed connection" do
+ # let(:ssh_cmd) { 'git-upload-pack gitlab-ci.git' }
+
+ # before {
+ # api.stub(:check_access).and_raise(GitlabNet::ApiUnreachableError)
+ # }
+ # after { subject.exec(ssh_cmd) }
+
+ # it "should not process the command" do
+ # subject.should_not_receive(:process_cmd)
+ # end
+
+ # it "should not execute the command" do
+ # subject.should_not_receive(:exec_cmd)
+ # end
+ # end
+
+ # context 'with an API command' do
+ # before do
+ # allow(subject).to receive(:continue?).and_return(true)
+ # end
+
+ # context 'when generating recovery codes' do
+ # let(:ssh_cmd) { '2fa_recovery_codes' }
+ # after do
+ # subject.exec(ssh_cmd)
+ # end
+
+ # it 'does not call verify_access' do
+ # expect(subject).not_to receive(:verify_access)
+ # end
+
+ # it 'calls the corresponding method' do
+ # expect(subject).to receive(:api_2fa_recovery_codes)
+ # end
+
+ # it 'outputs recovery codes' do
+ # expect($stdout).to receive(:puts)
+ # .with(/f67c514de60c4953\n41278385fc00c1e0/)
+ # end
+
+ # context 'when the process is unsuccessful' do
+ # it 'displays the error to the user' do
+ # api.stub(two_factor_recovery_codes: {
+ # 'success' => false,
+ # 'message' => 'Could not find the given key'
+ # })
+
+ # expect($stdout).to receive(:puts)
+ # .with(/Could not find the given key/)
+ # end
+ # end
+ # end
+ # end
+ # end
+
+ # describe '#validate_access' do
+ # let(:ssh_cmd) { "git-upload-pack gitlab-ci.git" }
+
+ # describe 'check access with api' do
+ # after { subject.exec(ssh_cmd) }
+
+ # it "should call api.check_access" do
+ # api.should_receive(:check_access).with('git-upload-pack', nil, 'gitlab-ci.git', key_id, '_any', 'ssh')
+ # end
+
+ # it "should disallow access and log the attempt if check_access returns false status" do
+ # api.stub(check_access: GitAccessStatus.new(
+ # false,
+ # 'denied',
+ # gl_repository: nil,
+ # gl_username: nil,
+ # repository_path: nil,
+ # gitaly: nil))
+ # message = 'Access denied'
+ # user_string = "user with key #{key_id}"
+ # $logger.should_receive(:warn).with(message, command: 'git-upload-pack gitlab-ci.git', user: user_string)
+ # end
+ # end
+
+ # describe 'set the repository path' do
+ # context 'with a correct path' do
+ # before { subject.exec(ssh_cmd) }
+
+ # its(:repo_path) { should == repo_path }
+ # end
+
+ # context "with a path that doesn't match an absolute path" do
+ # before do
+ # File.stub(:absolute_path) { 'y/gitlab-ci.git' }
+ # end
+
+ # it "refuses to assign the path" do
+ # $stderr.should_receive(:puts).with("GitLab: Invalid repository path")
+ # expect(subject.exec(ssh_cmd)).to be_falsey
+ # end
+ # end
+ # end
+ # end
+
+ # describe '#exec_cmd' do
+ # let(:shell) { GitlabShell.new(key_id) }
+ # let(:env) do
+ # {
+ # 'HOME' => ENV['HOME'],
+ # 'PATH' => ENV['PATH'],
+ # 'LD_LIBRARY_PATH' => ENV['LD_LIBRARY_PATH'],
+ # 'LANG' => ENV['LANG'],
+ # 'GL_ID' => key_id,
+ # 'GL_PROTOCOL' => 'ssh',
+ # 'GL_REPOSITORY' => gl_repository,
+ # 'GL_USERNAME' => 'testuser'
+ # }
+ # end
+ # let(:exec_options) { { unsetenv_others: true, chdir: ROOT_PATH } }
+ # before do
+ # Kernel.stub(:exec)
+ # shell.instance_variable_set(:@gl_repository, gl_repository)
+ # shell.instance_variable_set(:@username, gl_username)
+ # end
+
+ # it "uses Kernel::exec method" do
+ # Kernel.should_receive(:exec).with(env, 1, 2, exec_options).once
+ # shell.send :exec_cmd, 1, 2
+ # end
+
+ # it "refuses to execute a lone non-array argument" do
+ # expect { shell.send :exec_cmd, 1 }.to raise_error(GitlabShell::DisallowedCommandError)
+ # end
+
+ # it "allows one argument if it is an array" do
+ # Kernel.should_receive(:exec).with(env, [1, 2], exec_options).once
+ # shell.send :exec_cmd, [1, 2]
+ # end
+
+ # context "when specifying a git_tracing log file" do
+ # let(:git_trace_log_file) { '/tmp/git_trace_performance.log' }
+
+ # before do
+ # GitlabConfig.any_instance.stub(git_trace_log_file: git_trace_log_file)
+ # shell
+ # end
+
+ # it "uses GIT_TRACE_PERFORMANCE" do
+ # expected_hash = hash_including(
+ # 'GIT_TRACE' => git_trace_log_file,
+ # 'GIT_TRACE_PACKET' => git_trace_log_file,
+ # 'GIT_TRACE_PERFORMANCE' => git_trace_log_file
+ # )
+ # Kernel.should_receive(:exec).with(expected_hash, [1, 2], exec_options).once
+
+ # shell.send :exec_cmd, [1, 2]
+ # end
+
+ # context "when provides a relative path" do
+ # let(:git_trace_log_file) { 'git_trace_performance.log' }
+
+ # it "does not uses GIT_TRACE*" do
+ # # If we try to use it we'll show a warning to the users
+ # expected_hash = hash_excluding(
+ # 'GIT_TRACE', 'GIT_TRACE_PACKET', 'GIT_TRACE_PERFORMANCE'
+ # )
+ # Kernel.should_receive(:exec).with(expected_hash, [1, 2], exec_options).once
+
+ # shell.send :exec_cmd, [1, 2]
+ # end
+
+ # it "writes an entry on the log" do
+ # message = 'git trace log path must be absolute, ignoring'
+
+ # expect($logger).to receive(:warn).
+ # with(message, git_trace_log_file: git_trace_log_file)
+
+ # Kernel.should_receive(:exec).with(env, [1, 2], exec_options).once
+ # shell.send :exec_cmd, [1, 2]
+ # end
+ # end
+
+ # context "when provides a file not writable" do
+ # before do
+ # expect(File).to receive(:open).with(git_trace_log_file, 'a').and_raise(Errno::EACCES)
+ # end
+
+ # it "does not uses GIT_TRACE*" do
+ # # If we try to use it we'll show a warning to the users
+ # expected_hash = hash_excluding(
+ # 'GIT_TRACE', 'GIT_TRACE_PACKET', 'GIT_TRACE_PERFORMANCE'
+ # )
+ # Kernel.should_receive(:exec).with(expected_hash, [1, 2], exec_options).once
+
+ # shell.send :exec_cmd, [1, 2]
+ # end
+
+ # it "writes an entry on the log" do
+ # message = 'Failed to open git trace log file'
+ # error = 'Permission denied'
+
+ # expect($logger).to receive(:warn).
+ # with(message, git_trace_log_file: git_trace_log_file, error: error)
+
+ # Kernel.should_receive(:exec).with(env, [1, 2], exec_options).once
+ # shell.send :exec_cmd, [1, 2]
+ # end
+ # end
+ # end
+ # end
+
+ # describe '#api' do
+ # let(:shell) { GitlabShell.new(key_id) }
+ # subject { shell.send(:api) } # FIXME: don't access private methods
+
+ # it { should be_instance_of(GitlabNet) }
+ # end
end