PageRenderTime 48ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/gitlab/backend/grack_auth.rb

https://gitlab.com/gerfuls/gitlab-ce
Ruby | 160 lines | 115 code | 29 blank | 16 comment | 26 complexity | 0c165fddabdbf0b2d068cf2b99301f3b MD5 | raw file
Possible License(s): CC-BY-3.0
  1. require_relative 'shell_env'
  2. module Grack
  3. class Auth < Rack::Auth::Basic
  4. attr_accessor :user, :project, :env
  5. def call(env)
  6. @env = env
  7. @request = Rack::Request.new(env)
  8. @auth = Request.new(env)
  9. # Need this patch due to the rails mount
  10. # Need this if under RELATIVE_URL_ROOT
  11. unless Gitlab.config.gitlab.relative_url_root.empty?
  12. # If website is mounted using relative_url_root need to remove it first
  13. @env['PATH_INFO'] = @request.path.sub(Gitlab.config.gitlab.relative_url_root,'')
  14. else
  15. @env['PATH_INFO'] = @request.path
  16. end
  17. @env['SCRIPT_NAME'] = ""
  18. if project
  19. auth!
  20. else
  21. render_not_found
  22. end
  23. end
  24. private
  25. def auth!
  26. if @auth.provided?
  27. return bad_request unless @auth.basic?
  28. # Authentication with username and password
  29. login, password = @auth.credentials
  30. # Allow authentication for GitLab CI service
  31. # if valid token passed
  32. if gitlab_ci_request?(login, password)
  33. return @app.call(env)
  34. end
  35. @user = authenticate_user(login, password)
  36. if @user
  37. Gitlab::ShellEnv.set_env(@user)
  38. @env['REMOTE_USER'] = @auth.username
  39. end
  40. end
  41. if authorized_request?
  42. @app.call(env)
  43. else
  44. unauthorized
  45. end
  46. end
  47. def gitlab_ci_request?(login, password)
  48. if login == "gitlab-ci-token" && 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. return user if user.present?
  68. # At this point, we know the credentials were wrong. We let Rack::Attack
  69. # know there was a failed authentication attempt from this IP. This
  70. # information is stored in the Rails cache (Redis) and will be used by
  71. # the Rack::Attack middleware to decide whether to block requests from
  72. # this IP.
  73. config = Gitlab.config.rack_attack.git_basic_auth
  74. Rack::Attack::Allow2Ban.filter(@request.ip, config) do
  75. # Unless the IP is whitelisted, return true so that Allow2Ban
  76. # increments the counter (stored in Rails.cache) for the IP
  77. if config.ip_whitelist.include?(@request.ip)
  78. false
  79. else
  80. true
  81. end
  82. end
  83. nil # No user was found
  84. end
  85. def authorized_request?
  86. case git_cmd
  87. when *Gitlab::GitAccess::DOWNLOAD_COMMANDS
  88. if user
  89. Gitlab::GitAccess.new.download_access_check(user, project).allowed?
  90. elsif project.public?
  91. # Allow clone/fetch for public projects
  92. true
  93. else
  94. false
  95. end
  96. when *Gitlab::GitAccess::PUSH_COMMANDS
  97. if user
  98. # Skip user authorization on upload request.
  99. # It will be done by the pre-receive hook in the repository.
  100. true
  101. else
  102. false
  103. end
  104. else
  105. false
  106. end
  107. end
  108. def git_cmd
  109. if @request.get?
  110. @request.params['service']
  111. elsif @request.post?
  112. File.basename(@request.path)
  113. else
  114. nil
  115. end
  116. end
  117. def project
  118. @project ||= project_by_path(@request.path_info)
  119. end
  120. def project_by_path(path)
  121. if m = /^([\w\.\/-]+)\.git/.match(path).to_a
  122. path_with_namespace = m.last
  123. path_with_namespace.gsub!(/\.wiki$/, '')
  124. Project.find_with_namespace(path_with_namespace)
  125. end
  126. end
  127. def render_not_found
  128. [404, { "Content-Type" => "text/plain" }, ["Not Found"]]
  129. end
  130. end
  131. end