PageRenderTime 41ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/api/api_guard.rb

https://gitlab.com/artofhuman/gitlab-ce
Ruby | 144 lines | 100 code | 28 blank | 16 comment | 3 complexity | 2076285bf7fffb126860cc2885676fd3 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. helpers HelperMethods
  15. install_error_responders(base)
  16. end
  17. class_methods do
  18. # Set the authorization scope(s) allowed for an API endpoint.
  19. #
  20. # A call to this method maps the given scope(s) to the current API
  21. # endpoint class. If this method is called multiple times on the same class,
  22. # the scopes are all aggregated.
  23. def allow_access_with_scope(scopes, options = {})
  24. Array(scopes).each do |scope|
  25. allowed_scopes << Scope.new(scope, options)
  26. end
  27. end
  28. def allowed_scopes
  29. @scopes ||= []
  30. end
  31. end
  32. # Helper Methods for Grape Endpoint
  33. module HelperMethods
  34. include Gitlab::Auth::UserAuthFinders
  35. def find_current_user!
  36. user = find_user_from_sources
  37. return unless user
  38. unless api_access_allowed?(user)
  39. forbidden!(api_access_denied_message(user))
  40. end
  41. user
  42. end
  43. def find_user_from_sources
  44. find_user_from_access_token || find_user_from_warden
  45. end
  46. private
  47. # An array of scopes that were registered (using `allow_access_with_scope`)
  48. # for the current endpoint class. It also returns scopes registered on
  49. # `API::API`, since these are meant to apply to all API routes.
  50. def scopes_registered_for_endpoint
  51. @scopes_registered_for_endpoint ||=
  52. begin
  53. endpoint_classes = [options[:for].presence, ::API::API].compact
  54. endpoint_classes.reduce([]) do |memo, endpoint|
  55. if endpoint.respond_to?(:allowed_scopes)
  56. memo.concat(endpoint.allowed_scopes)
  57. else
  58. memo
  59. end
  60. end
  61. end
  62. end
  63. def api_access_allowed?(user)
  64. Gitlab::UserAccess.new(user).allowed? && user.can?(:access_api)
  65. end
  66. def api_access_denied_message(user)
  67. Gitlab::Auth::UserAccessDeniedReason.new(user).rejection_message
  68. end
  69. end
  70. class_methods do
  71. private
  72. def install_error_responders(base)
  73. error_classes = [Gitlab::Auth::MissingTokenError,
  74. Gitlab::Auth::TokenNotFoundError,
  75. Gitlab::Auth::ExpiredError,
  76. Gitlab::Auth::RevokedError,
  77. Gitlab::Auth::ImpersonationDisabled,
  78. Gitlab::Auth::InsufficientScopeError]
  79. base.__send__(:rescue_from, *error_classes, oauth2_bearer_token_error_handler) # rubocop:disable GitlabSecurity/PublicSend
  80. end
  81. def oauth2_bearer_token_error_handler
  82. proc do |e|
  83. response =
  84. case e
  85. when Gitlab::Auth::MissingTokenError
  86. Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new
  87. when Gitlab::Auth::TokenNotFoundError
  88. Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new(
  89. :invalid_token,
  90. "Bad Access Token.")
  91. when Gitlab::Auth::ExpiredError
  92. Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new(
  93. :invalid_token,
  94. "Token is expired. You can either do re-authorization or token refresh.")
  95. when Gitlab::Auth::RevokedError
  96. Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new(
  97. :invalid_token,
  98. "Token was revoked. You have to re-authorize from the user.")
  99. when Gitlab::Auth::ImpersonationDisabled
  100. Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new(
  101. :invalid_token,
  102. "Token is an impersonation token but impersonation was disabled.")
  103. when Gitlab::Auth::InsufficientScopeError
  104. # FIXME: ForbiddenError (inherited from Bearer::Forbidden of Rack::Oauth2)
  105. # does not include WWW-Authenticate header, which breaks the standard.
  106. Rack::OAuth2::Server::Resource::Bearer::Forbidden.new(
  107. :insufficient_scope,
  108. Rack::OAuth2::Server::Resource::ErrorMethods::DEFAULT_DESCRIPTION[:insufficient_scope],
  109. { scope: e.scopes })
  110. end
  111. response.finish
  112. end
  113. end
  114. end
  115. end
  116. end