PageRenderTime 31ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/gitlab/auth.rb

https://gitlab.com/espadav8/gitlab-ce
Ruby | 228 lines | 158 code | 49 blank | 21 comment | 27 complexity | c8cf97e7a65aec4e65e3d3ad1f346d57 MD5 | raw file
  1. module Gitlab
  2. module Auth
  3. MissingPersonalTokenError = Class.new(StandardError)
  4. REGISTRY_SCOPES = [:read_registry].freeze
  5. # Scopes used for GitLab API access
  6. API_SCOPES = [:api, :read_user].freeze
  7. # Scopes used for OpenID Connect
  8. OPENID_SCOPES = [:openid].freeze
  9. # Default scopes for OAuth applications that don't define their own
  10. DEFAULT_SCOPES = [:api].freeze
  11. AVAILABLE_SCOPES = (API_SCOPES + REGISTRY_SCOPES).freeze
  12. # Other available scopes
  13. OPTIONAL_SCOPES = (AVAILABLE_SCOPES + OPENID_SCOPES - DEFAULT_SCOPES).freeze
  14. class << self
  15. def find_for_git_client(login, password, project:, ip:)
  16. raise "Must provide an IP for rate limiting" if ip.nil?
  17. # `user_with_password_for_git` should be the last check
  18. # because it's the most expensive, especially when LDAP
  19. # is enabled.
  20. result =
  21. service_request_check(login, password, project) ||
  22. build_access_token_check(login, password) ||
  23. lfs_token_check(login, password) ||
  24. oauth_access_token_check(login, password) ||
  25. personal_access_token_check(password) ||
  26. user_with_password_for_git(login, password) ||
  27. Gitlab::Auth::Result.new
  28. rate_limit!(ip, success: result.success?, login: login)
  29. Gitlab::Auth::UniqueIpsLimiter.limit_user!(result.actor)
  30. result
  31. end
  32. def find_with_user_password(login, password)
  33. # Avoid resource intensive login checks if password is not provided
  34. return unless password.present?
  35. Gitlab::Auth::UniqueIpsLimiter.limit_user! do
  36. user = User.by_login(login)
  37. # If no user is found, or it's an LDAP server, try LDAP.
  38. # LDAP users are only authenticated via LDAP
  39. if user.nil? || user.ldap_user?
  40. # Second chance - try LDAP authentication
  41. return unless Gitlab::LDAP::Config.enabled?
  42. Gitlab::LDAP::Authentication.login(login, password)
  43. else
  44. user if user.active? && user.valid_password?(password)
  45. end
  46. end
  47. end
  48. def rate_limit!(ip, success:, login:)
  49. rate_limiter = Gitlab::Auth::IpRateLimiter.new(ip)
  50. return unless rate_limiter.enabled?
  51. if success
  52. # Repeated login 'failures' are normal behavior for some Git clients so
  53. # it is important to reset the ban counter once the client has proven
  54. # they are not a 'bad guy'.
  55. rate_limiter.reset!
  56. else
  57. # Register a login failure so that Rack::Attack can block the next
  58. # request from this IP if needed.
  59. rate_limiter.register_fail!
  60. if rate_limiter.banned?
  61. Rails.logger.info "IP #{ip} failed to login " \
  62. "as #{login} but has been temporarily banned from Git auth"
  63. end
  64. end
  65. end
  66. private
  67. def service_request_check(login, password, project)
  68. matched_login = /(?<service>^[a-zA-Z]*-ci)-token$/.match(login)
  69. return unless project && matched_login.present?
  70. underscored_service = matched_login['service'].underscore
  71. if Service.available_services_names.include?(underscored_service)
  72. # We treat underscored_service as a trusted input because it is included
  73. # in the Service.available_services_names whitelist.
  74. service = project.public_send("#{underscored_service}_service")
  75. if service && service.activated? && service.valid_token?(password)
  76. Gitlab::Auth::Result.new(nil, project, :ci, build_authentication_abilities)
  77. end
  78. end
  79. end
  80. def user_with_password_for_git(login, password)
  81. user = find_with_user_password(login, password)
  82. return unless user
  83. raise Gitlab::Auth::MissingPersonalTokenError if user.two_factor_enabled?
  84. Gitlab::Auth::Result.new(user, nil, :gitlab_or_ldap, full_authentication_abilities)
  85. end
  86. def oauth_access_token_check(login, password)
  87. if login == "oauth2" && password.present?
  88. token = Doorkeeper::AccessToken.by_token(password)
  89. if valid_oauth_token?(token)
  90. user = User.find_by(id: token.resource_owner_id)
  91. Gitlab::Auth::Result.new(user, nil, :oauth, full_authentication_abilities)
  92. end
  93. end
  94. end
  95. def personal_access_token_check(password)
  96. return unless password.present?
  97. token = PersonalAccessTokensFinder.new(state: 'active').find_by(token: password)
  98. if token && valid_scoped_token?(token, AVAILABLE_SCOPES.map(&:to_s))
  99. Gitlab::Auth::Result.new(token.user, nil, :personal_token, abilities_for_scope(token.scopes))
  100. end
  101. end
  102. def valid_oauth_token?(token)
  103. token && token.accessible? && valid_scoped_token?(token, ["api"])
  104. end
  105. def valid_scoped_token?(token, scopes)
  106. AccessTokenValidationService.new(token).include_any_scope?(scopes)
  107. end
  108. def abilities_for_scope(scopes)
  109. scopes.map do |scope|
  110. self.public_send(:"#{scope}_scope_authentication_abilities")
  111. end.flatten.uniq
  112. end
  113. def lfs_token_check(login, password)
  114. deploy_key_matches = login.match(/\Alfs\+deploy-key-(\d+)\z/)
  115. actor =
  116. if deploy_key_matches
  117. DeployKey.find(deploy_key_matches[1])
  118. else
  119. User.by_login(login)
  120. end
  121. return unless actor
  122. token_handler = Gitlab::LfsToken.new(actor)
  123. authentication_abilities =
  124. if token_handler.user?
  125. full_authentication_abilities
  126. else
  127. read_authentication_abilities
  128. end
  129. if Devise.secure_compare(token_handler.token, password)
  130. Gitlab::Auth::Result.new(actor, nil, token_handler.type, authentication_abilities)
  131. end
  132. end
  133. def build_access_token_check(login, password)
  134. return unless login == 'gitlab-ci-token'
  135. return unless password
  136. build = ::Ci::Build.running.find_by_token(password)
  137. return unless build
  138. return unless build.project.builds_enabled?
  139. if build.user
  140. # If user is assigned to build, use restricted credentials of user
  141. Gitlab::Auth::Result.new(build.user, build.project, :build, build_authentication_abilities)
  142. else
  143. # Otherwise use generic CI credentials (backward compatibility)
  144. Gitlab::Auth::Result.new(nil, build.project, :ci, build_authentication_abilities)
  145. end
  146. end
  147. public
  148. def build_authentication_abilities
  149. [
  150. :read_project,
  151. :build_download_code,
  152. :build_read_container_image,
  153. :build_create_container_image
  154. ]
  155. end
  156. def read_authentication_abilities
  157. [
  158. :read_project,
  159. :download_code,
  160. :read_container_image
  161. ]
  162. end
  163. def full_authentication_abilities
  164. read_authentication_abilities + [
  165. :push_code,
  166. :create_container_image
  167. ]
  168. end
  169. alias_method :api_scope_authentication_abilities, :full_authentication_abilities
  170. def read_registry_scope_authentication_abilities
  171. [:read_container_image]
  172. end
  173. # The currently used auth method doesn't allow any actions for this scope
  174. def read_user_scope_authentication_abilities
  175. []
  176. end
  177. end
  178. end
  179. end