summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacob Vosmaer <contact@jacobvosmaer.nl>2016-03-23 18:34:16 +0100
committerJacob Vosmaer <contact@jacobvosmaer.nl>2016-03-23 18:34:16 +0100
commit55f5a68f092cc64ae4782c0d7fbbf1d3d1ce6284 (patch)
tree0ef4d194ccc156720b168c9852e87a7591355f31
parent19a5e7c95e91baca58836ad3ae189190c9ba4ca2 (diff)
downloadgitlab-ce-55f5a68f092cc64ae4782c0d7fbbf1d3d1ce6284.tar.gz
Get Grack::Auth tests to pass
-rw-r--r--app/controllers/projects/application_controller.rb22
-rw-r--r--app/controllers/projects/git_http_controller.rb167
-rw-r--r--config/routes.rb10
3 files changed, 191 insertions, 8 deletions
diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb
index 657ee94cfd7..5f5dc1adadf 100644
--- a/app/controllers/projects/application_controller.rb
+++ b/app/controllers/projects/application_controller.rb
@@ -10,9 +10,6 @@ class Projects::ApplicationController < ApplicationController
def project
unless @project
- namespace = params[:namespace_id]
- id = params[:project_id] || params[:id]
-
# Redirect from
# localhost/group/project.git
# to
@@ -23,8 +20,7 @@ class Projects::ApplicationController < ApplicationController
return
end
- project_path = "#{namespace}/#{id}"
- @project = Project.find_with_namespace(project_path)
+ @project = find_project
if @project && can?(current_user, :read_project, @project)
if @project.path_with_namespace != project_path
@@ -44,6 +40,22 @@ class Projects::ApplicationController < ApplicationController
@project
end
+ def id
+ params[:project_id] || params[:id]
+ end
+
+ def namespace
+ params[:namespace_id]
+ end
+
+ def project_path
+ "#{namespace}/#{id}"
+ end
+
+ def find_project
+ Project.find_with_namespace(project_path)
+ end
+
def repository
@repository ||= project.repository
end
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
diff --git a/config/routes.rb b/config/routes.rb
index 4a3c23b7c1c..47ab1a89b8d 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -59,9 +59,6 @@ Rails.application.routes.draw do
mount Sidekiq::Web, at: '/admin/sidekiq', as: :sidekiq
end
- # Enable Grack support
- mount Grack::AuthSpawner, at: '/', constraints: lambda { |request| /[-\/\w\.]+\.git\//.match(request.path_info) }, via: [:get, :post, :put]
-
# Help
get 'help' => 'help#index'
get 'help/:category/:file' => 'help#show', as: :help_page, constraints: { category: /.*/, file: /[^\/\.]+/ }
@@ -426,6 +423,13 @@ Rails.application.routes.draw do
end
scope module: :projects do
+ # Git HTTP clients ('git clone' etc.)
+ scope constraints: { format: /(git|wiki\.git)/ } do
+ get '/info/refs', to: 'git_http#info_refs', only: :get
+ get '/git-upload-pack', to: 'git_http#git_upload_pack', only: :post
+ get '/git-receive-pack', to: 'git_http#git_receive_pack', only: :post
+ end
+
# Blob routes:
get '/new/*id', to: 'blob#new', constraints: { id: /.+/ }, as: 'new_blob'
post '/create/*id', to: 'blob#create', constraints: { id: /.+/ }, as: 'create_blob'