2018-10-11 16:12:21 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2017-11-07 04:52:05 -05:00
|
|
|
module Gitlab
|
|
|
|
module Auth
|
2017-11-17 07:33:21 -05:00
|
|
|
AuthenticationError = Class.new(StandardError)
|
|
|
|
MissingTokenError = Class.new(AuthenticationError)
|
|
|
|
TokenNotFoundError = Class.new(AuthenticationError)
|
|
|
|
ExpiredError = Class.new(AuthenticationError)
|
|
|
|
RevokedError = Class.new(AuthenticationError)
|
|
|
|
UnauthorizedError = Class.new(AuthenticationError)
|
|
|
|
|
|
|
|
class InsufficientScopeError < AuthenticationError
|
2017-11-16 11:03:19 -05:00
|
|
|
attr_reader :scopes
|
|
|
|
def initialize(scopes)
|
|
|
|
@scopes = scopes.map { |s| s.try(:name) || s }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-11-07 04:52:05 -05:00
|
|
|
module UserAuthFinders
|
2017-11-17 04:09:56 -05:00
|
|
|
include Gitlab::Utils::StrongMemoize
|
|
|
|
|
2017-11-08 13:41:07 -05:00
|
|
|
PRIVATE_TOKEN_HEADER = 'HTTP_PRIVATE_TOKEN'.freeze
|
|
|
|
PRIVATE_TOKEN_PARAM = :private_token
|
|
|
|
|
2017-11-07 04:52:05 -05:00
|
|
|
# Check the Rails session for valid authentication details
|
2017-11-07 13:17:41 -05:00
|
|
|
def find_user_from_warden
|
2017-11-09 13:04:19 -05:00
|
|
|
current_request.env['warden']&.authenticate if verified_request?
|
2017-11-07 04:52:05 -05:00
|
|
|
end
|
|
|
|
|
2018-05-31 10:01:04 -04:00
|
|
|
def find_user_from_feed_token
|
|
|
|
return unless rss_request? || ics_request?
|
2017-11-07 04:52:05 -05:00
|
|
|
|
2018-05-31 10:01:04 -04:00
|
|
|
# NOTE: feed_token was renamed from rss_token but both needs to be supported because
|
|
|
|
# users might have already added the feed to their RSS reader before the rename
|
|
|
|
token = current_request.params[:feed_token].presence || current_request.params[:rss_token].presence
|
2017-11-08 13:41:07 -05:00
|
|
|
return unless token
|
2017-11-07 04:52:05 -05:00
|
|
|
|
2018-05-31 10:01:04 -04:00
|
|
|
User.find_by_feed_token(token) || raise(UnauthorizedError)
|
2017-11-07 04:52:05 -05:00
|
|
|
end
|
|
|
|
|
2017-11-07 13:17:41 -05:00
|
|
|
def find_user_from_access_token
|
|
|
|
return unless access_token
|
2017-11-07 04:52:05 -05:00
|
|
|
|
2017-11-07 13:17:41 -05:00
|
|
|
validate_access_token!
|
2017-11-07 04:52:05 -05:00
|
|
|
|
2017-11-16 09:39:30 -05:00
|
|
|
access_token.user || raise(UnauthorizedError)
|
2017-11-07 13:17:41 -05:00
|
|
|
end
|
2017-11-07 04:52:05 -05:00
|
|
|
|
2017-11-07 13:17:41 -05:00
|
|
|
def validate_access_token!(scopes: [])
|
2017-11-08 13:41:07 -05:00
|
|
|
return unless access_token
|
|
|
|
|
|
|
|
case AccessTokenValidationService.new(access_token, request: request).validate(scopes: scopes)
|
|
|
|
when AccessTokenValidationService::INSUFFICIENT_SCOPE
|
2017-11-16 09:39:30 -05:00
|
|
|
raise InsufficientScopeError.new(scopes)
|
2017-11-08 13:41:07 -05:00
|
|
|
when AccessTokenValidationService::EXPIRED
|
2017-11-16 09:39:30 -05:00
|
|
|
raise ExpiredError
|
2017-11-08 13:41:07 -05:00
|
|
|
when AccessTokenValidationService::REVOKED
|
2017-11-16 09:39:30 -05:00
|
|
|
raise RevokedError
|
2017-11-08 13:41:07 -05:00
|
|
|
end
|
2017-11-07 04:52:05 -05:00
|
|
|
end
|
|
|
|
|
2017-11-07 13:17:41 -05:00
|
|
|
private
|
2017-11-07 04:52:05 -05:00
|
|
|
|
2018-05-18 08:00:44 -04:00
|
|
|
def route_authentication_setting
|
|
|
|
return {} unless respond_to?(:route_setting)
|
|
|
|
|
|
|
|
route_setting(:authentication) || {}
|
|
|
|
end
|
|
|
|
|
2017-11-07 13:17:41 -05:00
|
|
|
def access_token
|
2017-11-17 04:09:56 -05:00
|
|
|
strong_memoize(:access_token) do
|
|
|
|
find_oauth_access_token || find_personal_access_token
|
|
|
|
end
|
2017-11-07 13:17:41 -05:00
|
|
|
end
|
2017-11-07 10:13:00 -05:00
|
|
|
|
2018-08-27 11:31:01 -04:00
|
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
2017-11-10 05:41:33 -05:00
|
|
|
def find_personal_access_token
|
|
|
|
token =
|
|
|
|
current_request.params[PRIVATE_TOKEN_PARAM].presence ||
|
2017-11-09 13:04:19 -05:00
|
|
|
current_request.env[PRIVATE_TOKEN_HEADER].presence
|
2017-11-07 10:13:00 -05:00
|
|
|
|
2017-11-08 13:41:07 -05:00
|
|
|
return unless token
|
2017-11-07 10:13:00 -05:00
|
|
|
|
2017-11-07 13:17:41 -05:00
|
|
|
# Expiration, revocation and scopes are verified in `validate_access_token!`
|
2017-11-16 09:39:30 -05:00
|
|
|
PersonalAccessToken.find_by(token: token) || raise(UnauthorizedError)
|
2017-11-07 10:13:00 -05:00
|
|
|
end
|
2018-08-27 11:31:01 -04:00
|
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
2017-11-07 10:13:00 -05:00
|
|
|
|
2017-11-07 04:52:05 -05:00
|
|
|
def find_oauth_access_token
|
|
|
|
token = Doorkeeper::OAuth::Token.from_request(current_request, *Doorkeeper.configuration.access_token_methods)
|
2017-11-07 13:17:41 -05:00
|
|
|
return unless token
|
2017-11-07 04:52:05 -05:00
|
|
|
|
2017-11-07 13:17:41 -05:00
|
|
|
# Expiration, revocation and scopes are verified in `validate_access_token!`
|
2017-11-10 05:12:37 -05:00
|
|
|
oauth_token = OauthAccessToken.by_token(token)
|
2017-11-16 09:39:30 -05:00
|
|
|
raise UnauthorizedError unless oauth_token
|
2017-11-10 05:12:37 -05:00
|
|
|
|
|
|
|
oauth_token.revoke_previous_refresh_token!
|
|
|
|
oauth_token
|
2017-11-07 04:52:05 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
# Check if the request is GET/HEAD, or if CSRF token is valid.
|
|
|
|
def verified_request?
|
2017-11-09 13:04:19 -05:00
|
|
|
Gitlab::RequestForgeryProtection.verified?(current_request.env)
|
2017-11-07 04:52:05 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def ensure_action_dispatch_request(request)
|
2018-01-15 04:09:21 -05:00
|
|
|
ActionDispatch::Request.new(request.env.dup)
|
2017-11-07 04:52:05 -05:00
|
|
|
end
|
2017-11-09 13:04:19 -05:00
|
|
|
|
|
|
|
def current_request
|
|
|
|
@current_request ||= ensure_action_dispatch_request(request)
|
|
|
|
end
|
2018-05-31 10:01:04 -04:00
|
|
|
|
|
|
|
def rss_request?
|
|
|
|
current_request.path.ends_with?('.atom') || current_request.format.atom?
|
|
|
|
end
|
|
|
|
|
|
|
|
def ics_request?
|
|
|
|
current_request.path.ends_with?('.ics') || current_request.format.ics?
|
|
|
|
end
|
2017-11-07 04:52:05 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|