Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/oauth/authorizations_controller_spec.rb77
-rw-r--r--spec/factories/environments.rb8
-rw-r--r--spec/features/users/login_spec.rb19
-rw-r--r--spec/frontend/notebook/cells/markdown_spec.js13
-rw-r--r--spec/graphql/resolvers/timelog_resolver_spec.rb131
-rw-r--r--spec/graphql/types/timelog_type_spec.rb2
-rw-r--r--spec/helpers/markup_helper_spec.rb7
-rw-r--r--spec/initializers/lograge_spec.rb21
-rw-r--r--spec/lib/banzai/filter/truncate_source_filter_spec.rb70
-rw-r--r--spec/lib/banzai/pipeline/post_process_pipeline_spec.rb4
-rw-r--r--spec/lib/gitlab/auth/user_access_denied_reason_spec.rb8
-rw-r--r--spec/lib/gitlab/background_migration/update_users_where_two_factor_auth_required_from_group_spec.rb84
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb2
-rw-r--r--spec/lib/gitlab/git_access_spec.rb14
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml1
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml1
-rw-r--r--spec/lib/gitlab/utils/nokogiri_spec.rb34
-rw-r--r--spec/lib/gitlab/utils_spec.rb23
-rw-r--r--spec/lib/gitlab/x509/signature_spec.rb77
-rw-r--r--spec/migrations/20210601080039_group_protected_environments_add_index_and_constraint_spec.rb29
-rw-r--r--spec/migrations/backfill_escalation_policies_for_oncall_schedules_spec.rb100
-rw-r--r--spec/migrations/schedule_update_users_where_two_factor_auth_required_from_group_spec.rb29
-rw-r--r--spec/models/concerns/has_timelogs_report_spec.rb55
-rw-r--r--spec/models/timelog_spec.rb56
-rw-r--r--spec/policies/global_policy_spec.rb24
-rw-r--r--spec/policies/group_policy_spec.rb50
-rw-r--r--spec/policies/project_policy_spec.rb50
-rw-r--r--spec/requests/api/graphql/group/timelogs_spec.rb49
-rw-r--r--spec/requests/git_http_spec.rb61
-rw-r--r--spec/requests/lfs_http_spec.rb4
-rw-r--r--spec/tasks/gitlab/x509/update_rake_spec.rb20
31 files changed, 728 insertions, 395 deletions
diff --git a/spec/controllers/oauth/authorizations_controller_spec.rb b/spec/controllers/oauth/authorizations_controller_spec.rb
index 5fc5cdfc9b9..0e25f6a96d7 100644
--- a/spec/controllers/oauth/authorizations_controller_spec.rb
+++ b/spec/controllers/oauth/authorizations_controller_spec.rb
@@ -70,76 +70,59 @@ RSpec.describe Oauth::AuthorizationsController do
describe 'GET #new' do
subject { get :new, params: params }
- include_examples 'OAuth Authorizations require confirmed user'
include_examples "Implicit grant can't be used in confidential application"
- context 'rendering of views based on the ownership of the application' do
- shared_examples 'render views' do
- render_views
-
- it 'returns 200 and renders view with correct info', :aggregate_failures do
- subject
+ context 'when the user is confirmed' do
+ let(:confirmed_at) { 1.hour.ago }
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.body).to include(application.owner.name)
- expect(response).to render_template('doorkeeper/authorizations/new')
- end
- end
+ context 'when there is already an access token for the application with a matching scope' do
+ before do
+ scopes = Doorkeeper::OAuth::Scopes.from_string('api')
- subject { get :new, params: params }
+ allow(Doorkeeper.configuration).to receive(:scopes).and_return(scopes)
- context 'when auth app owner is a user' do
- context 'with valid params' do
- it_behaves_like 'render views'
+ create(:oauth_access_token, application: application, resource_owner_id: user.id, scopes: scopes)
end
- end
-
- context 'when auth app owner is a group' do
- let(:group) { create(:group) }
- context 'when auth app owner is a root group' do
- let(:application) { create(:oauth_application, owner_id: group.id, owner_type: 'Namespace') }
+ it 'authorizes the request and shows the user a page that redirects' do
+ subject
- it_behaves_like 'render views'
+ expect(request.session['user_return_to']).to be_nil
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to render_template('doorkeeper/authorizations/redirect')
end
+ end
- context 'when auth app owner is a subgroup' do
- let(:subgroup) { create(:group, parent: group) }
- let(:application) { create(:oauth_application, owner_id: subgroup.id, owner_type: 'Namespace') }
+ context 'without valid params' do
+ it 'returns 200 code and renders error view' do
+ get :new
- it_behaves_like 'render views'
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to render_template('doorkeeper/authorizations/error')
end
end
- context 'when there is no owner associated' do
- let(:application) { create(:oauth_application, owner_id: nil, owner_type: nil) }
+ context 'with valid params' do
+ render_views
- it 'renders view' do
+ it 'returns 200 code and renders view' do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template('doorkeeper/authorizations/new')
end
- end
- end
- context 'without valid params' do
- it 'returns 200 code and renders error view' do
- get :new
+ it 'deletes session.user_return_to and redirects when skip authorization' do
+ application.update!(trusted: true)
+ request.session['user_return_to'] = 'http://example.com'
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to render_template('doorkeeper/authorizations/error')
- end
- end
-
- it 'deletes session.user_return_to and redirects when skip authorization' do
- application.update!(trusted: true)
- request.session['user_return_to'] = 'http://example.com'
-
- subject
+ subject
- expect(request.session['user_return_to']).to be_nil
- expect(response).to have_gitlab_http_status(:found)
+ expect(request.session['user_return_to']).to be_nil
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to render_template('doorkeeper/authorizations/redirect')
+ end
+ end
end
end
diff --git a/spec/factories/environments.rb b/spec/factories/environments.rb
index 072a5f1f402..148ee64fb08 100644
--- a/spec/factories/environments.rb
+++ b/spec/factories/environments.rb
@@ -16,19 +16,19 @@ FactoryBot.define do
end
trait :production do
- tier { :production }
+ name { 'production' }
end
trait :staging do
- tier { :staging }
+ name { 'staging' }
end
trait :testing do
- tier { :testing }
+ name { 'testing' }
end
trait :development do
- tier { :development }
+ name { 'development' }
end
trait :with_review_app do |environment|
diff --git a/spec/features/users/login_spec.rb b/spec/features/users/login_spec.rb
index e60d9d6ab69..1d7099ba443 100644
--- a/spec/features/users/login_spec.rb
+++ b/spec/features/users/login_spec.rb
@@ -394,6 +394,25 @@ RSpec.describe 'Login' do
gitlab_sign_in(user)
end
+
+ context 'when the users password is expired' do
+ before do
+ user.update!(password_expires_at: Time.parse('2018-05-08 11:29:46 UTC'))
+ end
+
+ it 'asks for a new password' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+
+ visit new_user_session_path
+
+ fill_in 'user_login', with: user.email
+ fill_in 'user_password', with: '12345678'
+ click_button 'Sign in'
+
+ expect(current_path).to eq(new_profile_password_path)
+ end
+ end
end
context 'with invalid username and password' do
diff --git a/spec/frontend/notebook/cells/markdown_spec.js b/spec/frontend/notebook/cells/markdown_spec.js
index d250ffed1a9..deeee5d6589 100644
--- a/spec/frontend/notebook/cells/markdown_spec.js
+++ b/spec/frontend/notebook/cells/markdown_spec.js
@@ -39,7 +39,7 @@ describe('Markdown component', () => {
expect(vm.$el.querySelector('.markdown h1')).not.toBeNull();
});
- it('sanitizes output', async () => {
+ it('sanitizes Markdown output', async () => {
Object.assign(cell, {
source: [
'[XSS](data:text/html;base64,PHNjcmlwdD5hbGVydChkb2N1bWVudC5kb21haW4pPC9zY3JpcHQ+Cg==)\n',
@@ -50,6 +50,17 @@ describe('Markdown component', () => {
expect(vm.$el.querySelector('a').getAttribute('href')).toBeNull();
});
+ it('sanitizes HTML', async () => {
+ const findLink = () => vm.$el.querySelector('.xss-link');
+ Object.assign(cell, {
+ source: ['<a href="test.js" data-remote=true data-type="script" class="xss-link">XSS</a>\n'],
+ });
+
+ await vm.$nextTick();
+ expect(findLink().getAttribute('data-remote')).toBe(null);
+ expect(findLink().getAttribute('data-type')).toBe(null);
+ });
+
describe('tables', () => {
beforeEach(() => {
json = getJSONFixture('blob/notebook/markdown-table.json');
diff --git a/spec/graphql/resolvers/timelog_resolver_spec.rb b/spec/graphql/resolvers/timelog_resolver_spec.rb
index 585cd657e35..bb4938c751f 100644
--- a/spec/graphql/resolvers/timelog_resolver_spec.rb
+++ b/spec/graphql/resolvers/timelog_resolver_spec.rb
@@ -11,26 +11,27 @@ RSpec.describe Resolvers::TimelogResolver do
context "with a group" do
let_it_be(:current_user) { create(:user) }
- let_it_be(:group) { create(:group) }
- let_it_be(:project) { create(:project, :public, group: group) }
-
- before_all do
- group.add_developer(current_user)
- project.add_developer(current_user)
- end
-
- before do
- group.clear_memoization(:timelogs)
- end
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, :empty_repo, :public, group: group) }
describe '#resolve' do
+ let_it_be(:short_time_ago) { 5.days.ago.beginning_of_day }
+ let_it_be(:medium_time_ago) { 15.days.ago.beginning_of_day }
+
let_it_be(:issue) { create(:issue, project: project) }
- let_it_be(:issue2) { create(:issue, project: project) }
- let_it_be(:timelog1) { create(:issue_timelog, issue: issue, spent_at: 2.days.ago.beginning_of_day) }
- let_it_be(:timelog2) { create(:issue_timelog, issue: issue2, spent_at: 2.days.ago.end_of_day) }
- let_it_be(:timelog3) { create(:issue_timelog, issue: issue2, spent_at: 10.days.ago) }
+ let_it_be(:merge_request) { create(:merge_request, source_project: project) }
+
+ let_it_be(:timelog1) { create(:issue_timelog, issue: issue, spent_at: short_time_ago.beginning_of_day) }
+ let_it_be(:timelog2) { create(:issue_timelog, issue: issue, spent_at: short_time_ago.end_of_day) }
+ let_it_be(:timelog3) { create(:merge_request_timelog, merge_request: merge_request, spent_at: medium_time_ago) }
+
+ let(:args) { { start_time: short_time_ago, end_time: short_time_ago.noon } }
+
+ it 'finds all timelogs' do
+ timelogs = resolve_timelogs
- let(:args) { { start_time: 6.days.ago, end_time: 2.days.ago.noon } }
+ expect(timelogs).to contain_exactly(timelog1, timelog2, timelog3)
+ end
it 'finds all timelogs within given dates' do
timelogs = resolve_timelogs(**args)
@@ -38,15 +39,28 @@ RSpec.describe Resolvers::TimelogResolver do
expect(timelogs).to contain_exactly(timelog1)
end
- it 'return nothing when user has insufficient permissions' do
- user = create(:user)
- group.add_guest(current_user)
+ context 'when only start_date is present' do
+ let(:args) { { start_date: short_time_ago } }
+
+ it 'finds timelogs until the end of day of end_date' do
+ timelogs = resolve_timelogs(**args)
+
+ expect(timelogs).to contain_exactly(timelog1, timelog2)
+ end
+ end
+
+ context 'when only end_date is present' do
+ let(:args) { { end_date: medium_time_ago } }
+
+ it 'finds timelogs until the end of day of end_date' do
+ timelogs = resolve_timelogs(**args)
- expect(resolve_timelogs(user: user, **args)).to be_empty
+ expect(timelogs).to contain_exactly(timelog3)
+ end
end
context 'when start_time and end_date are present' do
- let(:args) { { start_time: 6.days.ago, end_date: 2.days.ago } }
+ let(:args) { { start_time: short_time_ago, end_date: short_time_ago } }
it 'finds timelogs until the end of day of end_date' do
timelogs = resolve_timelogs(**args)
@@ -56,7 +70,7 @@ RSpec.describe Resolvers::TimelogResolver do
end
context 'when start_date and end_time are present' do
- let(:args) { { start_date: 6.days.ago, end_time: 2.days.ago.noon } }
+ let(:args) { { start_date: short_time_ago, end_time: short_time_ago.noon } }
it 'finds all timelogs within start_date and end_time' do
timelogs = resolve_timelogs(**args)
@@ -68,95 +82,32 @@ RSpec.describe Resolvers::TimelogResolver do
context 'when arguments are invalid' do
let_it_be(:error_class) { Gitlab::Graphql::Errors::ArgumentError }
- context 'when no time or date arguments are present' do
- let(:args) { {} }
-
- it 'returns correct error' do
- expect { resolve_timelogs(**args) }
- .to raise_error(error_class, /Start and End arguments must be present/)
- end
- end
-
- context 'when only start_time is present' do
- let(:args) { { start_time: 6.days.ago } }
-
- it 'returns correct error' do
- expect { resolve_timelogs(**args) }
- .to raise_error(error_class, /Both Start and End arguments must be present/)
- end
- end
-
- context 'when only end_time is present' do
- let(:args) { { end_time: 2.days.ago } }
-
- it 'returns correct error' do
- expect { resolve_timelogs(**args) }
- .to raise_error(error_class, /Both Start and End arguments must be present/)
- end
- end
-
- context 'when only start_date is present' do
- let(:args) { { start_date: 6.days.ago } }
-
- it 'returns correct error' do
- expect { resolve_timelogs(**args) }
- .to raise_error(error_class, /Both Start and End arguments must be present/)
- end
- end
-
- context 'when only end_date is present' do
- let(:args) { { end_date: 2.days.ago } }
-
- it 'returns correct error' do
- expect { resolve_timelogs(**args) }
- .to raise_error(error_class, /Both Start and End arguments must be present/)
- end
- end
-
context 'when start_time and start_date are present' do
- let(:args) { { start_time: 6.days.ago, start_date: 6.days.ago } }
+ let(:args) { { start_time: short_time_ago, start_date: short_time_ago } }
it 'returns correct error' do
expect { resolve_timelogs(**args) }
- .to raise_error(error_class, /Both Start and End arguments must be present/)
+ .to raise_error(error_class, /Provide either a start date or time, but not both/)
end
end
context 'when end_time and end_date are present' do
- let(:args) { { end_time: 2.days.ago, end_date: 2.days.ago } }
+ let(:args) { { end_time: short_time_ago, end_date: short_time_ago } }
it 'returns correct error' do
expect { resolve_timelogs(**args) }
- .to raise_error(error_class, /Both Start and End arguments must be present/)
- end
- end
-
- context 'when three arguments are present' do
- let(:args) { { start_date: 6.days.ago, end_date: 2.days.ago, end_time: 2.days.ago } }
-
- it 'returns correct error' do
- expect { resolve_timelogs(**args) }
- .to raise_error(error_class, /Only Time or Date arguments must be present/)
+ .to raise_error(error_class, /Provide either an end date or time, but not both/)
end
end
context 'when start argument is after end argument' do
- let(:args) { { start_time: 2.days.ago, end_time: 6.days.ago } }
+ let(:args) { { start_time: short_time_ago, end_time: medium_time_ago } }
it 'returns correct error' do
expect { resolve_timelogs(**args) }
.to raise_error(error_class, /Start argument must be before End argument/)
end
end
-
- context 'when time range is more than 60 days' do
- let(:args) { { start_time: 3.months.ago, end_time: 2.days.ago } }
-
- it 'returns correct error' do
- expect { resolve_timelogs(**args) }
- .to raise_error(error_class, /The time range period cannot contain more than 60 days/)
- end
- end
end
end
end
diff --git a/spec/graphql/types/timelog_type_spec.rb b/spec/graphql/types/timelog_type_spec.rb
index 791c2fdb046..1344af89fb6 100644
--- a/spec/graphql/types/timelog_type_spec.rb
+++ b/spec/graphql/types/timelog_type_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe GitlabSchema.types['Timelog'] do
it { expect(described_class.graphql_name).to eq('Timelog') }
it { expect(described_class).to have_graphql_fields(fields) }
- it { expect(described_class).to require_graphql_authorizations(:read_group_timelogs) }
+ it { expect(described_class).to require_graphql_authorizations(:read_issue) }
describe 'user field' do
subject { described_class.fields['user'] }
diff --git a/spec/helpers/markup_helper_spec.rb b/spec/helpers/markup_helper_spec.rb
index 00a59f037e0..e946857ac77 100644
--- a/spec/helpers/markup_helper_spec.rb
+++ b/spec/helpers/markup_helper_spec.rb
@@ -418,6 +418,13 @@ FooBar
describe '#markup' do
let(:content) { 'Noël' }
+ it 'sets the :text_source to :blob in the context' do
+ context = {}
+ helper.markup('foo.md', content, context)
+
+ expect(context).to include(text_source: :blob)
+ end
+
it 'preserves encoding' do
expect(content.encoding.name).to eq('UTF-8')
expect(helper.markup('foo.rst', content).encoding.name).to eq('UTF-8')
diff --git a/spec/initializers/lograge_spec.rb b/spec/initializers/lograge_spec.rb
index abb1673bb88..421f6373eff 100644
--- a/spec/initializers/lograge_spec.rb
+++ b/spec/initializers/lograge_spec.rb
@@ -173,6 +173,27 @@ RSpec.describe 'lograge', type: :request do
end
end
+ describe 'with access token in url' do
+ before do
+ event.payload[:location] = 'http://example.com/auth.html#access_token=secret_token&token_type=Bearer'
+ end
+
+ it 'strips location from sensitive information' do
+ subscriber.redirect_to(event)
+ subscriber.process_action(event)
+
+ expect(log_data['location']).not_to include('secret_token')
+ expect(log_data['location']).to include('filtered')
+ end
+
+ it 'leaves non-sensitive information from location' do
+ subscriber.redirect_to(event)
+ subscriber.process_action(event)
+
+ expect(log_data['location']).to include('&token_type=Bearer')
+ end
+ end
+
context 'with db payload' do
context 'when RequestStore is enabled', :request_store do
it 'includes db counters' do
diff --git a/spec/lib/banzai/filter/truncate_source_filter_spec.rb b/spec/lib/banzai/filter/truncate_source_filter_spec.rb
index d5eb8b738b1..8970aa1d382 100644
--- a/spec/lib/banzai/filter/truncate_source_filter_spec.rb
+++ b/spec/lib/banzai/filter/truncate_source_filter_spec.rb
@@ -8,24 +8,68 @@ RSpec.describe Banzai::Filter::TruncateSourceFilter do
let(:short_text) { 'foo' * 10 }
let(:long_text) { ([short_text] * 10).join(' ') }
- it 'does nothing when limit is unspecified' do
- output = filter(long_text)
-
- expect(output).to eq(long_text)
+ before do
+ stub_const("#{described_class}::CHARACTER_COUNT_LIMIT", 50)
+ stub_const("#{described_class}::USER_MSG_LIMIT", 20)
end
- it 'does nothing to a short-enough text' do
- output = filter(short_text, limit: short_text.bytesize)
+ context 'when markdown belongs to a blob' do
+ it 'does nothing when limit is unspecified' do
+ output = filter(long_text, text_source: :blob)
+
+ expect(output).to eq(long_text)
+ end
+
+ it 'truncates normally when limit specified' do
+ truncated = 'foofoof...'
+
+ output = filter(long_text, text_source: :blob, limit: 10)
- expect(output).to eq(short_text)
+ expect(output).to eq(truncated)
+ end
end
- it 'truncates UTF-8 text by bytes, on a character boundary' do
- utf8_text = '日本語の文字が大きい'
- truncated = '日...'
+ context 'when markdown belongs to a field (non-blob)' do
+ it 'does nothing when limit is greater' do
+ output = filter(long_text, limit: 1.megabyte)
+
+ expect(output).to eq(long_text)
+ end
+
+ it 'truncates to the default when limit is unspecified' do
+ stub_const("#{described_class}::USER_MSG_LIMIT", 200)
+ truncated = 'foofoofoofoofoofoofoofoofoofoo foofoofoofoofoof...'
+
+ output = filter(long_text)
+
+ expect(output).to eq(truncated)
+ end
+
+ it 'prepends the user message' do
+ truncated = <<~TEXT
+ _The text is longer than 50 characters and has been visually truncated._
+
+ foofoofoofoofoofoofoofoofoofoo foofoofoofoofoof...
+ TEXT
+
+ output = filter(long_text)
+
+ expect(output).to eq(truncated.strip)
+ end
+
+ it 'does nothing to a short-enough text' do
+ output = filter(short_text, limit: short_text.bytesize)
+
+ expect(output).to eq(short_text)
+ end
+
+ it 'truncates UTF-8 text by bytes, on a character boundary' do
+ utf8_text = '日本語の文字が大きい'
+ truncated = '日...'
- expect(filter(utf8_text, limit: truncated.bytesize)).to eq(truncated)
- expect(filter(utf8_text, limit: utf8_text.bytesize)).to eq(utf8_text)
- expect(filter(utf8_text, limit: utf8_text.mb_chars.size)).not_to eq(utf8_text)
+ expect(filter(utf8_text, limit: truncated.bytesize)).to eq(truncated)
+ expect(filter(utf8_text, limit: utf8_text.bytesize)).to eq(utf8_text)
+ expect(filter(utf8_text, limit: utf8_text.mb_chars.size)).not_to eq(utf8_text)
+ end
end
end
diff --git a/spec/lib/banzai/pipeline/post_process_pipeline_spec.rb b/spec/lib/banzai/pipeline/post_process_pipeline_spec.rb
index 5a3ceccfae1..e8df395564a 100644
--- a/spec/lib/banzai/pipeline/post_process_pipeline_spec.rb
+++ b/spec/lib/banzai/pipeline/post_process_pipeline_spec.rb
@@ -36,9 +36,11 @@ RSpec.describe Banzai::Pipeline::PostProcessPipeline do
end
let(:doc) { HTML::Pipeline.parse(html) }
+ let(:non_related_xpath_calls) { 2 }
it 'searches for attributes only once' do
- expect(doc).to receive(:search).once.and_call_original
+ expect(doc).to receive(:xpath).exactly(non_related_xpath_calls + 1).times
+ .and_call_original
subject
end
diff --git a/spec/lib/gitlab/auth/user_access_denied_reason_spec.rb b/spec/lib/gitlab/auth/user_access_denied_reason_spec.rb
index d3c6cde5590..102d6fba97f 100644
--- a/spec/lib/gitlab/auth/user_access_denied_reason_spec.rb
+++ b/spec/lib/gitlab/auth/user_access_denied_reason_spec.rb
@@ -57,5 +57,13 @@ RSpec.describe Gitlab::Auth::UserAccessDeniedReason do
it { is_expected.to eq('Your account is pending approval from your administrator and hence blocked.') }
end
+
+ context 'when the user has expired password' do
+ before do
+ user.update!(password_expires_at: 2.days.ago)
+ end
+
+ it { is_expected.to eq('Your password expired. Please access GitLab from a web browser to update your password.') }
+ end
end
end
diff --git a/spec/lib/gitlab/background_migration/update_users_where_two_factor_auth_required_from_group_spec.rb b/spec/lib/gitlab/background_migration/update_users_where_two_factor_auth_required_from_group_spec.rb
new file mode 100644
index 00000000000..e14328b6150
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/update_users_where_two_factor_auth_required_from_group_spec.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::UpdateUsersWhereTwoFactorAuthRequiredFromGroup, :migration, schema: 20210519154058 do
+ include MigrationHelpers::NamespacesHelpers
+
+ let(:group_with_2fa_parent) { create_namespace('parent', Gitlab::VisibilityLevel::PRIVATE, require_two_factor_authentication: true) }
+ let(:group_with_2fa_child) { create_namespace('child', Gitlab::VisibilityLevel::PRIVATE, parent_id: group_with_2fa_parent.id) }
+ let(:members_table) { table(:members) }
+ let(:users_table) { table(:users) }
+
+ subject { described_class.new }
+
+ describe '#perform' do
+ context 'with group members' do
+ let(:user_1) { create_user('user@example.com') }
+ let!(:member) { create_group_member(user_1, group_with_2fa_parent) }
+ let!(:user_without_group) { create_user('user_without@example.com') }
+ let(:user_other) { create_user('user_other@example.com') }
+ let!(:member_other) { create_group_member(user_other, group_with_2fa_parent) }
+
+ it 'updates user when user should be required to establish two factor authentication' do
+ subject.perform(user_1.id, user_without_group.id)
+
+ expect(user_1.reload.require_two_factor_authentication_from_group).to eq(true)
+ end
+
+ it 'does not update user who is not in current batch' do
+ subject.perform(user_1.id, user_without_group.id)
+
+ expect(user_other.reload.require_two_factor_authentication_from_group).to eq(false)
+ end
+
+ it 'updates all users in current batch' do
+ subject.perform(user_1.id, user_other.id)
+
+ expect(user_other.reload.require_two_factor_authentication_from_group).to eq(true)
+ end
+
+ it 'updates user when user is member of group in which parent group requires two factor authentication' do
+ member.destroy!
+
+ subgroup = create_namespace('subgroup', Gitlab::VisibilityLevel::PRIVATE, require_two_factor_authentication: false, parent_id: group_with_2fa_child.id)
+ create_group_member(user_1, subgroup)
+
+ subject.perform(user_1.id, user_other.id)
+
+ expect(user_1.reload.require_two_factor_authentication_from_group).to eq(true)
+ end
+
+ it 'updates user when user is member of a group and the subgroup requires two factor authentication' do
+ member.destroy!
+
+ parent = create_namespace('other_parent', Gitlab::VisibilityLevel::PRIVATE, require_two_factor_authentication: false)
+ create_namespace('other_subgroup', Gitlab::VisibilityLevel::PRIVATE, require_two_factor_authentication: true, parent_id: parent.id)
+ create_group_member(user_1, parent)
+
+ subject.perform(user_1.id, user_other.id)
+
+ expect(user_1.reload.require_two_factor_authentication_from_group).to eq(true)
+ end
+
+ it 'does not update user when not a member of a group that requires two factor authentication' do
+ member_other.destroy!
+
+ other_group = create_namespace('other_group', Gitlab::VisibilityLevel::PRIVATE, require_two_factor_authentication: false)
+ create_group_member(user_other, other_group)
+
+ subject.perform(user_1.id, user_other.id)
+
+ expect(user_other.reload.require_two_factor_authentication_from_group).to eq(false)
+ end
+ end
+ end
+
+ def create_user(email, require_2fa: false)
+ users_table.create!(email: email, projects_limit: 10, require_two_factor_authentication_from_group: require_2fa)
+ end
+
+ def create_group_member(user, group)
+ members_table.create!(user_id: user.id, source_id: group.id, access_level: GroupMember::MAINTAINER, source_type: "Namespace", type: "GroupMember", notification_level: 3)
+ end
+end
diff --git a/spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb
index 175b12637e6..ad89f1f5cda 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb
@@ -128,7 +128,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Environment do
context 'when environment has already been created' do
before do
- create(:environment, :staging, project: project, name: 'customer-portal')
+ create(:environment, project: project, name: 'customer-portal', tier: :staging)
end
it 'does not overwrite the specified deployment tier' do
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index ae9c697e0b9..3d6c04fd484 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -433,6 +433,13 @@ RSpec.describe Gitlab::GitAccess do
expect { pull_access_check }.to raise_forbidden("Your account has been deactivated by your administrator. Please log back in from a web browser to reactivate your account at #{Gitlab.config.gitlab.url}")
end
+ it 'disallows users with expired password to pull' do
+ project.add_maintainer(user)
+ user.update!(password_expires_at: 2.minutes.ago)
+
+ expect { pull_access_check }.to raise_forbidden("Your password expired. Please access GitLab from a web browser to update your password.")
+ end
+
context 'when the project repository does not exist' do
before do
project.add_guest(user)
@@ -969,6 +976,13 @@ RSpec.describe Gitlab::GitAccess do
expect { push_access_check }.to raise_forbidden("Your account has been deactivated by your administrator. Please log back in from a web browser to reactivate your account at #{Gitlab.config.gitlab.url}")
end
+ it 'disallows users with expired password to push' do
+ project.add_maintainer(user)
+ user.update!(password_expires_at: 2.minutes.ago)
+
+ expect { push_access_check }.to raise_forbidden("Your password expired. Please access GitLab from a web browser to update your password.")
+ end
+
it 'cleans up the files' do
expect(project.repository).to receive(:clean_stale_repository_files).and_call_original
expect { push_access_check }.not_to raise_error
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index c7e77a34582..dc7fedd6b4f 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -624,6 +624,7 @@ metrics_setting:
- project
protected_environments:
- project
+- group
- deploy_access_levels
deploy_access_levels:
- protected_environment
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index 0be1b09851f..2173bee6b4b 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -701,6 +701,7 @@ ProjectSetting:
ProtectedEnvironment:
- id
- project_id
+- group_id
- name
- created_at
- updated_at
diff --git a/spec/lib/gitlab/utils/nokogiri_spec.rb b/spec/lib/gitlab/utils/nokogiri_spec.rb
new file mode 100644
index 00000000000..90f137f53c8
--- /dev/null
+++ b/spec/lib/gitlab/utils/nokogiri_spec.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Utils::Nokogiri do
+ describe '#css_to_xpath' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:css, :xpath) do
+ 'img' | "descendant-or-self::img"
+ 'a.gfm' | "descendant-or-self::a[contains(concat(' ',normalize-space(@class),' '),' gfm ')]"
+ 'a:not(.gfm)' | "descendant-or-self::a[not(contains(concat(' ',normalize-space(@class),' '),' gfm '))]"
+ 'video, audio' | "descendant-or-self::video|descendant-or-self::audio"
+ '[data-math-style]' | "descendant-or-self::*[@data-math-style]"
+ '[data-mermaid-style]' | "descendant-or-self::*[@data-mermaid-style]"
+ '.js-render-metrics' | "descendant-or-self::*[contains(concat(' ',normalize-space(@class),' '),' js-render-metrics ')]"
+ 'h1, h2, h3, h4, h5, h6' | "descendant-or-self::h1|descendant-or-self::h2|descendant-or-self::h3|descendant-or-self::h4|descendant-or-self::h5|descendant-or-self::h6"
+ 'pre.code.language-math' | "descendant-or-self::pre[contains(concat(' ',normalize-space(@class),' '),' code ') and contains(concat(' ',normalize-space(@class),' '),' language-math ')]"
+ 'pre > code[lang="plantuml"]' | "descendant-or-self::pre/code[@lang=\"plantuml\"]"
+ 'pre[lang="mermaid"] > code' | "descendant-or-self::pre[@lang=\"mermaid\"]/code"
+ 'pre.language-suggestion' | "descendant-or-self::pre[contains(concat(' ',normalize-space(@class),' '),' language-suggestion ')]"
+ 'pre.language-suggestion > code' | "descendant-or-self::pre[contains(concat(' ',normalize-space(@class),' '),' language-suggestion ')]/code"
+ 'a.gfm[data-reference-type="user"]' | "descendant-or-self::a[contains(concat(' ',normalize-space(@class),' '),' gfm ') and @data-reference-type=\"user\"]"
+ 'a:not(.gfm), img:not(.gfm), video:not(.gfm), audio:not(.gfm)' | "descendant-or-self::a[not(contains(concat(' ',normalize-space(@class),' '),' gfm '))]|descendant-or-self::img[not(contains(concat(' ',normalize-space(@class),' '),' gfm '))]|descendant-or-self::video[not(contains(concat(' ',normalize-space(@class),' '),' gfm '))]|descendant-or-self::audio[not(contains(concat(' ',normalize-space(@class),' '),' gfm '))]"
+ 'pre:not([data-math-style]):not([data-mermaid-style]):not([data-kroki-style]) > code' | "descendant-or-self::pre[not(@data-math-style) and not(@data-mermaid-style) and not(@data-kroki-style)]/code"
+ end
+
+ with_them do
+ it 'generates the xpath' do
+ expect(described_class.css_to_xpath(css)).to eq xpath
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/utils_spec.rb b/spec/lib/gitlab/utils_spec.rb
index 11dba610faf..a7ccce0aaab 100644
--- a/spec/lib/gitlab/utils_spec.rb
+++ b/spec/lib/gitlab/utils_spec.rb
@@ -417,6 +417,29 @@ RSpec.describe Gitlab::Utils do
end
end
+ describe '.removes_sensitive_data_from_url' do
+ it 'returns string object' do
+ expect(described_class.removes_sensitive_data_from_url('http://gitlab.com')).to be_instance_of(String)
+ end
+
+ it 'returns nil when URI cannot be parsed' do
+ expect(described_class.removes_sensitive_data_from_url('://gitlab.com')).to be nil
+ end
+
+ it 'returns nil with invalid parameter' do
+ expect(described_class.removes_sensitive_data_from_url(1)).to be nil
+ end
+
+ it 'returns string with filtered access_token param' do
+ expect(described_class.removes_sensitive_data_from_url('http://gitlab.com/auth.html#access_token=secret_token')).to eq('http://gitlab.com/auth.html#access_token=filtered')
+ end
+
+ it 'returns string with filtered access_token param but other params preserved' do
+ expect(described_class.removes_sensitive_data_from_url('http://gitlab.com/auth.html#access_token=secret_token&token_type=Bearer&state=test'))
+ .to include('&token_type=Bearer', '&state=test')
+ end
+ end
+
describe 'multiple_key_invert' do
it 'invert keys with array values' do
hash = {
diff --git a/spec/lib/gitlab/x509/signature_spec.rb b/spec/lib/gitlab/x509/signature_spec.rb
index 2ac9c1f3a3b..7ba15faf910 100644
--- a/spec/lib/gitlab/x509/signature_spec.rb
+++ b/spec/lib/gitlab/x509/signature_spec.rb
@@ -12,20 +12,30 @@ RSpec.describe Gitlab::X509::Signature do
end
shared_examples "a verified signature" do
- it 'returns a verified signature if email does match' do
- signature = described_class.new(
+ let_it_be(:user) { create(:user, email: X509Helpers::User1.certificate_email) }
+
+ subject(:signature) do
+ described_class.new(
X509Helpers::User1.signed_commit_signature,
X509Helpers::User1.signed_commit_base_data,
X509Helpers::User1.certificate_email,
X509Helpers::User1.signed_commit_time
)
+ end
+ it 'returns a verified signature if email does match' do
expect(signature.x509_certificate).to have_attributes(certificate_attributes)
expect(signature.x509_certificate.x509_issuer).to have_attributes(issuer_attributes)
expect(signature.verified_signature).to be_truthy
expect(signature.verification_status).to eq(:verified)
end
+ it "returns an unverified signature if the email matches but isn't confirmed" do
+ user.update!(confirmed_at: nil)
+
+ expect(signature.verification_status).to eq(:unverified)
+ end
+
it 'returns an unverified signature if email does not match' do
signature = described_class.new(
X509Helpers::User1.signed_commit_signature,
@@ -55,13 +65,6 @@ RSpec.describe Gitlab::X509::Signature do
end
it 'returns an unverified signature if certificate is revoked' do
- signature = described_class.new(
- X509Helpers::User1.signed_commit_signature,
- X509Helpers::User1.signed_commit_base_data,
- X509Helpers::User1.certificate_email,
- X509Helpers::User1.signed_commit_time
- )
-
expect(signature.verification_status).to eq(:verified)
signature.x509_certificate.revoked!
@@ -253,23 +256,25 @@ RSpec.describe Gitlab::X509::Signature do
end
describe '#user' do
- signature = described_class.new(
- X509Helpers::User1.signed_tag_signature,
- X509Helpers::User1.signed_tag_base_data,
- X509Helpers::User1.certificate_email,
- X509Helpers::User1.signed_commit_time
- )
+ subject do
+ described_class.new(
+ X509Helpers::User1.signed_tag_signature,
+ X509Helpers::User1.signed_tag_base_data,
+ X509Helpers::User1.certificate_email,
+ X509Helpers::User1.signed_commit_time
+ ).user
+ end
context 'if email is assigned to a user' do
let!(:user) { create(:user, email: X509Helpers::User1.certificate_email) }
it 'returns user' do
- expect(signature.user).to eq(user)
+ is_expected.to eq(user)
end
end
it 'if email is not assigned to a user, return nil' do
- expect(signature.user).to be_nil
+ is_expected.to be_nil
end
end
@@ -292,6 +297,17 @@ RSpec.describe Gitlab::X509::Signature do
end
context 'verified signature' do
+ let_it_be(:user) { create(:user, email: X509Helpers::User1.certificate_email) }
+
+ subject(:signature) do
+ described_class.new(
+ X509Helpers::User1.signed_tag_signature,
+ X509Helpers::User1.signed_tag_base_data,
+ X509Helpers::User1.certificate_email,
+ X509Helpers::User1.signed_commit_time
+ )
+ end
+
context 'with trusted certificate store' do
before do
store = OpenSSL::X509::Store.new
@@ -301,19 +317,18 @@ RSpec.describe Gitlab::X509::Signature do
end
it 'returns a verified signature if email does match' do
- signature = described_class.new(
- X509Helpers::User1.signed_tag_signature,
- X509Helpers::User1.signed_tag_base_data,
- X509Helpers::User1.certificate_email,
- X509Helpers::User1.signed_commit_time
- )
-
expect(signature.x509_certificate).to have_attributes(certificate_attributes)
expect(signature.x509_certificate.x509_issuer).to have_attributes(issuer_attributes)
expect(signature.verified_signature).to be_truthy
expect(signature.verification_status).to eq(:verified)
end
+ it "returns an unverified signature if the email matches but isn't confirmed" do
+ user.update!(confirmed_at: nil)
+
+ expect(signature.verification_status).to eq(:unverified)
+ end
+
it 'returns an unverified signature if email does not match' do
signature = described_class.new(
X509Helpers::User1.signed_tag_signature,
@@ -343,13 +358,6 @@ RSpec.describe Gitlab::X509::Signature do
end
it 'returns an unverified signature if certificate is revoked' do
- signature = described_class.new(
- X509Helpers::User1.signed_tag_signature,
- X509Helpers::User1.signed_tag_base_data,
- X509Helpers::User1.certificate_email,
- X509Helpers::User1.signed_commit_time
- )
-
expect(signature.verification_status).to eq(:verified)
signature.x509_certificate.revoked!
@@ -368,13 +376,6 @@ RSpec.describe Gitlab::X509::Signature do
end
it 'returns an unverified signature' do
- signature = described_class.new(
- X509Helpers::User1.signed_tag_signature,
- X509Helpers::User1.signed_tag_base_data,
- X509Helpers::User1.certificate_email,
- X509Helpers::User1.signed_commit_time
- )
-
expect(signature.x509_certificate).to have_attributes(certificate_attributes)
expect(signature.x509_certificate.x509_issuer).to have_attributes(issuer_attributes)
expect(signature.verified_signature).to be_falsey
diff --git a/spec/migrations/20210601080039_group_protected_environments_add_index_and_constraint_spec.rb b/spec/migrations/20210601080039_group_protected_environments_add_index_and_constraint_spec.rb
new file mode 100644
index 00000000000..d3154596b26
--- /dev/null
+++ b/spec/migrations/20210601080039_group_protected_environments_add_index_and_constraint_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!('group_protected_environments_add_index_and_constraint')
+
+RSpec.describe GroupProtectedEnvironmentsAddIndexAndConstraint do
+ let(:migration) { described_class.new }
+ let(:protected_environments) { table(:protected_environments) }
+ let(:group) { table(:namespaces).create!(name: 'group', path: 'group') }
+ let(:project) { table(:projects).create!(name: 'project', path: 'project', namespace_id: group.id) }
+
+ describe '#down' do
+ it 'deletes only group-level configurations' do
+ migration.up
+
+ project_protections = [
+ protected_environments.create!(project_id: project.id, name: 'production'),
+ protected_environments.create!(project_id: project.id, name: 'staging')
+ ]
+ protected_environments.create!(group_id: group.id, name: 'production')
+ protected_environments.create!(group_id: group.id, name: 'staging')
+
+ migration.down
+
+ expect(protected_environments.pluck(:id))
+ .to match_array project_protections.map(&:id)
+ end
+ end
+end
diff --git a/spec/migrations/backfill_escalation_policies_for_oncall_schedules_spec.rb b/spec/migrations/backfill_escalation_policies_for_oncall_schedules_spec.rb
new file mode 100644
index 00000000000..86415b1520e
--- /dev/null
+++ b/spec/migrations/backfill_escalation_policies_for_oncall_schedules_spec.rb
@@ -0,0 +1,100 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20210519220019_backfill_escalation_policies_for_oncall_schedules.rb')
+
+RSpec.describe BackfillEscalationPoliciesForOncallSchedules do
+ let_it_be(:projects) { table(:projects) }
+ let_it_be(:schedules) { table(:incident_management_oncall_schedules) }
+ let_it_be(:policies) { table(:incident_management_escalation_policies) }
+ let_it_be(:rules) { table(:incident_management_escalation_rules) }
+
+ # Project with no schedules
+ let_it_be(:namespace) { table(:namespaces).create!(name: 'gitlab', path: 'gitlab') }
+ let_it_be(:project_a) { projects.create!(namespace_id: namespace.id) }
+
+ context 'with backfill-able schedules' do
+ # Project with one schedule
+ let_it_be(:project_b) { projects.create!(namespace_id: namespace.id) }
+ let_it_be(:schedule_b1) { schedules.create!(project_id: project_b.id, iid: 1, name: 'Schedule B1') }
+
+ # Project with multiple schedules
+ let_it_be(:project_c) { projects.create!(namespace_id: namespace.id) }
+ let_it_be(:schedule_c1) { schedules.create!(project_id: project_c.id, iid: 1, name: 'Schedule C1') }
+ let_it_be(:schedule_c2) { schedules.create!(project_id: project_c.id, iid: 2, name: 'Schedule C2') }
+
+ # Project with a single schedule which already has a policy
+ let_it_be(:project_d) { projects.create!(namespace_id: namespace.id) }
+ let_it_be(:schedule_d1) { schedules.create!(project_id: project_d.id, iid: 1, name: 'Schedule D1') }
+ let_it_be(:policy_d1) { policies.create!(project_id: project_d.id, name: 'Policy D1') }
+ let_it_be(:rule_d1) { rules.create!(policy_id: policy_d1.id, oncall_schedule_id: schedule_d1.id, status: 2, elapsed_time_seconds: 60) }
+
+ # Project with a multiple schedule, one of which already has a policy
+ let_it_be(:project_e) { projects.create!(namespace_id: namespace.id) }
+ let_it_be(:schedule_e1) { schedules.create!(project_id: project_e.id, iid: 1, name: 'Schedule E1') }
+ let_it_be(:schedule_e2) { schedules.create!(project_id: project_e.id, iid: 2, name: 'Schedule E2') }
+ let_it_be(:policy_e1) { policies.create!(project_id: project_e.id, name: 'Policy E1') }
+ let_it_be(:rule_e1) { rules.create!(policy_id: policy_e1.id, oncall_schedule_id: schedule_e2.id, status: 2, elapsed_time_seconds: 60) }
+
+ # Project with a multiple schedule, with multiple policies
+ let_it_be(:project_f) { projects.create!(namespace_id: namespace.id) }
+ let_it_be(:schedule_f1) { schedules.create!(project_id: project_f.id, iid: 1, name: 'Schedule F1') }
+ let_it_be(:schedule_f2) { schedules.create!(project_id: project_f.id, iid: 2, name: 'Schedule F2') }
+ let_it_be(:policy_f1) { policies.create!(project_id: project_f.id, name: 'Policy F1') }
+ let_it_be(:rule_f1) { rules.create!(policy_id: policy_f1.id, oncall_schedule_id: schedule_f1.id, status: 2, elapsed_time_seconds: 60) }
+ let_it_be(:rule_f2) { rules.create!(policy_id: policy_f1.id, oncall_schedule_id: schedule_f2.id, status: 2, elapsed_time_seconds: 60) }
+ let_it_be(:policy_f2) { policies.create!(project_id: project_f.id, name: 'Policy F2') }
+ let_it_be(:rule_f3) { rules.create!(policy_id: policy_f2.id, oncall_schedule_id: schedule_f2.id, status: 1, elapsed_time_seconds: 10) }
+
+ it 'backfills escalation policies correctly' do
+ expect { migrate! }
+ .to change(policies, :count).by(2)
+ .and change(rules, :count).by(3)
+
+ new_policy_b1, new_policy_c1 = new_polices = policies.last(2)
+ new_rules = rules.last(3)
+
+ expect(new_polices).to all have_attributes(name: 'On-call Escalation Policy')
+ expect(new_policy_b1.description).to eq('Immediately notify Schedule B1')
+ expect(new_policy_c1.description).to eq('Immediately notify Schedule C1')
+ expect(policies.pluck(:project_id)).to eq([
+ project_d.id,
+ project_e.id,
+ project_f.id,
+ project_f.id,
+ project_b.id,
+ project_c.id
+ ])
+
+ expect(new_rules).to all have_attributes(status: 1, elapsed_time_seconds: 0)
+ expect(rules.pluck(:policy_id)).to eq([
+ rule_d1.policy_id,
+ rule_e1.policy_id,
+ rule_f1.policy_id,
+ rule_f2.policy_id,
+ rule_f3.policy_id,
+ new_policy_b1.id,
+ new_policy_c1.id,
+ new_policy_c1.id
+ ])
+ expect(rules.pluck(:oncall_schedule_id)).to eq([
+ rule_d1.oncall_schedule_id,
+ rule_e1.oncall_schedule_id,
+ rule_f1.oncall_schedule_id,
+ rule_f2.oncall_schedule_id,
+ rule_f3.oncall_schedule_id,
+ schedule_b1.id,
+ schedule_c1.id,
+ schedule_c2.id
+ ])
+ end
+ end
+
+ context 'with no schedules' do
+ it 'does nothing' do
+ expect { migrate! }
+ .to not_change(policies, :count)
+ .and not_change(rules, :count)
+ end
+ end
+end
diff --git a/spec/migrations/schedule_update_users_where_two_factor_auth_required_from_group_spec.rb b/spec/migrations/schedule_update_users_where_two_factor_auth_required_from_group_spec.rb
new file mode 100644
index 00000000000..cec141cacc9
--- /dev/null
+++ b/spec/migrations/schedule_update_users_where_two_factor_auth_required_from_group_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require Rails.root.join('db', 'migrate', '20210519154058_schedule_update_users_where_two_factor_auth_required_from_group.rb')
+
+RSpec.describe ScheduleUpdateUsersWhereTwoFactorAuthRequiredFromGroup do
+ let(:users) { table(:users) }
+ let!(:user_1) { users.create!(require_two_factor_authentication_from_group: false, name: "user1", email: "user1@example.com", projects_limit: 1) }
+ let!(:user_2) { users.create!(require_two_factor_authentication_from_group: true, name: "user2", email: "user2@example.com", projects_limit: 1) }
+ let!(:user_3) { users.create!(require_two_factor_authentication_from_group: false, name: "user3", email: "user3@example.com", projects_limit: 1) }
+
+ before do
+ stub_const("#{described_class.name}::BATCH_SIZE", 1)
+ end
+
+ it 'schedules jobs for users that do not require two factor authentication' do
+ Sidekiq::Testing.fake! do
+ freeze_time do
+ migrate!
+
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(
+ 2.minutes, user_1.id, user_1.id)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(
+ 4.minutes, user_3.id, user_3.id)
+ expect(BackgroundMigrationWorker.jobs.size).to eq(2)
+ end
+ end
+ end
+end
diff --git a/spec/models/concerns/has_timelogs_report_spec.rb b/spec/models/concerns/has_timelogs_report_spec.rb
deleted file mode 100644
index f0dca47fae1..00000000000
--- a/spec/models/concerns/has_timelogs_report_spec.rb
+++ /dev/null
@@ -1,55 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe HasTimelogsReport do
- let_it_be(:user) { create(:user) }
-
- let(:group) { create(:group) }
- let(:project) { create(:project, :public, group: group) }
- let(:issue1) { create(:issue, project: project) }
- let(:merge_request1) { create(:merge_request, source_project: project) }
-
- describe '#timelogs' do
- let_it_be(:start_time) { 20.days.ago }
- let_it_be(:end_time) { 8.days.ago }
-
- let!(:timelog1) { create_timelog(15.days.ago, issue: issue1) }
- let!(:timelog2) { create_timelog(10.days.ago, merge_request: merge_request1) }
- let!(:timelog3) { create_timelog(5.days.ago, issue: issue1) }
-
- before do
- group.add_developer(user)
- end
-
- it 'returns collection of timelogs between given times' do
- expect(group.timelogs(start_time, end_time).to_a).to match_array([timelog1, timelog2])
- end
-
- it 'returns empty collection if times are not present' do
- expect(group.timelogs(nil, nil)).to be_empty
- end
-
- it 'returns empty collection if time range is invalid' do
- expect(group.timelogs(end_time, start_time)).to be_empty
- end
- end
-
- describe '#user_can_access_group_timelogs?' do
- it 'returns true if user can access group timelogs' do
- group.add_developer(user)
-
- expect(group).to be_user_can_access_group_timelogs(user)
- end
-
- it 'returns false if user has insufficient permissions' do
- group.add_guest(user)
-
- expect(group).not_to be_user_can_access_group_timelogs(user)
- end
- end
-
- def create_timelog(time, issue: nil, merge_request: nil)
- create(:timelog, issue: issue, merge_request: merge_request, user: user, spent_at: time)
- end
-end
diff --git a/spec/models/timelog_spec.rb b/spec/models/timelog_spec.rb
index c3432907112..bc042f7a639 100644
--- a/spec/models/timelog_spec.rb
+++ b/spec/models/timelog_spec.rb
@@ -64,25 +64,57 @@ RSpec.describe Timelog do
let_it_be(:subgroup_issue) { create(:issue, project: subgroup_project) }
let_it_be(:subgroup_merge_request) { create(:merge_request, source_project: subgroup_project) }
- let_it_be(:timelog) { create(:issue_timelog, spent_at: 65.days.ago) }
- let_it_be(:timelog1) { create(:issue_timelog, spent_at: 15.days.ago, issue: group_issue) }
- let_it_be(:timelog2) { create(:issue_timelog, spent_at: 5.days.ago, issue: subgroup_issue) }
- let_it_be(:timelog3) { create(:merge_request_timelog, spent_at: 65.days.ago) }
- let_it_be(:timelog4) { create(:merge_request_timelog, spent_at: 15.days.ago, merge_request: group_merge_request) }
- let_it_be(:timelog5) { create(:merge_request_timelog, spent_at: 5.days.ago, merge_request: subgroup_merge_request) }
-
- describe 'in_group' do
+ let_it_be(:short_time_ago) { 5.days.ago }
+ let_it_be(:medium_time_ago) { 15.days.ago }
+ let_it_be(:long_time_ago) { 65.days.ago }
+
+ let_it_be(:timelog) { create(:issue_timelog, spent_at: long_time_ago) }
+ let_it_be(:timelog1) { create(:issue_timelog, spent_at: medium_time_ago, issue: group_issue) }
+ let_it_be(:timelog2) { create(:issue_timelog, spent_at: short_time_ago, issue: subgroup_issue) }
+ let_it_be(:timelog3) { create(:merge_request_timelog, spent_at: long_time_ago) }
+ let_it_be(:timelog4) { create(:merge_request_timelog, spent_at: medium_time_ago, merge_request: group_merge_request) }
+ let_it_be(:timelog5) { create(:merge_request_timelog, spent_at: short_time_ago, merge_request: subgroup_merge_request) }
+
+ describe '.in_group' do
it 'return timelogs created for group issues and merge requests' do
expect(described_class.in_group(group)).to contain_exactly(timelog1, timelog2, timelog4, timelog5)
end
end
- describe 'between_times' do
- it 'returns collection of timelogs within given times' do
- timelogs = described_class.between_times(20.days.ago, 10.days.ago)
+ describe '.at_or_after' do
+ it 'returns timelogs at the time limit' do
+ timelogs = described_class.at_or_after(short_time_ago)
- expect(timelogs).to contain_exactly(timelog1, timelog4)
+ expect(timelogs).to contain_exactly(timelog2, timelog5)
end
+
+ it 'returns timelogs after given time' do
+ timelogs = described_class.at_or_after(just_before(short_time_ago))
+
+ expect(timelogs).to contain_exactly(timelog2, timelog5)
+ end
+ end
+
+ describe '.at_or_before' do
+ it 'returns timelogs at the time limit' do
+ timelogs = described_class.at_or_before(long_time_ago)
+
+ expect(timelogs).to contain_exactly(timelog, timelog3)
+ end
+
+ it 'returns timelogs before given time' do
+ timelogs = described_class.at_or_before(just_after(long_time_ago))
+
+ expect(timelogs).to contain_exactly(timelog, timelog3)
+ end
+ end
+
+ def just_before(time)
+ time - 1.day
+ end
+
+ def just_after(time)
+ time + 1.day
end
end
end
diff --git a/spec/policies/global_policy_spec.rb b/spec/policies/global_policy_spec.rb
index e677f5558fd..bbbc5d08c07 100644
--- a/spec/policies/global_policy_spec.rb
+++ b/spec/policies/global_policy_spec.rb
@@ -239,6 +239,14 @@ RSpec.describe GlobalPolicy do
it { is_expected.not_to be_allowed(:access_api) }
end
+ context 'user with expired password' do
+ before do
+ current_user.update!(password_expires_at: 2.minutes.ago)
+ end
+
+ it { is_expected.not_to be_allowed(:access_api) }
+ end
+
context 'when terms are enforced' do
before do
enforce_terms
@@ -418,6 +426,14 @@ RSpec.describe GlobalPolicy do
it { is_expected.not_to be_allowed(:access_git) }
end
+
+ context 'user with expired password' do
+ before do
+ current_user.update!(password_expires_at: 2.minutes.ago)
+ end
+
+ it { is_expected.not_to be_allowed(:access_git) }
+ end
end
describe 'read instance metadata' do
@@ -494,6 +510,14 @@ RSpec.describe GlobalPolicy do
it { is_expected.not_to be_allowed(:use_slash_commands) }
end
+
+ context 'user with expired password' do
+ before do
+ current_user.update!(password_expires_at: 2.minutes.ago)
+ end
+
+ it { is_expected.not_to be_allowed(:use_slash_commands) }
+ end
end
describe 'create_snippet' do
diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb
index f5e389ff338..ee87a2da189 100644
--- a/spec/policies/group_policy_spec.rb
+++ b/spec/policies/group_policy_spec.rb
@@ -923,54 +923,4 @@ RSpec.describe GroupPolicy do
it { expect(described_class.new(current_user, subgroup)).to be_allowed(:read_label) }
end
end
-
- context 'timelogs' do
- context 'with admin' do
- let(:current_user) { admin }
-
- context 'when admin mode is enabled', :enable_admin_mode do
- it { is_expected.to be_allowed(:read_group_timelogs) }
- end
-
- context 'when admin mode is disabled' do
- it { is_expected.to be_disallowed(:read_group_timelogs) }
- end
- end
-
- context 'with owner' do
- let(:current_user) { owner }
-
- it { is_expected.to be_allowed(:read_group_timelogs) }
- end
-
- context 'with maintainer' do
- let(:current_user) { maintainer }
-
- it { is_expected.to be_allowed(:read_group_timelogs) }
- end
-
- context 'with reporter' do
- let(:current_user) { reporter }
-
- it { is_expected.to be_allowed(:read_group_timelogs) }
- end
-
- context 'with guest' do
- let(:current_user) { guest }
-
- it { is_expected.to be_disallowed(:read_group_timelogs) }
- end
-
- context 'with non member' do
- let(:current_user) { create(:user) }
-
- it { is_expected.to be_disallowed(:read_group_timelogs) }
- end
-
- context 'with anonymous' do
- let(:current_user) { nil }
-
- it { is_expected.to be_disallowed(:read_group_timelogs) }
- end
- end
end
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index 46da42a4787..d0abcfbd091 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -1385,54 +1385,4 @@ RSpec.describe ProjectPolicy do
end
end
end
-
- context 'timelogs' do
- context 'with admin' do
- let(:current_user) { admin }
-
- context 'when admin mode enabled', :enable_admin_mode do
- it { is_expected.to be_allowed(:read_group_timelogs) }
- end
-
- context 'when admin mode disabled' do
- it { is_expected.to be_disallowed(:read_group_timelogs) }
- end
- end
-
- context 'with owner' do
- let(:current_user) { owner }
-
- it { is_expected.to be_allowed(:read_group_timelogs) }
- end
-
- context 'with maintainer' do
- let(:current_user) { maintainer }
-
- it { is_expected.to be_allowed(:read_group_timelogs) }
- end
-
- context 'with reporter' do
- let(:current_user) { reporter }
-
- it { is_expected.to be_allowed(:read_group_timelogs) }
- end
-
- context 'with guest' do
- let(:current_user) { guest }
-
- it { is_expected.to be_disallowed(:read_group_timelogs) }
- end
-
- context 'with non member' do
- let(:current_user) { non_member }
-
- it { is_expected.to be_disallowed(:read_group_timelogs) }
- end
-
- context 'with anonymous' do
- let(:current_user) { anonymous }
-
- it { is_expected.to be_disallowed(:read_group_timelogs) }
- end
- end
end
diff --git a/spec/requests/api/graphql/group/timelogs_spec.rb b/spec/requests/api/graphql/group/timelogs_spec.rb
index 6e21a73afa9..05b6ee3ff89 100644
--- a/spec/requests/api/graphql/group/timelogs_spec.rb
+++ b/spec/requests/api/graphql/group/timelogs_spec.rb
@@ -17,8 +17,37 @@ RSpec.describe 'Timelogs through GroupQuery' do
let(:timelogs_data) { graphql_data['group']['timelogs']['nodes'] }
- before do
- group.add_developer(user)
+ context 'when the project is private' do
+ let_it_be(:group2) { create(:group) }
+ let_it_be(:project2) { create(:project, :private, group: group2) }
+ let_it_be(:issue2) { create(:issue, project: project2) }
+ let_it_be(:timelog3) { create(:timelog, issue: issue2, spent_at: '2019-08-13 14:00:00') }
+
+ subject { post_graphql(query(full_path: group2.full_path), current_user: user) }
+
+ context 'when the user is not a member of the project' do
+ it 'returns no timelogs' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(graphql_errors).to be_nil
+ expect(timelog_array.size).to eq 0
+ end
+ end
+
+ context 'when the user is a member of the project' do
+ before do
+ project2.add_developer(user)
+ end
+
+ it 'returns timelogs' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(graphql_errors).to be_nil
+ expect(timelog_array.size).to eq 1
+ end
+ end
end
context 'when the request is correct' do
@@ -74,18 +103,6 @@ RSpec.describe 'Timelogs through GroupQuery' do
expect(timelogs_data).to be_empty
end
end
-
- context 'when user has no permission to read group timelogs' do
- it 'returns empty result' do
- guest = create(:user)
- group.add_guest(guest)
- post_graphql(query, current_user: guest)
-
- expect(response).to have_gitlab_http_status(:success)
- expect(graphql_errors).to be_nil
- expect(timelogs_data).to be_empty
- end
- end
end
end
@@ -95,7 +112,7 @@ RSpec.describe 'Timelogs through GroupQuery' do
end
end
- def query(timelog_params = params)
+ def query(timelog_params: params, full_path: group.full_path)
timelog_nodes = <<~NODE
nodes {
spentAt
@@ -114,7 +131,7 @@ RSpec.describe 'Timelogs through GroupQuery' do
graphql_query_for(
:group,
- { full_path: group.full_path },
+ { full_path: full_path },
query_graphql_field(:timelogs, timelog_params, timelog_nodes)
)
end
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index a1e28c18769..279c65fc2f4 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -35,6 +35,26 @@ RSpec.describe 'Git HTTP requests' do
expect(response.header['WWW-Authenticate']).to start_with('Basic ')
end
end
+
+ context "when password is expired" do
+ it "responds to downloads with status 401 Unauthorized" do
+ user.update!(password_expires_at: 2.days.ago)
+
+ download(path, user: user.username, password: user.password) do |response|
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
+ end
+
+ context "when user is blocked" do
+ let(:user) { create(:user, :blocked) }
+
+ it "responds to downloads with status 401 Unauthorized" do
+ download(path, user: user.username, password: user.password) do |response|
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
+ end
end
context "when authentication succeeds" do
@@ -75,6 +95,15 @@ RSpec.describe 'Git HTTP requests' do
expect(response.header['WWW-Authenticate']).to start_with('Basic ')
end
end
+
+ context "when password is expired" do
+ it "responds to uploads with status 401 Unauthorized" do
+ user.update!(password_expires_at: 2.days.ago)
+ upload(path, user: user.username, password: user.password) do |response|
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
+ end
end
context "when authentication succeeds" do
@@ -576,6 +605,16 @@ RSpec.describe 'Git HTTP requests' do
it_behaves_like 'pulls are allowed'
it_behaves_like 'pushes are allowed'
+
+ context "when password is expired" do
+ it "responds to downloads with status 401 unauthorized" do
+ user.update!(password_expires_at: 2.days.ago)
+
+ download(path, **env) do |response|
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
+ end
end
context 'when user has 2FA enabled' do
@@ -649,6 +688,18 @@ RSpec.describe 'Git HTTP requests' do
expect(response).to have_gitlab_http_status(:ok)
end
end
+
+ context "when password is expired" do
+ it "responds to uploads with status 401 unauthorized" do
+ user.update!(password_expires_at: 2.days.ago)
+
+ write_access_token = create(:personal_access_token, user: user, scopes: [:write_repository])
+
+ upload(path, user: user.username, password: write_access_token.token) do |response|
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
+ end
end
end
@@ -860,6 +911,16 @@ RSpec.describe 'Git HTTP requests' do
expect(response).to have_gitlab_http_status(:not_found)
end
+
+ context 'when users password is expired' do
+ it 'rejects pulls with 401 unauthorized' do
+ user.update!(password_expires_at: 2.days.ago)
+
+ download(path, user: 'gitlab-ci-token', password: build.token) do |response|
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
+ end
end
end
end
diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb
index 4e18c9cb4ca..0e3a0252638 100644
--- a/spec/requests/lfs_http_spec.rb
+++ b/spec/requests/lfs_http_spec.rb
@@ -346,9 +346,7 @@ RSpec.describe 'Git LFS API and storage' do
let_it_be(:user) { create(:user, password_expires_at: 1.minute.ago)}
let(:role) { :reporter}
- # TODO: This should return a 404 response
- # https://gitlab.com/gitlab-org/gitlab/-/issues/292006
- it_behaves_like 'LFS http 200 response'
+ it_behaves_like 'LFS http 401 response'
end
context 'when user is blocked' do
diff --git a/spec/tasks/gitlab/x509/update_rake_spec.rb b/spec/tasks/gitlab/x509/update_rake_spec.rb
index 93e97ab38ad..b166e73935a 100644
--- a/spec/tasks/gitlab/x509/update_rake_spec.rb
+++ b/spec/tasks/gitlab/x509/update_rake_spec.rb
@@ -8,12 +8,13 @@ RSpec.describe 'gitlab:x509 namespace rake task' do
end
describe 'update_signatures' do
- subject { run_rake_task('gitlab:x509:update_signatures') }
-
- let(:project) { create :project, :repository, path: X509Helpers::User1.path }
+ let(:user) { create(:user, email: X509Helpers::User1.certificate_email) }
+ let(:project) { create(:project, :repository, path: X509Helpers::User1.path, creator: user) }
let(:x509_signed_commit) { project.commit_by(oid: '189a6c924013fc3fe40d6f1ec1dc20214183bc97') }
let(:x509_commit) { Gitlab::X509::Commit.new(x509_signed_commit).signature }
+ subject { run_rake_task('gitlab:x509:update_signatures') }
+
it 'changes from unverified to verified if the certificate store contains the root certificate' do
x509_commit
@@ -22,21 +23,14 @@ RSpec.describe 'gitlab:x509 namespace rake task' do
store.add_cert(certificate)
allow(OpenSSL::X509::Store).to receive(:new).and_return(store)
- expect(x509_commit.verification_status).to eq('unverified')
expect_any_instance_of(Gitlab::X509::Commit).to receive(:update_signature!).and_call_original
-
- subject
-
- x509_commit.reload
- expect(x509_commit.verification_status).to eq('verified')
+ expect { subject }.to change { x509_commit.reload.verification_status }.from('unverified').to('verified')
end
it 'returns if no signature is available' do
- expect_any_instance_of(Gitlab::X509::Commit) do |x509_commit|
- expect(x509_commit).not_to receive(:update_signature!)
+ expect_any_instance_of(Gitlab::X509::Commit).not_to receive(:update_signature!)
- subject
- end
+ subject
end
end
end