diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/gitlab_access.rb | 21 | ||||
-rw-r--r-- | lib/gitlab_net.rb | 87 | ||||
-rw-r--r-- | lib/gitlab_post_receive.rb | 10 | ||||
-rw-r--r-- | lib/gitlab_shell.rb | 26 |
4 files changed, 79 insertions, 65 deletions
diff --git a/lib/gitlab_access.rb b/lib/gitlab_access.rb index 547b81d..22343fd 100644 --- a/lib/gitlab_access.rb +++ b/lib/gitlab_access.rb @@ -18,15 +18,20 @@ class GitlabAccess end def exec - status = api.check_access('git-receive-pack', @repo_name, @actor, @changes) - if status.allowed? - true - else - # reset GL_ID env since we stop git push here - ENV['GL_ID'] = nil - puts "GitLab: #{status.message}" - false + begin + status = api.check_access('git-receive-pack', @repo_name, @actor, @changes) + + return true if status.allowed? + + message = status.message + rescue GitlabNet::ApiUnreachableError + message = "Failed to authorize your Git request: internal API unreachable" end + + # reset GL_ID env since we stop git push here + ENV['GL_ID'] = nil + puts "GitLab: #{message}" + false end protected diff --git a/lib/gitlab_net.rb b/lib/gitlab_net.rb index 9bfc0d5..e74c97f 100644 --- a/lib/gitlab_net.rb +++ b/lib/gitlab_net.rb @@ -7,6 +7,8 @@ require_relative 'gitlab_logger' require_relative 'gitlab_access' class GitlabNet + class ApiUnreachableError < StandardError; end + def check_access(cmd, repo, actor, changes) project_name = repo.gsub("'", "") project_name = project_name.gsub(/\.git\Z/, "") @@ -64,63 +66,66 @@ class GitlabNet "#{config.gitlab_url}/api/v3/internal" end - def http_client_for(url) - Net::HTTP.new(url.host, url.port).tap do |http| - if URI::HTTPS === url - http.use_ssl = true - http.cert_store = cert_store - http.verify_mode = OpenSSL::SSL::VERIFY_NONE if config.http_settings['self_signed_cert'] - end + def http_client_for(uri) + http = Net::HTTP.new(uri.host, uri.port) + + if uri.is_a?(URI::HTTPS) + http.use_ssl = true + http.cert_store = cert_store + http.verify_mode = OpenSSL::SSL::VERIFY_NONE if config.http_settings['self_signed_cert'] end + + http end - def http_request_for(url, method = :get) + def http_request_for(method, uri, params = {}) + request_klass = method == :get ? Net::HTTP::Get : Net::HTTP::Post + request = request_klass.new(uri.request_uri) + user = config.http_settings['user'] password = config.http_settings['password'] + request.basic_auth(user, password) if user && password + + request.set_form_data(params.merge(secret_token: secret_token)) + + request + end + + def request(method, url, params = {}) + $logger.debug "Performing #{method.to_s.upcase} #{url}" + + uri = URI.parse(url) + + http = http_client_for(uri) + request = http_request_for(method, uri, params) - if method == :get - Net::HTTP::Get.new(url.request_uri).tap { |r| r.basic_auth(user, password) if user && password } + begin + response = http.start { http.request(request) } + rescue => e + $logger.warn "Failed to connect to internal API <#{method.to_s.upcase} #{url}>: #{e.inspect}" + raise ApiUnreachableError + end + + if response.code == "200" + $logger.debug "Received response #{response.code} => <#{response.body}>." else - Net::HTTP::Post.new(url.request_uri).tap { |r| r.basic_auth(user, password) if user && password } + $logger.error "API call <#{method.to_s.upcase} #{url}> failed: #{response.code} => <#{response.body}>." end + + response end def get(url) - $logger.debug "Performing GET #{url}" - - url = URI.parse(url) - http = http_client_for url - request = http_request_for url - request.set_form_data(secret_token: secret_token) - - http.start { |http| http.request(request) }.tap do |resp| - if resp.code == "200" - $logger.debug { "Received response #{resp.code} => <#{resp.body}>." } - else - $logger.error { "API call <GET #{url}> failed: #{resp.code} => <#{resp.body}>." } - end - end + request(:get, url) end def post(url, params) - $logger.debug "Performing POST #{url}" - - url = URI.parse(url) - http = http_client_for(url) - request = http_request_for(url, :post) - request.set_form_data(params.merge(secret_token: secret_token)) - - http.start { |http| http.request(request) }.tap do |resp| - if resp.code == "200" - $logger.debug { "Received response #{resp.code} => <#{resp.body}>." } - else - $logger.error { "API call <POST #{url}> failed: #{resp.code} => <#{resp.body}>." } - end - end + request(:post, url, params) end def cert_store - @cert_store ||= OpenSSL::X509::Store.new.tap do |store| + @cert_store ||= begin + store = OpenSSL::X509::Store.new store.set_default_paths if ca_file = config.http_settings['ca_file'] @@ -130,6 +135,8 @@ class GitlabNet if ca_path = config.http_settings['ca_path'] store.add_path(ca_path) end + + store end end diff --git a/lib/gitlab_post_receive.rb b/lib/gitlab_post_receive.rb index 7cd5535..98b935b 100644 --- a/lib/gitlab_post_receive.rb +++ b/lib/gitlab_post_receive.rb @@ -18,9 +18,13 @@ class GitlabPostReceive update_redis - if broadcast_message = GitlabNet.new.broadcast_message - puts - print_broadcast_message(broadcast_message["message"]) + begin + broadcast_message = GitlabNet.new.broadcast_message + if broadcast_message + puts + print_broadcast_message(broadcast_message["message"]) + end + rescue GitlabNet::ApiUnreachableError end end diff --git a/lib/gitlab_shell.rb b/lib/gitlab_shell.rb index a8494bf..b916122 100644 --- a/lib/gitlab_shell.rb +++ b/lib/gitlab_shell.rb @@ -21,15 +21,14 @@ class GitlabShell if git_cmds.include?(@git_cmd) ENV['GL_ID'] = @key_id - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - # TODO: Fix validation for git-annex-shell !!!! - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - if validate_access + access = api.check_access(@git_cmd, @repo_name, @key_id, '_any') + + if access.allowed? process_cmd else message = "gitlab-shell: Access denied for git command <#{@origin_cmd}> by #{log_username}." $logger.warn message - $stderr.puts "Access denied." + puts access.message end else raise DisallowedCommandError @@ -37,10 +36,12 @@ class GitlabShell else puts "Welcome to GitLab, #{username}!" end + rescue GitlabNet::ApiUnreachableError => ex + puts "Failed to authorize your Git request: internal API unreachable" rescue DisallowedCommandError => ex message = "gitlab-shell: Attempt to execute disallowed command <#{@origin_cmd}> by #{log_username}." $logger.warn message - puts 'Not allowed command' + puts 'Disallowed command' end protected @@ -90,10 +91,6 @@ class GitlabShell end end - def validate_access - api.check_access(@git_cmd, @repo_name, @key_id, '_any').allowed? - end - # This method is not covered by Rspec because it ends the current Ruby process. def exec_cmd(*args) Kernel::exec({'PATH' => ENV['PATH'], 'LD_LIBRARY_PATH' => ENV['LD_LIBRARY_PATH'], 'GL_ID' => ENV['GL_ID']}, *args, unsetenv_others: true) @@ -104,11 +101,12 @@ class GitlabShell end def user - # Can't use "@user ||=" because that will keep hitting the API when @user is really nil! - if instance_variable_defined?('@user') - @user - else + return @user if defined?(@user) + + begin @user = api.discover(@key_id) + rescue GitlabNet::ApiUnreachableError + @user = nil end end |