diff options
Diffstat (limited to 'spec')
12 files changed, 303 insertions, 26 deletions
diff --git a/spec/channels/application_cable/connection_spec.rb b/spec/channels/application_cable/connection_spec.rb index affde0095cf..f5b2cdd2fca 100644 --- a/spec/channels/application_cable/connection_spec.rb +++ b/spec/channels/application_cable/connection_spec.rb @@ -12,7 +12,7 @@ RSpec.describe ApplicationCable::Connection, :clean_gitlab_redis_sessions do context 'when user is logged in' do let(:user) { create(:user) } - let(:session_hash) { { 'warden.user.user.key' => [[user.id], user.encrypted_password[0, 29]] } } + let(:session_hash) { { 'warden.user.user.key' => [[user.id], user.authenticatable_salt] } } it 'sets current_user' do connect @@ -21,7 +21,7 @@ RSpec.describe ApplicationCable::Connection, :clean_gitlab_redis_sessions do end context 'with a stale password' do - let(:partial_password_hash) { build(:user, password: 'some_old_password').encrypted_password[0, 29] } + let(:partial_password_hash) { build(:user, password: 'some_old_password').authenticatable_salt } let(:session_hash) { { 'warden.user.user.key' => [[user.id], partial_password_hash] } } it 'sets current_user to nil' do diff --git a/spec/features/admin/admin_system_info_spec.rb b/spec/features/admin/admin_system_info_spec.rb index 2225f25aa1e..8ff31dfded7 100644 --- a/spec/features/admin/admin_system_info_spec.rb +++ b/spec/features/admin/admin_system_info_spec.rb @@ -24,7 +24,7 @@ RSpec.describe 'Admin System Info' do expect(page).to have_content 'CPU 2 cores' expect(page).to have_content 'Memory Usage 4 GB / 16 GB' expect(page).to have_content 'Disk Usage' - expect(page).to have_content 'Uptime' + expect(page).to have_content 'System started' end end @@ -39,7 +39,7 @@ RSpec.describe 'Admin System Info' do expect(page).to have_content 'CPU Unable to collect CPU info' expect(page).to have_content 'Memory Usage 4 GB / 16 GB' expect(page).to have_content 'Disk Usage' - expect(page).to have_content 'Uptime' + expect(page).to have_content 'System started' end end @@ -54,7 +54,7 @@ RSpec.describe 'Admin System Info' do expect(page).to have_content 'CPU 2 cores' expect(page).to have_content 'Memory Usage Unable to collect memory info' expect(page).to have_content 'Disk Usage' - expect(page).to have_content 'Uptime' + expect(page).to have_content 'System started' end end end diff --git a/spec/frontend/vue_shared/components/color_select_dropdown/color_select_root_spec.js b/spec/frontend/vue_shared/components/color_select_dropdown/color_select_root_spec.js index 4c69a7f1e46..441e21ee905 100644 --- a/spec/frontend/vue_shared/components/color_select_dropdown/color_select_root_spec.js +++ b/spec/frontend/vue_shared/components/color_select_dropdown/color_select_root_spec.js @@ -135,7 +135,7 @@ describe('LabelsSelectRoot', () => { it('handles DropdownContents setColor', () => { findDropdownContents().vm.$emit('setColor', color); - expect(wrapper.emitted('updateSelectedColor')).toEqual([[color]]); + expect(wrapper.emitted('updateSelectedColor')).toEqual([[{ color }]]); }); }); @@ -157,7 +157,7 @@ describe('LabelsSelectRoot', () => { createComponent({ propsData: { iid: undefined } }); findDropdownContents().vm.$emit('setColor', color); - expect(wrapper.emitted('updateSelectedColor')).toEqual([[color]]); + expect(wrapper.emitted('updateSelectedColor')).toEqual([[{ color }]]); }); describe('when updating color for epic', () => { diff --git a/spec/frontend/work_items/mock_data.js b/spec/frontend/work_items/mock_data.js index 3df30971aef..f33d884144b 100644 --- a/spec/frontend/work_items/mock_data.js +++ b/spec/frontend/work_items/mock_data.js @@ -112,6 +112,7 @@ export const workItemResponseFactory = ({ canUpdate = false, allowsMultipleAssignees = true, assigneesWidgetPresent = true, + parent = null, } = {}) => ({ data: { workItem: { @@ -150,11 +151,6 @@ export const workItemResponseFactory = ({ { __typename: 'WorkItemWidgetHierarchy', type: 'HIERARCHY', - parent: { - id: 'gid://gitlab/Issue/1', - iid: '5', - title: 'Parent title', - }, children: { edges: [ { @@ -164,6 +160,7 @@ export const workItemResponseFactory = ({ }, ], }, + parent, }, ], }, @@ -535,3 +532,11 @@ export const projectLabelsResponse = { }, }, }; + +export const mockParent = { + parent: { + id: 'gid://gitlab/Issue/1', + iid: '5', + title: 'Parent title', + }, +}; diff --git a/spec/frontend/work_items/pages/work_item_detail_spec.js b/spec/frontend/work_items/pages/work_item_detail_spec.js index 4373f576250..dd19ecd3391 100644 --- a/spec/frontend/work_items/pages/work_item_detail_spec.js +++ b/spec/frontend/work_items/pages/work_item_detail_spec.js @@ -1,4 +1,4 @@ -import { GlAlert, GlSkeletonLoader } from '@gitlab/ui'; +import { GlAlert, GlSkeletonLoader, GlButton } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import Vue from 'vue'; import VueApollo from 'vue-apollo'; @@ -15,7 +15,11 @@ import { i18n } from '~/work_items/constants'; import workItemQuery from '~/work_items/graphql/work_item.query.graphql'; import workItemTitleSubscription from '~/work_items/graphql/work_item_title.subscription.graphql'; import { temporaryConfig } from '~/work_items/graphql/provider'; -import { workItemTitleSubscriptionResponse, workItemResponseFactory } from '../mock_data'; +import { + workItemTitleSubscriptionResponse, + workItemResponseFactory, + mockParent, +} from '../mock_data'; describe('WorkItemDetail component', () => { let wrapper; @@ -35,6 +39,7 @@ describe('WorkItemDetail component', () => { const findWorkItemLabels = () => wrapper.findComponent(WorkItemLabels); const findWorkItemWeight = () => wrapper.findComponent(WorkItemWeight); const findParent = () => wrapper.find('[data-testid="work-item-parent"]'); + const findParentButton = () => findParent().findComponent(GlButton); const findCloseButton = () => wrapper.find('[data-testid="work-item-close"]'); const createComponent = ({ @@ -150,18 +155,35 @@ describe('WorkItemDetail component', () => { }); describe('secondary breadcrumbs', () => { - it('does not show secondary breadcrumbs if there is no parent', () => { + it('does not show secondary breadcrumbs by default', () => { createComponent(); expect(findParent().exists()).toBe(false); }); - it('shows secondary breadcrumbs if there is a parent', async () => { + it('does not show secondary breadcrumbs if there is not a parent', async () => { createComponent(); await waitForPromises(); - expect(findParent().exists()).toBe(true); + expect(findParent().exists()).toBe(false); + }); + + describe('with parent', () => { + beforeEach(async () => { + const parentResponse = workItemResponseFactory(mockParent); + createComponent({ handler: jest.fn().mockResolvedValue(parentResponse) }); + + await waitForPromises(); + }); + + it('shows secondary breadcrumbs if there is a parent', () => { + expect(findParent().exists()).toBe(true); + }); + + it('sets the parent breadcrumb URL', () => { + expect(findParentButton().attributes().href).toBe('../../issues/5'); + }); }); }); diff --git a/spec/lib/gitlab/bare_repository_import/importer_spec.rb b/spec/lib/gitlab/bare_repository_import/importer_spec.rb index bbf8d81b251..8fb903154f3 100644 --- a/spec/lib/gitlab/bare_repository_import/importer_spec.rb +++ b/spec/lib/gitlab/bare_repository_import/importer_spec.rb @@ -2,12 +2,12 @@ require 'spec_helper' -RSpec.describe Gitlab::BareRepositoryImport::Importer, :seed_helper do +RSpec.describe Gitlab::BareRepositoryImport::Importer do let!(:admin) { create(:admin) } let!(:base_dir) { Dir.mktmpdir + '/' } let(:bare_repository) { Gitlab::BareRepositoryImport::Repository.new(base_dir, File.join(base_dir, "#{project_path}.git")) } let(:gitlab_shell) { Gitlab::Shell.new } - let(:source_project) { TEST_REPO_PATH } + let(:source_project) { TestEnv.factory_repo_bundle_path } subject(:importer) { described_class.new(admin, bare_repository) } @@ -17,8 +17,6 @@ RSpec.describe Gitlab::BareRepositoryImport::Importer, :seed_helper do after do FileUtils.rm_rf(base_dir) - TestEnv.clean_test_path - ensure_seeds end shared_examples 'importing a repository' do @@ -150,7 +148,6 @@ RSpec.describe Gitlab::BareRepositoryImport::Importer, :seed_helper do end context 'with a repository already on disk' do - let!(:base_dir) { TestEnv.repos_path } # This is a quick way to get a valid repository instead of copying an # existing one. Since it's not persisted, the importer will try to # create the project. diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index bd94351e6a3..d3b229c2094 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -5921,8 +5921,44 @@ RSpec.describe User do end end + describe '#authenticatable_salt' do + let(:user) { create(:user) } + + subject(:authenticatable_salt) { user.authenticatable_salt } + + it 'uses password_salt' do + expect(authenticatable_salt).to eq(user.password_salt) + end + + context 'when the encrypted_password is an unknown type' do + let(:encrypted_password) { '$argon2i$v=19$m=512,t=4,p=2$eM+ZMyYkpDRGaI3xXmuNcQ$c5DeJg3eb5dskVt1mDdxfw' } + + before do + user.update_attribute(:encrypted_password, encrypted_password) + end + + it 'returns the first 30 characters of the encrypted_password' do + expect(authenticatable_salt).to eq(encrypted_password[0, 29]) + end + end + + context 'when pbkdf2_password_encryption is disabled' do + before do + stub_feature_flags(pbkdf2_password_encryption: false) + end + + it 'returns the first 30 characters of the encrypted_password' do + expect(authenticatable_salt).to eq(user.encrypted_password[0, 29]) + end + end + end + + def compare_pbkdf2_password(user, password) + Devise::Pbkdf2Encryptable::Encryptors::Pbkdf2Sha512.compare(user.encrypted_password, password) + end + describe '#valid_password?' do - subject { user.valid_password?(password) } + subject(:validate_password) { user.valid_password?(password) } context 'user with password not in disallowed list' do let(:user) { create(:user) } @@ -5935,6 +5971,15 @@ RSpec.describe User do it { is_expected.to be_falsey } end + + context 'when pbkdf2_sha512_encryption is disabled and the user password is pbkdf2+sha512' do + it 'does not validate correctly' do + user # Create the user while the feature is enabled + stub_feature_flags(pbkdf2_password_encryption: false) + + expect(validate_password).to be_falsey + end + end end context 'user with disallowed password' do @@ -5949,6 +5994,174 @@ RSpec.describe User do it { is_expected.to be_falsey } end end + + context 'user with a bcrypt password hash' do + # Plaintext password 'eiFubohV6iro' + let(:encrypted_password) { '$2a$10$xLTxCKOa75IU4RQGqqOrTuZOgZdJEzfSzjG6ZSEi/C31TB/yLZYpi' } + let(:user) { create(:user, encrypted_password: encrypted_password) } + + shared_examples 'not re-encrypting with PBKDF2' do + it 'does not re-encrypt with PBKDF2' do + validate_password + + expect(user.reload.encrypted_password).to eq(encrypted_password) + end + end + + context 'using the wrong password' do + let(:password) { 'WRONG PASSWORD' } + + it { is_expected.to be_falsey } + it_behaves_like 'not re-encrypting with PBKDF2' + + context 'when pbkdf2_password_encryption is disabled' do + before do + stub_feature_flags(pbkdf2_password_encryption: false) + end + + it { is_expected.to be_falsey } + it_behaves_like 'not re-encrypting with PBKDF2' + end + end + + context 'using the correct password' do + let(:password) { 'eiFubohV6iro' } + + it { is_expected.to be_truthy } + + it 'validates the password and re-encrypts with PBKDF2' do + validate_password + + current_encrypted_password = user.reload.encrypted_password + + expect(compare_pbkdf2_password(user, password)).to eq(true) + expect { ::BCrypt::Password.new(current_encrypted_password) } + .to raise_error(::BCrypt::Errors::InvalidHash) + end + + context 'when pbkdf2_password_encryption is disabled' do + before do + stub_feature_flags(pbkdf2_password_encryption: false) + end + + it { is_expected.to be_truthy } + it_behaves_like 'not re-encrypting with PBKDF2' + end + + context 'when pbkdf2_password_encryption_write is disabled' do + before do + stub_feature_flags(pbkdf2_password_encryption_write: false) + end + + it { is_expected.to be_truthy } + it_behaves_like 'not re-encrypting with PBKDF2' + end + end + end + + context 'user with password hash that is neither PBKDF2 nor BCrypt' do + let(:user) { create(:user, encrypted_password: '$argon2i$v=19$m=512,t=4,p=2$eM+ZMyYkpDRGaI3xXmuNcQ$c5DeJg3eb5dskVt1mDdxfw') } + let(:password) { 'password' } + + it { is_expected.to be_falsey } + + context 'when pbkdf2_password_encryption is disabled' do + before do + stub_feature_flags(pbkdf2_password_encryption: false) + end + + it { is_expected.to be_falsey } + end + end + end + + # These entire test section can be removed once the :pbkdf2_password_encryption feature flag is removed. + describe '#password=' do + let(:user) { create(:user) } + let(:password) { 'Oot5iechahqu' } + + def compare_bcrypt_password(user, password) + Devise::Encryptor.compare(User, user.encrypted_password, password) + end + + context 'when pbkdf2_password_encryption is enabled' do + it 'calls PBKDF2 digest and not the default Devise encryptor' do + expect(Devise::Pbkdf2Encryptable::Encryptors::Pbkdf2Sha512).to receive(:digest).at_least(:once).and_call_original + expect(Devise::Encryptor).not_to receive(:digest) + + user.password = password + end + + it 'saves the password in PBKDF2 format' do + user.password = password + user.save! + + expect(compare_pbkdf2_password(user, password)).to eq(true) + expect { compare_bcrypt_password(user, password) }.to raise_error(::BCrypt::Errors::InvalidHash) + end + + context 'when pbkdf2_password_encryption_write is disabled' do + before do + stub_feature_flags(pbkdf2_password_encryption_write: false) + end + + it 'calls default Devise encryptor and not the PBKDF2 encryptor' do + expect(Devise::Encryptor).to receive(:digest).at_least(:once).and_call_original + expect(Devise::Pbkdf2Encryptable::Encryptors::Pbkdf2Sha512).not_to receive(:digest) + + user.password = password + end + end + end + + context 'when pbkdf2_password_encryption is disabled' do + before do + stub_feature_flags(pbkdf2_password_encryption: false) + end + + it 'calls default Devise encryptor and not the PBKDF2 encryptor' do + expect(Devise::Encryptor).to receive(:digest).at_least(:once).and_call_original + expect(Devise::Pbkdf2Encryptable::Encryptors::Pbkdf2Sha512).not_to receive(:digest) + + user.password = password + end + + it 'saves the password in BCrypt format' do + user.password = password + user.save! + + expect { compare_pbkdf2_password(user, password) }.to raise_error Devise::Pbkdf2Encryptable::Encryptors::InvalidHash + expect(compare_bcrypt_password(user, password)).to eq(true) + end + end + end + + describe '#password_strategy' do + let(:user) { create(:user, encrypted_password: encrypted_password) } + + context 'with a PBKDF2+SHA512 encrypted password' do + let(:encrypted_password) { '$pbkdf2-sha512$20000$boHGAw0hEyI$DBA67J7zNZebyzLtLk2X9wRDbmj1LNKVGnZLYyz6PGrIDGIl45fl/BPH0y1TPZnV90A20i.fD9C3G9Bp8jzzOA' } + + it 'extracts the correct strategy', :aggregate_failures do + expect(user.password_strategy).to eq(:pbkdf2_sha512) + end + end + + context 'with a BCrypt encrypted password' do + let(:encrypted_password) { '$2a$10$xLTxCKOa75IU4RQGqqOrTuZOgZdJEzfSzjG6ZSEi/C31TB/yLZYpi' } + + it 'extracts the correct strategy', :aggregate_failures do + expect(user.password_strategy).to eq(:bcrypt) + end + end + + context 'with an unknown encrypted password' do + let(:encrypted_password) { '$pbkdf2-sha256$6400$.6UI/S.nXIk8jcbdHx3Fhg$98jZicV16ODfEsEZeYPGHU3kbrUrvUEXOPimVSQDD44' } + + it 'returns unknown strategy' do + expect(user.password_strategy).to eq(:unknown) + end + end end describe '#password_expired?' do diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index 4c338a16f83..9ef845f06bf 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -394,7 +394,7 @@ RSpec.describe API::Commits do context 'when using warden', :snowplow, :clean_gitlab_redis_sessions do before do - stub_session('warden.user.user.key' => [[user.id], user.encrypted_password[0, 29]]) + stub_session('warden.user.user.key' => [[user.id], user.authenticatable_salt]) end subject { post api(url), params: valid_c_params } diff --git a/spec/requests/api/graphql/mutations/snippets/update_spec.rb b/spec/requests/api/graphql/mutations/snippets/update_spec.rb index bf93c2aaa55..1a5d3620f22 100644 --- a/spec/requests/api/graphql/mutations/snippets/update_spec.rb +++ b/spec/requests/api/graphql/mutations/snippets/update_spec.rb @@ -189,7 +189,7 @@ RSpec.describe 'Updating a Snippet' do context 'when not sessionless', :clean_gitlab_redis_sessions do before do - stub_session('warden.user.user.key' => [[current_user.id], current_user.encrypted_password[0, 29]]) + stub_session('warden.user.user.key' => [[current_user.id], current_user.authenticatable_salt]) end it_behaves_like 'Snowplow event tracking' do diff --git a/spec/services/ci/job_artifacts/destroy_batch_service_spec.rb b/spec/services/ci/job_artifacts/destroy_batch_service_spec.rb index 3a04a3af03e..05069054483 100644 --- a/spec/services/ci/job_artifacts/destroy_batch_service_spec.rb +++ b/spec/services/ci/job_artifacts/destroy_batch_service_spec.rb @@ -181,6 +181,26 @@ RSpec.describe Ci::JobArtifacts::DestroyBatchService do end end + context 'when artifact belongs to a project not undergoing refresh' do + context 'and skip_projects_on_refresh is set to false (default)' do + it 'does not log any warnings', :aggregate_failures do + expect(Gitlab::ProjectStatsRefreshConflictsLogger).not_to receive(:warn_artifact_deletion_during_stats_refresh) + + expect { subject }.to change { Ci::JobArtifact.count }.by(-2) + end + end + + context 'and skip_projects_on_refresh is set to true' do + let(:skip_projects_on_refresh) { true } + + it 'does not log any warnings', :aggregate_failures do + expect(Gitlab::ProjectStatsRefreshConflictsLogger).not_to receive(:warn_skipped_artifact_deletion_during_stats_refresh) + + expect { subject }.to change { Ci::JobArtifact.count }.by(-2) + end + end + end + context 'ProjectStatistics' do it 'resets project statistics' do expect(ProjectStatistics).to receive(:increment_statistic).once diff --git a/spec/services/preview_markdown_service_spec.rb b/spec/services/preview_markdown_service_spec.rb index 53f8f5b7253..fe1ab6b1d58 100644 --- a/spec/services/preview_markdown_service_spec.rb +++ b/spec/services/preview_markdown_service_spec.rb @@ -172,4 +172,24 @@ RSpec.describe PreviewMarkdownService do expect(result[:commands]).to eq 'Tags this commit to v1.2.3 with "Stable release".' end end + + context 'note with multiple quick actions' do + let(:issue) { create(:issue, project: project) } + let(:params) do + { + text: "/confidential\n/due 2001-12-31\n/estimate 2y\n/assign #{user.to_reference}", + target_type: 'Issue', + target_id: issue.id + } + end + + let(:service) { described_class.new(project, user, params) } + + it 'renders quick actions on multiple lines' do + result = service.execute + + expect(result[:commands]).to eq "Makes this issue confidential.<br>Sets the due date to Dec 31, 2001.<br>" \ + "Sets time estimate to 2y.<br>Assigns #{user.to_reference}." + end + end end diff --git a/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb index a42a1fda62e..b459e479c91 100644 --- a/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb @@ -22,7 +22,7 @@ RSpec.shared_examples 'snippet edit usage data counters' do context 'when user is not sessionless', :clean_gitlab_redis_sessions do before do - stub_session('warden.user.user.key' => [[current_user.id], current_user.encrypted_password[0, 29]]) + stub_session('warden.user.user.key' => [[current_user.id], current_user.authenticatable_salt]) end it 'tracks usage data actions', :clean_gitlab_redis_sessions do |