PageRenderTime 47ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/gitlab/backend/grack_auth.rb

https://gitlab.com/win32/gitlab-ce
Ruby | 182 lines | 127 code | 33 blank | 22 comment | 32 complexity | 2bf4d2b5b6036df721a4b1c204ae3552 MD5 | raw file
Possible License(s): CC-BY-3.0
  1. require_relative 'rack_attack_helpers'
  2. require_relative 'shell_env'
  3. module Grack
  4. class Auth < Rack::Auth::Basic
  5. attr_accessor :user, :project, :env
  6. def call(env)
  7. @env = env
  8. @request = Rack::Request.new(env)
  9. @auth = Request.new(env)
  10. @gitlab_ci = false
  11. # Need this patch due to the rails mount
  12. # Need this if under RELATIVE_URL_ROOT
  13. unless Gitlab.config.gitlab.relative_url_root.empty?
  14. # If website is mounted using relative_url_root need to remove it first
  15. @env['PATH_INFO'] = @request.path.sub(Gitlab.config.gitlab.relative_url_root,'')
  16. else
  17. @env['PATH_INFO'] = @request.path
  18. end
  19. @env['SCRIPT_NAME'] = ""
  20. auth!
  21. if project && authorized_request?
  22. @app.call(env)
  23. elsif @user.nil? && !@gitlab_ci
  24. unauthorized
  25. else
  26. render_not_found
  27. end
  28. end
  29. private
  30. def auth!
  31. return unless @auth.provided?
  32. return bad_request unless @auth.basic?
  33. # Authentication with username and password
  34. login, password = @auth.credentials
  35. # Allow authentication for GitLab CI service
  36. # if valid token passed
  37. if gitlab_ci_request?(login, password)
  38. @gitlab_ci = true
  39. return
  40. end
  41. @user = authenticate_user(login, password)
  42. if @user
  43. Gitlab::ShellEnv.set_env(@user)
  44. @env['REMOTE_USER'] = @auth.username
  45. end
  46. end
  47. def gitlab_ci_request?(login, password)
  48. if login == "gitlab-ci-token" && project && project.gitlab_ci?
  49. token = project.gitlab_ci_service.token
  50. if token.present? && token == password && git_cmd == 'git-upload-pack'
  51. return true
  52. end
  53. end
  54. false
  55. end
  56. def oauth_access_token_check(login, password)
  57. if login == "oauth2" && git_cmd == 'git-upload-pack' && password.present?
  58. token = Doorkeeper::AccessToken.by_token(password)
  59. token && token.accessible? && User.find_by(id: token.resource_owner_id)
  60. end
  61. end
  62. def authenticate_user(login, password)
  63. user = Gitlab::Auth.new.find(login, password)
  64. unless user
  65. user = oauth_access_token_check(login, password)
  66. end
  67. # If the user authenticated successfully, we reset the auth failure count
  68. # from Rack::Attack for that IP. A client may attempt to authenticate
  69. # with a username and blank password first, and only after it receives
  70. # a 401 error does it present a password. Resetting the count prevents
  71. # false positives from occurring.
  72. #
  73. # Otherwise, we let Rack::Attack know there was a failed authentication
  74. # attempt from this IP. This information is stored in the Rails cache
  75. # (Redis) and will be used by the Rack::Attack middleware to decide
  76. # whether to block requests from this IP.
  77. config = Gitlab.config.rack_attack.git_basic_auth
  78. if config.enabled
  79. if user
  80. # A successful login will reset the auth failure count from this IP
  81. Rack::Attack::Allow2Ban.reset(@request.ip, config)
  82. else
  83. banned = Rack::Attack::Allow2Ban.filter(@request.ip, config) do
  84. # Unless the IP is whitelisted, return true so that Allow2Ban
  85. # increments the counter (stored in Rails.cache) for the IP
  86. if config.ip_whitelist.include?(@request.ip)
  87. false
  88. else
  89. true
  90. end
  91. end
  92. if banned
  93. Rails.logger.info "IP #{@request.ip} failed to login " \
  94. "as #{login} but has been temporarily banned from Git auth"
  95. end
  96. end
  97. end
  98. user
  99. end
  100. def authorized_request?
  101. return true if @gitlab_ci
  102. case git_cmd
  103. when *Gitlab::GitAccess::DOWNLOAD_COMMANDS
  104. if user
  105. Gitlab::GitAccess.new(user, project).download_access_check.allowed?
  106. elsif project.public?
  107. # Allow clone/fetch for public projects
  108. true
  109. else
  110. false
  111. end
  112. when *Gitlab::GitAccess::PUSH_COMMANDS
  113. if user
  114. # Skip user authorization on upload request.
  115. # It will be done by the pre-receive hook in the repository.
  116. true
  117. else
  118. false
  119. end
  120. else
  121. false
  122. end
  123. end
  124. def git_cmd
  125. if @request.get?
  126. @request.params['service']
  127. elsif @request.post?
  128. File.basename(@request.path)
  129. else
  130. nil
  131. end
  132. end
  133. def project
  134. return @project if defined?(@project)
  135. @project = project_by_path(@request.path_info)
  136. end
  137. def project_by_path(path)
  138. if m = /^([\w\.\/-]+)\.git/.match(path).to_a
  139. path_with_namespace = m.last
  140. path_with_namespace.gsub!(/\.wiki$/, '')
  141. path_with_namespace[0] = '' if path_with_namespace.start_with?('/')
  142. Project.find_with_namespace(path_with_namespace)
  143. end
  144. end
  145. def render_not_found
  146. [404, { "Content-Type" => "text/plain" }, ["Not Found"]]
  147. end
  148. end
  149. end