2019-10-28 20:06:10 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2013-03-28 14:37:44 -04:00
|
|
|
require 'spec_helper'
|
2021-06-04 11:10:25 -04:00
|
|
|
require 'raven/transports/dummy'
|
2017-10-02 20:52:19 -04:00
|
|
|
require_relative '../../../config/initializers/sentry'
|
2013-03-28 14:37:44 -04:00
|
|
|
|
2020-06-24 02:09:01 -04:00
|
|
|
RSpec.describe API::Helpers do
|
2016-11-22 04:04:23 -05:00
|
|
|
include API::APIGuard::HelperMethods
|
2017-05-01 11:13:33 -04:00
|
|
|
include described_class
|
2018-05-08 09:07:55 -04:00
|
|
|
include TermsHelper
|
2016-04-18 05:17:38 -04:00
|
|
|
|
2020-09-25 02:09:42 -04:00
|
|
|
let_it_be(:user, reload: true) { create(:user) }
|
2021-06-28 23:07:32 -04:00
|
|
|
|
2013-03-28 14:37:44 -04:00
|
|
|
let(:admin) { create(:admin) }
|
|
|
|
let(:key) { create(:key, user: user) }
|
|
|
|
|
2017-07-26 06:34:52 -04:00
|
|
|
let(:csrf_token) { SecureRandom.base64(ActionController::RequestForgeryProtection::AUTHENTICITY_TOKEN_LENGTH) }
|
|
|
|
let(:env) do
|
|
|
|
{
|
|
|
|
'rack.input' => '',
|
|
|
|
'rack.session' => {
|
|
|
|
_csrf_token: csrf_token
|
|
|
|
},
|
2017-11-09 13:04:19 -05:00
|
|
|
'REQUEST_METHOD' => 'GET',
|
|
|
|
'CONTENT_TYPE' => 'text/plain;charset=utf-8'
|
2017-07-26 06:34:52 -04:00
|
|
|
}
|
|
|
|
end
|
2020-08-10 23:11:00 -04:00
|
|
|
|
2022-07-29 14:08:58 -04:00
|
|
|
let(:header) {}
|
|
|
|
let(:request) { Grape::Request.new(env) }
|
2017-11-09 13:04:19 -05:00
|
|
|
let(:params) { request.params }
|
2013-03-28 14:37:44 -04:00
|
|
|
|
2017-06-20 08:02:25 -04:00
|
|
|
before do
|
|
|
|
allow_any_instance_of(self.class).to receive(:options).and_return({})
|
|
|
|
end
|
2017-06-20 04:27:45 -04:00
|
|
|
|
2016-09-16 13:38:07 -04:00
|
|
|
def warden_authenticate_returns(value)
|
|
|
|
warden = double("warden", authenticate: value)
|
|
|
|
env['warden'] = warden
|
|
|
|
end
|
|
|
|
|
2017-01-19 17:41:12 -05:00
|
|
|
def error!(message, status, header)
|
2021-05-04 11:10:36 -04:00
|
|
|
raise StandardError, "#{status} - #{message}"
|
2013-03-28 14:37:44 -04:00
|
|
|
end
|
|
|
|
|
2017-11-09 13:04:19 -05:00
|
|
|
def set_param(key, value)
|
|
|
|
request.update_param(key, value)
|
|
|
|
end
|
|
|
|
|
2013-03-28 14:37:44 -04:00
|
|
|
describe ".current_user" do
|
2016-09-16 13:38:07 -04:00
|
|
|
subject { current_user }
|
|
|
|
|
2017-07-26 06:34:52 -04:00
|
|
|
describe "Warden authentication", :allow_forgery_protection do
|
2016-09-22 08:56:43 -04:00
|
|
|
context "with invalid credentials" do
|
|
|
|
context "GET request" do
|
2017-06-14 14:18:56 -04:00
|
|
|
before do
|
|
|
|
env['REQUEST_METHOD'] = 'GET'
|
|
|
|
end
|
|
|
|
|
2016-09-22 08:56:43 -04:00
|
|
|
it { is_expected.to be_nil }
|
|
|
|
end
|
2016-09-16 13:38:07 -04:00
|
|
|
end
|
|
|
|
|
2016-09-22 08:56:43 -04:00
|
|
|
context "with valid credentials" do
|
2017-06-14 14:18:56 -04:00
|
|
|
before do
|
|
|
|
warden_authenticate_returns user
|
|
|
|
end
|
2016-09-16 13:38:07 -04:00
|
|
|
|
2016-09-22 08:56:43 -04:00
|
|
|
context "GET request" do
|
2017-06-14 14:18:56 -04:00
|
|
|
before do
|
|
|
|
env['REQUEST_METHOD'] = 'GET'
|
|
|
|
end
|
|
|
|
|
2016-09-22 08:56:43 -04:00
|
|
|
it { is_expected.to eq(user) }
|
2018-01-06 03:41:13 -05:00
|
|
|
|
|
|
|
it 'sets the environment with data of the current user' do
|
|
|
|
subject
|
|
|
|
|
|
|
|
expect(env[API::Helpers::API_USER_ENV]).to eq({ user_id: subject.id, username: subject.username })
|
|
|
|
end
|
2016-09-22 08:56:43 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
context "HEAD request" do
|
2017-06-14 14:18:56 -04:00
|
|
|
before do
|
|
|
|
env['REQUEST_METHOD'] = 'HEAD'
|
|
|
|
end
|
|
|
|
|
2016-09-22 08:56:43 -04:00
|
|
|
it { is_expected.to eq(user) }
|
2020-09-02 11:10:54 -04:00
|
|
|
|
|
|
|
context 'when user should have 2fa enabled' do
|
|
|
|
before do
|
|
|
|
allow(user).to receive(:require_two_factor_authentication_from_group?).and_return(true)
|
|
|
|
allow_next_instance_of(Gitlab::Auth::TwoFactorAuthVerifier) do |verifier|
|
|
|
|
allow(verifier).to receive(:two_factor_grace_period_expired?).and_return(true)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when 2fa is not enabled' do
|
|
|
|
it { is_expected.to be_nil }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when 2fa is enabled' do
|
|
|
|
before do
|
|
|
|
allow(user).to receive(:two_factor_enabled?).and_return(true)
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.to eq(user) }
|
|
|
|
end
|
|
|
|
end
|
2016-09-22 08:56:43 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
context "PUT request" do
|
2017-06-14 14:18:56 -04:00
|
|
|
before do
|
|
|
|
env['REQUEST_METHOD'] = 'PUT'
|
|
|
|
end
|
|
|
|
|
2017-07-26 06:34:52 -04:00
|
|
|
context 'without CSRF token' do
|
|
|
|
it { is_expected.to be_nil }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with CSRF token' do
|
|
|
|
before do
|
|
|
|
env['HTTP_X_CSRF_TOKEN'] = csrf_token
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.to eq(user) }
|
|
|
|
end
|
2016-09-22 08:56:43 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
context "POST request" do
|
2017-06-14 14:18:56 -04:00
|
|
|
before do
|
|
|
|
env['REQUEST_METHOD'] = 'POST'
|
|
|
|
end
|
|
|
|
|
2017-07-26 06:34:52 -04:00
|
|
|
context 'without CSRF token' do
|
|
|
|
it { is_expected.to be_nil }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with CSRF token' do
|
|
|
|
before do
|
|
|
|
env['HTTP_X_CSRF_TOKEN'] = csrf_token
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.to eq(user) }
|
|
|
|
end
|
2016-09-22 08:56:43 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
context "DELETE request" do
|
2017-06-14 14:18:56 -04:00
|
|
|
before do
|
|
|
|
env['REQUEST_METHOD'] = 'DELETE'
|
|
|
|
end
|
|
|
|
|
2017-07-26 06:34:52 -04:00
|
|
|
context 'without CSRF token' do
|
|
|
|
it { is_expected.to be_nil }
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'with CSRF token' do
|
|
|
|
before do
|
|
|
|
env['HTTP_X_CSRF_TOKEN'] = csrf_token
|
|
|
|
end
|
|
|
|
|
|
|
|
it { is_expected.to eq(user) }
|
|
|
|
end
|
2016-09-22 08:56:43 -04:00
|
|
|
end
|
2016-09-16 13:38:07 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-04-18 06:18:54 -04:00
|
|
|
describe "when authenticating using a user's personal access tokens" do
|
|
|
|
let(:personal_access_token) { create(:personal_access_token, user: user) }
|
|
|
|
|
2017-09-27 09:31:52 -04:00
|
|
|
it "returns a 401 response for an invalid token" do
|
2019-12-11 07:08:10 -05:00
|
|
|
env[Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER] = 'invalid token'
|
2016-11-28 02:00:41 -05:00
|
|
|
|
2017-09-27 09:31:52 -04:00
|
|
|
expect { current_user }.to raise_error /401/
|
2016-04-18 06:18:54 -04:00
|
|
|
end
|
|
|
|
|
2017-10-12 08:38:39 -04:00
|
|
|
it "returns a 403 response for a user without access" do
|
2019-12-11 07:08:10 -05:00
|
|
|
env[Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
|
2016-07-18 04:16:56 -04:00
|
|
|
allow_any_instance_of(Gitlab::UserAccess).to receive(:allowed?).and_return(false)
|
2016-11-28 02:00:41 -05:00
|
|
|
|
2017-10-12 08:38:39 -04:00
|
|
|
expect { current_user }.to raise_error /403/
|
2017-09-27 09:31:52 -04:00
|
|
|
end
|
|
|
|
|
2017-10-12 08:38:39 -04:00
|
|
|
it 'returns a 403 response for a user who is blocked' do
|
2017-09-27 09:31:52 -04:00
|
|
|
user.block!
|
2019-12-11 07:08:10 -05:00
|
|
|
env[Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
|
2017-09-27 09:31:52 -04:00
|
|
|
|
2017-10-12 08:38:39 -04:00
|
|
|
expect { current_user }.to raise_error /403/
|
2016-04-18 06:18:54 -04:00
|
|
|
end
|
2018-05-08 09:07:55 -04:00
|
|
|
|
|
|
|
context 'when terms are enforced' do
|
|
|
|
before do
|
|
|
|
enforce_terms
|
2019-12-11 07:08:10 -05:00
|
|
|
env[Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
|
2018-05-08 09:07:55 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns a 403 when a user has not accepted the terms' do
|
2018-05-24 03:46:30 -04:00
|
|
|
expect { current_user }.to raise_error /must accept the Terms of Service/
|
2018-05-08 09:07:55 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'sets the current user when the user accepted the terms' do
|
|
|
|
accept_terms(user)
|
|
|
|
|
|
|
|
expect(current_user).to eq(user)
|
|
|
|
end
|
|
|
|
end
|
2016-04-18 06:18:54 -04:00
|
|
|
|
2017-10-30 13:49:46 -04:00
|
|
|
it "sets current_user" do
|
2019-12-11 07:08:10 -05:00
|
|
|
env[Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
|
2016-04-18 06:18:54 -04:00
|
|
|
expect(current_user).to eq(user)
|
|
|
|
end
|
|
|
|
|
2017-09-27 09:56:48 -04:00
|
|
|
it "does not allow tokens without the appropriate scope" do
|
|
|
|
personal_access_token = create(:personal_access_token, user: user, scopes: ['read_user'])
|
2019-12-11 07:08:10 -05:00
|
|
|
env[Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
|
2017-09-27 09:56:48 -04:00
|
|
|
|
2017-11-16 11:03:19 -05:00
|
|
|
expect { current_user }.to raise_error Gitlab::Auth::InsufficientScopeError
|
2017-09-27 09:56:48 -04:00
|
|
|
end
|
|
|
|
|
2016-04-18 06:18:54 -04:00
|
|
|
it 'does not allow revoked tokens' do
|
|
|
|
personal_access_token.revoke!
|
2019-12-11 07:08:10 -05:00
|
|
|
env[Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
|
2016-11-28 02:00:41 -05:00
|
|
|
|
2017-11-16 11:03:19 -05:00
|
|
|
expect { current_user }.to raise_error Gitlab::Auth::RevokedError
|
2016-04-18 06:18:54 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'does not allow expired tokens' do
|
2018-07-02 06:43:06 -04:00
|
|
|
personal_access_token.update!(expires_at: 1.day.ago)
|
2019-12-11 07:08:10 -05:00
|
|
|
env[Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
|
2016-11-28 02:00:41 -05:00
|
|
|
|
2017-11-16 11:03:19 -05:00
|
|
|
expect { current_user }.to raise_error Gitlab::Auth::ExpiredError
|
2016-04-18 06:18:54 -04:00
|
|
|
end
|
2018-11-24 07:39:16 -05:00
|
|
|
|
|
|
|
context 'when impersonation is disabled' do
|
|
|
|
let(:personal_access_token) { create(:personal_access_token, :impersonation, user: user) }
|
|
|
|
|
|
|
|
before do
|
|
|
|
stub_config_setting(impersonation_enabled: false)
|
2019-12-11 07:08:10 -05:00
|
|
|
env[Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
|
2018-11-24 07:39:16 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'does not allow impersonation tokens' do
|
|
|
|
expect { current_user }.to raise_error Gitlab::Auth::ImpersonationDisabled
|
|
|
|
end
|
|
|
|
end
|
2013-03-28 14:37:44 -04:00
|
|
|
end
|
2020-09-25 02:09:42 -04:00
|
|
|
|
|
|
|
describe "when authenticating using a job token" do
|
|
|
|
let_it_be(:job, reload: true) do
|
|
|
|
create(:ci_build, user: user, status: :running)
|
|
|
|
end
|
|
|
|
|
|
|
|
let(:route_authentication_setting) { {} }
|
|
|
|
|
|
|
|
before do
|
|
|
|
allow_any_instance_of(self.class).to receive(:route_authentication_setting)
|
|
|
|
.and_return(route_authentication_setting)
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when route is allowed to be authenticated' do
|
|
|
|
let(:route_authentication_setting) { { job_token_allowed: true } }
|
|
|
|
|
|
|
|
it "returns a 401 response for an invalid token" do
|
|
|
|
env[Gitlab::Auth::AuthFinders::JOB_TOKEN_HEADER] = 'invalid token'
|
|
|
|
|
|
|
|
expect { current_user }.to raise_error /401/
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns a 401 response for a job that's not running" do
|
|
|
|
job.update!(status: :success)
|
|
|
|
env[Gitlab::Auth::AuthFinders::JOB_TOKEN_HEADER] = job.token
|
|
|
|
|
|
|
|
expect { current_user }.to raise_error /401/
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns a 403 response for a user without access" do
|
|
|
|
env[Gitlab::Auth::AuthFinders::JOB_TOKEN_HEADER] = job.token
|
|
|
|
allow_any_instance_of(Gitlab::UserAccess).to receive(:allowed?).and_return(false)
|
|
|
|
|
|
|
|
expect { current_user }.to raise_error /403/
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns a 403 response for a user who is blocked' do
|
|
|
|
user.block!
|
|
|
|
env[Gitlab::Auth::AuthFinders::JOB_TOKEN_HEADER] = job.token
|
|
|
|
|
|
|
|
expect { current_user }.to raise_error /403/
|
|
|
|
end
|
|
|
|
|
|
|
|
it "sets current_user" do
|
|
|
|
env[Gitlab::Auth::AuthFinders::JOB_TOKEN_HEADER] = job.token
|
|
|
|
|
|
|
|
expect(current_user).to eq(user)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when route is not allowed to be authenticated' do
|
|
|
|
let(:route_authentication_setting) { { job_token_allowed: false } }
|
|
|
|
|
|
|
|
it "sets current_user to nil" do
|
|
|
|
env[Gitlab::Auth::AuthFinders::JOB_TOKEN_HEADER] = job.token
|
|
|
|
allow_any_instance_of(Gitlab::UserAccess).to receive(:allowed?).and_return(true)
|
|
|
|
|
|
|
|
expect(current_user).to be_nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2013-03-28 14:37:44 -04:00
|
|
|
end
|
2016-07-12 10:31:55 -04:00
|
|
|
|
2016-08-18 20:06:33 -04:00
|
|
|
describe '.handle_api_exception' do
|
|
|
|
before do
|
|
|
|
allow_any_instance_of(self.class).to receive(:rack_response)
|
2018-12-05 15:54:40 -05:00
|
|
|
|
2019-04-30 16:06:08 -04:00
|
|
|
stub_sentry_settings
|
|
|
|
|
2019-12-16 07:07:43 -05:00
|
|
|
expect(Gitlab::ErrorTracking).to receive(:sentry_dsn).and_return(Gitlab.config.sentry.dsn)
|
|
|
|
Gitlab::ErrorTracking.configure
|
2016-08-18 20:06:33 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'does not report a MethodNotAllowed exception to Sentry' do
|
|
|
|
exception = Grape::Exceptions::MethodNotAllowed.new({ 'X-GitLab-Test' => '1' })
|
|
|
|
allow(exception).to receive(:backtrace).and_return(caller)
|
|
|
|
|
2021-03-03 07:11:16 -05:00
|
|
|
expect(Gitlab::ErrorTracking).not_to receive(:track_exception).with(exception)
|
2016-08-18 20:06:33 -04:00
|
|
|
|
|
|
|
handle_api_exception(exception)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'does report RuntimeError to Sentry' do
|
|
|
|
exception = RuntimeError.new('test error')
|
|
|
|
allow(exception).to receive(:backtrace).and_return(caller)
|
|
|
|
|
2021-03-03 07:11:16 -05:00
|
|
|
expect(Gitlab::ErrorTracking).to receive(:track_exception).with(exception)
|
2016-08-18 20:06:33 -04:00
|
|
|
|
2019-02-18 15:57:22 -05:00
|
|
|
Labkit::Correlation::CorrelationId.use_id('new-correlation-id') do
|
2018-12-05 15:54:40 -05:00
|
|
|
handle_api_exception(exception)
|
|
|
|
end
|
2016-08-18 20:06:33 -04:00
|
|
|
end
|
2017-09-29 07:14:08 -04:00
|
|
|
|
|
|
|
context 'with a personal access token given' do
|
|
|
|
let(:token) { create(:personal_access_token, scopes: ['api'], user: user) }
|
|
|
|
|
2019-09-18 10:02:45 -04:00
|
|
|
# Regression test for https://gitlab.com/gitlab-org/gitlab-foss/issues/38571
|
2017-09-29 07:14:08 -04:00
|
|
|
it 'does not raise an additional exception because of missing `request`' do
|
|
|
|
# We need to stub at a lower level than #sentry_enabled? otherwise
|
|
|
|
# Sentry is not enabled when the request below is made, and the test
|
|
|
|
# would pass even without the fix
|
|
|
|
expect(ProjectsFinder).to receive(:new).and_raise('Runtime Error!')
|
|
|
|
|
|
|
|
get api('/projects', personal_access_token: token)
|
|
|
|
|
|
|
|
# The 500 status is expected as we're testing a case where an exception
|
|
|
|
# is raised, but Grape shouldn't raise an additional exception
|
2020-02-25 07:08:48 -05:00
|
|
|
expect(response).to have_gitlab_http_status(:internal_server_error)
|
2017-09-29 07:14:08 -04:00
|
|
|
expect(json_response['message']).not_to include("undefined local variable or method `request'")
|
|
|
|
expect(json_response['message']).to start_with("\nRuntimeError (Runtime Error!):")
|
|
|
|
end
|
|
|
|
end
|
2016-08-18 20:06:33 -04:00
|
|
|
end
|
2016-11-30 09:48:19 -05:00
|
|
|
|
|
|
|
describe '.authenticate_non_get!' do
|
|
|
|
%w[HEAD GET].each do |method_name|
|
|
|
|
context "method is #{method_name}" do
|
|
|
|
before do
|
2016-12-23 11:03:25 -05:00
|
|
|
expect_any_instance_of(self.class).to receive(:route).and_return(double(request_method: method_name))
|
2016-11-30 09:48:19 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'does not raise an error' do
|
|
|
|
expect_any_instance_of(self.class).not_to receive(:authenticate!)
|
|
|
|
|
|
|
|
expect { authenticate_non_get! }.not_to raise_error
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
%w[POST PUT PATCH DELETE].each do |method_name|
|
|
|
|
context "method is #{method_name}" do
|
|
|
|
before do
|
2016-12-23 11:03:25 -05:00
|
|
|
expect_any_instance_of(self.class).to receive(:route).and_return(double(request_method: method_name))
|
2016-11-30 09:48:19 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'calls authenticate!' do
|
|
|
|
expect_any_instance_of(self.class).to receive(:authenticate!)
|
|
|
|
|
|
|
|
authenticate_non_get!
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '.authenticate!' do
|
|
|
|
context 'current_user is nil' do
|
|
|
|
before do
|
|
|
|
expect_any_instance_of(self.class).to receive(:current_user).and_return(nil)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'returns a 401 response' do
|
2017-09-27 09:31:52 -04:00
|
|
|
expect { authenticate! }.to raise_error /401/
|
2020-04-23 08:09:46 -04:00
|
|
|
|
|
|
|
expect(env[described_class::API_RESPONSE_STATUS_CODE]).to eq(401)
|
2016-11-30 09:48:19 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'current_user is present' do
|
2017-04-21 02:44:47 -04:00
|
|
|
let(:user) { build(:user) }
|
|
|
|
|
2016-11-30 09:48:19 -05:00
|
|
|
before do
|
2017-09-27 09:31:52 -04:00
|
|
|
expect_any_instance_of(self.class).to receive(:current_user).and_return(user)
|
2016-11-30 09:48:19 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'does not raise an error' do
|
|
|
|
expect { authenticate! }.not_to raise_error
|
2020-04-23 08:09:46 -04:00
|
|
|
|
|
|
|
expect(env[described_class::API_RESPONSE_STATUS_CODE]).to be_nil
|
2016-11-30 09:48:19 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2017-10-30 13:49:46 -04:00
|
|
|
|
|
|
|
context 'sudo' do
|
2019-09-26 08:06:00 -04:00
|
|
|
include_context 'custom session'
|
|
|
|
|
2017-10-30 13:49:46 -04:00
|
|
|
shared_examples 'successful sudo' do
|
|
|
|
it 'sets current_user' do
|
|
|
|
expect(current_user).to eq(user)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'sets sudo?' do
|
|
|
|
expect(sudo?).to be_truthy
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
shared_examples 'sudo' do
|
|
|
|
context 'when admin' do
|
|
|
|
before do
|
|
|
|
token.user = admin
|
|
|
|
token.save!
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when token has sudo scope' do
|
|
|
|
before do
|
|
|
|
token.scopes = %w[sudo]
|
|
|
|
token.save!
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when user exists' do
|
|
|
|
context 'when using header' do
|
|
|
|
context 'when providing username' do
|
|
|
|
before do
|
|
|
|
env[API::Helpers::SUDO_HEADER] = user.username
|
|
|
|
end
|
|
|
|
|
|
|
|
it_behaves_like 'successful sudo'
|
|
|
|
end
|
|
|
|
|
2018-10-18 05:06:44 -04:00
|
|
|
context 'when providing username (case insensitive)' do
|
|
|
|
before do
|
|
|
|
env[API::Helpers::SUDO_HEADER] = user.username.upcase
|
|
|
|
end
|
|
|
|
|
|
|
|
it_behaves_like 'successful sudo'
|
|
|
|
end
|
|
|
|
|
2017-10-30 13:49:46 -04:00
|
|
|
context 'when providing user ID' do
|
|
|
|
before do
|
|
|
|
env[API::Helpers::SUDO_HEADER] = user.id.to_s
|
|
|
|
end
|
|
|
|
|
|
|
|
it_behaves_like 'successful sudo'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when using param' do
|
|
|
|
context 'when providing username' do
|
|
|
|
before do
|
2017-11-09 13:04:19 -05:00
|
|
|
set_param(API::Helpers::SUDO_PARAM, user.username)
|
2017-10-30 13:49:46 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it_behaves_like 'successful sudo'
|
|
|
|
end
|
|
|
|
|
2018-10-18 05:06:44 -04:00
|
|
|
context 'when providing username (case insensitive)' do
|
|
|
|
before do
|
|
|
|
set_param(API::Helpers::SUDO_PARAM, user.username.upcase)
|
|
|
|
end
|
|
|
|
|
|
|
|
it_behaves_like 'successful sudo'
|
|
|
|
end
|
|
|
|
|
2017-10-30 13:49:46 -04:00
|
|
|
context 'when providing user ID' do
|
|
|
|
before do
|
2017-11-09 13:04:19 -05:00
|
|
|
set_param(API::Helpers::SUDO_PARAM, user.id.to_s)
|
2017-10-30 13:49:46 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it_behaves_like 'successful sudo'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when user does not exist' do
|
|
|
|
before do
|
2017-11-09 13:04:19 -05:00
|
|
|
set_param(API::Helpers::SUDO_PARAM, 'nonexistent')
|
2017-10-30 13:49:46 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'raises an error' do
|
|
|
|
expect { current_user }.to raise_error /User with ID or username 'nonexistent' Not Found/
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when token does not have sudo scope' do
|
|
|
|
before do
|
|
|
|
token.scopes = %w[api]
|
|
|
|
token.save!
|
|
|
|
|
2017-11-09 13:04:19 -05:00
|
|
|
set_param(API::Helpers::SUDO_PARAM, user.id.to_s)
|
2017-10-30 13:49:46 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'raises an error' do
|
2017-11-16 11:03:19 -05:00
|
|
|
expect { current_user }.to raise_error Gitlab::Auth::InsufficientScopeError
|
2017-10-30 13:49:46 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when not admin' do
|
|
|
|
before do
|
|
|
|
token.user = user
|
|
|
|
token.save!
|
|
|
|
|
2017-11-09 13:04:19 -05:00
|
|
|
set_param(API::Helpers::SUDO_PARAM, user.id.to_s)
|
2017-10-30 13:49:46 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it 'raises an error' do
|
|
|
|
expect { current_user }.to raise_error /Must be admin to use sudo/
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'using an OAuth token' do
|
|
|
|
let(:token) { create(:oauth_access_token) }
|
|
|
|
|
|
|
|
before do
|
2022-08-12 17:11:43 -04:00
|
|
|
env['HTTP_AUTHORIZATION'] = "Bearer #{token.plaintext_token}"
|
2017-10-30 13:49:46 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it_behaves_like 'sudo'
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'using a personal access token' do
|
|
|
|
let(:token) { create(:personal_access_token) }
|
|
|
|
|
|
|
|
context 'passed as param' do
|
|
|
|
before do
|
2019-12-11 07:08:10 -05:00
|
|
|
set_param(Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_PARAM, token.token)
|
2017-10-30 13:49:46 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it_behaves_like 'sudo'
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'passed as header' do
|
|
|
|
before do
|
2019-12-11 07:08:10 -05:00
|
|
|
env[Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER] = token.token
|
2017-10-30 13:49:46 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it_behaves_like 'sudo'
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'using warden authentication' do
|
|
|
|
before do
|
|
|
|
warden_authenticate_returns admin
|
|
|
|
env[API::Helpers::SUDO_HEADER] = user.username
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'raises an error' do
|
|
|
|
expect { current_user }.to raise_error /Must be authenticated using an OAuth or Personal Access Token to use sudo/
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2014-04-11 15:45:56 -04:00
|
|
|
end
|