843548cc76
When doing an HTTP push, git (as of v1.7.9) first do an info/refs request, and only if this request requires authentication it asks the user for its password and authenticates further requests. The initial request normally clears without auth on public repos as it doesn't update any ref. This patch forces every git-receive-pack requests to provide authentication.
142 lines
3.3 KiB
Ruby
142 lines
3.3 KiB
Ruby
require_relative 'shell_env'
|
|
require_relative 'grack_helpers'
|
|
|
|
module Grack
|
|
class Auth < Rack::Auth::Basic
|
|
include Helpers
|
|
|
|
attr_accessor :user, :project, :ref, :env
|
|
|
|
def call(env)
|
|
@env = env
|
|
@request = Rack::Request.new(env)
|
|
@auth = Request.new(env)
|
|
|
|
# Need this patch due to the rails mount
|
|
|
|
# Need this if under RELATIVE_URL_ROOT
|
|
unless Gitlab.config.gitlab.relative_url_root.empty?
|
|
# If website is mounted using relative_url_root need to remove it first
|
|
@env['PATH_INFO'] = @request.path.sub(Gitlab.config.gitlab.relative_url_root,'')
|
|
else
|
|
@env['PATH_INFO'] = @request.path
|
|
end
|
|
|
|
@env['SCRIPT_NAME'] = ""
|
|
|
|
auth!
|
|
end
|
|
|
|
private
|
|
|
|
def auth!
|
|
return render_not_found unless project
|
|
|
|
if @auth.provided?
|
|
return bad_request unless @auth.basic?
|
|
|
|
# Authentication with username and password
|
|
login, password = @auth.credentials
|
|
|
|
# Allow authentication for GitLab CI service
|
|
# if valid token passed
|
|
if login == "gitlab-ci-token" && project.gitlab_ci?
|
|
token = project.gitlab_ci_service.token
|
|
|
|
if token.present? && token == password && service_name == 'git-upload-pack'
|
|
return @app.call(env)
|
|
end
|
|
end
|
|
|
|
@user = authenticate_user(login, password)
|
|
|
|
if @user
|
|
Gitlab::ShellEnv.set_env(@user)
|
|
@env['REMOTE_USER'] = @auth.username
|
|
else
|
|
return unauthorized
|
|
end
|
|
|
|
else
|
|
return unauthorized unless project.public?
|
|
end
|
|
|
|
if authorized_git_request?
|
|
@app.call(env)
|
|
else
|
|
unauthorized
|
|
end
|
|
end
|
|
|
|
def authorized_git_request?
|
|
authorize_request(service_name)
|
|
end
|
|
|
|
def authenticate_user(login, password)
|
|
auth = Gitlab::Auth.new
|
|
auth.find(login, password)
|
|
end
|
|
|
|
def authorize_request(service)
|
|
case service
|
|
when 'git-upload-pack'
|
|
can?(user, :download_code, project)
|
|
when'git-receive-pack'
|
|
refs.each do |ref|
|
|
action = if project.protected_branch?(ref)
|
|
:push_code_to_protected_branches
|
|
else
|
|
:push_code
|
|
end
|
|
|
|
return false unless can?(user, action, project)
|
|
end
|
|
|
|
# Never let git-receive-pack trough unauthenticated; it's
|
|
# harmless but git < 1.8 doesn't like it
|
|
return false if user.nil?
|
|
true
|
|
else
|
|
false
|
|
end
|
|
end
|
|
|
|
def service_name
|
|
if @request.get?
|
|
@request.params['service']
|
|
elsif @request.post?
|
|
File.basename(@request.path)
|
|
else
|
|
nil
|
|
end
|
|
end
|
|
|
|
def project
|
|
@project ||= project_by_path(@request.path_info)
|
|
end
|
|
|
|
def refs
|
|
@refs ||= parse_refs
|
|
end
|
|
|
|
def parse_refs
|
|
input = if @env["HTTP_CONTENT_ENCODING"] =~ /gzip/
|
|
Zlib::GzipReader.new(@request.body).read
|
|
else
|
|
@request.body.read
|
|
end
|
|
|
|
# Need to reset seek point
|
|
@request.body.rewind
|
|
|
|
# Parse refs
|
|
refs = input.force_encoding('ascii-8bit').scan(/refs\/heads\/([\/\w\.-]+)/n).flatten.compact
|
|
|
|
# Cleanup grabare from refs
|
|
# if push to multiple branches
|
|
refs.map do |ref|
|
|
ref.gsub(/00.*/, "")
|
|
end
|
|
end
|
|
end
|
|
end
|