Skip to content
Snippets Groups Projects
Select Git revision
  • move-gl-dropdown
  • improve-table-pagination-spec
  • move-markdown-preview
  • winh-fix-merge-request-spec
  • master default
  • index-namespaces-lower-name
  • winh-single-karma-test
  • 10-3-stable
  • 36782-replace-team-user-role-with-add_role-user-in-specs
  • winh-modal-internal-state
  • tz-ide-file-icons
  • 38869-milestone-select
  • update-autodevops-template
  • jivl-activate-repo-cookie-preferences
  • qa-add-deploy-key
  • docs-move-article-ldap
  • 40780-choose-file
  • 22643-manual-job-page
  • refactor-cluster-show-page-conservative
  • dm-sidekiq-versioning
  • v10.4.0.pre
  • v10.3.0
  • v10.3.0-rc5
  • v10.3.0-rc4
  • v10.3.0-rc3
  • v10.3.0-rc2
  • v10.2.5
  • v10.3.0-rc1
  • v10.0.7
  • v10.1.5
  • v10.2.4
  • v10.2.3
  • v10.2.2
  • v10.2.1
  • v10.3.0.pre
  • v10.2.0
  • v10.2.0-rc4
  • v10.2.0-rc3
  • v10.1.4
  • v10.2.0-rc2
40 results

api_guard.rb

Blame
  • Forked from GitLab.org / GitLab FOSS
    16292 commits behind the upstream repository.
    api_guard.rb 4.25 KiB
    # Guard API with OAuth 2.0 Access Token
    
    require 'rack/oauth2'
    
    module API
      module APIGuard
        extend ActiveSupport::Concern
    
        included do |base|
          # OAuth2 Resource Server Authentication
          use Rack::OAuth2::Server::Resource::Bearer, 'The API' do |request|
            # The authenticator only fetches the raw token string
    
            # Must yield access token to store it in the env
            request.access_token
          end
    
          helpers HelperMethods
    
          install_error_responders(base)
        end
    
        # Helper Methods for Grape Endpoint
        module HelperMethods
          # Invokes the doorkeeper guard.
          #
          # If token is presented and valid, then it sets @current_user.
          #
          # If the token does not have sufficient scopes to cover the requred scopes,
          # then it raises InsufficientScopeError.
          #
          # If the token is expired, then it raises ExpiredError.
          #
          # If the token is revoked, then it raises RevokedError.
          #
          # If the token is not found (nil), then it returns nil
          #
          # Arguments:
          #
          #   scopes: (optional) scopes required for this guard.
          #           Defaults to empty array.
          #
          def doorkeeper_guard(scopes: [])
            access_token = find_access_token
            return nil unless access_token
    
            case validate_access_token(access_token, scopes)
            when Oauth2::AccessTokenValidationService::INSUFFICIENT_SCOPE
              raise InsufficientScopeError.new(scopes)
    
            when Oauth2::AccessTokenValidationService::EXPIRED
              raise ExpiredError
    
            when Oauth2::AccessTokenValidationService::REVOKED
              raise RevokedError
    
            when Oauth2::AccessTokenValidationService::VALID
              @current_user = User.find(access_token.resource_owner_id)
            end
          end
    
          def current_user
            @current_user
          end
    
          private
    
          def find_access_token
            @access_token ||= Doorkeeper.authenticate(doorkeeper_request, Doorkeeper.configuration.access_token_methods)
          end
    
          def doorkeeper_request
            @doorkeeper_request ||= ActionDispatch::Request.new(env)
          end
    
          def validate_access_token(access_token, scopes)
            Oauth2::AccessTokenValidationService.validate(access_token, scopes: scopes)
          end
        end
    
        module ClassMethods
          private
    
          def install_error_responders(base)
            error_classes = [ MissingTokenError, TokenNotFoundError,
                              ExpiredError, RevokedError, InsufficientScopeError]
    
            base.send :rescue_from, *error_classes, oauth2_bearer_token_error_handler
          end
    
          def oauth2_bearer_token_error_handler
            Proc.new do |e|
              response =
                case e
                when MissingTokenError
                  Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new
    
                when TokenNotFoundError
                  Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new(
                    :invalid_token,
                    "Bad Access Token.")
    
                when ExpiredError
                  Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new(
                    :invalid_token,
                    "Token is expired. You can either do re-authorization or token refresh.")
    
                when RevokedError
                  Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new(
                    :invalid_token,
                    "Token was revoked. You have to re-authorize from the user.")
    
                when InsufficientScopeError
                  # FIXME: ForbiddenError (inherited from Bearer::Forbidden of Rack::Oauth2)
                  # does not include WWW-Authenticate header, which breaks the standard.
                  Rack::OAuth2::Server::Resource::Bearer::Forbidden.new(
                    :insufficient_scope,
                    Rack::OAuth2::Server::Resource::ErrorMethods::DEFAULT_DESCRIPTION[:insufficient_scope],
                    { scope: e.scopes })
                end
    
              response.finish
            end
          end
        end
    
        #
        # Exceptions
        #
    
        class MissingTokenError < StandardError; end
    
        class TokenNotFoundError < StandardError; end
    
        class ExpiredError < StandardError; end
    
        class RevokedError < StandardError; end
    
        class InsufficientScopeError < StandardError
          attr_reader :scopes
          def initialize(scopes)
            @scopes = scopes
          end
        end
      end
    end