Commit ecba183b authored by Ash McKenzie's avatar Ash McKenzie

Utilise new Actions

* Move gitaly, git-lfs and 2FA logic out from gitlab_shell.rb
* Streamline parsing of origin_cmd in GitlabShell
* Utilise proper HTTP status codes sent from the API
* Also support 200 OK with status of true/false (ideally get rid of this)
* Use HTTP status constants
* Use attr_reader definitions (var over @var)
* Rspec deprecation fixes
parent 252a96d6
...@@ -20,12 +20,9 @@ class GitlabAccess ...@@ -20,12 +20,9 @@ class GitlabAccess
end end
def exec def exec
status = GitlabMetrics.measure('check-access:git-receive-pack') do GitlabMetrics.measure('check-access:git-receive-pack') do
api.check_access('git-receive-pack', @gl_repository, @repo_path, @key_id, @changes, @protocol, env: ObjectDirsHelper.all_attributes.to_json) api.check_access('git-receive-pack', gl_repository, repo_path, key_id, changes, protocol, env: ObjectDirsHelper.all_attributes.to_json)
end end
raise AccessDeniedError, status.message unless status.allowed?
true true
rescue GitlabNet::ApiUnreachableError rescue GitlabNet::ApiUnreachableError
$stderr.puts "GitLab: Failed to authorize your Git request: internal API unreachable" $stderr.puts "GitLab: Failed to authorize your Git request: internal API unreachable"
......
require 'json' require 'json'
require_relative 'errors'
require_relative 'gitlab_logger' require_relative 'gitlab_logger'
require_relative 'gitlab_access' require_relative 'gitlab_access'
require_relative 'gitlab_lfs_authentication' require_relative 'gitlab_lfs_authentication'
require_relative 'http_helper' require_relative 'http_helper'
require_relative 'action'
class GitlabNet # rubocop:disable Metrics/ClassLength class GitlabNet
include HTTPHelper include HTTPHelper
CHECK_TIMEOUT = 5 CHECK_TIMEOUT = 5
GL_PROTOCOL = 'ssh'.freeze GL_PROTOCOL = 'ssh'.freeze
API_INACCESSIBLE_ERROR = 'API is not accessible'.freeze
def check_access(cmd, gl_repository, repo, key_id, changes, protocol = GL_PROTOCOL, env: {}) def check_access(cmd, gl_repository, repo, key_id, changes, protocol = GL_PROTOCOL, env: {})
changes = changes.join("\n") unless changes.is_a?(String) changes = changes.join("\n") unless changes.is_a?(String)
...@@ -26,22 +29,15 @@ class GitlabNet # rubocop:disable Metrics/ClassLength ...@@ -26,22 +29,15 @@ class GitlabNet # rubocop:disable Metrics/ClassLength
resp = post("#{internal_api_endpoint}/allowed", params) resp = post("#{internal_api_endpoint}/allowed", params)
if resp.code == '200' determine_action(key_id, resp)
GitAccessStatus.create_from_json(resp.body)
else
GitAccessStatus.new(false,
'API is not accessible',
gl_repository: nil,
gl_username: nil,
repository_path: nil,
gitaly: nil)
end
end end
def discover(key) def discover(key)
key_id = key.gsub("key-", "") key_id = key.gsub("key-", "")
resp = get("#{internal_api_endpoint}/discover?key_id=#{key_id}") resp = get("#{internal_api_endpoint}/discover?key_id=#{key_id}")
JSON.parse(resp.body) rescue nil JSON.parse(resp.body)
rescue JSON::ParserError, ApiUnreachableError
nil
end end
def lfs_authenticate(key, repo) def lfs_authenticate(key, repo)
...@@ -97,11 +93,7 @@ class GitlabNet # rubocop:disable Metrics/ClassLength ...@@ -97,11 +93,7 @@ class GitlabNet # rubocop:disable Metrics/ClassLength
end end
def post_receive(gl_repository, identifier, changes) def post_receive(gl_repository, identifier, changes)
params = { params = { gl_repository: gl_repository, identifier: identifier, changes: changes }
gl_repository: gl_repository,
identifier: identifier,
changes: changes
}
resp = post("#{internal_api_endpoint}/post_receive", params) resp = post("#{internal_api_endpoint}/post_receive", params)
raise NotFound if resp.code == HTTP_NOT_FOUND raise NotFound if resp.code == HTTP_NOT_FOUND
...@@ -120,4 +112,25 @@ class GitlabNet # rubocop:disable Metrics/ClassLength ...@@ -120,4 +112,25 @@ class GitlabNet # rubocop:disable Metrics/ClassLength
def sanitize_path(repo) def sanitize_path(repo)
repo.delete("'") repo.delete("'")
end end
def determine_action(key_id, resp)
json = JSON.parse(resp.body)
message = json['message']
case resp.code
when HTTP_SUCCESS
# TODO: This raise can be removed once internal API can respond with correct
# HTTP status codes, instead of relying upon parsing the body and
# accessing the 'status' key.
raise AccessDeniedError, message unless json['status']
Action::Gitaly.create_from_json(key_id, json)
when HTTP_UNAUTHORIZED, HTTP_NOT_FOUND
raise AccessDeniedError, message
else
raise UnknownError, "#{API_INACCESSIBLE_ERROR}: #{message}"
end
rescue JSON::ParserError
raise UnknownError, API_INACCESSIBLE_ERROR
end
end end
...@@ -3,18 +3,11 @@ require 'pathname' ...@@ -3,18 +3,11 @@ require 'pathname'
require_relative 'gitlab_net' require_relative 'gitlab_net'
require_relative 'gitlab_metrics' require_relative 'gitlab_metrics'
require_relative 'user'
class GitlabShell # rubocop:disable Metrics/ClassLength class GitlabShell
API_2FA_RECOVERY_CODES_COMMAND = '2fa_recovery_codes'.freeze
GITALY_MIGRATED_COMMANDS = {
'git-upload-pack' => File.join(ROOT_PATH, 'bin', 'gitaly-upload-pack'),
'git-upload-archive' => File.join(ROOT_PATH, 'bin', 'gitaly-upload-archive'),
'git-receive-pack' => File.join(ROOT_PATH, 'bin', 'gitaly-receive-pack')
}.freeze
API_COMMANDS = %w(2fa_recovery_codes).freeze
attr_accessor :key_id, :gl_repository, :repo_name, :command, :git_access
attr_reader :repo_path
GIT_UPLOAD_PACK_COMMAND = 'git-upload-pack'.freeze GIT_UPLOAD_PACK_COMMAND = 'git-upload-pack'.freeze
GIT_RECEIVE_PACK_COMMAND = 'git-receive-pack'.freeze GIT_RECEIVE_PACK_COMMAND = 'git-receive-pack'.freeze
GIT_UPLOAD_ARCHIVE_COMMAND = 'git-upload-archive'.freeze GIT_UPLOAD_ARCHIVE_COMMAND = 'git-upload-archive'.freeze
...@@ -33,29 +26,23 @@ class GitlabShell # rubocop:disable Metrics/ClassLength ...@@ -33,29 +26,23 @@ class GitlabShell # rubocop:disable Metrics/ClassLength
# 'evil command'. # 'evil command'.
def exec(origin_cmd) def exec(origin_cmd)
if !origin_cmd || origin_cmd.empty? if !origin_cmd || origin_cmd.empty?
puts "Welcome to GitLab, #{username}!" puts "Welcome to GitLab, #{user.username}!"
return true return true
end end
args = Shellwords.shellwords(origin_cmd) command, git_access_command, repo_name, args = parse_cmd(origin_cmd)
args = parse_cmd(args) action = determine_action(command, git_access_command, repo_name)
if GIT_COMMANDS.include?(args.first)
GitlabMetrics.measure('verify-access') { verify_access }
end
process_cmd(args) action.execute(command, args)
true
rescue GitlabNet::ApiUnreachableError rescue GitlabNet::ApiUnreachableError
$stderr.puts "GitLab: Failed to authorize your Git request: internal API unreachable" $stderr.puts "GitLab: Failed to authorize your Git request: internal API unreachable"
false false
rescue AccessDeniedError => ex rescue AccessDeniedError, UnknownError => ex
$logger.warn('Access denied', command: origin_cmd, user: log_username) $logger.warn('Access denied', command: origin_cmd, user: user.log_username)
$stderr.puts "GitLab: #{ex.message}" $stderr.puts "GitLab: #{ex.message}"
false false
rescue DisallowedCommandError rescue DisallowedCommandError
$logger.warn('Denied disallowed command', command: origin_cmd, user: log_username) $logger.warn('Denied disallowed command', command: origin_cmd, user: user.log_username)
$stderr.puts 'GitLab: Disallowed command' $stderr.puts 'GitLab: Disallowed command'
false false
rescue InvalidRepositoryPathError rescue InvalidRepositoryPathError
...@@ -65,217 +52,67 @@ class GitlabShell # rubocop:disable Metrics/ClassLength ...@@ -65,217 +52,67 @@ class GitlabShell # rubocop:disable Metrics/ClassLength
private private
attr_reader :config, :key_id
def user
@user ||= User.new(key_id, audit_usernames: config.audit_usernames)
end
def parse_cmd(cmd)
args = Shellwords.shellwords(cmd)
def parse_cmd(args)
# Handle Git for Windows 2.14 using "git upload-pack" instead of git-upload-pack # Handle Git for Windows 2.14 using "git upload-pack" instead of git-upload-pack
if args.length == 3 && args.first == 'git' if args.length == 3 && args.first == 'git'
@command = "git-#{args[1]}" command = "git-#{args[1]}"
args = [@command, args.last] args = [command, args.last]
else else
@command = args.first command = args.first
end end
@git_access = @command git_access_command = command
return args if API_COMMANDS.include?(@command) return [command, git_access_command, nil, args] if command == API_2FA_RECOVERY_CODES_COMMAND
raise DisallowedCommandError unless GIT_COMMANDS.include?(@command) raise DisallowedCommandError unless GIT_COMMANDS.include?(command)
case @command case command
when 'git-lfs-authenticate' when 'git-lfs-authenticate'
raise DisallowedCommandError unless args.count >= 2 raise DisallowedCommandError unless args.count >= 2
@repo_name = args[1] repo_name = args[1]
case args[2] git_access_command = case args[2]
when 'download' when 'download'
@git_access = 'git-upload-pack' GIT_UPLOAD_PACK_COMMAND
when 'upload' when 'upload'
@git_access = 'git-receive-pack' GIT_RECEIVE_PACK_COMMAND
else else
raise DisallowedCommandError raise DisallowedCommandError
end end
else else
raise DisallowedCommandError unless args.count == 2 raise DisallowedCommandError unless args.count == 2
@repo_name = args.last repo_name = args.last
end end
args [command, git_access_command, repo_name, args]
end end
def verify_access def determine_action(command, git_access_command, repo_name)
status = api.check_access(@git_access, nil, @repo_name, @key_id, '_any', GitlabNet::GL_PROTOCOL) return Action::API2FARecovery.new(key_id) if command == API_2FA_RECOVERY_CODES_COMMAND
raise AccessDeniedError, status.message unless status.allowed?
self.repo_path = status.repository_path
@gl_repository = status.gl_repository
@gitaly = status.gitaly
@username = status.gl_username
end
def process_cmd(args) GitlabMetrics.measure('verify-access') do
return send("api_#{@command}") if API_COMMANDS.include?(@command) # GitlatNet#check_access will raise exception in the event of a problem
initial_action = api.check_access(git_access_command, nil,
repo_name, key_id, '_any')
if @command == 'git-lfs-authenticate' case command
GitlabMetrics.measure('lfs-authenticate') do when GIT_LFS_AUTHENTICATE_COMMAND
$logger.info('Processing LFS authentication', user: log_username) Action::GitLFSAuthenticate.new(key_id, repo_name)
lfs_authenticate else
initial_action
end end
return
end end
executable = @command
args = [repo_path]
if GITALY_MIGRATED_COMMANDS.key?(executable) && @gitaly
executable = GITALY_MIGRATED_COMMANDS[executable]
gitaly_address = @gitaly['address']
# The entire gitaly_request hash should be built in gitlab-ce and passed
# on as-is. For now we build a fake one on the spot.
gitaly_request = {
'repository' => @gitaly['repository'],
'gl_repository' => @gl_repository,
'gl_id' => @key_id,
'gl_username' => @username
}
args = [gitaly_address, JSON.dump(gitaly_request)]
end
args_string = [File.basename(executable), *args].join(' ')
$logger.info('executing git command', command: args_string, user: log_username)
exec_cmd(executable, *args)
end
# This method is not covered by Rspec because it ends the current Ruby process.
def exec_cmd(*args)
# If you want to call a command without arguments, use
# exec_cmd(['my_command', 'my_command']) . Otherwise use
# exec_cmd('my_command', 'my_argument', ...).
if args.count == 1 && !args.first.is_a?(Array)
raise DisallowedCommandError
end
env = {
'HOME' => ENV['HOME'],
'PATH' => ENV['PATH'],
'LD_LIBRARY_PATH' => ENV['LD_LIBRARY_PATH'],
'LANG' => ENV['LANG'],
'GL_ID' => @key_id,
'GL_PROTOCOL' => GitlabNet::GL_PROTOCOL,
'GL_REPOSITORY' => @gl_repository,
'GL_USERNAME' => @username
}
if @gitaly && @gitaly.include?('token')
env['GITALY_TOKEN'] = @gitaly['token']
end
if git_trace_available?
env.merge!(
'GIT_TRACE' => @config.git_trace_log_file,
'GIT_TRACE_PACKET' => @config.git_trace_log_file,
'GIT_TRACE_PERFORMANCE' => @config.git_trace_log_file
)
end
# We use 'chdir: ROOT_PATH' to let the next executable know where config.yml is.
Kernel.exec(env, *args, unsetenv_others: true, chdir: ROOT_PATH)
end end
def api def api
GitlabNet.new GitlabNet.new
end end
def user
return @user if defined?(@user)
begin
@user = api.discover(@key_id)
rescue GitlabNet::ApiUnreachableError
@user = nil
end
end
def username_from_discover
return nil unless user && user['username']
"@#{user['username']}"
end
def username
@username ||= username_from_discover || 'Anonymous'
end
# User identifier to be used in log messages.
def log_username
@config.audit_usernames ? username : "user with key #{@key_id}"
end
def lfs_authenticate
lfs_access = api.lfs_authenticate(@key_id, @repo_name)
return unless lfs_access
puts lfs_access.authentication_payload
end
private
def continue?(question)
puts "#{question} (yes/no)"
STDOUT.flush # Make sure the question gets output before we wait for input
continue = STDIN.gets.chomp
puts '' # Add a buffer in the output
continue == 'yes'
end
def api_2fa_recovery_codes
continue = continue?(
"Are you sure you want to generate new two-factor recovery codes?\n" \
"Any existing recovery codes you saved will be invalidated."
)
unless continue
puts 'New recovery codes have *not* been generated. Existing codes will remain valid.'
return
end
resp = api.two_factor_recovery_codes(key_id)
if resp['success']
codes = resp['recovery_codes'].join("\n")
puts "Your two-factor authentication recovery codes are:\n\n" \
"#{codes}\n\n" \
"During sign in, use one of the codes above when prompted for\n" \
"your two-factor code. Then, visit your Profile Settings and add\n" \
"a new device so you do not lose access to your account again."
else
puts "An error occurred while trying to generate new recovery codes.\n" \
"#{resp['message']}"
end
end
def git_trace_available?
return false unless @config.git_trace_log_file
if Pathname(@config.git_trace_log_file).relative?
$logger.warn('git trace log path must be absolute, ignoring', git_trace_log_file: @config.git_trace_log_file)
return false
end
begin
File.open(@config.git_trace_log_file, 'a') { nil }
return true
rescue => ex
$logger.warn('Failed to open git trace log file', git_trace_log_file: @config.git_trace_log_file, error: ex.to_s)
return false
end
end
def repo_path=(repo_path)
raise ArgumentError, "Repository path not provided. Please make sure you're using GitLab v8.10 or later." unless repo_path
raise InvalidRepositoryPathError if File.absolute_path(repo_path) != repo_path
@repo_path = repo_path
end
end end
...@@ -7,12 +7,7 @@ describe GitlabAccess do ...@@ -7,12 +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, allow(api).to receive(:check_access).and_return(Action::Gitaly.new('key-1', 'project-1', 'testuser', '/home/git/repositories', nil))
'ok',
gl_repository: 'project-1',
gl_username: 'testuser',
repository_path: '/home/git/repositories',
gitaly: nil))
end end
end end
subject do subject do
...@@ -26,12 +21,6 @@ describe GitlabAccess do ...@@ -26,12 +21,6 @@ describe GitlabAccess do
allow_any_instance_of(GitlabConfig).to receive(:repos_path).and_return(repository_path) allow_any_instance_of(GitlabConfig).to receive(:repos_path).and_return(repository_path)
end end
describe :initialize do
it { expect(subject.send(:repo_path)).to eql repo_path } # FIXME: don't access private instance variables
it { expect(subject.send(:changes)).to eql ['wow'] } # FIXME: don't access private instance variables
it { expect(subject.send(:protocol)).to eql 'ssh' } # FIXME: don't access private instance variables
end
describe "#exec" do describe "#exec" do
context "access is granted" do context "access is granted" do
...@@ -41,16 +30,8 @@ describe GitlabAccess do ...@@ -41,16 +30,8 @@ describe GitlabAccess do
end end
context "access is denied" do context "access is denied" do
before do before do
api.stub(check_access: GitAccessStatus.new( allow(api).to receive(:check_access).and_raise(AccessDeniedError)
false,
'denied',
gl_repository: nil,
gl_username: nil,
repository_path: nil,
gitaly: nil
))
end end
it "returns false" do it "returns false" do
......
...@@ -57,7 +57,7 @@ describe GitlabNet, vcr: true do ...@@ -57,7 +57,7 @@ describe GitlabNet, vcr: true do
it "raises an exception if the connection fails" do it "raises an exception if the connection fails" do
VCR.use_cassette("discover-ok") do VCR.use_cassette("discover-ok") do
allow_any_instance_of(Net::HTTP).to receive(:request).and_raise(StandardError) allow_any_instance_of(Net::HTTP).to receive(:request).and_raise(StandardError)
expect { gitlab_net.discover(key) }.to raise_error(GitlabNet::ApiUnreachableError) expect(gitlab_net.discover(key)).to be_nil
end end
end end
end end
...@@ -263,11 +263,33 @@ describe GitlabNet, vcr: true do ...@@ -263,11 +263,33 @@ describe GitlabNet, vcr: true do
end end
describe '#check_access' do describe '#check_access' do
context 'something is wrong with the API response' do
context 'but response is JSON parsable' do
it 'raises an UnknownError exception' do
VCR.use_cassette('failed-push') do
expect do
gitlab_net.check_access('git-receive-pack', nil, project, key, changes, 'ssh')
end.to raise_error(UnknownError, 'API is not accessible: An internal server error occurred')
end
end
end
context 'but response is not JSON parsable' do
it 'raises an UnknownError exception' do
VCR.use_cassette('failed-push-unparsable') do
expect do
gitlab_net.check_access('git-receive-pack', nil, project, key, changes, 'ssh')
end.to raise_error(UnknownError, 'API is not accessible')
end
end
end
end
context 'ssh key with access nil, to project' do context 'ssh key with access nil, to project' do
it 'should allow pull access for host' do it 'should allow push access for host' do
VCR.use_cassette("allowed-pull") do VCR.use_cassette('allowed-push') do
access = gitlab_net.check_access('git-receive-pack', nil, project, key, changes, 'ssh') action = gitlab_net.check_access('git-receive-pack', nil, project, key, changes, 'ssh')
access.allowed?.should be_truthy expect(action).to be_instance_of(Action::Gitaly)
end end
end end
...@@ -278,75 +300,120 @@ describe GitlabNet, vcr: true do ...@@ -278,75 +300,120 @@ describe GitlabNet, vcr: true do
end end
end end
it 'should allow push access for host' do it 'should allow pull access for host' do
VCR.use_cassette("allowed-push") do VCR.use_cassette("allowed-pull") do
access = gitlab_net.check_access('git-upload-pack', nil, project, key, changes, 'ssh') action = gitlab_net.check_access('git-upload-pack', nil, project, key, changes, 'ssh')
access.allowed?.should be_truthy expect(action).to be_instance_of(Action::Gitaly)
end end
end end
end end
context 'ssh access has been disabled' do context 'ssh access has been disabled' do
it 'should deny pull access for host' do it 'should deny pull access for host' do
VCR.use_cassette('ssh-pull-disabled-old') do
expect do
gitlab_net.check_access('git-upload-pack', nil, project, key, changes, 'http')
end.to raise_error(AccessDeniedError, 'Git access over SSH is not allowed')
end
VCR.use_cassette('ssh-pull-disabled') do VCR.use_cassette('ssh-pull-disabled') do
access = gitlab_net.check_access('git-upload-pack', nil, project, key, changes, 'ssh') expect do
access.allowed?.should be_falsey gitlab_net.check_access('git-upload-pack', nil, project, key, changes, 'http')
access.message.should eq 'Git access over SSH is not allowed' end.to raise_error(AccessDeniedError, 'Git access over SSH is not allowed')
end end
end end
it 'should deny push access for host' do it 'should deny push access for host' do
VCR.use_cassette('ssh-push-disabled-old') do
expect do
gitlab_net.check_access('git-receive-pack', nil, project, key, changes, 'ssh')
end.to raise_error(AccessDeniedError, 'Git access over SSH is not allowed')
end
VCR.use_cassette('ssh-push-disabled') do VCR.use_cassette('ssh-push-disabled') do
access = gitlab_net.check_access('git-receive-pack', nil, project, key, changes, 'ssh') expect do
access.allowed?.should be_falsey gitlab_net.check_access('git-receive-pack', nil, project, key, changes, 'ssh')
access.message.should eq 'Git access over SSH is not allowed' end.to raise_error(AccessDeniedError, 'Git access over SSH is not allowed')
end end
end end
end end
context 'http access has been disabled' do context 'http access has been disabled' do
it 'should deny pull access for host' do it 'should deny pull access for host' do
VCR.use_cassette('http-pull-disabled-old') do
expect do
gitlab_net.check_access('git-upload-pack', nil, project, key, changes, 'http')
end.to raise_error(AccessDeniedError, 'Pulling over HTTP is not allowed.')
end
VCR.use_cassette('http-pull-disabled') do VCR.use_cassette('http-pull-disabled') do
access = gitlab_net.check_access('git-upload-pack', nil, project, key, changes, 'http') expect do
access.allowed?.should be_falsey gitlab_net.check_access('git-upload-pack', nil, project, key, changes, 'http')
access.message.should eq 'Pulling over HTTP is not allowed.' end.to raise_error(AccessDeniedError, 'Pulling over HTTP is not allowed.')
end end
end end
it 'should deny push access for host' do it 'should deny push access for host' do
VCR.use_cassette("http-push-disabled") do VCR.use_cassette('http-push-disabled-old') do
access = gitlab_net.check_access('git-receive-pack', nil, project, key, changes, 'http') expect do
access.allowed?.should be_falsey gitlab_net.check_access('git-receive-pack', nil, project, key, changes, 'http')
access.message.should eq 'Pushing over HTTP is not allowed.' end.to raise_error(AccessDeniedError, 'Pushing over HTTP is not allowed.')
end
VCR.use_cassette('http-push-disabled') do
expect do
gitlab_net.check_access('git-receive-pack', nil, project, key, changes, 'http')
end.to raise_error(AccessDeniedError, 'Pushing over HTTP is not allowed.')
end end
end end
end end
context 'ssh key without access to project' do context 'ssh key without access to project' do
it 'should deny pull access for host' do it 'should deny pull access for host' do
VCR.use_cassette("ssh-pull-project-denied") do VCR.use_cassette('ssh-pull-project-denied-old') do
access = gitlab_net.check_access('git-receive-pack', nil, project, key2, changes, 'ssh') expect do
access.allowed?.should be_falsey gitlab_net.check_access('git-receive-pack', nil, project, key2, changes, 'ssh')
end.to raise_error(AccessDeniedError, 'Git access over SSH is not allowed')
end
VCR.use_cassette('ssh-pull-project-denied') do
expect do
gitlab_net.check_access('git-receive-pack', nil, project, key2, changes, 'ssh')
end.to raise_error(AccessDeniedError, 'Git access over SSH is not allowed')
end end
end end
it 'should deny push access for host' do it 'should deny push access for host' do
VCR.use_cassette("ssh-push-project-denied") do VCR.use_cassette('ssh-push-project-denied-old') do
access = gitlab_net.check_access('git-upload-pack', nil, project, key2, changes, 'ssh') expect do
access.allowed?.should be_falsey gitlab_net.check_access('git-upload-pack', nil, project, key2, changes, 'ssh')
end.to raise_error(AccessDeniedError, 'Git access over SSH is not allowed')
end
VCR.use_cassette('ssh-push-project-denied') do
expect do
gitlab_net.check_access('git-upload-pack', nil, project, key2, changes, 'ssh')
end.to raise_error(AccessDeniedError, 'Git access over SSH is not allowed')
end end
end end
it 'should deny push access for host (with user)' do it 'should deny push access for host (with user)' do
VCR.use_cassette("ssh-push-project-denied-with-user") do VCR.use_cassette('ssh-push-project-denied-with-user-old') do
access = gitlab_net.check_access('git-upload-pack', nil, project, 'user-2', changes, 'ssh') expect do
access.allowed?.should be_falsey gitlab_net.check_access('git-upload-pack', nil, project, 'user-2', changes, 'ssh')
end.to raise_error(AccessDeniedError, 'Git access over SSH is not allowed')
end
VCR.use_cassette('ssh-push-project-denied-with-user') do
expect do
gitlab_net.check_access('git-upload-pack', nil, project, 'user-2', changes, 'ssh')
end.to raise_error(AccessDeniedError, 'Git access over SSH is not allowed')
end end
end end
end end
it "raises an exception if the connection fails" do it "raises an exception if the connection fails" do
Net::HTTP.any_instance.stub(:request).and_raise(StandardError) allow_any_instance_of(Net::HTTP).to receive(:request).and_raise(StandardError)
expect { expect {
gitlab_net.check_access('git-upload-pack', nil, project, 'user-1', changes, 'ssh') gitlab_net.check_access('git-upload-pack', nil, project, 'user-1', changes, 'ssh')
}.to raise_error(GitlabNet::ApiUnreachableError) }.to raise_error(GitlabNet::ApiUnreachableError)
......
require_relative 'spec_helper' require_relative 'spec_helper'
require_relative '../lib/gitlab_shell' require_relative '../lib/gitlab_shell'
require_relative '../lib/gitlab_access_status' require_relative '../lib/action'
describe GitlabShell do describe GitlabShell do
before do before do
...@@ -8,544 +8,202 @@ describe GitlabShell do ...@@ -8,544 +8,202 @@ describe GitlabShell do
FileUtils.mkdir_p(tmp_repos_path) FileUtils.mkdir_p(tmp_repos_path)
end end
after do after { FileUtils.rm_rf(tmp_repos_path) }
FileUtils.rm_rf(tmp_repos_path)
end
subject do subject { described_class.new(key_id) }
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
end
let(:key_id) { "key-#{rand(100) + 100}" } let(:key_id) { "key-#{rand(100) + 100}" }
let(:ssh_cmd) { nil }
let(:tmp_repos_path) { File.join(ROOT_PATH, 'tmp', 'repositories') } let(:tmp_repos_path) { File.join(ROOT_PATH, 'tmp', 'repositories') }
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(:gl_repository) { 'project-1' } let(:gl_repository) { 'project-1' }
let(:gl_username) { 'testuser' } let(:gl_username) { 'testuser' }
before do let(:api) { double(GitlabNet) }
GitlabConfig.any_instance.stub(audit_usernames: false)
end
describe :initialize do let(:gitaly_action) { Action::Gitaly.new(
let(:ssh_cmd) { 'git-receive-pack' } key_id,
gl_repository,
gl_username,
repo_path,
{ 'repository' => { 'relative_path' => repo_name, 'storage_name' => 'default' } , 'address' => 'unix:gitaly.socket' })
}
let(:api_2fa_recovery_action) { Action::API2FARecovery.new(key_id) }
let(:git_lfs_authenticate_action) { Action::GitLFSAuthenticate.new(key_id, repo_name) }
its(:key_id) { should == key_id } before do
allow(GitlabNet).to receive(:new).and_return(api)
allow(api).to receive(:discover).with(key_id).and_return('username' => gl_username)
end end
describe :parse_cmd do describe '#exec' do
describe 'git' do context "when we don't have a valid user" 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.command).to eq('2fa_recovery_codes')
end
it 'does not set repo name' do
expect(subject.repo_name).to be_nil
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 before do
subject.send :parse_cmd, ssh_args allow(api).to receive(:discover).with(key_id).and_return(nil)
end end
its(:repo_name) { should == 'dzaporozhets/gitlab.git' } it 'prints Welcome.. and returns true' do
its(:command) { should == 'git-lfs-authenticate' } expect {
its(:git_access) { should == 'git-upload-pack' } expect(subject.exec(nil)).to be_truthy
end }.to output("Welcome to GitLab, Anonymous!\n").to_stdout
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
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(:process_cmd).with(%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
end end
context 'git-upload-pack' do context 'when we have a valid user' do
it_behaves_like 'upload-pack', 'git-upload-pack' context 'when origin_cmd is nil' do
end it 'prints Welcome.. and returns true' do
expect {
context 'git upload-pack' do expect(subject.exec(nil)).to be_truthy
it_behaves_like 'upload-pack', 'git upload-pack' }.to output("Welcome to GitLab, @testuser!\n").to_stdout
end 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(:process_cmd).with(%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 end
it "should use usernames if configured to do so" do context 'when origin_cmd is empty' do
GitlabConfig.any_instance.stub(audit_usernames: true) it 'prints Welcome.. and returns true' do
$logger.should_receive(:info).with("executing git command", hash_including(user: 'testuser')) expect {
expect(subject.exec('')).to be_truthy
}.to output("Welcome to GitLab, @testuser!\n").to_stdout
end
end end
end end
context 'git-receive-pack' do context 'when origin_cmd is invalid' do
let(:ssh_cmd) { "git-receive-pack gitlab-ci.git" } it 'prints a message to stderr and returns false' do
after { subject.exec(ssh_cmd) } expect {
expect(subject.exec("git-invalid-command #{repo_name}")).to be_falsey
it "should process the command" do }.to output("GitLab: Disallowed command\n").to_stderr
subject.should_receive(:process_cmd).with(%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
end end
context 'gitaly-receive-pack' do context 'when origin_cmd is valid, but incomplete' do
let(:ssh_cmd) { "git-receive-pack gitlab-ci.git" } it 'prints a message to stderr and returns false' do
before { expect {
api.stub(check_access: gitaly_check_access) expect(subject.exec('git-upload-pack')).to be_falsey
} }.to output("GitLab: Disallowed command\n").to_stderr
after { subject.exec(ssh_cmd) }
it "should process the command" do
subject.should_receive(:process_cmd).with(%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
end end
shared_examples_for 'upload-archive' do |command| context 'when origin_cmd is git-lfs-authenticate' do
let(:ssh_cmd) { "#{command} gitlab-ci.git" } context 'but incomplete' do
let(:exec_cmd_params) { ['git-upload-archive', repo_path] } it 'prints a message to stderr and returns false' do
let(:exec_cmd_log_params) { exec_cmd_params } expect {
expect(subject.exec('git-lfs-authenticate')).to be_falsey
after { subject.exec(ssh_cmd) } }.to output("GitLab: Disallowed command\n").to_stderr
end
it "should process the command" do
subject.should_receive(:process_cmd).with(%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 end
it "should use usernames if configured to do so" do context 'but invalid' do
GitlabConfig.any_instance.stub(audit_usernames: true) it 'prints a message to stderr and returns false' do
$logger.should_receive(:info).with("executing git command", hash_including(user: 'testuser')) expect {
expect(subject.exec("git-lfs-authenticate #{repo_name} invalid")).to be_falsey
}.to output("GitLab: Disallowed command\n").to_stderr
end
end end
end end
context 'git-upload-archive' do context 'when origin_cmd is 2fa_recovery_codes' do
it_behaves_like 'upload-archive', 'git-upload-archive' let(:origin_cmd) { '2fa_recovery_codes' }
end let(:git_access) { '2fa_recovery_codes' }
context 'git upload-archive' do
it_behaves_like 'upload-archive', 'git upload-archive'
end
context 'gitaly-upload-archive' do
before do before do
api.stub(check_access: gitaly_check_access) expect(Action::API2FARecovery).to receive(:new).with(key_id).and_return(api_2fa_recovery_action)
end end
it_behaves_like 'upload-archive', 'git-upload-archive' do it 'returns true' do
let(:gitaly_executable) { "gitaly-upload-archive" } expect(api_2fa_recovery_action).to receive(:execute).with('2fa_recovery_codes', %w{ 2fa_recovery_codes }).and_return(true)
let(:exec_cmd_params) do expect(subject.exec(origin_cmd)).to be_truthy
[
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
end end
context 'arbitrary command' do context 'when access to the repo is denied' do
let(:ssh_cmd) { 'arbitrary command' } before do
after { subject.exec(ssh_cmd) } expect(api).to receive(:check_access).with('git-upload-pack', nil, repo_name, key_id, '_any').and_raise(AccessDeniedError, 'Sorry, access denied')
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
end
context 'no command' do
after { subject.exec(nil) }
it "should call api.discover" do it 'prints a message to stderr and returns false' do
api.should_receive(:discover).with(key_id) expect($stderr).to receive(:puts).with('GitLab: Sorry, access denied')
expect(subject.exec("git-upload-pack #{repo_name}")).to be_falsey
end end
end end
context "failed connection" do context 'when the API is unavailable' do
let(:ssh_cmd) { 'git-upload-pack gitlab-ci.git' } before do
expect(api).to receive(:check_access).with('git-upload-pack', nil, repo_name, key_id, '_any').and_raise(GitlabNet::ApiUnreachableError)
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 end
it "should not execute the command" do it 'prints a message to stderr and returns false' do
subject.should_not_receive(:exec_cmd) 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
end end
context 'with an API command' do context 'when access has been verified OK' do
before 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 end
context 'when generating recovery codes' do context 'when origin_cmd is git-upload-pack' do
let(:ssh_cmd) { '2fa_recovery_codes' } let(:origin_cmd) { 'git-upload-pack' }
after do let(:git_access) { 'git-upload-pack' }
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 it 'returns true' do
expect($stdout).to receive(:puts) expect(gitaly_action).to receive(:execute).with('git-upload-pack', %W{git-upload-pack #{repo_name}}).and_return(true)
.with(/f67c514de60c4953\n41278385fc00c1e0/) expect(subject.exec("#{origin_cmd} #{repo_name}")).to be_truthy
end end
context 'when the process is unsuccessful' do context 'but repo path is invalid' do
it 'displays the error to the user' do it 'prints a message to stderr and returns false' do
api.stub(two_factor_recovery_codes: { expect(gitaly_action).to receive(:execute).with('git-upload-pack', %W{git-upload-pack #{repo_name}}).and_raise(InvalidRepositoryPathError)
'success' => false, expect($stderr).to receive(:puts).with('GitLab: Invalid repository path')
'message' => 'Could not find the given key' expect(subject.exec("#{origin_cmd} #{repo_name}")).to be_falsey
})
expect($stdout).to receive(:puts)
.with(/Could not find the given key/)
end end
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 context "but we're using an old git version for Windows 2.14" do
$stderr.should_receive(:puts).with("GitLab: Invalid repository path") it 'returns true' do
expect(subject.exec(ssh_cmd)).to be_falsey 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 #NOTE: 'git upload-pack' vs. 'git-upload-pack'
end
end end
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.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 context 'when origin_cmd is git-lfs-authenticate' do
let(:git_trace_log_file) { '/tmp/git_trace_performance.log' } let(:origin_cmd) { 'git-lfs-authenticate' }
# let(:fake_payload) { 'FAKE PAYLOAD' }
let(:lfs_access) { double(GitlabLfsAuthentication, authentication_payload: fake_payload)}
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 before do
expect(File).to receive(:open).with(git_trace_log_file, 'a').and_raise(Errno::EACCES) expect(Action::GitLFSAuthenticate).to receive(:new).with(key_id, repo_name).and_return(git_lfs_authenticate_action)
end end
it "does not uses GIT_TRACE*" do context 'upload' do
# If we try to use it we'll show a warning to the users let(:git_access) { 'git-receive-pack' }
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] it 'returns true' do
expect(git_lfs_authenticate_action).to receive(:execute).with('git-lfs-authenticate', %w{ git-lfs-authenticate gitlab-ci.git upload }).and_return(true)
# expect($stdout).to receive(:puts).with(fake_payload)
expect(subject.exec("#{origin_cmd} #{repo_name} upload")).to be_truthy
end
end end
it "writes an entry on the log" do context 'download' do
message = 'Failed to open git trace log file' let(:git_access) { 'git-upload-pack' }
error = 'Permission denied'
expect($logger).to receive(:warn). it 'returns true' do
with(message, git_trace_log_file: git_trace_log_file, error: error) expect(git_lfs_authenticate_action).to receive(:execute).with('git-lfs-authenticate', %w{ git-lfs-authenticate gitlab-ci.git download }).and_return(true)
# expect($stdout).to receive(:puts).with(fake_payload)
expect(subject.exec("#{origin_cmd} #{repo_name} download")).to be_truthy
end
Kernel.should_receive(:exec).with(env, [1, 2], exec_options).once context 'for old git-lfs clients' do
shell.send :exec_cmd, [1, 2] it 'returns true' do
expect(git_lfs_authenticate_action).to receive(:execute).with('git-lfs-authenticate', %w{ git-lfs-authenticate gitlab-ci.git download long_oid }).and_return(true)
# expect($stdout).to receive(:puts).with(fake_payload)
expect(subject.exec("#{origin_cmd} #{repo_name} download long_oid")).to be_truthy
end
end
end end
end end
end end
end end
describe :api do
let(:shell) { GitlabShell.new(key_id) }
subject { shell.send :api }
it { should be_a(GitlabNet) }
end
end end
---
http_interactions:
- request:
method: post
uri: http://localhost:3000/api/v4/internal/allowed
body:
encoding: US-ASCII
string: action=git-upload-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&key_id=1&secret_token=0a3938d9d95d807e94d937af3a4fbbea%0A
headers:
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept:
- "*/*"
User-Agent:
- Ruby
Content-Type:
- application/x-www-form-urlencoded
response:
status:
code: 500
message: Internal Server Error
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '155'
Content-Type:
- application/json
Date:
- Wed, 21 Jun 2017 10:44:52 GMT
Etag:
- W/"45654cae433b5a9c5fbba1d45d382e52"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- 67ab4954-19e6-42ce-aae6-55c8ae5a365e
X-Runtime:
- '0.230871'
body:
encoding: UTF-8
string: '""'
http_version:
recorded_at: Wed, 21 Jun 2017 10:44:52 GMT
recorded_with: VCR 2.4.0
---
http_interactions:
- request:
method: post
uri: http://localhost:3000/api/v4/internal/allowed
body:
encoding: US-ASCII
string: action=git-upload-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&key_id=1&secret_token=0a3938d9d95d807e94d937af3a4fbbea%0A
headers:
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept:
- "*/*"
User-Agent:
- Ruby
Content-Type:
- application/x-www-form-urlencoded
response:
status:
code: 500
message: Internal Server Error
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '155'
Content-Type:
- application/json
Date:
- Wed, 21 Jun 2017 10:44:52 GMT
Etag:
- W/"45654cae433b5a9c5fbba1d45d382e52"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- 67ab4954-19e6-42ce-aae6-55c8ae5a365e
X-Runtime:
- '0.230871'
body:
encoding: UTF-8
string: '{"status":false,"message":"An internal server error occurred"}'
http_version:
recorded_at: Wed, 21 Jun 2017 10:44:52 GMT
recorded_with: VCR 2.4.0
---
http_interactions:
- request:
method: post
uri: http://localhost:3000/api/v4/internal/allowed
body:
encoding: US-ASCII
string: action=git-upload-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=http&env=%7B%7D&key_id=1&secret_token=0a3938d9d95d807e94d937af3a4fbbea%0A
headers:
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept:
- "*/*"
User-Agent:
- Ruby
Content-Type:
- application/x-www-form-urlencoded
response:
status:
code: 200
message: OK
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '62'
Content-Type:
- application/json
Date:
- Wed, 21 Jun 2017 10:32:01 GMT
Etag:
- W/"71e09fcf8a60a03cd1acc22806386ead"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- 70bdecc9-0078-4a4b-aa6b-cac1b2578886
X-Runtime:
- '0.324202'
body:
encoding: UTF-8
string: '{"status":false,"message":"Pulling over HTTP is not allowed."}'
http_version:
recorded_at: Wed, 21 Jun 2017 10:32:01 GMT
recorded_with: VCR 2.4.0
...@@ -17,8 +17,8 @@ http_interactions: ...@@ -17,8 +17,8 @@ http_interactions:
- application/x-www-form-urlencoded - application/x-www-form-urlencoded
response: response:
status: status:
code: 200 code: 401
message: OK message: Unauthorized
headers: headers:
Cache-Control: Cache-Control:
- max-age=0, private, must-revalidate - max-age=0, private, must-revalidate
......
---
http_interactions:
- request:
method: post
uri: http://localhost:3000/api/v4/internal/allowed
body:
encoding: US-ASCII
string: action=git-receive-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=http&env=%7B%7D&key_id=1&secret_token=0a3938d9d95d807e94d937af3a4fbbea%0A
headers:
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept:
- "*/*"
User-Agent:
- Ruby
Content-Type:
- application/x-www-form-urlencoded
response:
status:
code: 200
message: OK
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '62'
Content-Type:
- application/json
Date:
- Wed, 21 Jun 2017 10:32:01 GMT
Etag:
- W/"7f14e23ac07cc8b0a53c567fcf9432fd"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- 573f3584-87c6-41cb-a5bf-5e7ee76d4250
X-Runtime:
- '0.266135'
body:
encoding: UTF-8
string: '{"status":false,"message":"Pushing over HTTP is not allowed."}'
http_version:
recorded_at: Wed, 21 Jun 2017 10:32:01 GMT
recorded_with: VCR 2.4.0
...@@ -17,8 +17,8 @@ http_interactions: ...@@ -17,8 +17,8 @@ http_interactions:
- application/x-www-form-urlencoded - application/x-www-form-urlencoded
response: response:
status: status:
code: 200 code: 401
message: OK message: Unauthorized
headers: headers:
Cache-Control: Cache-Control:
- max-age=0, private, must-revalidate - max-age=0, private, must-revalidate
......
---
http_interactions:
- request:
method: post
uri: http://localhost:3000/api/v4/internal/allowed
body:
encoding: US-ASCII
string: action=git-upload-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&key_id=1&secret_token=0a3938d9d95d807e94d937af3a4fbbea%0A
headers:
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept:
- "*/*"
User-Agent:
- Ruby
Content-Type:
- application/x-www-form-urlencoded
response:
status:
code: 200
message: OK
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '63'
Content-Type:
- application/json
Date:
- Wed, 21 Jun 2017 12:23:57 GMT
Etag:
- W/"76a32010244f80700d5e1ba8a55d094c"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- 096ae253-c6fe-4360-b4d4-48f4b5435ca6
X-Runtime:
- '6.377187'
body:
encoding: UTF-8
string: '{"status":false,"message":"Git access over SSH is not allowed"}'
http_version:
recorded_at: Wed, 21 Jun 2017 12:23:57 GMT
recorded_with: VCR 2.4.0
...@@ -17,8 +17,8 @@ http_interactions: ...@@ -17,8 +17,8 @@ http_interactions:
- application/x-www-form-urlencoded - application/x-www-form-urlencoded
response: response:
status: status:
code: 200 code: 401
message: OK message: Unauthorized
headers: headers:
Cache-Control: Cache-Control:
- max-age=0, private, must-revalidate - max-age=0, private, must-revalidate
......
---
http_interactions:
- request:
method: post
uri: http://localhost:3000/api/v4/internal/allowed
body:
encoding: US-ASCII
string: action=git-receive-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&key_id=2&secret_token=0a3938d9d95d807e94d937af3a4fbbea%0A
headers:
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept:
- "*/*"
User-Agent:
- Ruby
Content-Type:
- application/x-www-form-urlencoded
response:
status:
code: 200
message: OK
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '63'
Content-Type:
- application/json
Date:
- Wed, 21 Jun 2017 12:24:04 GMT
Etag:
- W/"76a32010244f80700d5e1ba8a55d094c"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- c843a5a3-fc08-46eb-aa45-caceae515638
X-Runtime:
- '7.359835'
body:
encoding: UTF-8
string: '{"status":false,"message":"Git access over SSH is not allowed"}'
http_version:
recorded_at: Wed, 21 Jun 2017 12:24:04 GMT
recorded_with: VCR 2.4.0
...@@ -17,8 +17,8 @@ http_interactions: ...@@ -17,8 +17,8 @@ http_interactions:
- application/x-www-form-urlencoded - application/x-www-form-urlencoded
response: response:
status: status:
code: 200 code: 401
message: OK message: Unauthorized
headers: headers:
Cache-Control: Cache-Control:
- max-age=0, private, must-revalidate - max-age=0, private, must-revalidate
......
---
http_interactions:
- request:
method: post
uri: http://localhost:3000/api/v4/internal/allowed
body:
encoding: US-ASCII
string: action=git-receive-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&key_id=1&secret_token=0a3938d9d95d807e94d937af3a4fbbea%0A
headers:
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept:
- "*/*"
User-Agent:
- Ruby
Content-Type:
- application/x-www-form-urlencoded
response:
status:
code: 200
message: OK
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '63'
Content-Type:
- application/json
Date:
- Wed, 21 Jun 2017 12:23:57 GMT
Etag:
- W/"76a32010244f80700d5e1ba8a55d094c"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- 93620e06-fda9-4be5-855e-300f5d62fa3c
X-Runtime:
- '0.207159'
body:
encoding: UTF-8
string: '{"status":false,"message":"Git access over SSH is not allowed"}'
http_version:
recorded_at: Wed, 21 Jun 2017 12:23:57 GMT
recorded_with: VCR 2.4.0
...@@ -17,8 +17,8 @@ http_interactions: ...@@ -17,8 +17,8 @@ http_interactions:
- application/x-www-form-urlencoded - application/x-www-form-urlencoded
response: response:
status: status:
code: 200 code: 401
message: OK message: Unauthorized
headers: headers:
Cache-Control: Cache-Control:
- max-age=0, private, must-revalidate - max-age=0, private, must-revalidate
......
---
http_interactions:
- request:
method: post
uri: http://localhost:3000/api/v4/internal/allowed
body:
encoding: US-ASCII
string: action=git-upload-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&key_id=2&secret_token=0a3938d9d95d807e94d937af3a4fbbea%0A
headers:
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept:
- "*/*"
User-Agent:
- Ruby
Content-Type:
- application/x-www-form-urlencoded
response:
status:
code: 200
message: OK
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '63'
Content-Type:
- application/json
Date:
- Wed, 21 Jun 2017 12:24:04 GMT
Etag:
- W/"76a32010244f80700d5e1ba8a55d094c"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- 8ce54f29-9ed0-46e5-aedb-37edaa3d52da
X-Runtime:
- '0.228256'
body:
encoding: UTF-8
string: '{"status":false,"message":"Git access over SSH is not allowed"}'
http_version:
recorded_at: Wed, 21 Jun 2017 12:24:04 GMT
recorded_with: VCR 2.4.0
---
http_interactions:
- request:
method: post
uri: http://localhost:3000/api/v4/internal/allowed
body:
encoding: US-ASCII
string: action=git-upload-pack&changes=0000000000000000000000000000000000000000+92d0970eefd7acb6d548878925ce2208cfe2d2ec+refs%2Fheads%2Fbranch4&gl_repository&project=gitlab-org%2Fgitlab-test.git&protocol=ssh&env=%7B%7D&user_id=2&secret_token=0a3938d9d95d807e94d937af3a4fbbea%0A
headers:
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept:
- "*/*"
User-Agent:
- Ruby
Content-Type:
- application/x-www-form-urlencoded
response:
status:
code: 200
message: OK
headers:
Cache-Control:
- max-age=0, private, must-revalidate
Content-Length:
- '63'
Content-Type:
- application/json
Date:
- Wed, 21 Jun 2017 12:24:05 GMT
Etag:
- W/"76a32010244f80700d5e1ba8a55d094c"
Vary:
- Origin
X-Frame-Options:
- SAMEORIGIN
X-Request-Id:
- 3b242d73-d860-48ac-8fef-80e2d0d3daca
X-Runtime:
- '0.342469'
body:
encoding: UTF-8
string: '{"status":false,"message":"Git access over SSH is not allowed"}'
http_version:
recorded_at: Wed, 21 Jun 2017 12:24:05 GMT
recorded_with: VCR 2.4.0
...@@ -17,8 +17,8 @@ http_interactions: ...@@ -17,8 +17,8 @@ http_interactions:
- application/x-www-form-urlencoded - application/x-www-form-urlencoded
response: response:
status: status:
code: 200 code: 401
message: OK message: Unauthorized
headers: headers:
Cache-Control: Cache-Control:
- max-age=0, private, must-revalidate - max-age=0, private, must-revalidate
......
...@@ -17,8 +17,8 @@ http_interactions: ...@@ -17,8 +17,8 @@ http_interactions:
- application/x-www-form-urlencoded - application/x-www-form-urlencoded
response: response:
status: status:
code: 200 code: 401
message: OK message: Unauthorized
headers: headers:
Cache-Control: Cache-Control:
- max-age=0, private, must-revalidate - max-age=0, private, must-revalidate
......
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