summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorTimothy Andrew <mail@timothyandrew.net>2016-11-22 14:34:23 +0530
committerTimothy Andrew <mail@timothyandrew.net>2016-12-16 16:29:31 +0530
commit7fa06ed55d18af4d055041eb27d38fecf9b5548f (patch)
treed2565cdc70269e5f244e7cf542170b0d5d8cf7aa /lib
parent6c809dfae84e702f7a49d3fac5725745264e0ff9 (diff)
downloadgitlab-ce-7fa06ed55d18af4d055041eb27d38fecf9b5548f.tar.gz
Calls to the API are checked for scope.
- Move the `Oauth2::AccessTokenValidationService` class to `AccessTokenValidationService`, since it is now being used for personal access token validation as well. - Each API endpoint declares the scopes it accepts (if any). Currently, the top level API module declares the `api` scope, and the `Users` API module declares the `read_user` scope (for GET requests). - Move the `find_user_by_private_token` from the API `Helpers` module to the `APIGuard` module, to avoid littering `Helpers` with more auth-related methods to support `find_user_by_private_token`
Diffstat (limited to 'lib')
-rw-r--r--lib/api/api.rb2
-rw-r--r--lib/api/api_guard.rb62
-rw-r--r--lib/api/helpers.rb15
-rw-r--r--lib/api/users.rb5
-rw-r--r--lib/gitlab/auth.rb4
5 files changed, 58 insertions, 30 deletions
diff --git a/lib/api/api.rb b/lib/api/api.rb
index cec2702e44d..9d5adffd8f4 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -3,6 +3,8 @@ module API
include APIGuard
version 'v3', using: :path
+ before { allow_access_with_scope :api }
+
rescue_from Gitlab::Access::AccessDeniedError do
rack_response({ 'message' => '403 Forbidden' }.to_json, 403)
end
diff --git a/lib/api/api_guard.rb b/lib/api/api_guard.rb
index 8cc7a26f1fa..cd266669b1e 100644
--- a/lib/api/api_guard.rb
+++ b/lib/api/api_guard.rb
@@ -6,6 +6,9 @@ module API
module APIGuard
extend ActiveSupport::Concern
+ PRIVATE_TOKEN_HEADER = "HTTP_PRIVATE_TOKEN"
+ PRIVATE_TOKEN_PARAM = :private_token
+
included do |base|
# OAuth2 Resource Server Authentication
use Rack::OAuth2::Server::Resource::Bearer, 'The API' do |request|
@@ -41,30 +44,59 @@ module API
# 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)
+ if access_token = find_access_token
+ case AccessTokenValidationService.validate(access_token, scopes: scopes)
+ when AccessTokenValidationService::INSUFFICIENT_SCOPE
+ raise InsufficientScopeError.new(scopes)
+ when AccessTokenValidationService::EXPIRED
+ raise ExpiredError
+ when AccessTokenValidationService::REVOKED
+ raise RevokedError
+ when AccessTokenValidationService::VALID
+ @current_user = User.find(access_token.resource_owner_id)
+ end
+ end
+ end
- when Oauth2::AccessTokenValidationService::EXPIRED
- raise ExpiredError
+ def find_user_by_private_token(scopes: [])
+ token_string = (params[PRIVATE_TOKEN_PARAM] || env[PRIVATE_TOKEN_HEADER]).to_s
- when Oauth2::AccessTokenValidationService::REVOKED
- raise RevokedError
+ return nil unless token_string.present?
- when Oauth2::AccessTokenValidationService::VALID
- @current_user = User.find(access_token.resource_owner_id)
- end
+ find_user_by_authentication_token(token_string) || find_user_by_personal_access_token(token_string, scopes)
end
def current_user
@current_user
end
+ # Set the authorization scope(s) allowed for the current request.
+ #
+ # Note: A call to this method adds to any previous scopes in place. This is done because
+ # `Grape` callbacks run from the outside-in: the top-level callback (API::API) runs first, then
+ # the next-level callback (API::API::Users, for example) runs. All these scopes are valid for the
+ # given endpoint (GET `/api/users` is accessible by the `api` and `read_user` scopes), and so they
+ # need to be stored.
+ def allow_access_with_scope(*scopes)
+ @scopes ||= []
+ @scopes.concat(scopes.map(&:to_s))
+ end
+
private
+ def find_user_by_authentication_token(token_string)
+ User.find_by_authentication_token(token_string)
+ end
+
+ def find_user_by_personal_access_token(token_string, scopes)
+ access_token = PersonalAccessToken.active.find_by_token(token_string)
+ return unless access_token
+
+ if AccessTokenValidationService.sufficient_scope?(access_token, scopes)
+ User.find(access_token.user_id)
+ end
+ end
+
def find_access_token
@access_token ||= Doorkeeper.authenticate(doorkeeper_request, Doorkeeper.configuration.access_token_methods)
end
@@ -72,10 +104,6 @@ module API
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
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 746849ef4c0..4be659fc20b 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -2,8 +2,6 @@ module API
module Helpers
include Gitlab::Utils
- PRIVATE_TOKEN_HEADER = "HTTP_PRIVATE_TOKEN"
- PRIVATE_TOKEN_PARAM = :private_token
SUDO_HEADER = "HTTP_SUDO"
SUDO_PARAM = :sudo
@@ -308,7 +306,7 @@ module API
private
def private_token
- params[PRIVATE_TOKEN_PARAM] || env[PRIVATE_TOKEN_HEADER]
+ params[APIGuard::PRIVATE_TOKEN_PARAM] || env[APIGuard::PRIVATE_TOKEN_HEADER]
end
def warden
@@ -323,18 +321,11 @@ module API
warden.try(:authenticate) if %w[GET HEAD].include?(env['REQUEST_METHOD'])
end
- def find_user_by_private_token
- token = private_token
- return nil unless token.present?
-
- User.find_by_authentication_token(token) || User.find_by_personal_access_token(token)
- end
-
def initial_current_user
return @initial_current_user if defined?(@initial_current_user)
- @initial_current_user ||= find_user_by_private_token
- @initial_current_user ||= doorkeeper_guard
+ @initial_current_user ||= find_user_by_private_token(scopes: @scopes)
+ @initial_current_user ||= doorkeeper_guard(scopes: @scopes)
@initial_current_user ||= find_user_from_warden
unless @initial_current_user && Gitlab::UserAccess.new(@initial_current_user).allowed?
diff --git a/lib/api/users.rb b/lib/api/users.rb
index c7db2d71017..0842c3874c5 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -2,7 +2,10 @@ module API
class Users < Grape::API
include PaginationParams
- before { authenticate! }
+ before do
+ allow_access_with_scope :read_user if request.get?
+ authenticate!
+ end
resource :users, requirements: { uid: /[0-9]*/, id: /[0-9]*/ } do
helpers do
diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb
index aca5d0020cf..c3c464248ef 100644
--- a/lib/gitlab/auth.rb
+++ b/lib/gitlab/auth.rb
@@ -2,6 +2,10 @@ module Gitlab
module Auth
class MissingPersonalTokenError < StandardError; end
+ SCOPES = [:api, :read_user]
+ DEFAULT_SCOPES = [:api]
+ OPTIONAL_SCOPES = SCOPES - DEFAULT_SCOPES
+
class << self
def find_for_git_client(login, password, project:, ip:)
raise "Must provide an IP for rate limiting" if ip.nil?