Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorRémy Coutable <remy@rymai.me>2016-08-11 17:36:40 +0300
committerRémy Coutable <remy@rymai.me>2016-08-11 17:36:40 +0300
commit4c29c25497c9a20a5d1f57cd287123cd41edad96 (patch)
treea9eda345190f046c80456e4979973f3489ddfa21 /lib
parent7e9b41896d8c2ffae36152401f35479b39297e78 (diff)
parent6e6ad3e2fc386044134cbb33395cceb97e913fa0 (diff)
Merge branch 'remove-grack-lfs' into 'master'
Remove Grack::Auth: part 2 (LFS) Deprecate Grack::Auth and handle LFS in Rails controllers under the Project namespace. Closes https://gitlab.com/gitlab-org/gitlab-ce/issues/14501 See merge request !5369
Diffstat (limited to 'lib')
-rw-r--r--lib/gitlab/backend/grack_auth.rb163
-rw-r--r--lib/gitlab/lfs/response.rb329
-rw-r--r--lib/gitlab/lfs/router.rb98
3 files changed, 0 insertions, 590 deletions
diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb
deleted file mode 100644
index ab94abeda77..00000000000
--- a/lib/gitlab/backend/grack_auth.rb
+++ /dev/null
@@ -1,163 +0,0 @@
-module Grack
- class AuthSpawner
- def self.call(env)
- # Avoid issues with instance variables in Grack::Auth persisting across
- # requests by creating a new instance for each request.
- Auth.new({}).call(env)
- end
- end
-
- class Auth < Rack::Auth::Basic
- attr_accessor :user, :project, :env
-
- def call(env)
- @env = env
- @request = Rack::Request.new(env)
- @auth = Request.new(env)
-
- @ci = false
-
- # 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!
-
- lfs_response = Gitlab::Lfs::Router.new(project, @user, @ci, @request).try_call
- return lfs_response unless lfs_response.nil?
-
- if @user.nil? && !@ci
- unauthorized
- else
- render_not_found
- end
- end
-
- private
-
- def auth!
- return unless @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 ci_request?(login, password)
- @ci = true
- return
- end
-
- @user = authenticate_user(login, password)
- end
-
- def ci_request?(login, password)
- matched_login = /(?<s>^[a-zA-Z]*-ci)-token$/.match(login)
-
- if project && matched_login.present?
- 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" && git_cmd == 'git-upload-pack' && password.present?
- token = Doorkeeper::AccessToken.by_token(password)
- token && token.accessible? && User.find_by(id: token.resource_owner_id)
- end
- end
-
- def authenticate_user(login, password)
- user = Gitlab::Auth.find_with_user_password(login, password)
-
- unless user
- user = oauth_access_token_check(login, password)
- end
-
- # 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
-
- if 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
- end
-
- user
- end
-
- def git_cmd
- if @request.get?
- @request.params['service']
- elsif @request.post?
- File.basename(@request.path)
- else
- nil
- end
- end
-
- def project
- return @project if defined?(@project)
-
- @project = project_by_path(@request.path_info)
- end
-
- def project_by_path(path)
- if m = /^([\w\.\/-]+)\.git/.match(path).to_a
- path_with_namespace = m.last
- path_with_namespace.gsub!(/\.wiki$/, '')
-
- path_with_namespace[0] = '' if path_with_namespace.start_with?('/')
- Project.find_with_namespace(path_with_namespace)
- end
- end
-
- def render_not_found
- [404, { "Content-Type" => "text/plain" }, ["Not Found"]]
- end
- end
-end
diff --git a/lib/gitlab/lfs/response.rb b/lib/gitlab/lfs/response.rb
deleted file mode 100644
index a1ee1aa81ff..00000000000
--- a/lib/gitlab/lfs/response.rb
+++ /dev/null
@@ -1,329 +0,0 @@
-module Gitlab
- module Lfs
- class Response
- def initialize(project, user, ci, request)
- @origin_project = project
- @project = storage_project(project)
- @user = user
- @ci = ci
- @env = request.env
- @request = request
- end
-
- def render_download_object_response(oid)
- render_response_to_download do
- if check_download_sendfile_header?
- render_lfs_sendfile(oid)
- else
- render_not_found
- end
- end
- end
-
- def render_batch_operation_response
- request_body = JSON.parse(@request.body.read)
- case request_body["operation"]
- when "download"
- render_batch_download(request_body)
- when "upload"
- render_batch_upload(request_body)
- else
- render_not_found
- end
- end
-
- def render_storage_upload_authorize_response(oid, size)
- render_response_to_push do
- [
- 200,
- { "Content-Type" => "application/json; charset=utf-8" },
- [JSON.dump({
- 'StoreLFSPath' => "#{Gitlab.config.lfs.storage_path}/tmp/upload",
- 'LfsOid' => oid,
- 'LfsSize' => size
- })]
- ]
- end
- end
-
- def render_storage_upload_store_response(oid, size, tmp_file_name)
- return render_forbidden unless tmp_file_name
-
- render_response_to_push do
- render_lfs_upload_ok(oid, size, tmp_file_name)
- end
- end
-
- def render_unsupported_deprecated_api
- [
- 501,
- { "Content-Type" => "application/json; charset=utf-8" },
- [JSON.dump({
- 'message' => 'Server supports batch API only, please update your Git LFS client to version 1.0.1 and up.',
- 'documentation_url' => "#{Gitlab.config.gitlab.url}/help",
- })]
- ]
- end
-
- private
-
- def render_not_enabled
- [
- 501,
- {
- "Content-Type" => "application/json; charset=utf-8",
- },
- [JSON.dump({
- 'message' => 'Git LFS is not enabled on this GitLab server, contact your admin.',
- 'documentation_url' => "#{Gitlab.config.gitlab.url}/help",
- })]
- ]
- end
-
- def render_unauthorized
- [
- 401,
- {
- 'Content-Type' => 'text/plain'
- },
- ['Unauthorized']
- ]
- end
-
- def render_not_found
- [
- 404,
- {
- "Content-Type" => "application/vnd.git-lfs+json"
- },
- [JSON.dump({
- 'message' => 'Not found.',
- 'documentation_url' => "#{Gitlab.config.gitlab.url}/help",
- })]
- ]
- end
-
- def render_forbidden
- [
- 403,
- {
- "Content-Type" => "application/vnd.git-lfs+json"
- },
- [JSON.dump({
- 'message' => 'Access forbidden. Check your access level.',
- 'documentation_url' => "#{Gitlab.config.gitlab.url}/help",
- })]
- ]
- end
-
- def render_lfs_sendfile(oid)
- return render_not_found unless oid.present?
-
- lfs_object = object_for_download(oid)
-
- if lfs_object && lfs_object.file.exists?
- [
- 200,
- {
- # GitLab-workhorse will forward Content-Type header
- "Content-Type" => "application/octet-stream",
- "X-Sendfile" => lfs_object.file.path
- },
- []
- ]
- else
- render_not_found
- end
- end
-
- def render_batch_upload(body)
- return render_not_found if body.empty? || body['objects'].nil?
-
- render_response_to_push do
- response = build_upload_batch_response(body['objects'])
- [
- 200,
- {
- "Content-Type" => "application/json; charset=utf-8",
- "Cache-Control" => "private",
- },
- [JSON.dump(response)]
- ]
- end
- end
-
- def render_batch_download(body)
- return render_not_found if body.empty? || body['objects'].nil?
-
- render_response_to_download do
- response = build_download_batch_response(body['objects'])
- [
- 200,
- {
- "Content-Type" => "application/json; charset=utf-8",
- "Cache-Control" => "private",
- },
- [JSON.dump(response)]
- ]
- end
- end
-
- def render_lfs_upload_ok(oid, size, tmp_file)
- if store_file(oid, size, tmp_file)
- [
- 200,
- {
- 'Content-Type' => 'text/plain',
- 'Content-Length' => 0
- },
- []
- ]
- else
- [
- 422,
- { 'Content-Type' => 'text/plain' },
- ["Unprocessable entity"]
- ]
- end
- end
-
- def render_response_to_download
- return render_not_enabled unless Gitlab.config.lfs.enabled
-
- unless @project.public?
- return render_unauthorized unless @user || @ci
- return render_forbidden unless user_can_fetch?
- end
-
- yield
- end
-
- def render_response_to_push
- return render_not_enabled unless Gitlab.config.lfs.enabled
- return render_unauthorized unless @user
- return render_forbidden unless user_can_push?
-
- yield
- end
-
- def check_download_sendfile_header?
- @env['HTTP_X_SENDFILE_TYPE'].to_s == "X-Sendfile"
- end
-
- def user_can_fetch?
- # Check user access against the project they used to initiate the pull
- @ci || @user.can?(:download_code, @origin_project)
- end
-
- def user_can_push?
- # Check user access against the project they used to initiate the push
- @user.can?(:push_code, @origin_project)
- end
-
- def storage_project(project)
- if project.forked?
- storage_project(project.forked_from_project)
- else
- project
- end
- end
-
- def store_file(oid, size, tmp_file)
- tmp_file_path = File.join("#{Gitlab.config.lfs.storage_path}/tmp/upload", tmp_file)
-
- object = LfsObject.find_or_create_by(oid: oid, size: size)
- if object.file.exists?
- success = true
- else
- success = move_tmp_file_to_storage(object, tmp_file_path)
- end
-
- if success
- success = link_to_project(object)
- end
-
- success
- ensure
- # Ensure that the tmp file is removed
- FileUtils.rm_f(tmp_file_path)
- end
-
- def object_for_download(oid)
- @project.lfs_objects.find_by(oid: oid)
- end
-
- def move_tmp_file_to_storage(object, path)
- File.open(path) do |f|
- object.file = f
- end
-
- object.file.store!
- object.save
- end
-
- def link_to_project(object)
- if object && !object.projects.exists?(@project.id)
- object.projects << @project
- object.save
- end
- end
-
- def select_existing_objects(objects)
- objects_oids = objects.map { |o| o['oid'] }
- @project.lfs_objects.where(oid: objects_oids).pluck(:oid).to_set
- end
-
- def build_upload_batch_response(objects)
- selected_objects = select_existing_objects(objects)
-
- upload_hypermedia_links(objects, selected_objects)
- end
-
- def build_download_batch_response(objects)
- selected_objects = select_existing_objects(objects)
-
- download_hypermedia_links(objects, selected_objects)
- end
-
- def download_hypermedia_links(all_objects, existing_objects)
- all_objects.each do |object|
- if existing_objects.include?(object['oid'])
- object['actions'] = {
- 'download' => {
- 'href' => "#{@origin_project.http_url_to_repo}/gitlab-lfs/objects/#{object['oid']}",
- 'header' => {
- 'Authorization' => @env['HTTP_AUTHORIZATION']
- }.compact
- }
- }
- else
- object['error'] = {
- 'code' => 404,
- 'message' => "Object does not exist on the server or you don't have permissions to access it",
- }
- end
- end
-
- { 'objects' => all_objects }
- end
-
- def upload_hypermedia_links(all_objects, existing_objects)
- all_objects.each do |object|
- # generate actions only for non-existing objects
- next if existing_objects.include?(object['oid'])
-
- object['actions'] = {
- 'upload' => {
- 'href' => "#{@origin_project.http_url_to_repo}/gitlab-lfs/objects/#{object['oid']}/#{object['size']}",
- 'header' => {
- 'Authorization' => @env['HTTP_AUTHORIZATION']
- }.compact
- }
- }
- end
-
- { 'objects' => all_objects }
- end
- end
- end
-end
diff --git a/lib/gitlab/lfs/router.rb b/lib/gitlab/lfs/router.rb
deleted file mode 100644
index f2a76a56b8f..00000000000
--- a/lib/gitlab/lfs/router.rb
+++ /dev/null
@@ -1,98 +0,0 @@
-module Gitlab
- module Lfs
- class Router
- attr_reader :project, :user, :ci, :request
-
- def initialize(project, user, ci, request)
- @project = project
- @user = user
- @ci = ci
- @env = request.env
- @request = request
- end
-
- def try_call
- return unless @request && @request.path.present?
-
- case @request.request_method
- when 'GET'
- get_response
- when 'POST'
- post_response
- when 'PUT'
- put_response
- else
- nil
- end
- end
-
- private
-
- def get_response
- path_match = @request.path.match(/\/(info\/lfs|gitlab-lfs)\/objects\/([0-9a-f]{64})$/)
- return nil unless path_match
-
- oid = path_match[2]
- return nil unless oid
-
- case path_match[1]
- when "info/lfs"
- lfs.render_unsupported_deprecated_api
- when "gitlab-lfs"
- lfs.render_download_object_response(oid)
- else
- nil
- end
- end
-
- def post_response
- post_path = @request.path.match(/\/info\/lfs\/objects(\/batch)?$/)
- return nil unless post_path
-
- # Check for Batch API
- if post_path[0].ends_with?("/info/lfs/objects/batch")
- lfs.render_batch_operation_response
- elsif post_path[0].ends_with?("/info/lfs/objects")
- lfs.render_unsupported_deprecated_api
- else
- nil
- end
- end
-
- def put_response
- object_match = @request.path.match(/\/gitlab-lfs\/objects\/([0-9a-f]{64})\/([0-9]+)(|\/authorize){1}$/)
- return nil if object_match.nil?
-
- oid = object_match[1]
- size = object_match[2].try(:to_i)
- return nil if oid.nil? || size.nil?
-
- # GitLab-workhorse requests
- # 1. Try to authorize the request
- # 2. send a request with a header containing the name of the temporary file
- if object_match[3] && object_match[3] == '/authorize'
- lfs.render_storage_upload_authorize_response(oid, size)
- else
- tmp_file_name = sanitize_tmp_filename(@request.env['HTTP_X_GITLAB_LFS_TMP'])
- lfs.render_storage_upload_store_response(oid, size, tmp_file_name)
- end
- end
-
- def lfs
- return unless @project
-
- Gitlab::Lfs::Response.new(@project, @user, @ci, @request)
- end
-
- def sanitize_tmp_filename(name)
- if name.present?
- name.gsub!(/^.*(\\|\/)/, '')
- name = name.match(/[0-9a-f]{73}/)
- name[0] if name
- else
- nil
- end
- end
- end
- end
-end