PageRenderTime 45ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/gitlab/auth.rb

https://gitlab.com/stanhu/gitlab-ce
Ruby | 328 lines | 219 code | 73 blank | 36 comment | 37 complexity | a3628959b13bb59cf5ac11fca120a7c8 MD5 | raw file
Possible License(s): CC-BY-SA-4.0, Apache-2.0, CC-BY-3.0, CC0-1.0
  1. # frozen_string_literal: true
  2. module Gitlab
  3. module Auth
  4. MissingPersonalAccessTokenError = Class.new(StandardError)
  5. # Scopes used for GitLab API access
  6. API_SCOPES = [:api, :read_user].freeze
  7. # Scopes used for GitLab Repository access
  8. REPOSITORY_SCOPES = [:read_repository, :write_repository].freeze
  9. # Scopes used for GitLab Docker Registry access
  10. REGISTRY_SCOPES = [:read_registry].freeze
  11. # Scopes used for GitLab as admin
  12. ADMIN_SCOPES = [:sudo].freeze
  13. # Scopes used for OpenID Connect
  14. OPENID_SCOPES = [:openid].freeze
  15. # OpenID Connect profile scopes
  16. PROFILE_SCOPES = [:profile, :email].freeze
  17. # Default scopes for OAuth applications that don't define their own
  18. DEFAULT_SCOPES = [:api].freeze
  19. class << self
  20. def omniauth_enabled?
  21. Gitlab.config.omniauth.enabled
  22. end
  23. def find_for_git_client(login, password, project:, ip:)
  24. raise "Must provide an IP for rate limiting" if ip.nil?
  25. # `user_with_password_for_git` should be the last check
  26. # because it's the most expensive, especially when LDAP
  27. # is enabled.
  28. result =
  29. service_request_check(login, password, project) ||
  30. build_access_token_check(login, password) ||
  31. lfs_token_check(login, password, project) ||
  32. oauth_access_token_check(login, password) ||
  33. personal_access_token_check(password) ||
  34. deploy_token_check(login, password) ||
  35. user_with_password_for_git(login, password) ||
  36. Gitlab::Auth::Result.new
  37. rate_limit!(ip, success: result.success?, login: login) unless skip_rate_limit?(login: login)
  38. Gitlab::Auth::UniqueIpsLimiter.limit_user!(result.actor)
  39. return result if result.success? || authenticate_using_internal_or_ldap_password?
  40. # If sign-in is disabled and LDAP is not configured, recommend a
  41. # personal access token on failed auth attempts
  42. raise Gitlab::Auth::MissingPersonalAccessTokenError
  43. end
  44. def find_with_user_password(login, password)
  45. # Avoid resource intensive checks if login credentials are not provided
  46. return unless login.present? && password.present?
  47. # Nothing to do here if internal auth is disabled and LDAP is
  48. # not configured
  49. return unless authenticate_using_internal_or_ldap_password?
  50. Gitlab::Auth::UniqueIpsLimiter.limit_user! do
  51. user = User.by_login(login)
  52. break if user && !user.active?
  53. authenticators = []
  54. if user
  55. authenticators << Gitlab::Auth::OAuth::Provider.authentication(user, 'database')
  56. # Add authenticators for all identities if user is not nil
  57. user&.identities&.each do |identity|
  58. authenticators << Gitlab::Auth::OAuth::Provider.authentication(user, identity.provider)
  59. end
  60. else
  61. # If no user is provided, try LDAP.
  62. # LDAP users are only authenticated via LDAP
  63. authenticators << Gitlab::Auth::LDAP::Authentication
  64. end
  65. authenticators.compact!
  66. # return found user that was authenticated first for given login credentials
  67. authenticators.find do |auth|
  68. authenticated_user = auth.login(login, password)
  69. break authenticated_user if authenticated_user
  70. end
  71. end
  72. end
  73. # rubocop:disable Gitlab/RailsLogger
  74. def rate_limit!(ip, success:, login:)
  75. rate_limiter = Gitlab::Auth::IpRateLimiter.new(ip)
  76. return unless rate_limiter.enabled?
  77. if success
  78. # Repeated login 'failures' are normal behavior for some Git clients so
  79. # it is important to reset the ban counter once the client has proven
  80. # they are not a 'bad guy'.
  81. rate_limiter.reset!
  82. else
  83. # Register a login failure so that Rack::Attack can block the next
  84. # request from this IP if needed.
  85. rate_limiter.register_fail!
  86. if rate_limiter.banned?
  87. Rails.logger.info "IP #{ip} failed to login " \
  88. "as #{login} but has been temporarily banned from Git auth"
  89. end
  90. end
  91. end
  92. # rubocop:enable Gitlab/RailsLogger
  93. private
  94. def skip_rate_limit?(login:)
  95. ::Ci::Build::CI_REGISTRY_USER == login
  96. end
  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. # rubocop: disable CodeReuse/ActiveRecord
  120. def oauth_access_token_check(login, password)
  121. if login == "oauth2" && password.present?
  122. token = Doorkeeper::AccessToken.by_token(password)
  123. if valid_oauth_token?(token)
  124. user = User.find_by(id: token.resource_owner_id)
  125. Gitlab::Auth::Result.new(user, nil, :oauth, full_authentication_abilities)
  126. end
  127. end
  128. end
  129. # rubocop: enable CodeReuse/ActiveRecord
  130. def personal_access_token_check(password)
  131. return unless password.present?
  132. token = PersonalAccessTokensFinder.new(state: 'active').find_by_token(password)
  133. if token && valid_scoped_token?(token, all_available_scopes)
  134. Gitlab::Auth::Result.new(token.user, nil, :personal_access_token, abilities_for_scopes(token.scopes))
  135. end
  136. end
  137. def valid_oauth_token?(token)
  138. token && token.accessible? && valid_scoped_token?(token, [:api])
  139. end
  140. def valid_scoped_token?(token, scopes)
  141. AccessTokenValidationService.new(token).include_any_scope?(scopes)
  142. end
  143. def abilities_for_scopes(scopes)
  144. abilities_by_scope = {
  145. api: full_authentication_abilities,
  146. read_registry: [:read_container_image],
  147. read_repository: [:download_code],
  148. write_repository: [:download_code, :push_code]
  149. }
  150. scopes.flat_map do |scope|
  151. abilities_by_scope.fetch(scope.to_sym, [])
  152. end.uniq
  153. end
  154. # rubocop: disable CodeReuse/ActiveRecord
  155. def deploy_token_check(login, password)
  156. return unless password.present?
  157. token =
  158. DeployToken.active.find_by(token: password)
  159. return unless token && login
  160. return if login != token.username
  161. scopes = abilities_for_scopes(token.scopes)
  162. if valid_scoped_token?(token, all_available_scopes)
  163. Gitlab::Auth::Result.new(token, token.project, :deploy_token, scopes)
  164. end
  165. end
  166. # rubocop: enable CodeReuse/ActiveRecord
  167. def lfs_token_check(login, encoded_token, project)
  168. deploy_key_matches = login.match(/\Alfs\+deploy-key-(\d+)\z/)
  169. actor =
  170. if deploy_key_matches
  171. DeployKey.find(deploy_key_matches[1])
  172. else
  173. User.by_login(login)
  174. end
  175. return unless actor
  176. token_handler = Gitlab::LfsToken.new(actor)
  177. authentication_abilities =
  178. if token_handler.user?
  179. full_authentication_abilities
  180. elsif token_handler.deploy_key_pushable?(project)
  181. read_write_authentication_abilities
  182. else
  183. read_only_authentication_abilities
  184. end
  185. if token_handler.token_valid?(encoded_token)
  186. Gitlab::Auth::Result.new(actor, nil, token_handler.type, authentication_abilities)
  187. end
  188. end
  189. def build_access_token_check(login, password)
  190. return unless login == 'gitlab-ci-token'
  191. return unless password
  192. build = find_build_by_token(password)
  193. return unless build
  194. return unless build.project.builds_enabled?
  195. if build.user
  196. # If user is assigned to build, use restricted credentials of user
  197. Gitlab::Auth::Result.new(build.user, build.project, :build, build_authentication_abilities)
  198. else
  199. # Otherwise use generic CI credentials (backward compatibility)
  200. Gitlab::Auth::Result.new(nil, build.project, :ci, build_authentication_abilities)
  201. end
  202. end
  203. public
  204. def build_authentication_abilities
  205. [
  206. :read_project,
  207. :build_download_code,
  208. :build_read_container_image,
  209. :build_create_container_image
  210. ]
  211. end
  212. def read_only_authentication_abilities
  213. [
  214. :read_project,
  215. :download_code,
  216. :read_container_image
  217. ]
  218. end
  219. def read_write_authentication_abilities
  220. read_only_authentication_abilities + [
  221. :push_code,
  222. :create_container_image
  223. ]
  224. end
  225. def full_authentication_abilities
  226. read_write_authentication_abilities + [
  227. :admin_container_image
  228. ]
  229. end
  230. def available_scopes_for(current_user)
  231. scopes = non_admin_available_scopes
  232. scopes += ADMIN_SCOPES if current_user.admin?
  233. scopes
  234. end
  235. def all_available_scopes
  236. non_admin_available_scopes + ADMIN_SCOPES
  237. end
  238. # Other available scopes
  239. def optional_scopes
  240. all_available_scopes + OPENID_SCOPES + PROFILE_SCOPES - DEFAULT_SCOPES
  241. end
  242. def registry_scopes
  243. return [] unless Gitlab.config.registry.enabled
  244. REGISTRY_SCOPES
  245. end
  246. private
  247. def non_admin_available_scopes
  248. API_SCOPES + REPOSITORY_SCOPES + registry_scopes
  249. end
  250. def find_build_by_token(token)
  251. ::Ci::Build.running.find_by_token(token)
  252. end
  253. end
  254. end
  255. end