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

/lib/api/api_guard.rb

https://gitlab.com/siemens/gitlab-ce
Ruby | 173 lines | 123 code | 33 blank | 17 comment | 4 complexity | 6d9836aabbe91c0bf7be0a317525b1e4 MD5 | raw file
  1. # frozen_string_literal: true
  2. # Guard API with OAuth 2.0 Access Token
  3. require 'rack/oauth2'
  4. module API
  5. module APIGuard
  6. extend ActiveSupport::Concern
  7. included do |base|
  8. # OAuth2 Resource Server Authentication
  9. use Rack::OAuth2::Server::Resource::Bearer, 'The API' do |request|
  10. # The authenticator only fetches the raw token string
  11. # Must yield access token to store it in the env
  12. request.access_token
  13. end
  14. use AdminModeMiddleware
  15. helpers HelperMethods
  16. install_error_responders(base)
  17. end
  18. class_methods do
  19. # Set the authorization scope(s) allowed for an API endpoint.
  20. #
  21. # A call to this method maps the given scope(s) to the current API
  22. # endpoint class. If this method is called multiple times on the same class,
  23. # the scopes are all aggregated.
  24. def allow_access_with_scope(scopes, options = {})
  25. Array(scopes).each do |scope|
  26. allowed_scopes << Scope.new(scope, options)
  27. end
  28. end
  29. def allowed_scopes
  30. @scopes ||= []
  31. end
  32. end
  33. # Helper Methods for Grape Endpoint
  34. module HelperMethods
  35. prepend_if_ee('EE::API::APIGuard::HelperMethods') # rubocop: disable Cop/InjectEnterpriseEditionModule
  36. include Gitlab::Auth::AuthFinders
  37. def find_current_user!
  38. user = find_user_from_sources
  39. return unless user
  40. unless api_access_allowed?(user)
  41. forbidden!(api_access_denied_message(user))
  42. end
  43. # Set admin mode for API requests (if admin)
  44. if Feature.enabled?(:user_mode_in_session)
  45. current_user_mode = Gitlab::Auth::CurrentUserMode.new(user)
  46. current_user_mode.enable_sessionless_admin_mode!
  47. end
  48. user
  49. end
  50. def find_user_from_sources
  51. find_user_from_access_token ||
  52. find_user_from_job_token ||
  53. find_user_from_warden
  54. end
  55. private
  56. # An array of scopes that were registered (using `allow_access_with_scope`)
  57. # for the current endpoint class. It also returns scopes registered on
  58. # `API::API`, since these are meant to apply to all API routes.
  59. def scopes_registered_for_endpoint
  60. @scopes_registered_for_endpoint ||=
  61. begin
  62. endpoint_classes = [options[:for].presence, ::API::API].compact
  63. endpoint_classes.reduce([]) do |memo, endpoint|
  64. if endpoint.respond_to?(:allowed_scopes)
  65. memo.concat(endpoint.allowed_scopes)
  66. else
  67. memo
  68. end
  69. end
  70. end
  71. end
  72. def api_access_allowed?(user)
  73. Gitlab::UserAccess.new(user).allowed? && user.can?(:access_api)
  74. end
  75. def api_access_denied_message(user)
  76. Gitlab::Auth::UserAccessDeniedReason.new(user).rejection_message
  77. end
  78. end
  79. class_methods do
  80. private
  81. def install_error_responders(base)
  82. error_classes = [Gitlab::Auth::MissingTokenError,
  83. Gitlab::Auth::TokenNotFoundError,
  84. Gitlab::Auth::ExpiredError,
  85. Gitlab::Auth::RevokedError,
  86. Gitlab::Auth::ImpersonationDisabled,
  87. Gitlab::Auth::InsufficientScopeError]
  88. base.__send__(:rescue_from, *error_classes, oauth2_bearer_token_error_handler) # rubocop:disable GitlabSecurity/PublicSend
  89. end
  90. def oauth2_bearer_token_error_handler
  91. proc do |e|
  92. response =
  93. case e
  94. when Gitlab::Auth::MissingTokenError
  95. Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new
  96. when Gitlab::Auth::TokenNotFoundError
  97. Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new(
  98. :invalid_token,
  99. "Bad Access Token.")
  100. when Gitlab::Auth::ExpiredError
  101. Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new(
  102. :invalid_token,
  103. "Token is expired. You can either do re-authorization or token refresh.")
  104. when Gitlab::Auth::RevokedError
  105. Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new(
  106. :invalid_token,
  107. "Token was revoked. You have to re-authorize from the user.")
  108. when Gitlab::Auth::ImpersonationDisabled
  109. Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new(
  110. :invalid_token,
  111. "Token is an impersonation token but impersonation was disabled.")
  112. when Gitlab::Auth::InsufficientScopeError
  113. # FIXME: ForbiddenError (inherited from Bearer::Forbidden of Rack::Oauth2)
  114. # does not include WWW-Authenticate header, which breaks the standard.
  115. Rack::OAuth2::Server::Resource::Bearer::Forbidden.new(
  116. :insufficient_scope,
  117. Rack::OAuth2::Server::Resource::ErrorMethods::DEFAULT_DESCRIPTION[:insufficient_scope],
  118. { scope: e.scopes })
  119. end
  120. response.finish
  121. end
  122. end
  123. end
  124. class AdminModeMiddleware < ::Grape::Middleware::Base
  125. def initialize(app, **options)
  126. super
  127. end
  128. def call(env)
  129. if Feature.enabled?(:user_mode_in_session)
  130. session = {}
  131. Gitlab::Session.with_session(session) do
  132. app.call(env)
  133. end
  134. else
  135. app.call(env)
  136. end
  137. end
  138. end
  139. end
  140. end