Commit ec3150db authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent 221b1159d254
......@@ -446,7 +446,7 @@ describe('Pipelines', () => {
};
vm.$nextTick(() => {
vm.$el.querySelector('.js-next-button .page-link').click();
vm.$el.querySelector('.next-page-item').click();
expect(vm.updateContent).toHaveBeenCalledWith({ scope: 'all', page: '2' });
......
import Vue from 'vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
import dropdownSearchInputComponent from '~/vue_shared/components/dropdown/dropdown_search_input.vue';
const componentConfig = {
placeholderText: 'Search something',
};
const createComponent = (config = componentConfig) => {
const Component = Vue.extend(dropdownSearchInputComponent);
return mountComponent(Component, config);
};
describe('DropdownSearchInputComponent', () => {
let vm;
beforeEach(() => {
vm = createComponent();
});
afterEach(() => {
vm.$destroy();
});
describe('template', () => {
it('renders input element with type `search`', () => {
const inputEl = vm.$el.querySelector('input.dropdown-input-field');
expect(inputEl).not.toBeNull();
expect(inputEl.getAttribute('type')).toBe('search');
});
it('renders search icon element', () => {
expect(vm.$el.querySelector('.fa-search.dropdown-input-search')).not.toBeNull();
});
it('renders clear search icon element', () => {
expect(
vm.$el.querySelector('.fa-times.dropdown-input-clear.js-dropdown-input-clear'),
).not.toBeNull();
});
it('displays custom placeholder text', () => {
const inputEl = vm.$el.querySelector('input.dropdown-input-field');
expect(inputEl.getAttribute('placeholder')).toBe(componentConfig.placeholderText);
});
});
});
......@@ -2,7 +2,7 @@
require 'spec_helper'
describe Gitlab::Auth::UserAuthFinders do
describe Gitlab::Auth::AuthFinders do
include described_class
let(:user) { create(:user) }
......@@ -196,13 +196,13 @@ def set_param(key, value)
context 'when validate_access_token! returns valid' do
it 'returns user' do
env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
env[described_class::PRIVATE_TOKEN_HEADER] = personal_access_token.token
expect(find_user_from_access_token).to eq user
end
it 'returns exception if token has no user' do
env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
env[described_class::PRIVATE_TOKEN_HEADER] = personal_access_token.token
allow_any_instance_of(PersonalAccessToken).to receive(:user).and_return(nil)
expect { find_user_from_access_token }.to raise_error(Gitlab::Auth::UnauthorizedError)
......@@ -228,7 +228,7 @@ def set_param(key, value)
let(:personal_access_token) { create(:personal_access_token, user: user) }
before do
env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
env[described_class::PRIVATE_TOKEN_HEADER] = personal_access_token.token
end
it 'returns exception if token has no user' do
......@@ -279,7 +279,7 @@ def set_param(key, value)
context 'passed as header' do
it 'returns token if valid personal_access_token' do
env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
env[described_class::PRIVATE_TOKEN_HEADER] = personal_access_token.token
expect(find_personal_access_token).to eq personal_access_token
end
......@@ -287,7 +287,7 @@ def set_param(key, value)
context 'passed as param' do
it 'returns token if valid personal_access_token' do
set_param(Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_PARAM, personal_access_token.token)
set_param(described_class::PRIVATE_TOKEN_PARAM, personal_access_token.token)
expect(find_personal_access_token).to eq personal_access_token
end
......@@ -298,7 +298,7 @@ def set_param(key, value)
end
it 'returns exception if invalid personal_access_token' do
env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = 'invalid_token'
env[described_class::PRIVATE_TOKEN_HEADER] = 'invalid_token'
expect { find_personal_access_token }.to raise_error(Gitlab::Auth::UnauthorizedError)
end
......@@ -379,4 +379,58 @@ def set_param(key, value)
end
end
end
describe '#find_runner_from_token' do
let(:runner) { create(:ci_runner) }
context 'with API requests' do
before do
env['SCRIPT_NAME'] = '/api/endpoint'
end
it 'returns the runner if token is valid' do
set_param(:token, runner.token)
expect(find_runner_from_token).to eq(runner)
end
it 'returns nil if token is not present' do
expect(find_runner_from_token).to be_nil
end
it 'returns nil if token is blank' do
set_param(:token, '')
expect(find_runner_from_token).to be_nil
end
it 'returns exception if invalid token' do
set_param(:token, 'invalid_token')
expect { find_runner_from_token }.to raise_error(Gitlab::Auth::UnauthorizedError)
end
end
context 'without API requests' do
before do
env['SCRIPT_NAME'] = 'url.ics'
end
it 'returns nil if token is valid' do
set_param(:token, runner.token)
expect(find_runner_from_token).to be_nil
end
it 'returns nil if token is blank' do
expect(find_runner_from_token).to be_nil
end
it 'returns nil if invalid token' do
set_param(:token, 'invalid_token')
expect(find_runner_from_token).to be_nil
end
end
end
end
......@@ -62,69 +62,90 @@
context 'when the user is an admin' do
let(:user) { build(:user, :admin) }
it 'is false by default' do
expect(subject.admin_mode?).to be(false)
end
it 'cannot be enabled with an invalid password' do
subject.enable_admin_mode!(password: nil)
expect(subject.admin_mode?).to be(false)
end
context 'when admin mode not requested' do
it 'is false by default' do
expect(subject.admin_mode?).to be(false)
end
it 'can be enabled with a valid password' do
subject.enable_admin_mode!(password: user.password)
it 'raises exception if we try to enable it' do
expect do
subject.enable_admin_mode!(password: user.password)
end.to raise_error(::Gitlab::Auth::CurrentUserMode::NotRequestedError)
expect(subject.admin_mode?).to be(true)
expect(subject.admin_mode?).to be(false)
end
end
it 'can be disabled' do
subject.enable_admin_mode!(password: user.password)
subject.disable_admin_mode!
expect(subject.admin_mode?).to be(false)
end
context 'when admin mode requested first' do
before do
subject.request_admin_mode!
end
it 'will expire in the future' do
subject.enable_admin_mode!(password: user.password)
expect(subject.admin_mode?).to be(true), 'admin mode is not active in the present'
it 'is false by default' do
expect(subject.admin_mode?).to be(false)
end
Timecop.freeze(Gitlab::Auth::CurrentUserMode::MAX_ADMIN_MODE_TIME.from_now) do
# in the future this will be a new request, simulate by clearing the RequestStore
Gitlab::SafeRequestStore.clear!
it 'cannot be enabled with an invalid password' do
subject.enable_admin_mode!(password: nil)
expect(subject.admin_mode?).to be(false), 'admin mode did not expire in the future'
expect(subject.admin_mode?).to be(false)
end
end
context 'skipping password validation' do
it 'can be enabled with a valid password' do
subject.enable_admin_mode!(password: user.password, skip_password_validation: true)
subject.enable_admin_mode!(password: user.password)
expect(subject.admin_mode?).to be(true)
end
it 'can be enabled with an invalid password' do
subject.enable_admin_mode!(skip_password_validation: true)
it 'can be disabled' do
subject.enable_admin_mode!(password: user.password)
subject.disable_admin_mode!
expect(subject.admin_mode?).to be(true)
expect(subject.admin_mode?).to be(false)
end
end
context 'with two independent sessions' do
let(:another_session) { {} }
let(:another_subject) { described_class.new(user) }
it 'will expire in the future' do
subject.enable_admin_mode!(password: user.password)
expect(subject.admin_mode?).to be(true), 'admin mode is not active in the present'
before do
allow(ActiveSession).to receive(:list_sessions).with(user).and_return([session, another_session])
Timecop.freeze(Gitlab::Auth::CurrentUserMode::MAX_ADMIN_MODE_TIME.from_now) do
# in the future this will be a new request, simulate by clearing the RequestStore
Gitlab::SafeRequestStore.clear!
expect(subject.admin_mode?).to be(false), 'admin mode did not expire in the future'
end
end
it 'can be enabled in one and seen in the other' do
Gitlab::Session.with_session(another_session) do
another_subject.enable_admin_mode!(password: user.password)
context 'skipping password validation' do
it 'can be enabled with a valid password' do
subject.enable_admin_mode!(password: user.password, skip_password_validation: true)
expect(subject.admin_mode?).to be(true)
end
expect(subject.admin_mode?).to be(true)
it 'can be enabled with an invalid password' do
subject.enable_admin_mode!(skip_password_validation: true)
expect(subject.admin_mode?).to be(true)
end
end
context 'with two independent sessions' do
let(:another_session) { {} }
let(:another_subject) { described_class.new(user) }
before do
allow(ActiveSession).to receive(:list_sessions).with(user).and_return([session, another_session])
end
it 'can be enabled in one and seen in the other' do
Gitlab::Session.with_session(another_session) do
another_subject.request_admin_mode!
another_subject.enable_admin_mode!(password: user.password)
end
expect(subject.admin_mode?).to be(true)
end
end
end
end
......@@ -134,16 +155,28 @@
let(:user) { build(:user, :admin) }
it 'creates a timestamp in the session' do
subject.request_admin_mode!
subject.enable_admin_mode!(password: user.password)
expect(session).to include(expected_session_entry(be_within(1.second).of Time.now))
end
end
describe '#enable_sessionless_admin_mode!' do
let(:user) { build(:user, :admin) }
it 'enabled admin mode without password' do
subject.enable_sessionless_admin_mode!
expect(subject.admin_mode?).to be(true)
end
end
describe '#disable_admin_mode!' do
let(:user) { build(:user, :admin) }
it 'sets the session timestamp to nil' do
subject.request_admin_mode!
subject.disable_admin_mode!
expect(session).to include(expected_session_entry(be_nil))
......
......@@ -66,4 +66,28 @@
expect(subject.find_sessionless_user([:api])).to be_blank
end
end
describe '#runner' do
let!(:runner) { build(:ci_runner) }
it 'returns the runner using #find_runner_from_token' do
expect_any_instance_of(described_class)
.to receive(:find_runner_from_token)
.and_return(runner)
expect(subject.runner).to eq runner
end
it 'returns nil if no runner is found' do
expect(subject.runner).to be_blank
end
it 'rescue Gitlab::Auth::AuthenticationError exceptions' do
expect_any_instance_of(described_class)
.to receive(:find_runner_from_token)
.and_raise(Gitlab::Auth::UnauthorizedError)
expect(subject.runner).to be_blank
end
end
end
......@@ -443,6 +443,7 @@ project:
- downstream_project_subscriptions
- service_desk_setting
- import_failures
- container_expiration_policy
award_emoji:
- awardable
- user
......
......@@ -773,3 +773,13 @@ ZoomMeeting:
ServiceDeskSetting:
- project_id
- issue_template_key
ContainerExpirationPolicy:
- created_at
- updated_at
- next_run_at
- project_id
- name_regex
- cadence
- older_than
- keep_n
- enabled
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe ContainerExpirationPolicy, type: :model do
describe 'relationships' do
it { is_expected.to belong_to(:project) }
end
describe 'validations' do
it { is_expected.to validate_presence_of(:project) }
describe '#enabled' do
it { is_expected.to allow_value(true).for(:enabled) }
it { is_expected.to allow_value(false).for(:enabled) }
it { is_expected.not_to allow_value(nil).for(:enabled) }
end
describe '#cadence' do
it { is_expected.to validate_presence_of(:cadence) }
it { is_expected.to allow_value('1d').for(:cadence) }
it { is_expected.to allow_value('1month').for(:cadence) }
it { is_expected.not_to allow_value('123asdf').for(:cadence) }
it { is_expected.not_to allow_value(nil).for(:cadence) }
end
describe '#older_than' do
it { is_expected.to allow_value('7d').for(:older_than) }
it { is_expected.to allow_value('14d').for(:older_than) }
it { is_expected.to allow_value(nil).for(:older_than) }
it { is_expected.not_to allow_value('123asdf').for(:older_than) }
end
describe '#keep_n' do
it { is_expected.to allow_value(10).for(:keep_n) }
it { is_expected.to allow_value(nil).for(:keep_n) }
it { is_expected.not_to allow_value('foo').for(:keep_n) }
end
end
end
......@@ -199,6 +199,13 @@
expect(described_class.find_by_pages_host(host)).to eq(namespace)
end
it "returns no result if the provided host is not subdomain of the Pages host" do
create(:namespace, name: 'namespace.io')
host = "namespace.io"
expect(described_class.find_by_pages_host(host)).to eq(nil)
end
end
describe '#ancestors_upto' do
......
......@@ -62,6 +62,7 @@
it { is_expected.to have_one(:external_wiki_service) }
it { is_expected.to have_one(:project_feature) }
it { is_expected.to have_one(:project_repository) }
it { is_expected.to have_one(:container_expiration_policy) }
it { is_expected.to have_one(:statistics).class_name('ProjectStatistics') }
it { is_expected.to have_one(:import_data).class_name('ProjectImportData') }
it { is_expected.to have_one(:last_event).class_name('Event') }
......@@ -137,6 +138,13 @@
expect(project.ci_cd_settings).to be_persisted
end
it 'automatically creates a container expiration policy row' do
project = create(:project)
expect(project.container_expiration_policy).to be_an_instance_of(ContainerExpirationPolicy)
expect(project.container_expiration_policy).to be_persisted
end
it 'automatically creates a Pages metadata row' do
project = create(:project)
......
......@@ -2839,6 +2839,7 @@ def add_user(access)
context 'when admin mode is enabled' do
before do
Gitlab::Auth::CurrentUserMode.new(user).request_admin_mode!
Gitlab::Auth::CurrentUserMode.new(user).enable_admin_mode!(password: user.password)
end
......
......@@ -146,13 +146,13 @@ def set_param(key, value)
let(:personal_access_token) { create(:personal_access_token, user: user) }
it "returns a 401 response for an invalid token" do
env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = 'invalid token'
env[Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER] = 'invalid token'
expect { current_user }.to raise_error /401/
end
it "returns a 403 response for a user without access" do
env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
env[Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
allow_any_instance_of(Gitlab::UserAccess).to receive(:allowed?).and_return(false)
expect { current_user }.to raise_error /403/
......@@ -160,7 +160,7 @@ def set_param(key, value)
it 'returns a 403 response for a user who is blocked' do
user.block!
env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
env[Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
expect { current_user }.to raise_error /403/
end
......@@ -168,7 +168,7 @@ def set_param(key, value)
context 'when terms are enforced' do
before do
enforce_terms
env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
env[Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
end
it 'returns a 403 when a user has not accepted the terms' do
......@@ -183,27 +183,27 @@ def set_param(key, value)
end
it "sets current_user" do
env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
env[Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
expect(current_user).to eq(user)
end
it "does not allow tokens without the appropriate scope" do
personal_access_token = create(:personal_access_token, user: user, scopes: ['read_user'])
env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
env[Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
expect { current_user }.to raise_error Gitlab::Auth::InsufficientScopeError
end
it 'does not allow revoked tokens' do
personal_access_token.revoke!
env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
env[Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
expect { current_user }.to raise_error Gitlab::Auth::RevokedError
end
it 'does not allow expired tokens' do
personal_access_token.update!(expires_at: 1.day.ago)
env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
env[Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
expect { current_user }.to raise_error Gitlab::Auth::ExpiredError
end
......@@ -213,7 +213,7 @@ def set_param(key, value)
before do
stub_config_setting(impersonation_enabled: false)
env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
env[Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token
end
it 'does not allow impersonation tokens' do
......@@ -478,7 +478,7 @@ def set_param(key, value)
context 'passed as param' do
before do
set_param(Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_PARAM, token.token)
set_param(Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_PARAM, token.token)
end
it_behaves_like 'sudo'
......@@ -486,7 +486,7 @@ def set_param(key, value)
context 'passed as header' do
before do
env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = token.token
env[Gitlab::Auth::AuthFinders::PRIVATE_TOKEN_HEADER] = token.token
end
it_behaves_like 'sudo'
......
......@@ -100,6 +100,18 @@
end
end
context 'when the request is authenticated by a runner token' do
let(:request_jobs_url) { '/api/v4/jobs/request' }
let(:runner) { create(:ci_runner) }
it 'does not cont as unauthenticated' do
(1 + requests_per_period).times do
post request_jobs_url, params: { token: runner.token }
expect(response).to have_http_status 204
end
end
end
it 'logs RackAttack info into structured logs' do
requests_per_period.times do
get url_that_does_not_require_authentication
......