PageRenderTime 50ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/gitlab/auth.rb

https://bitbucket.org/terrchen/gitlab-ce
Ruby | 306 lines | 215 code | 66 blank | 25 comment | 37 complexity | 78db2784c8f5a21dcf56646fb34dfe68 MD5 | raw file
Possible License(s): Apache-2.0, CC0-1.0
  1. module Gitlab
  2. module Auth
  3. MissingPersonalAccessTokenError = Class.new(StandardError)
  4. REGISTRY_SCOPES = [:read_registry].freeze
  5. # Scopes used for GitLab API access
  6. API_SCOPES = [:api, :read_user, :sudo, :read_repository].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. class << self
  12. def omniauth_customized_providers
  13. @omniauth_customized_providers ||= %w[bitbucket jwt]
  14. end
  15. def omniauth_setup_providers(provider_names)
  16. provider_names.each do |provider|
  17. omniauth_setup_a_provider(provider)
  18. end
  19. end
  20. def omniauth_setup_a_provider(provider)
  21. case provider
  22. when 'kerberos'
  23. require 'omniauth-kerberos'
  24. when *omniauth_customized_providers
  25. require_dependency "omni_auth/strategies/#{provider}"
  26. end
  27. end
  28. def find_for_git_client(login, password, project:, ip:)
  29. raise "Must provide an IP for rate limiting" if ip.nil?
  30. # `user_with_password_for_git` should be the last check
  31. # because it's the most expensive, especially when LDAP
  32. # is enabled.
  33. result =
  34. service_request_check(login, password, project) ||
  35. build_access_token_check(login, password) ||
  36. lfs_token_check(login, password, project) ||
  37. oauth_access_token_check(login, password) ||
  38. personal_access_token_check(password) ||
  39. deploy_token_check(login, password) ||
  40. user_with_password_for_git(login, password) ||
  41. Gitlab::Auth::Result.new
  42. rate_limit!(ip, success: result.success?, login: login)
  43. Gitlab::Auth::UniqueIpsLimiter.limit_user!(result.actor)
  44. return result if result.success? || authenticate_using_internal_or_ldap_password?
  45. # If sign-in is disabled and LDAP is not configured, recommend a
  46. # personal access token on failed auth attempts
  47. raise Gitlab::Auth::MissingPersonalAccessTokenError
  48. end
  49. def find_with_user_password(login, password)
  50. # Avoid resource intensive checks if login credentials are not provided
  51. return unless login.present? && password.present?
  52. # Nothing to do here if internal auth is disabled and LDAP is
  53. # not configured
  54. return unless authenticate_using_internal_or_ldap_password?
  55. Gitlab::Auth::UniqueIpsLimiter.limit_user! do
  56. user = User.by_login(login)
  57. break if user && !user.active?
  58. authenticators = []
  59. if user
  60. authenticators << Gitlab::Auth::OAuth::Provider.authentication(user, 'database')
  61. # Add authenticators for all identities if user is not nil
  62. user&.identities&.each do |identity|
  63. authenticators << Gitlab::Auth::OAuth::Provider.authentication(user, identity.provider)
  64. end
  65. else
  66. # If no user is provided, try LDAP.
  67. # LDAP users are only authenticated via LDAP
  68. authenticators << Gitlab::Auth::LDAP::Authentication
  69. end
  70. authenticators.compact!
  71. # return found user that was authenticated first for given login credentials
  72. authenticators.find do |auth|
  73. authenticated_user = auth.login(login, password)
  74. break authenticated_user if authenticated_user
  75. end
  76. end
  77. end
  78. def rate_limit!(ip, success:, login:)
  79. rate_limiter = Gitlab::Auth::IpRateLimiter.new(ip)
  80. return unless rate_limiter.enabled?
  81. if success
  82. # Repeated login 'failures' are normal behavior for some Git clients so
  83. # it is important to reset the ban counter once the client has proven
  84. # they are not a 'bad guy'.
  85. rate_limiter.reset!
  86. else
  87. # Register a login failure so that Rack::Attack can block the next
  88. # request from this IP if needed.
  89. rate_limiter.register_fail!
  90. if rate_limiter.banned?
  91. Rails.logger.info "IP #{ip} failed to login " \
  92. "as #{login} but has been temporarily banned from Git auth"
  93. end
  94. end
  95. end
  96. private
  97. def authenticate_using_internal_or_ldap_password?
  98. Gitlab::CurrentSettings.password_authentication_enabled_for_git? || Gitlab::Auth::LDAP::Config.enabled?
  99. end
  100. def service_request_check(login, password, project)
  101. matched_login = /(?<service>^[a-zA-Z]*-ci)-token$/.match(login)
  102. return unless project && matched_login.present?
  103. underscored_service = matched_login['service'].underscore
  104. if Service.available_services_names.include?(underscored_service)
  105. # We treat underscored_service as a trusted input because it is included
  106. # in the Service.available_services_names whitelist.
  107. service = project.public_send("#{underscored_service}_service") # rubocop:disable GitlabSecurity/PublicSend
  108. if service && service.activated? && service.valid_token?(password)
  109. Gitlab::Auth::Result.new(nil, project, :ci, build_authentication_abilities)
  110. end
  111. end
  112. end
  113. def user_with_password_for_git(login, password)
  114. user = find_with_user_password(login, password)
  115. return unless user
  116. raise Gitlab::Auth::MissingPersonalAccessTokenError if user.two_factor_enabled?
  117. Gitlab::Auth::Result.new(user, nil, :gitlab_or_ldap, full_authentication_abilities)
  118. end
  119. def oauth_access_token_check(login, password)
  120. if login == "oauth2" && password.present?
  121. token = Doorkeeper::AccessToken.by_token(password)
  122. if valid_oauth_token?(token)
  123. user = User.find_by(id: token.resource_owner_id)
  124. Gitlab::Auth::Result.new(user, nil, :oauth, full_authentication_abilities)
  125. end
  126. end
  127. end
  128. def personal_access_token_check(password)
  129. return unless password.present?
  130. token = PersonalAccessTokensFinder.new(state: 'active').find_by(token: password)
  131. if token && valid_scoped_token?(token, available_scopes)
  132. Gitlab::Auth::Result.new(token.user, nil, :personal_access_token, abilities_for_scopes(token.scopes))
  133. end
  134. end
  135. def valid_oauth_token?(token)
  136. token && token.accessible? && valid_scoped_token?(token, [:api])
  137. end
  138. def valid_scoped_token?(token, scopes)
  139. AccessTokenValidationService.new(token).include_any_scope?(scopes)
  140. end
  141. def abilities_for_scopes(scopes)
  142. abilities_by_scope = {
  143. api: full_authentication_abilities,
  144. read_registry: [:read_container_image],
  145. read_repository: [:download_code]
  146. }
  147. scopes.flat_map do |scope|
  148. abilities_by_scope.fetch(scope.to_sym, [])
  149. end.uniq
  150. end
  151. def deploy_token_check(login, password)
  152. return unless password.present?
  153. token =
  154. DeployToken.active.find_by(token: password)
  155. return unless token && login
  156. return if login != token.username
  157. scopes = abilities_for_scopes(token.scopes)
  158. if valid_scoped_token?(token, available_scopes)
  159. Gitlab::Auth::Result.new(token, token.project, :deploy_token, scopes)
  160. end
  161. end
  162. def lfs_token_check(login, password, project)
  163. deploy_key_matches = login.match(/\Alfs\+deploy-key-(\d+)\z/)
  164. actor =
  165. if deploy_key_matches
  166. DeployKey.find(deploy_key_matches[1])
  167. else
  168. User.by_login(login)
  169. end
  170. return unless actor
  171. token_handler = Gitlab::LfsToken.new(actor)
  172. authentication_abilities =
  173. if token_handler.user?
  174. full_authentication_abilities
  175. elsif token_handler.deploy_key_pushable?(project)
  176. read_write_authentication_abilities
  177. else
  178. read_authentication_abilities
  179. end
  180. if Devise.secure_compare(token_handler.token, password)
  181. Gitlab::Auth::Result.new(actor, nil, token_handler.type, authentication_abilities)
  182. end
  183. end
  184. def build_access_token_check(login, password)
  185. return unless login == 'gitlab-ci-token'
  186. return unless password
  187. build = ::Ci::Build.running.find_by_token(password)
  188. return unless build
  189. return unless build.project.builds_enabled?
  190. if build.user
  191. # If user is assigned to build, use restricted credentials of user
  192. Gitlab::Auth::Result.new(build.user, build.project, :build, build_authentication_abilities)
  193. else
  194. # Otherwise use generic CI credentials (backward compatibility)
  195. Gitlab::Auth::Result.new(nil, build.project, :ci, build_authentication_abilities)
  196. end
  197. end
  198. public
  199. def build_authentication_abilities
  200. [
  201. :read_project,
  202. :build_download_code,
  203. :build_read_container_image,
  204. :build_create_container_image
  205. ]
  206. end
  207. def read_authentication_abilities
  208. [
  209. :read_project,
  210. :download_code,
  211. :read_container_image
  212. ]
  213. end
  214. def read_write_authentication_abilities
  215. read_authentication_abilities + [
  216. :push_code,
  217. :create_container_image
  218. ]
  219. end
  220. def full_authentication_abilities
  221. read_write_authentication_abilities + [
  222. :admin_container_image
  223. ]
  224. end
  225. def available_scopes(current_user = nil)
  226. scopes = API_SCOPES + registry_scopes
  227. scopes.delete(:sudo) if current_user && !current_user.admin?
  228. scopes
  229. end
  230. # Other available scopes
  231. def optional_scopes
  232. available_scopes + OPENID_SCOPES - DEFAULT_SCOPES
  233. end
  234. def registry_scopes
  235. return [] unless Gitlab.config.registry.enabled
  236. REGISTRY_SCOPES
  237. end
  238. end
  239. end
  240. end