diff options
author | Jacob Vosmaer <contact@jacobvosmaer.nl> | 2016-03-23 20:34:16 +0300 |
---|---|---|
committer | Jacob Vosmaer <contact@jacobvosmaer.nl> | 2016-03-23 20:34:16 +0300 |
commit | 55f5a68f092cc64ae4782c0d7fbbf1d3d1ce6284 (patch) | |
tree | 0ef4d194ccc156720b168c9852e87a7591355f31 /app/controllers/projects/git_http_controller.rb | |
parent | 19a5e7c95e91baca58836ad3ae189190c9ba4ca2 (diff) |
Get Grack::Auth tests to pass
Diffstat (limited to 'app/controllers/projects/git_http_controller.rb')
-rw-r--r-- | app/controllers/projects/git_http_controller.rb | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/app/controllers/projects/git_http_controller.rb b/app/controllers/projects/git_http_controller.rb new file mode 100644 index 00000000000..129e87dbf13 --- /dev/null +++ b/app/controllers/projects/git_http_controller.rb @@ -0,0 +1,167 @@ +class Projects::GitHttpController < Projects::ApplicationController + skip_before_action :repository + before_action :authenticate_user + before_action :project_found? + + def git_rpc + if upload_pack? && upload_pack_allowed? + render_ok and return + end + + render_not_found + end + + %i{info_refs git_receive_pack git_upload_pack}.each do |method| + alias_method method, :git_rpc + end + + private + + def authenticate_user + return if project && project.public? && upload_pack? + + authenticate_or_request_with_http_basic do |login, password| + return @ci = true if ci_request?(login, password) + + @user = Gitlab::Auth.new.find(login, password) + @user ||= oauth_access_token_check(login, password) + rate_limit_ip!(login, @user) + end + end + + def project_found? + render_not_found if project.nil? + end + + def ci_request?(login, password) + matched_login = /(?<s>^[a-zA-Z]*-ci)-token$/.match(login) + + if project && matched_login.present? && upload_pack? + underscored_service = matched_login['s'].underscore + + if underscored_service == 'gitlab_ci' + return project && project.valid_build_token?(password) + elsif Service.available_services_names.include?(underscored_service) + service_method = "#{underscored_service}_service" + service = project.send(service_method) + + return service && service.activated? && service.valid_token?(password) + end + end + + false + end + + def oauth_access_token_check(login, password) + if login == "oauth2" && upload_pack? && password.present? + token = Doorkeeper::AccessToken.by_token(password) + token && token.accessible? && User.find_by(id: token.resource_owner_id) + end + end + + def rate_limit_ip!(login, user) + # If the user authenticated successfully, we reset the auth failure count + # from Rack::Attack for that IP. A client may attempt to authenticate + # with a username and blank password first, and only after it receives + # a 401 error does it present a password. Resetting the count prevents + # false positives from occurring. + # + # Otherwise, we let Rack::Attack know there was a failed authentication + # attempt from this IP. This information is stored in the Rails cache + # (Redis) and will be used by the Rack::Attack middleware to decide + # whether to block requests from this IP. + + config = Gitlab.config.rack_attack.git_basic_auth + return user unless config.enabled + + if user + # A successful login will reset the auth failure count from this IP + Rack::Attack::Allow2Ban.reset(request.ip, config) + else + banned = Rack::Attack::Allow2Ban.filter(request.ip, config) do + # Unless the IP is whitelisted, return true so that Allow2Ban + # increments the counter (stored in Rails.cache) for the IP + if config.ip_whitelist.include?(request.ip) + false + else + true + end + end + + if banned + Rails.logger.info "IP #{request.ip} failed to login " \ + "as #{login} but has been temporarily banned from Git auth" + end + end + + user + end + + def project + return @project if defined?(@project) + @project = find_project + end + + def id + id = params[:project_id] + return if id.nil? + + if id.end_with?('.wiki.git') + id.slice(0, id.length - 9) + elsif id.end_with?('.git') + id.slice(0, id.length - 4) + end + end + + def repo_path + @repo_path ||= begin + if params[:project_id].end_with?('.wiki.git') + project.wiki.wiki.path + else + repository.path_to_repo + end + end + end + + def upload_pack? + if action_name == 'info_refs' + params[:service] == 'git-upload-pack' + else + action_name == 'git_upload_pack' + end + end + + def render_ok + render json: { + 'GL_ID' => Gitlab::ShellEnv.gl_id(@user), + 'RepoPath' => repo_path, + } + end + + def render_not_found + render text: 'Not Found', status: :not_found + end + + def ci? + !!@ci + end + + def user + @user + end + + def upload_pack_allowed? + if !Gitlab.config.gitlab_shell.upload_pack + false + elsif ci? + true + elsif user + Gitlab::GitAccess.new(user, project).download_access_check.allowed? + elsif project.public? + # Allow clone/fetch for public projects + true + else + false + end + end +end |