diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2024-01-16 13:42:19 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2024-01-16 13:42:19 +0300 |
commit | 84d1bd786125c1c14a3ba5f63e38a4cc736a9027 (patch) | |
tree | f550fa965f507077e20dbb6d61a8269a99ef7107 /spec/features/admin | |
parent | 3a105e36e689f7b75482236712f1a47fd5a76814 (diff) |
Add latest changes from gitlab-org/gitlab@16-8-stable-eev16.8.0-rc42
Diffstat (limited to 'spec/features/admin')
37 files changed, 957 insertions, 934 deletions
diff --git a/spec/features/admin/admin_abuse_reports_spec.rb b/spec/features/admin/admin_abuse_reports_spec.rb index 5e98d2ffcf3..9c425f83508 100644 --- a/spec/features/admin/admin_abuse_reports_spec.rb +++ b/spec/features/admin/admin_abuse_reports_spec.rb @@ -17,7 +17,7 @@ RSpec.describe "Admin::AbuseReports", :js, feature_category: :insider_threat do before do sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) + enable_admin_mode!(admin) visit admin_abuse_reports_path end diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb index 71c904b3a19..ec63e43d183 100644 --- a/spec/features/admin/admin_appearance_spec.rb +++ b/spec/features/admin/admin_appearance_spec.rb @@ -10,189 +10,180 @@ RSpec.describe 'Admin Appearance', feature_category: :shared do stub_feature_flags(edit_user_profile_vue: false) end - flag_values = [true, false] - flag_values.each do |val| - context "with #{val}" do - before do - stub_feature_flags(restyle_login_page: val) - end + it 'create new appearance' do + sign_in(admin) + enable_admin_mode!(admin) + visit admin_application_settings_appearances_path + + fill_in 'appearance_title', with: 'MyCompany' + fill_in 'appearance_description', with: 'dev server' + fill_in 'appearance_pwa_name', with: 'GitLab PWA' + fill_in 'appearance_pwa_short_name', with: 'GitLab' + fill_in 'appearance_pwa_description', with: 'GitLab as PWA' + fill_in 'appearance_new_project_guidelines', with: 'Custom project guidelines' + fill_in 'appearance_profile_image_guidelines', with: 'Custom profile image guidelines' + click_button 'Update appearance settings' + + expect(page).to have_current_path admin_application_settings_appearances_path, ignore_query: true + expect(page).to have_content 'Appearance' + + expect(page).to have_field('appearance_title', with: 'MyCompany') + expect(page).to have_field('appearance_description', with: 'dev server') + expect(page).to have_field('appearance_pwa_name', with: 'GitLab PWA') + expect(page).to have_field('appearance_pwa_short_name', with: 'GitLab') + expect(page).to have_field('appearance_pwa_description', with: 'GitLab as PWA') + expect(page).to have_field('appearance_new_project_guidelines', with: 'Custom project guidelines') + expect(page).to have_field('appearance_profile_image_guidelines', with: 'Custom profile image guidelines') + expect(page).to have_content 'Last edit' + end - it 'create new appearance' do - sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) - visit admin_application_settings_appearances_path + it 'preview sign-in page appearance' do + sign_in(admin) + enable_admin_mode!(admin) - fill_in 'appearance_title', with: 'MyCompany' - fill_in 'appearance_description', with: 'dev server' - fill_in 'appearance_pwa_name', with: 'GitLab PWA' - fill_in 'appearance_pwa_short_name', with: 'GitLab' - fill_in 'appearance_pwa_description', with: 'GitLab as PWA' - fill_in 'appearance_new_project_guidelines', with: 'Custom project guidelines' - fill_in 'appearance_profile_image_guidelines', with: 'Custom profile image guidelines' - click_button 'Update appearance settings' - - expect(page).to have_current_path admin_application_settings_appearances_path, ignore_query: true - expect(page).to have_content 'Appearance' - - expect(page).to have_field('appearance_title', with: 'MyCompany') - expect(page).to have_field('appearance_description', with: 'dev server') - expect(page).to have_field('appearance_pwa_name', with: 'GitLab PWA') - expect(page).to have_field('appearance_pwa_short_name', with: 'GitLab') - expect(page).to have_field('appearance_pwa_description', with: 'GitLab as PWA') - expect(page).to have_field('appearance_new_project_guidelines', with: 'Custom project guidelines') - expect(page).to have_field('appearance_profile_image_guidelines', with: 'Custom profile image guidelines') - expect(page).to have_content 'Last edit' - end + visit admin_application_settings_appearances_path + click_link "Sign-in page" - it 'preview sign-in page appearance' do - sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) + expect(find('#login')).to be_disabled + expect(find('#password')).to be_disabled + expect(find('button')).to be_disabled - visit admin_application_settings_appearances_path - click_link "Sign-in page" + expect_custom_sign_in_appearance(appearance) + end - expect(find('#login')).to be_disabled - expect(find('#password')).to be_disabled - expect(find('button')).to be_disabled + it 'preview new project page appearance', :js do + sign_in(admin) + enable_admin_mode!(admin) - expect_custom_sign_in_appearance(appearance) - end + visit admin_application_settings_appearances_path + click_link "New project page" + + expect_custom_new_project_appearance(appearance) + end - it 'preview new project page appearance', :js do - sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) + context 'Custom system header and footer' do + before do + sign_in(admin) + enable_admin_mode!(admin) + end + context 'when system header and footer messages are empty' do + it 'shows custom system header and footer fields' do visit admin_application_settings_appearances_path - click_link "New project page" - expect_custom_new_project_appearance(appearance) + expect(page).to have_field('appearance_header_message', with: '') + expect(page).to have_field('appearance_footer_message', with: '') + expect(page).to have_field('appearance_message_background_color') + expect(page).to have_field('appearance_message_font_color') end + end - context 'Custom system header and footer' do - before do - sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) - end - - context 'when system header and footer messages are empty' do - it 'shows custom system header and footer fields' do - visit admin_application_settings_appearances_path - - expect(page).to have_field('appearance_header_message', with: '') - expect(page).to have_field('appearance_footer_message', with: '') - expect(page).to have_field('appearance_message_background_color') - expect(page).to have_field('appearance_message_font_color') - end - end - - context 'when system header and footer messages are not empty' do - before do - appearance.update!(header_message: 'Foo', footer_message: 'Bar') - end - - it 'shows custom system header and footer fields' do - visit admin_application_settings_appearances_path - - expect(page).to have_field('appearance_header_message', with: appearance.header_message) - expect(page).to have_field('appearance_footer_message', with: appearance.footer_message) - expect(page).to have_field('appearance_message_background_color') - expect(page).to have_field('appearance_message_font_color') - end - end + context 'when system header and footer messages are not empty' do + before do + appearance.update!(header_message: 'Foo', footer_message: 'Bar') end - it 'custom sign-in page' do - visit new_user_session_path + it 'shows custom system header and footer fields' do + visit admin_application_settings_appearances_path - expect_custom_sign_in_appearance(appearance) + expect(page).to have_field('appearance_header_message', with: appearance.header_message) + expect(page).to have_field('appearance_footer_message', with: appearance.footer_message) + expect(page).to have_field('appearance_message_background_color') + expect(page).to have_field('appearance_message_font_color') end + end + end - it 'custom new project page', :js do - sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) - visit new_project_path - click_link 'Create blank project' + it 'custom sign-in page' do + visit new_user_session_path - expect_custom_new_project_appearance(appearance) - end + expect_custom_sign_in_appearance(appearance) + end - context 'Profile page with custom profile image guidelines' do - before do - sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) - visit admin_application_settings_appearances_path - fill_in 'appearance_profile_image_guidelines', with: 'Custom profile image guidelines, please :smile:!' - click_button 'Update appearance settings' - end - - it 'renders guidelines when set' do - sign_in create(:user) - visit profile_path - - expect(page).to have_content 'Custom profile image guidelines, please 😄!' - end - end + it 'custom new project page', :js do + sign_in(admin) + enable_admin_mode!(admin) + visit new_project_path + click_link 'Create blank project' - it 'appearance logo' do - sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) - visit admin_application_settings_appearances_path + expect_custom_new_project_appearance(appearance) + end - attach_file(:appearance_logo, logo_fixture) - click_button 'Update appearance settings' - expect(page).to have_css(logo_selector) + context 'Profile page with custom profile image guidelines' do + before do + sign_in(admin) + enable_admin_mode!(admin) + visit admin_application_settings_appearances_path + fill_in 'appearance_profile_image_guidelines', with: 'Custom profile image guidelines, please :smile:!' + click_button 'Update appearance settings' + end - click_link 'Remove logo' - expect(page).not_to have_css(logo_selector) - end + it 'renders guidelines when set' do + sign_in create(:user) + visit profile_path - it 'appearance pwa icon' do - sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) - visit admin_application_settings_appearances_path + expect(page).to have_content 'Custom profile image guidelines, please 😄!' + end + end - attach_file(:appearance_pwa_icon, logo_fixture) - click_button 'Update appearance settings' - expect(page).to have_css(pwa_icon_selector) + it 'appearance logo' do + sign_in(admin) + enable_admin_mode!(admin) + visit admin_application_settings_appearances_path - click_link 'Remove icon' - expect(page).not_to have_css(pwa_icon_selector) - end + attach_file(:appearance_logo, logo_fixture) + click_button 'Update appearance settings' + expect(page).to have_css(logo_selector) - it 'header logos' do - sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) - visit admin_application_settings_appearances_path + click_link 'Remove logo' + expect(page).not_to have_css(logo_selector) + end - attach_file(:appearance_header_logo, logo_fixture) - click_button 'Update appearance settings' - expect(page).to have_css(header_logo_selector) + it 'appearance pwa icon' do + sign_in(admin) + enable_admin_mode!(admin) + visit admin_application_settings_appearances_path - click_link 'Remove header logo' - expect(page).not_to have_css(header_logo_selector) - end + attach_file(:appearance_pwa_icon, logo_fixture) + click_button 'Update appearance settings' + expect(page).to have_css(pwa_icon_selector) - it 'Favicon' do - sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) - visit admin_application_settings_appearances_path + click_link 'Remove icon' + expect(page).not_to have_css(pwa_icon_selector) + end - attach_file(:appearance_favicon, logo_fixture) - click_button 'Update appearance settings' + it 'header logos' do + sign_in(admin) + enable_admin_mode!(admin) + visit admin_application_settings_appearances_path - expect(page).to have_css('.appearance-light-logo-preview') + attach_file(:appearance_header_logo, logo_fixture) + click_button 'Update appearance settings' + expect(page).to have_css(header_logo_selector) - click_link 'Remove favicon' + click_link 'Remove header logo' + expect(page).not_to have_css(header_logo_selector) + end - expect(page).not_to have_css('.appearance-light-logo-preview') + it 'Favicon' do + sign_in(admin) + enable_admin_mode!(admin) + visit admin_application_settings_appearances_path - # allowed file types - attach_file(:appearance_favicon, Rails.root.join('spec', 'fixtures', 'sanitized.svg')) - click_button 'Update appearance settings' + attach_file(:appearance_favicon, logo_fixture) + click_button 'Update appearance settings' - expect(page).to have_content 'Favicon You are not allowed to upload "svg" files, allowed types: png, ico' - end - end + expect(page).to have_css('.appearance-light-logo-preview') + + click_link 'Remove favicon' + + expect(page).not_to have_css('.appearance-light-logo-preview') + + # allowed file types + attach_file(:appearance_favicon, Rails.root.join('spec', 'fixtures', 'sanitized.svg')) + click_button 'Update appearance settings' + + expect(page).to have_content 'Favicon You are not allowed to upload "svg" files, allowed types: png, ico' end def expect_custom_sign_in_appearance(appearance) diff --git a/spec/features/admin/admin_browse_spam_logs_spec.rb b/spec/features/admin/admin_browse_spam_logs_spec.rb index f781e2adf07..1f89232759c 100644 --- a/spec/features/admin/admin_browse_spam_logs_spec.rb +++ b/spec/features/admin/admin_browse_spam_logs_spec.rb @@ -8,7 +8,7 @@ RSpec.describe 'Admin browse spam logs', feature_category: :shared do before do sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) + enable_admin_mode!(admin) end it 'browse spam logs' do diff --git a/spec/features/admin/admin_deploy_keys_spec.rb b/spec/features/admin/admin_deploy_keys_spec.rb index f9510ef296a..d6d021aeafc 100644 --- a/spec/features/admin/admin_deploy_keys_spec.rb +++ b/spec/features/admin/admin_deploy_keys_spec.rb @@ -12,7 +12,7 @@ RSpec.describe 'admin deploy keys', :js, feature_category: :system_access do before do sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) + enable_admin_mode!(admin) end it 'show all public deploy keys' do diff --git a/spec/features/admin/admin_dev_ops_reports_spec.rb b/spec/features/admin/admin_dev_ops_reports_spec.rb index 99d43e6b0da..e820ae866a1 100644 --- a/spec/features/admin/admin_dev_ops_reports_spec.rb +++ b/spec/features/admin/admin_dev_ops_reports_spec.rb @@ -6,7 +6,7 @@ RSpec.describe 'DevOps Report page', :js, feature_category: :devops_reports do before do admin = create(:admin) sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) + enable_admin_mode!(admin) end context 'without licensed feature devops adoption' do diff --git a/spec/features/admin/admin_disables_git_access_protocol_spec.rb b/spec/features/admin/admin_disables_git_access_protocol_spec.rb index 76620b93557..039968025a9 100644 --- a/spec/features/admin/admin_disables_git_access_protocol_spec.rb +++ b/spec/features/admin/admin_disables_git_access_protocol_spec.rb @@ -12,7 +12,7 @@ RSpec.describe 'Admin disables Git access protocol', :js, feature_category: :sou before do stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) + enable_admin_mode!(admin) end context 'with HTTP disabled' do diff --git a/spec/features/admin/admin_disables_two_factor_spec.rb b/spec/features/admin/admin_disables_two_factor_spec.rb index eed20d449cd..cb3c8973872 100644 --- a/spec/features/admin/admin_disables_two_factor_spec.rb +++ b/spec/features/admin/admin_disables_two_factor_spec.rb @@ -8,7 +8,7 @@ RSpec.describe 'Admin disables 2FA for a user', feature_category: :system_access it 'successfully', :js do admin = create(:admin) sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) + enable_admin_mode!(admin) user = create(:user, :two_factor) edit_user(user) @@ -27,7 +27,7 @@ RSpec.describe 'Admin disables 2FA for a user', feature_category: :system_access it 'for a user without 2FA enabled' do admin = create(:admin) sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) + enable_admin_mode!(admin) user = create(:user) edit_user(user) diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb index f071da1835a..283caddab6a 100644 --- a/spec/features/admin/admin_groups_spec.rb +++ b/spec/features/admin/admin_groups_spec.rb @@ -15,7 +15,7 @@ RSpec.describe 'Admin Groups', feature_category: :groups_and_projects do before do sign_in(current_user) - gitlab_enable_admin_mode_sign_in(current_user) + enable_admin_mode!(current_user) stub_application_setting(default_group_visibility: internal) end diff --git a/spec/features/admin/admin_health_check_spec.rb b/spec/features/admin/admin_health_check_spec.rb index 66014e676d5..fa91159c5f5 100644 --- a/spec/features/admin/admin_health_check_spec.rb +++ b/spec/features/admin/admin_health_check_spec.rb @@ -10,7 +10,7 @@ RSpec.describe "Admin Health Check", :js, feature_category: :error_budgets do before do stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) + enable_admin_mode!(admin) end describe '#show' do diff --git a/spec/features/admin/admin_hook_logs_spec.rb b/spec/features/admin/admin_hook_logs_spec.rb index 0a537e65b99..146c83dbeee 100644 --- a/spec/features/admin/admin_hook_logs_spec.rb +++ b/spec/features/admin/admin_hook_logs_spec.rb @@ -9,7 +9,7 @@ RSpec.describe 'Admin::HookLogs', feature_category: :webhooks do before do sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) + enable_admin_mode!(admin) end it 'show list of hook logs' do diff --git a/spec/features/admin/admin_hooks_spec.rb b/spec/features/admin/admin_hooks_spec.rb index 2aec5baf351..9bdb7ce000f 100644 --- a/spec/features/admin/admin_hooks_spec.rb +++ b/spec/features/admin/admin_hooks_spec.rb @@ -9,7 +9,7 @@ RSpec.describe 'Admin::Hooks', feature_category: :webhooks do before do sign_in(user) - gitlab_enable_admin_mode_sign_in(user) + enable_admin_mode!(user) end describe 'GET /admin/hooks' do diff --git a/spec/features/admin/admin_jobs_spec.rb b/spec/features/admin/admin_jobs_spec.rb index b3e21d02354..fc775c180ff 100644 --- a/spec/features/admin/admin_jobs_spec.rb +++ b/spec/features/admin/admin_jobs_spec.rb @@ -8,7 +8,7 @@ RSpec.describe 'Admin Jobs', :js, feature_category: :continuous_integration do before do admin = create(:admin) sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) + enable_admin_mode!(admin) end describe 'GET /admin/jobs' do diff --git a/spec/features/admin/admin_labels_spec.rb b/spec/features/admin/admin_labels_spec.rb index 47dc8577037..2fe68363062 100644 --- a/spec/features/admin/admin_labels_spec.rb +++ b/spec/features/admin/admin_labels_spec.rb @@ -11,7 +11,7 @@ RSpec.describe 'admin issues labels', feature_category: :team_planning do before do admin = create(:admin) sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) + enable_admin_mode!(admin) end describe 'list' do diff --git a/spec/features/admin/admin_manage_applications_spec.rb b/spec/features/admin/admin_manage_applications_spec.rb index b4c77e802a8..3e351af6121 100644 --- a/spec/features/admin/admin_manage_applications_spec.rb +++ b/spec/features/admin/admin_manage_applications_spec.rb @@ -10,7 +10,7 @@ RSpec.describe 'admin manage applications', feature_category: :system_access do before do admin = create(:admin) sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) + enable_admin_mode!(admin) end include_examples 'manage applications' diff --git a/spec/features/admin/admin_mode/login_spec.rb b/spec/features/admin/admin_mode/login_spec.rb index f2262464386..5298407deb3 100644 --- a/spec/features/admin/admin_mode/login_spec.rb +++ b/spec/features/admin/admin_mode/login_spec.rb @@ -13,256 +13,248 @@ RSpec.describe 'Admin Mode Login', feature_category: :system_access do click_button 'Verify code' end - flag_values = [true, false] - flag_values.each do |val| - context "with #{val}" do - before do - stub_feature_flags(restyle_login_page: val) - end - context 'with valid username/password' do - let(:user) { create(:admin, :two_factor) } + context 'with valid username/password' do + let(:user) { create(:admin, :two_factor) } - context 'using one-time code' do - it 'blocks login if we reuse the same code immediately' do - gitlab_sign_in(user, remember: true) + context 'using one-time code' do + it 'blocks login if we reuse the same code immediately' do + gitlab_sign_in(user, remember: true) - expect(page).to have_content(_('Enter verification code')) + expect(page).to have_content(_('Enter verification code')) - repeated_otp = user.current_otp - enter_code(repeated_otp) - gitlab_enable_admin_mode_sign_in(user, use_mock_admin_mode: false) + repeated_otp = user.current_otp + enter_code(repeated_otp) + enable_admin_mode!(user, use_ui: true) - expect(page).to have_content(_('Enter verification code')) + expect(page).to have_content(_('Enter verification code')) - enter_code(repeated_otp) + enter_code(repeated_otp) - expect(page).to have_current_path admin_session_path, ignore_query: true - expect(page).to have_content('Invalid two-factor code') - end + expect(page).to have_current_path admin_session_path, ignore_query: true + expect(page).to have_content('Invalid two-factor code') + end - context 'not re-using codes' do - before do - gitlab_sign_in(user, remember: true) + context 'not re-using codes' do + before do + gitlab_sign_in(user, remember: true) - expect(page).to have_content('Enter verification code') + expect(page).to have_content('Enter verification code') - enter_code(user.current_otp) - gitlab_enable_admin_mode_sign_in(user, use_mock_admin_mode: false) + enter_code(user.current_otp) + enable_admin_mode!(user, use_ui: true) - expect(page).to have_content(_('Enter verification code')) - end + expect(page).to have_content(_('Enter verification code')) + end - it 'allows login with valid code' do - # Cannot reuse the TOTP - travel_to(30.seconds.from_now) do - enter_code(user.current_otp) + it 'allows login with valid code' do + # Cannot reuse the TOTP + travel_to(30.seconds.from_now) do + enter_code(user.current_otp) - expect(page).to have_current_path admin_root_path, ignore_query: true - expect(page).to have_content('Admin mode enabled') - end - end + expect(page).to have_current_path admin_root_path, ignore_query: true + expect(page).to have_content('Admin mode enabled') + end + end - it 'blocks login with invalid code' do - # Cannot reuse the TOTP - travel_to(30.seconds.from_now) do - enter_code('foo') + it 'blocks login with invalid code' do + # Cannot reuse the TOTP + travel_to(30.seconds.from_now) do + enter_code('foo') - expect(page).to have_content('Invalid two-factor code') - end - end + expect(page).to have_content('Invalid two-factor code') + end + end - it 'allows login with invalid code, then valid code' do - # Cannot reuse the TOTP - travel_to(30.seconds.from_now) do - enter_code('foo') + it 'allows login with invalid code, then valid code' do + # Cannot reuse the TOTP + travel_to(30.seconds.from_now) do + enter_code('foo') - expect(page).to have_content('Invalid two-factor code') + expect(page).to have_content('Invalid two-factor code') - enter_code(user.current_otp) + enter_code(user.current_otp) - expect(page).to have_current_path admin_root_path, ignore_query: true - expect(page).to have_content('Admin mode enabled') - end - end + expect(page).to have_current_path admin_root_path, ignore_query: true + expect(page).to have_content('Admin mode enabled') + end + end - context 'using backup code' do - let(:codes) { user.generate_otp_backup_codes! } + context 'using backup code' do + let(:codes) { user.generate_otp_backup_codes! } - before do - expect(codes.size).to eq 10 + before do + expect(codes.size).to eq 10 - # Ensure the generated codes get saved - user.save! - end + # Ensure the generated codes get saved + user.save! + end - context 'with valid code' do - it 'allows login' do - enter_code(codes.sample) + context 'with valid code' do + it 'allows login' do + enter_code(codes.sample) - expect(page).to have_current_path admin_root_path, ignore_query: true - expect(page).to have_content('Admin mode enabled') - end + expect(page).to have_current_path admin_root_path, ignore_query: true + expect(page).to have_content('Admin mode enabled') + end - it 'invalidates the used code' do - expect { enter_code(codes.sample) } - .to change { user.reload.otp_backup_codes.size }.by(-1) - end - end + it 'invalidates the used code' do + expect { enter_code(codes.sample) } + .to change { user.reload.otp_backup_codes.size }.by(-1) + end + end - context 'with invalid code' do - it 'blocks login' do - code = codes.sample - expect(user.invalidate_otp_backup_code!(code)).to eq true + context 'with invalid code' do + it 'blocks login' do + code = codes.sample + expect(user.invalidate_otp_backup_code!(code)).to eq true - user.save! - expect(user.reload.otp_backup_codes.size).to eq 9 + user.save! + expect(user.reload.otp_backup_codes.size).to eq 9 - enter_code(code) + enter_code(code) - expect(page).to have_content('Invalid two-factor code.') - end - end + expect(page).to have_content('Invalid two-factor code.') end end end + end + end - context 'when logging in via omniauth' do - let(:user) { create(:omniauth_user, :admin, :two_factor, extern_uid: 'my-uid', provider: 'saml', password_automatically_set: false) } - let(:mock_saml_response) do - File.read('spec/fixtures/authentication/saml_response.xml') - end + context 'when logging in via omniauth' do + let(:user) { create(:omniauth_user, :admin, :two_factor, extern_uid: 'my-uid', provider: 'saml', password_automatically_set: false) } + let(:mock_saml_response) do + File.read('spec/fixtures/authentication/saml_response.xml') + end - before do - stub_omniauth_saml_config(enabled: true, auto_link_saml_user: true, allow_single_sign_on: ['saml'], providers: [mock_saml_config_with_upstream_two_factor_authn_contexts]) - end + before do + stub_omniauth_saml_config(enabled: true, auto_link_saml_user: true, allow_single_sign_on: ['saml'], providers: [mock_saml_config_with_upstream_two_factor_authn_contexts]) + end - context 'when authn_context is worth two factors' do - let(:mock_saml_response) do - File.read('spec/fixtures/authentication/saml_response.xml') - .gsub( - 'urn:oasis:names:tc:SAML:2.0:ac:classes:Password', - 'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS' - ) - end + context 'when authn_context is worth two factors' do + let(:mock_saml_response) do + File.read('spec/fixtures/authentication/saml_response.xml') + .gsub( + 'urn:oasis:names:tc:SAML:2.0:ac:classes:Password', + 'urn:oasis:names:tc:SAML:2.0:ac:classes:SecondFactorOTPSMS' + ) + end - it 'signs user in without prompting for second factor' do - sign_in_using_saml! + it 'signs user in without prompting for second factor' do + sign_in_using_saml! - expect(page).not_to have_content(_('Enter verification code')) + expect(page).not_to have_content(_('Enter verification code')) - enable_admin_mode_using_saml! + enable_admin_mode_using_saml! - expect(page).not_to have_content(_('Enter verification code')) - expect(page).to have_current_path admin_root_path, ignore_query: true - expect(page).to have_content('Admin mode enabled') - end - end + expect(page).not_to have_content(_('Enter verification code')) + expect(page).to have_current_path admin_root_path, ignore_query: true + expect(page).to have_content('Admin mode enabled') + end + end - context 'when two factor authentication is required' do - it 'shows 2FA prompt after omniauth login' do - sign_in_using_saml! + context 'when two factor authentication is required' do + it 'shows 2FA prompt after omniauth login' do + sign_in_using_saml! - expect(page).to have_content(_('Enter verification code')) - enter_code(user.current_otp) + expect(page).to have_content(_('Enter verification code')) + enter_code(user.current_otp) - enable_admin_mode_using_saml! + enable_admin_mode_using_saml! - expect(page).to have_content(_('Enter verification code')) + expect(page).to have_content(_('Enter verification code')) - # Cannot reuse the TOTP - travel_to(30.seconds.from_now) do - enter_code(user.current_otp) + # Cannot reuse the TOTP + travel_to(30.seconds.from_now) do + enter_code(user.current_otp) - expect(page).to have_current_path admin_root_path, ignore_query: true - expect(page).to have_content('Admin mode enabled') - end - end + expect(page).to have_current_path admin_root_path, ignore_query: true + expect(page).to have_content('Admin mode enabled') end + end + end - def sign_in_using_saml! - gitlab_sign_in_via('saml', user, 'my-uid', mock_saml_response) - end + def sign_in_using_saml! + gitlab_sign_in_via('saml', user, 'my-uid', mock_saml_response) + end - def enable_admin_mode_using_saml! - gitlab_enable_admin_mode_sign_in_via('saml', user, 'my-uid', mock_saml_response) - end - end + def enable_admin_mode_using_saml! + gitlab_enable_admin_mode_sign_in_via('saml', user, 'my-uid', mock_saml_response) + end + end - context 'when logging in via ldap' do - let(:uid) { 'my-uid' } - let(:provider_label) { 'Main LDAP' } - let(:provider_name) { 'main' } - let(:provider) { "ldap#{provider_name}" } - let(:ldap_server_config) do - { - 'label' => provider_label, - 'provider_name' => provider, - 'attributes' => {}, - 'encryption' => 'plain', - 'uid' => 'uid', - 'base' => 'dc=example,dc=com' - } - end + context 'when logging in via ldap' do + let(:uid) { 'my-uid' } + let(:provider_label) { 'Main LDAP' } + let(:provider_name) { 'main' } + let(:provider) { "ldap#{provider_name}" } + let(:ldap_server_config) do + { + 'label' => provider_label, + 'provider_name' => provider, + 'attributes' => {}, + 'encryption' => 'plain', + 'uid' => 'uid', + 'base' => 'dc=example,dc=com' + } + end - let(:user) { create(:omniauth_user, :admin, :two_factor, extern_uid: uid, provider: provider) } + let(:user) { create(:omniauth_user, :admin, :two_factor, extern_uid: uid, provider: provider) } - before do - setup_ldap(provider, user, uid, ldap_server_config) - end + before do + setup_ldap(provider, user, uid, ldap_server_config) + end - context 'when two factor authentication is required' do - it 'shows 2FA prompt after ldap login' do - sign_in_using_ldap!(user, provider_label) - expect(page).to have_content(_('Enter verification code')) + context 'when two factor authentication is required' do + it 'shows 2FA prompt after ldap login' do + sign_in_using_ldap!(user, provider_label) + expect(page).to have_content(_('Enter verification code')) - enter_code(user.current_otp) - enable_admin_mode_using_ldap!(user) + enter_code(user.current_otp) + enable_admin_mode_using_ldap!(user) - expect(page).to have_content(_('Enter verification code')) + expect(page).to have_content(_('Enter verification code')) - # Cannot reuse the TOTP - travel_to(30.seconds.from_now) do - enter_code(user.current_otp) + # Cannot reuse the TOTP + travel_to(30.seconds.from_now) do + enter_code(user.current_otp) - expect(page).to have_current_path admin_root_path, ignore_query: true - expect(page).to have_content('Admin mode enabled') - end - end + expect(page).to have_current_path admin_root_path, ignore_query: true + expect(page).to have_content('Admin mode enabled') end + end + end - def setup_ldap(provider, user, uid, ldap_server_config) - stub_ldap_setting(enabled: true) + def setup_ldap(provider, user, uid, ldap_server_config) + stub_ldap_setting(enabled: true) - allow(::Gitlab::Auth::Ldap::Config).to receive_messages(enabled: true, servers: [ldap_server_config]) - allow(Gitlab::Auth::OAuth::Provider).to receive_messages(providers: [provider.to_sym]) + allow(::Gitlab::Auth::Ldap::Config).to receive_messages(enabled: true, servers: [ldap_server_config]) + allow(Gitlab::Auth::OAuth::Provider).to receive_messages(providers: [provider.to_sym]) - Ldap::OmniauthCallbacksController.define_providers! - Rails.application.reload_routes! + Ldap::OmniauthCallbacksController.define_providers! + Rails.application.reload_routes! - mock_auth_hash(provider, uid, user.email) - allow(Gitlab::Auth::Ldap::Access).to receive(:allowed?).with(user).and_return(true) + mock_auth_hash(provider, uid, user.email) + allow(Gitlab::Auth::Ldap::Access).to receive(:allowed?).with(user).and_return(true) - allow_any_instance_of(ActionDispatch::Routing::RoutesProxy) - .to receive(:"user_#{provider}_omniauth_callback_path") - .and_return("/users/auth/#{provider}/callback") - end + allow_any_instance_of(ActionDispatch::Routing::RoutesProxy) + .to receive(:"user_#{provider}_omniauth_callback_path") + .and_return("/users/auth/#{provider}/callback") + end - def sign_in_using_ldap!(user, provider_label) - visit new_user_session_path - click_link provider_label - fill_in 'username', with: user.username - fill_in 'password', with: user.password - click_button 'Sign in' - end + def sign_in_using_ldap!(user, provider_label) + visit new_user_session_path + click_link provider_label + fill_in 'username', with: user.username + fill_in 'password', with: user.password + click_button 'Sign in' + end - def enable_admin_mode_using_ldap!(user) - visit new_admin_session_path - click_link provider_label - fill_in 'username', with: user.username - fill_in 'password', with: user.password - click_button 'Enter admin mode' - end - end + def enable_admin_mode_using_ldap!(user) + visit new_admin_session_path + click_link provider_label + fill_in 'username', with: user.username + fill_in 'password', with: user.password + click_button 'Enter admin mode' end end end diff --git a/spec/features/admin/admin_mode/logout_spec.rb b/spec/features/admin/admin_mode/logout_spec.rb index 584151726a6..aa0569b9f0d 100644 --- a/spec/features/admin/admin_mode/logout_spec.rb +++ b/spec/features/admin/admin_mode/logout_spec.rb @@ -12,7 +12,7 @@ RSpec.describe 'Admin Mode Logout', :js, feature_category: :system_access do # TODO: This used to use gitlab_sign_in, instead of sign_in, but that is buggy. See # this issue to look into why: https://gitlab.com/gitlab-org/gitlab/-/issues/331851 sign_in(user) - gitlab_enable_admin_mode_sign_in(user, use_mock_admin_mode: false) + enable_admin_mode!(user, use_ui: true) visit admin_root_path end @@ -21,7 +21,7 @@ RSpec.describe 'Admin Mode Logout', :js, feature_category: :system_access do expect(page).to have_current_path root_path, ignore_query: true - click_button 'Search or go to…' + find_by_testid('user-menu-toggle').click expect(page).to have_link(href: new_admin_session_path) end @@ -42,7 +42,7 @@ RSpec.describe 'Admin Mode Logout', :js, feature_category: :system_access do expect(page).to have_current_path root_path, ignore_query: true - click_button 'Search or go to…' + find_by_testid('user-menu-toggle').click expect(page).to have_link(href: new_admin_session_path) end diff --git a/spec/features/admin/admin_mode/workers_spec.rb b/spec/features/admin/admin_mode/workers_spec.rb index 124c43eef9d..f0cea425bb7 100644 --- a/spec/features/admin/admin_mode/workers_spec.rb +++ b/spec/features/admin/admin_mode/workers_spec.rb @@ -34,7 +34,7 @@ RSpec.describe 'Admin mode for workers', :request_store, feature_category: :syst context 'when admin mode enabled', :delete do before do - gitlab_enable_admin_mode_sign_in(user) + enable_admin_mode!(user) end it 'can delete user', :js do @@ -67,6 +67,6 @@ RSpec.describe 'Admin mode for workers', :request_store, feature_category: :syst Sidekiq::Worker.drain_all sign_in(user) - gitlab_enable_admin_mode_sign_in(user) + enable_admin_mode!(user) end end diff --git a/spec/features/admin/admin_mode_spec.rb b/spec/features/admin/admin_mode_spec.rb index 2a655cdb1f4..b58953989d2 100644 --- a/spec/features/admin/admin_mode_spec.rb +++ b/spec/features/admin/admin_mode_spec.rb @@ -20,18 +20,18 @@ RSpec.describe 'Admin mode', :js, feature_category: :shared do context 'when not in admin mode' do it 'has no leave admin mode button' do visit new_admin_session_path - open_search_modal + open_user_menu expect(page).not_to have_link(href: destroy_admin_session_path) end it 'can open pages not in admin scope' do visit new_admin_session_path - open_search_modal + open_user_menu - click_link('View all my projects') + click_link('Edit profile') - expect(page).to have_current_path(dashboard_projects_path) + expect(page).to have_current_path(profile_path) end it 'is necessary to provide credentials again before opening pages in admin scope' do @@ -41,11 +41,14 @@ RSpec.describe 'Admin mode', :js, feature_category: :shared do end it 'can enter admin mode' do - visit new_admin_session_path + visit root_dashboard_path + open_user_menu + click_link 'Enter Admin Mode' fill_in 'user_password', with: admin.password click_button 'Enter admin mode' + click_link 'Admin Area' expect(page).to have_current_path(admin_root_path) end @@ -69,38 +72,30 @@ RSpec.describe 'Admin mode', :js, feature_category: :shared do context 'when in admin_mode' do before do - gitlab_enable_admin_mode_sign_in(admin, use_mock_admin_mode: false) + enable_admin_mode!(admin, use_ui: true) end it 'contains link to leave admin mode' do - open_search_modal - - expect(page).to have_link(href: destroy_admin_session_path) - end - - it 'can leave admin mode using main dashboard link' do - gitlab_disable_admin_mode - - open_search_modal - - expect(page).to have_link(href: new_admin_session_path) + open_user_menu + click_link('Leave Admin Mode', href: destroy_admin_session_path) + expect(page).to have_text 'Admin mode disabled' end it 'can open pages not in admin scope' do - open_search_modal + open_user_menu - click_link('View all my projects') + click_link('Edit profile') - expect(page).to have_current_path(dashboard_projects_path) + expect(page).to have_current_path(profile_path) end - context 'nav bar' do - it 'shows admin dashboard links on bigger screen' do + context 'sidebar' do + it 'shows admin dashboard link' do visit root_dashboard_path - open_search_modal - expect(page).to have_link(text: 'Admin', href: admin_root_path, visible: true) - expect(page).to have_link(text: 'Leave admin mode', href: destroy_admin_session_path, visible: true) + within '#super-sidebar' do + expect(page).to have_link('Admin Area') + end end end @@ -112,7 +107,7 @@ RSpec.describe 'Admin mode', :js, feature_category: :shared do it 'can leave admin mode' do gitlab_disable_admin_mode - open_search_modal + open_user_menu expect(page).to have_link(href: new_admin_session_path) end @@ -128,14 +123,14 @@ RSpec.describe 'Admin mode', :js, feature_category: :shared do it 'shows no admin mode buttons in navbar' do visit admin_root_path - open_search_modal + open_user_menu expect(page).not_to have_link(href: new_admin_session_path) expect(page).not_to have_link(href: destroy_admin_session_path) end end - def open_search_modal - click_button 'Search or go to…' + def open_user_menu + find_by_testid('user-menu-toggle').click end end diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb index b793299e253..978b2176431 100644 --- a/spec/features/admin/admin_projects_spec.rb +++ b/spec/features/admin/admin_projects_spec.rb @@ -14,7 +14,7 @@ RSpec.describe "Admin::Projects", feature_category: :groups_and_projects do before do sign_in(current_user) - gitlab_enable_admin_mode_sign_in(current_user) + enable_admin_mode!(current_user) end describe 'when membership is set to expire', :js do diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb index 653458710e3..2b4ceb05f00 100644 --- a/spec/features/admin/admin_runners_spec.rb +++ b/spec/features/admin/admin_runners_spec.rb @@ -11,7 +11,7 @@ RSpec.describe "Admin Runners", feature_category: :fleet_visibility do before do sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) + enable_admin_mode!(admin) end describe "Admin Runners page", :js do diff --git a/spec/features/admin/admin_search_settings_spec.rb b/spec/features/admin/admin_search_settings_spec.rb index 3254bf75738..c20a50cf218 100644 --- a/spec/features/admin/admin_search_settings_spec.rb +++ b/spec/features/admin/admin_search_settings_spec.rb @@ -8,7 +8,7 @@ RSpec.describe 'Admin searches application settings', :js, feature_category: :gl before do sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) + enable_admin_mode!(admin) end context 'in appearances page' do diff --git a/spec/features/admin/admin_sees_background_migrations_spec.rb b/spec/features/admin/admin_sees_background_migrations_spec.rb index ae307b8038c..43b920c657c 100644 --- a/spec/features/admin/admin_sees_background_migrations_spec.rb +++ b/spec/features/admin/admin_sees_background_migrations_spec.rb @@ -18,7 +18,7 @@ RSpec.describe "Admin > Admin sees background migrations", feature_category: :da before do sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) + enable_admin_mode!(admin) end it 'can navigate to background migrations', :js do diff --git a/spec/features/admin/admin_sees_project_statistics_spec.rb b/spec/features/admin/admin_sees_project_statistics_spec.rb index d977735daf8..07f90d247cd 100644 --- a/spec/features/admin/admin_sees_project_statistics_spec.rb +++ b/spec/features/admin/admin_sees_project_statistics_spec.rb @@ -7,7 +7,7 @@ RSpec.describe "Admin > Admin sees project statistics", feature_category: :group before do sign_in(current_user) - gitlab_enable_admin_mode_sign_in(current_user) + enable_admin_mode!(current_user) visit admin_project_path(project) end diff --git a/spec/features/admin/admin_sees_projects_statistics_spec.rb b/spec/features/admin/admin_sees_projects_statistics_spec.rb index 3363a67ea90..cc6ccbab0a0 100644 --- a/spec/features/admin/admin_sees_projects_statistics_spec.rb +++ b/spec/features/admin/admin_sees_projects_statistics_spec.rb @@ -10,7 +10,7 @@ RSpec.describe "Admin > Admin sees projects statistics", feature_category: :grou create(:project, :repository) { |project| project.statistics.destroy! } sign_in(current_user) - gitlab_enable_admin_mode_sign_in(current_user) + enable_admin_mode!(current_user) visit admin_projects_path end diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb index 77707a67d58..d1fdbfc5329 100644 --- a/spec/features/admin/admin_settings_spec.rb +++ b/spec/features/admin/admin_settings_spec.rb @@ -13,7 +13,7 @@ RSpec.describe 'Admin updates settings', feature_category: :shared do before do stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin, use_mock_admin_mode: false) + enable_admin_mode!(admin, use_ui: true) end context 'General page' do diff --git a/spec/features/admin/admin_system_info_spec.rb b/spec/features/admin/admin_system_info_spec.rb index 71a0b829932..f285fefe6db 100644 --- a/spec/features/admin/admin_system_info_spec.rb +++ b/spec/features/admin/admin_system_info_spec.rb @@ -6,7 +6,7 @@ RSpec.describe 'Admin System Info', feature_category: :shared do before do admin = create(:admin) sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) + enable_admin_mode!(admin) end describe 'GET /admin/system_info' do diff --git a/spec/features/admin/admin_users_impersonation_tokens_spec.rb b/spec/features/admin/admin_users_impersonation_tokens_spec.rb index 543dc2cc2a6..7cfe0cdbc81 100644 --- a/spec/features/admin/admin_users_impersonation_tokens_spec.rb +++ b/spec/features/admin/admin_users_impersonation_tokens_spec.rb @@ -11,7 +11,7 @@ RSpec.describe 'Admin > Users > Impersonation Tokens', :js, feature_category: :s before do sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) + enable_admin_mode!(admin) end describe "token creation" do diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb index 9ab5b1fd3bb..f09c1a59b0d 100644 --- a/spec/features/admin/admin_users_spec.rb +++ b/spec/features/admin/admin_users_spec.rb @@ -7,7 +7,7 @@ RSpec.describe "Admin::Users", feature_category: :user_management do before do sign_in(current_user) - gitlab_enable_admin_mode_sign_in(current_user) + enable_admin_mode!(current_user) end describe 'Tabs' do diff --git a/spec/features/admin/admin_uses_repository_checks_spec.rb b/spec/features/admin/admin_uses_repository_checks_spec.rb index 05232de35e5..a628344bfeb 100644 --- a/spec/features/admin/admin_uses_repository_checks_spec.rb +++ b/spec/features/admin/admin_uses_repository_checks_spec.rb @@ -25,7 +25,7 @@ RSpec.describe 'Admin uses repository checks', :request_store, feature_category: context 'when admin mode is enabled' do before do - gitlab_enable_admin_mode_sign_in(admin) + enable_admin_mode!(admin) end it 'to trigger a single check', :js do diff --git a/spec/features/admin/broadcast_messages_spec.rb b/spec/features/admin/broadcast_messages_spec.rb index e4a2e31ee1c..16651ffa07e 100644 --- a/spec/features/admin/broadcast_messages_spec.rb +++ b/spec/features/admin/broadcast_messages_spec.rb @@ -7,7 +7,7 @@ RSpec.describe 'Admin Broadcast Messages', :js, feature_category: :onboarding do it 'previews, creates and edits a broadcast message' do admin = create(:admin) sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) + enable_admin_mode!(admin) # create visit admin_broadcast_messages_path diff --git a/spec/features/admin/dashboard_spec.rb b/spec/features/admin/dashboard_spec.rb index 06f9c531e74..9e8f5138815 100644 --- a/spec/features/admin/dashboard_spec.rb +++ b/spec/features/admin/dashboard_spec.rb @@ -8,7 +8,7 @@ RSpec.describe 'admin visits dashboard' do before do admin = create(:admin) sign_in(admin) - gitlab_enable_admin_mode_sign_in(admin) + enable_admin_mode!(admin) end context 'counting forks', :js, feature_category: :source_code_management do diff --git a/spec/features/admin/users/admin_impersonates_user_spec.rb b/spec/features/admin/users/admin_impersonates_user_spec.rb new file mode 100644 index 00000000000..e37b4bf1562 --- /dev/null +++ b/spec/features/admin/users/admin_impersonates_user_spec.rb @@ -0,0 +1,208 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Admin impersonates user', feature_category: :user_management do + let_it_be(:user) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') } + let_it_be(:current_user) { create(:admin) } + + before do + sign_in(current_user) + enable_admin_mode!(current_user, use_ui: true) + end + + describe 'GET /admin/users/:id' do + describe 'Impersonation' do + let_it_be(:another_user) { create(:user) } + + context 'before impersonating' do + subject { visit admin_user_path(user_to_visit) } + + let_it_be(:user_to_visit) { another_user } + + shared_examples "user that cannot be impersonated" do + it 'disables impersonate button' do + subject + + impersonate_btn = find_by_testid('impersonate-user-link') + + expect(impersonate_btn).not_to be_nil + expect(impersonate_btn['disabled']).not_to be_nil + end + + it "shows tooltip with correct error message" do + subject + + expect(find("span[title='#{impersonation_error_msg}']")).not_to be_nil + end + end + + context 'for other users' do + it 'shows impersonate button for other users' do + subject + + expect(page).to have_content('Impersonate') + impersonate_btn = find_by_testid('impersonate-user-link') + expect(impersonate_btn['disabled']).to be_nil + end + end + + context 'for admin itself' do + let(:user_to_visit) { current_user } + + it 'does not show impersonate button for admin itself' do + subject + + expect(page).to have_no_content('Impersonate') + end + end + + context 'for blocked user' do + let_it_be(:blocked_user) { create(:user, :blocked) } + let(:user_to_visit) { blocked_user } + let(:impersonation_error_msg) { _('You cannot impersonate a blocked user') } + + it_behaves_like "user that cannot be impersonated" + end + + context 'for user with expired password' do + let_it_be(:user_to_visit) do + another_user.update!(password_expires_at: Time.zone.now - 5.minutes) + another_user + end + + let(:impersonation_error_msg) { _("You cannot impersonate a user with an expired password") } + + it_behaves_like "user that cannot be impersonated" + end + + context 'for internal user' do + let_it_be(:internal_user) { create(:user, :bot) } + let(:user_to_visit) { internal_user } + let(:impersonation_error_msg) { _("You cannot impersonate an internal user") } + + it_behaves_like "user that cannot be impersonated" + end + + context 'for locked user' do + let_it_be(:locked_user) { create(:user, :locked) } + let(:user_to_visit) { locked_user } + let(:impersonation_error_msg) { _("You cannot impersonate a user who cannot log in") } + + it_behaves_like "user that cannot be impersonated" + end + + context 'when already impersonating another user' do + let_it_be(:admin_user) { create(:user, :admin) } + let(:impersonation_error_msg) { _("You are already impersonating another user") } + + subject do + visit admin_user_path(admin_user) + click_link 'Impersonate' + visit admin_user_path(another_user) + end + + it_behaves_like "user that cannot be impersonated" + end + + context 'when impersonation is disabled' do + before do + stub_config_setting(impersonation_enabled: false) + end + + it 'does not show impersonate button' do + subject + + expect(page).to have_no_content('Impersonate') + end + end + end + + context 'when impersonating' do + subject { click_link 'Impersonate' } + + before do + visit admin_user_path(another_user) + end + + it 'logs in as the user when impersonate is clicked', :js do + subject + + expect(page).to have_button("#{another_user.name} user’s menu") + end + + it 'sees impersonation log out icon', :js do + subject + + icon = first('[data-testid="incognito-icon"]') + expect(icon).not_to be nil + end + + context 'when viewing the confirm email warning', :js do + before do + stub_application_setting_enum('email_confirmation_setting', 'soft') + end + + let_it_be(:another_user) { create(:user, :unconfirmed) } + let(:warning_alert) { page.find(:css, '[data-testid="alert-warning"]') } + + context 'with an email that does not contain HTML' do + before do + subject + end + + it 'displays the warning alert including the email' do + expect(warning_alert.text).to include("Please check your email (#{another_user.email}) to verify") + end + end + + context 'with an email that contains HTML' do + let(:malicious_email) { "malicious@test.com<form><input/title='<script>alert(document.domain)</script>'>" } + let(:another_user) { create(:user, confirmed_at: nil, unconfirmed_email: malicious_email) } + + before do + subject + end + + it 'displays the impersonation alert, excludes email, and disables links' do + expect(warning_alert.text).to include("check your email (#{another_user.unconfirmed_email}) to verify") + end + end + end + end + + context 'ending impersonation', :js do + subject { click_on 'Stop impersonating' } + + before do + visit admin_user_path(another_user) + click_link 'Impersonate' + end + + it 'logs out of impersonated user back to original user' do + subject + + expect(page).to have_button("#{current_user.name} user’s menu") + end + + it 'is redirected back to the impersonated users page in the admin after stopping' do + subject + + expect(page).to have_current_path("/admin/users/#{another_user.username}", ignore_query: true) + end + + context 'a user with an expired password' do + before do + another_user.update!(password_expires_at: Time.zone.now - 5.minutes) + end + + it 'is redirected back to the impersonated users page in the admin after stopping' do + subject + + expect(page).to have_current_path("/admin/users/#{another_user.username}", ignore_query: true) + end + end + end + end + end +end diff --git a/spec/features/admin/users/admin_manages_user_identities_spec.rb b/spec/features/admin/users/admin_manages_user_identities_spec.rb new file mode 100644 index 00000000000..cbb7eed9e1c --- /dev/null +++ b/spec/features/admin/users/admin_manages_user_identities_spec.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Admin manages user identities', feature_category: :user_management do + let_it_be(:user) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') } + let_it_be(:current_user) { create(:admin) } + + before do + sign_in(current_user) + enable_admin_mode!(current_user, use_ui: true) + end + + describe 'GET /admin/users/:id' do + describe 'show user identities' do + it 'shows user identities', :aggregate_failures do + visit admin_user_identities_path(user) + + expect(page).to( + have_content(user.name) + .and(have_content('twitter')) + ) + end + end + + describe 'update user identities' do + before do + allow(Gitlab::Auth::OAuth::Provider).to receive(:providers).and_return([:twitter, :twitter_updated]) + end + + it 'modifies twitter identity', :aggregate_failures do + visit admin_user_identities_path(user) + + find('.table').find(:link, 'Edit').click + fill_in 'identity_extern_uid', with: '654321' + select 'twitter_updated', from: 'identity_provider' + click_button 'Save changes' + + expect(page).to have_content(user.name) + expect(page).to have_content('twitter_updated') + expect(page).to have_content('654321') + end + end + + describe 'remove user with identities' do + it 'removes user with twitter identity', :aggregate_failures do + visit admin_user_identities_path(user) + + click_link 'Delete' + + expect(page).to have_content(user.name) + expect(page).not_to have_content('twitter') + end + end + end +end diff --git a/spec/features/admin/users/admin_sees_unconfirmed_user_spec.rb b/spec/features/admin/users/admin_sees_unconfirmed_user_spec.rb new file mode 100644 index 00000000000..7b45e5b5cde --- /dev/null +++ b/spec/features/admin/users/admin_sees_unconfirmed_user_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Admin sees unconfirmed user', feature_category: :user_management do + include Spec::Support::Helpers::ModalHelpers + + let_it_be(:user) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') } + let_it_be(:current_user) { create(:admin) } + + before do + sign_in(current_user) + enable_admin_mode!(current_user, use_ui: true) + end + + context 'when user has an unconfirmed email', :js do + # Email address contains HTML to ensure email address is displayed in an HTML safe way. + let_it_be(:unconfirmed_email) { "#{generate(:email)}<h2>testing<img/src=http://localhost:8000/test.png>" } + let_it_be(:unconfirmed_user) { create(:user, :unconfirmed, unconfirmed_email: unconfirmed_email) } + + where(:path_helper) do + [ + [-> (user) { admin_user_path(user) }], + [-> (user) { projects_admin_user_path(user) }], + [-> (user) { keys_admin_user_path(user) }], + [-> (user) { admin_user_identities_path(user) }], + [-> (user) { admin_user_impersonation_tokens_path(user) }] + ] + end + + with_them do + it "allows an admin to force confirmation of the user's email", :aggregate_failures do + visit path_helper.call(unconfirmed_user) + + click_button 'Confirm user' + + within_modal do + expect(page).to have_content("Confirm user #{unconfirmed_user.name}?") + expect(page).to( + have_content( + "This user has an unconfirmed email address (#{unconfirmed_email}). You may force a confirmation.") + ) + + click_button 'Confirm user' + end + + expect(page).to have_content('Successfully confirmed') + expect(page).not_to have_button('Confirm user') + end + end + end +end diff --git a/spec/features/admin/users/admin_sees_user_spec.rb b/spec/features/admin/users/admin_sees_user_spec.rb new file mode 100644 index 00000000000..d8c142d402f --- /dev/null +++ b/spec/features/admin/users/admin_sees_user_spec.rb @@ -0,0 +1,248 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Admin::Users::User', feature_category: :user_management do + include Features::AdminUsersHelpers + include Spec::Support::Helpers::ModalHelpers + + let_it_be(:user) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') } + let_it_be(:current_user) { create(:admin) } + + before do + sign_in(current_user) + enable_admin_mode!(current_user, use_ui: true) + end + + describe 'GET /admin/users/:id' do + it 'has user info', :js, :aggregate_failures do + visit admin_user_path(user) + + expect(page).to have_content(user.email) + expect(page).to have_content(user.name) + expect(page).to have_content("ID: #{user.id}") + expect(page).to have_content("Namespace ID: #{user.namespace_id}") + + click_user_dropdown_toggle(user.id) + + expect(page).to have_button('Block') + expect(page).to have_button('Deactivate') + expect(page).to have_button('Delete user') + expect(page).to have_button('Delete user and contributions') + end + + context 'when blocking/unblocking the user' do + it 'shows confirmation and allows blocking and unblocking', :js do + visit admin_user_path(user) + + click_action_in_user_dropdown(user.id, 'Block') + + expect(page).to have_content('Block user') + expect(page).to have_content('You can always unblock their account, their data will remain intact.') + + find('.modal-footer button', text: 'Block').click + + wait_for_requests + + expect(page).to have_content('Successfully blocked') + + click_action_in_user_dropdown(user.id, 'Unblock') + + expect(page).to have_content('Unblock user') + expect(page).to have_content('You can always block their account again if needed.') + + find('.modal-footer button', text: 'Unblock').click + + expect(page).to have_content('Successfully unblocked') + + click_user_dropdown_toggle(user.id) + expect(page).to have_content('Block') + end + end + + context 'when deactivating/re-activating the user' do + it 'shows confirmation and allows deactivating/re-activating', :js do + visit admin_user_path(user) + + click_action_in_user_dropdown(user.id, 'Deactivate') + + expect(page).to have_content('Deactivate user') + expect(page).to have_content('You can always re-activate their account, their data will remain intact.') + + find('.modal-footer button', text: 'Deactivate').click + + wait_for_requests + + expect(page).to have_content('Successfully deactivated') + + click_action_in_user_dropdown(user.id, 'Activate') + + expect(page).to have_content('Activate user') + expect(page).to have_content('You can always deactivate their account again if needed.') + + find('.modal-footer button', text: 'Activate').click + + wait_for_requests + + expect(page).to have_content('Successfully activated') + + click_user_dropdown_toggle(user.id) + expect(page).to have_content('Deactivate') + end + end + + context 'when user is the sole owner of a group' do + let_it_be(:group) { create(:group) } + let_it_be(:user_sole_owner_of_group) { create(:user) } + + before do + group.add_owner(user_sole_owner_of_group) + end + + it 'shows `Delete user and contributions` action but not `Delete user` action', :js do + visit admin_user_path(user_sole_owner_of_group) + + click_user_dropdown_toggle(user_sole_owner_of_group.id) + + expect(page).to have_button('Delete user and contributions') + expect(page).not_to have_button('Delete user', exact: true) + end + + it 'allows user to be deleted by using the `Delete user and contributions` action', :js do + visit admin_user_path(user_sole_owner_of_group) + + click_action_in_user_dropdown(user_sole_owner_of_group.id, 'Delete user and contributions') + + within_modal do + fill_in('username', with: user_sole_owner_of_group.name) + click_button('Delete user and contributions') + end + + wait_for_requests + + expect(page).to have_content('The user is being deleted.') + end + end + + context 'when a user is locked', time_travel_to: '2020-02-02 10:30:45 -0700' do + let_it_be(:locked_user) { create(:user, locked_at: DateTime.parse('2020-02-02 10:30:00 -0700')) } + + before do + visit admin_user_path(locked_user) + end + + it "displays `(Locked)` next to user's name" do + expect(page).to have_content("#{locked_user.name} (Locked)") + end + + it 'allows a user to be unlocked from the `User administration dropdown', :js do + accept_gl_confirm("Unlock user #{locked_user.name}?", button_text: 'Unlock') do + click_action_in_user_dropdown(locked_user.id, 'Unlock') + end + + expect(page).not_to have_content("#{locked_user.name} (Locked)") + end + end + + describe 'Two-factor Authentication status' do + it 'shows when enabled' do + user.update!(otp_required_for_login: true) + + visit admin_user_path(user) + + expect_two_factor_status('Enabled') + end + + it 'shows when disabled' do + user.update!(otp_required_for_login: false) + + visit admin_user_path(user) + + expect_two_factor_status('Disabled') + end + + def expect_two_factor_status(status) + page.within('.two-factor-status') do + expect(page).to have_content(status) + end + end + end + + describe 'Email verification status' do + let_it_be(:secondary_email) do + create :email, email: 'secondary@example.com', user: user + end + + it 'displays the correct status for an unverified email address', :aggregate_failures do + user.update!(confirmed_at: nil, unconfirmed_email: user.email) + visit admin_user_path(user) + + expect(page).to have_content("#{user.email} Unverified") + expect(page).to have_content("#{secondary_email.email} Unverified") + end + + it 'displays the correct status for a verified email address' do + visit admin_user_path(user) + expect(page).to have_content("#{user.email} Verified") + + secondary_email.confirm + expect(secondary_email.confirmed?).to be_truthy + + visit admin_user_path(user) + expect(page).to have_content("#{secondary_email.email} Verified") + end + end + + describe 'remove users secondary email', :js do + let_it_be(:secondary_email) do + create :email, email: 'secondary@example.com', user: user + end + + it do + visit admin_user_path(user.username) + + expect(page).to have_content("Secondary email: #{secondary_email.email}") + + accept_gl_confirm { find("#remove_email_#{secondary_email.id}").click } + + expect(page).not_to have_content(secondary_email.email) + end + end + + describe 'show user keys', :js do + it do + key1 = create(:key, user: user, title: 'ssh-rsa Key1') + key2 = create(:key, user: user, title: 'ssh-rsa Key2') + + visit admin_user_path(user) + + click_link 'SSH keys' + + expect(page).to have_content(key1.title) + expect(page).to have_content(key2.title) + + click_link key2.title + + expect(page).to have_content(key2.title) + expect(page).to have_content(key2.key) + + click_button 'Delete' + + page.within('.modal') do + page.click_button('Delete') + end + + expect(page).not_to have_content(key2.title) + end + end + + describe 'show user attributes' do + it 'has expected attributes', :aggregate_failures do + visit admin_user_path(user) + + expect(page).to have_content 'Account' + expect(page).to have_content 'Personal projects limit' + end + end + end +end diff --git a/spec/features/admin/users/user_spec.rb b/spec/features/admin/users/user_spec.rb deleted file mode 100644 index d0110b3e013..00000000000 --- a/spec/features/admin/users/user_spec.rb +++ /dev/null @@ -1,537 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Admin::Users::User', feature_category: :user_management do - include Features::AdminUsersHelpers - include Spec::Support::Helpers::ModalHelpers - - let_it_be(:user) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') } - let_it_be(:current_user) { create(:admin) } - - before do - sign_in(current_user) - gitlab_enable_admin_mode_sign_in(current_user, use_mock_admin_mode: false) - end - - describe 'GET /admin/users/:id' do - it 'has user info', :js, :aggregate_failures do - visit admin_user_path(user) - - expect(page).to have_content(user.email) - expect(page).to have_content(user.name) - expect(page).to have_content("ID: #{user.id}") - expect(page).to have_content("Namespace ID: #{user.namespace_id}") - - click_user_dropdown_toggle(user.id) - - expect(page).to have_button('Block') - expect(page).to have_button('Deactivate') - expect(page).to have_button('Delete user') - expect(page).to have_button('Delete user and contributions') - end - - context 'when blocking/unblocking the user' do - it 'shows confirmation and allows blocking and unblocking', :js do - visit admin_user_path(user) - - click_action_in_user_dropdown(user.id, 'Block') - - expect(page).to have_content('Block user') - expect(page).to have_content('You can always unblock their account, their data will remain intact.') - - find('.modal-footer button', text: 'Block').click - - wait_for_requests - - expect(page).to have_content('Successfully blocked') - - click_action_in_user_dropdown(user.id, 'Unblock') - - expect(page).to have_content('Unblock user') - expect(page).to have_content('You can always block their account again if needed.') - - find('.modal-footer button', text: 'Unblock').click - - expect(page).to have_content('Successfully unblocked') - - click_user_dropdown_toggle(user.id) - expect(page).to have_content('Block') - end - end - - context 'when deactivating/re-activating the user' do - it 'shows confirmation and allows deactivating/re-activating', :js do - visit admin_user_path(user) - - click_action_in_user_dropdown(user.id, 'Deactivate') - - expect(page).to have_content('Deactivate user') - expect(page).to have_content('You can always re-activate their account, their data will remain intact.') - - find('.modal-footer button', text: 'Deactivate').click - - wait_for_requests - - expect(page).to have_content('Successfully deactivated') - - click_action_in_user_dropdown(user.id, 'Activate') - - expect(page).to have_content('Activate user') - expect(page).to have_content('You can always deactivate their account again if needed.') - - find('.modal-footer button', text: 'Activate').click - - wait_for_requests - - expect(page).to have_content('Successfully activated') - - click_user_dropdown_toggle(user.id) - expect(page).to have_content('Deactivate') - end - end - - context 'when user is the sole owner of a group' do - let_it_be(:group) { create(:group) } - let_it_be(:user_sole_owner_of_group) { create(:user) } - - before do - group.add_owner(user_sole_owner_of_group) - end - - it 'shows `Delete user and contributions` action but not `Delete user` action', :js do - visit admin_user_path(user_sole_owner_of_group) - - click_user_dropdown_toggle(user_sole_owner_of_group.id) - - expect(page).to have_button('Delete user and contributions') - expect(page).not_to have_button('Delete user', exact: true) - end - - it 'allows user to be deleted by using the `Delete user and contributions` action', :js do - visit admin_user_path(user_sole_owner_of_group) - - click_action_in_user_dropdown(user_sole_owner_of_group.id, 'Delete user and contributions') - - within_modal do - fill_in('username', with: user_sole_owner_of_group.name) - click_button('Delete user and contributions') - end - - wait_for_requests - - expect(page).to have_content('The user is being deleted.') - end - end - - context 'when a user is locked', time_travel_to: '2020-02-02 10:30:45 -0700' do - let_it_be(:locked_user) { create(:user, locked_at: DateTime.parse('2020-02-02 10:30:00 -0700')) } - - before do - visit admin_user_path(locked_user) - end - - it "displays `(Locked)` next to user's name" do - expect(page).to have_content("#{locked_user.name} (Locked)") - end - - it 'allows a user to be unlocked from the `User administration dropdown', :js do - accept_gl_confirm("Unlock user #{locked_user.name}?", button_text: 'Unlock') do - click_action_in_user_dropdown(locked_user.id, 'Unlock') - end - - expect(page).not_to have_content("#{locked_user.name} (Locked)") - end - end - - describe 'Impersonation' do - let_it_be(:another_user) { create(:user) } - - context 'before impersonating' do - subject { visit admin_user_path(user_to_visit) } - - let_it_be(:user_to_visit) { another_user } - - shared_examples "user that cannot be impersonated" do - it 'disables impersonate button' do - subject - - impersonate_btn = find_by_testid('impersonate-user-link') - - expect(impersonate_btn).not_to be_nil - expect(impersonate_btn['disabled']).not_to be_nil - end - - it "shows tooltip with correct error message" do - subject - - expect(find("span[title='#{impersonation_error_msg}']")).not_to be_nil - end - end - - context 'for other users' do - it 'shows impersonate button for other users' do - subject - - expect(page).to have_content('Impersonate') - impersonate_btn = find_by_testid('impersonate-user-link') - expect(impersonate_btn['disabled']).to be_nil - end - end - - context 'for admin itself' do - let(:user_to_visit) { current_user } - - it 'does not show impersonate button for admin itself' do - subject - - expect(page).not_to have_content('Impersonate') - end - end - - context 'for blocked user' do - let_it_be(:blocked_user) { create(:user, :blocked) } - let(:user_to_visit) { blocked_user } - let(:impersonation_error_msg) { _('You cannot impersonate a blocked user') } - - it_behaves_like "user that cannot be impersonated" - end - - context 'for user with expired password' do - let(:user_to_visit) do - another_user.update!(password_expires_at: Time.zone.now - 5.minutes) - another_user - end - - let(:impersonation_error_msg) { _("You cannot impersonate a user with an expired password") } - - it_behaves_like "user that cannot be impersonated" - end - - context 'for internal user' do - let_it_be(:internal_user) { create(:user, :bot) } - let(:user_to_visit) { internal_user } - let(:impersonation_error_msg) { _("You cannot impersonate an internal user") } - - it_behaves_like "user that cannot be impersonated" - end - - context 'for locked user' do - let_it_be(:locked_user) { create(:user, :locked) } - let(:user_to_visit) { locked_user } - let(:impersonation_error_msg) { _("You cannot impersonate a user who cannot log in") } - - it_behaves_like "user that cannot be impersonated" - end - - context 'when already impersonating another user' do - let_it_be(:admin_user) { create(:user, :admin) } - let(:impersonation_error_msg) { _("You are already impersonating another user") } - - subject do - visit admin_user_path(admin_user) - click_link 'Impersonate' - visit admin_user_path(another_user) - end - - it_behaves_like "user that cannot be impersonated" - end - - context 'when impersonation is disabled' do - before do - stub_config_setting(impersonation_enabled: false) - end - - it 'does not show impersonate button' do - subject - - expect(page).not_to have_content('Impersonate') - end - end - end - - context 'when impersonating' do - subject { click_link 'Impersonate' } - - before do - visit admin_user_path(another_user) - end - - it 'logs in as the user when impersonate is clicked', :js do - subject - - expect(page).to have_button("#{another_user.name} user’s menu") - end - - it 'sees impersonation log out icon', :js do - subject - - icon = first('[data-testid="incognito-icon"]') - expect(icon).not_to be nil - end - - context 'when viewing the confirm email warning', :js do - before do - stub_application_setting_enum('email_confirmation_setting', 'soft') - end - - let_it_be(:another_user) { create(:user, :unconfirmed) } - let(:warning_alert) { page.find(:css, '[data-testid="alert-warning"]') } - let(:expected_styling) { { 'pointer-events' => 'none', 'cursor' => 'default' } } - - context 'with an email that does not contain HTML' do - before do - subject - end - - it 'displays the warning alert including the email' do - expect(warning_alert.text).to include("Please check your email (#{another_user.email}) to verify") - end - end - - context 'with an email that contains HTML' do - let(:malicious_email) { "malicious@test.com<form><input/title='<script>alert(document.domain)</script>'>" } - let(:another_user) { create(:user, confirmed_at: nil, unconfirmed_email: malicious_email) } - - before do - subject - end - - it 'displays the impersonation alert, excludes email, and disables links' do - expect(warning_alert.text).to include("check your email (#{another_user.unconfirmed_email}) to verify") - end - end - end - end - - context 'ending impersonation', :js do - subject { click_on 'Stop impersonating' } - - before do - visit admin_user_path(another_user) - click_link 'Impersonate' - end - - it 'logs out of impersonated user back to original user' do - subject - - expect(page).to have_button("#{current_user.name} user’s menu") - end - - it 'is redirected back to the impersonated users page in the admin after stopping' do - subject - - expect(page).to have_current_path("/admin/users/#{another_user.username}", ignore_query: true) - end - - context 'a user with an expired password' do - before do - another_user.update!(password_expires_at: Time.zone.now - 5.minutes) - end - - it 'is redirected back to the impersonated users page in the admin after stopping' do - subject - - expect(page).to have_current_path("/admin/users/#{another_user.username}", ignore_query: true) - end - end - end - end - - describe 'Two-factor Authentication status' do - it 'shows when enabled' do - user.update!(otp_required_for_login: true) - - visit admin_user_path(user) - - expect_two_factor_status('Enabled') - end - - it 'shows when disabled' do - user.update!(otp_required_for_login: false) - - visit admin_user_path(user) - - expect_two_factor_status('Disabled') - end - - def expect_two_factor_status(status) - page.within('.two-factor-status') do - expect(page).to have_content(status) - end - end - end - - describe 'Email verification status' do - let_it_be(:secondary_email) do - create :email, email: 'secondary@example.com', user: user - end - - it 'displays the correct status for an unverified email address', :aggregate_failures do - user.update!(confirmed_at: nil, unconfirmed_email: user.email) - visit admin_user_path(user) - - expect(page).to have_content("#{user.email} Unverified") - expect(page).to have_content("#{secondary_email.email} Unverified") - end - - it 'displays the correct status for a verified email address' do - visit admin_user_path(user) - expect(page).to have_content("#{user.email} Verified") - - secondary_email.confirm - expect(secondary_email.confirmed?).to be_truthy - - visit admin_user_path(user) - expect(page).to have_content("#{secondary_email.email} Verified") - end - end - - describe 'show user identities' do - it 'shows user identities', :aggregate_failures do - visit admin_user_identities_path(user) - - expect(page).to have_content(user.name) - expect(page).to have_content('twitter') - end - end - - describe 'update user identities' do - before do - allow(Gitlab::Auth::OAuth::Provider).to receive(:providers).and_return([:twitter, :twitter_updated]) - end - - it 'modifies twitter identity', :aggregate_failures do - visit admin_user_identities_path(user) - - find('.table').find(:link, 'Edit').click - fill_in 'identity_extern_uid', with: '654321' - select 'twitter_updated', from: 'identity_provider' - click_button 'Save changes' - - expect(page).to have_content(user.name) - expect(page).to have_content('twitter_updated') - expect(page).to have_content('654321') - end - end - - describe 'remove users secondary email', :js do - let_it_be(:secondary_email) do - create :email, email: 'secondary@example.com', user: user - end - - it do - visit admin_user_path(user.username) - - expect(page).to have_content("Secondary email: #{secondary_email.email}") - - accept_gl_confirm { find("#remove_email_#{secondary_email.id}").click } - - expect(page).not_to have_content(secondary_email.email) - end - end - - describe 'remove user with identities' do - it 'removes user with twitter identity', :aggregate_failures do - visit admin_user_identities_path(user) - - click_link 'Delete' - - expect(page).to have_content(user.name) - expect(page).not_to have_content('twitter') - end - end - - describe 'show user keys', :js do - it do - key1 = create(:key, user: user, title: 'ssh-rsa Key1') - key2 = create(:key, user: user, title: 'ssh-rsa Key2') - - visit admin_user_path(user) - - click_link 'SSH keys' - - expect(page).to have_content(key1.title) - expect(page).to have_content(key2.title) - - click_link key2.title - - expect(page).to have_content(key2.title) - expect(page).to have_content(key2.key) - - click_button 'Delete' - - page.within('.modal') do - page.click_button('Delete') - end - - expect(page).not_to have_content(key2.title) - end - end - - describe 'show user attributes' do - it 'has expected attributes', :aggregate_failures do - visit admin_user_path(user) - - expect(page).to have_content 'Account' - expect(page).to have_content 'Personal projects limit' - end - end - end - - describe 'GET /admin/users', :js do - context 'user pending approval' do - it 'shows user info', :aggregate_failures do - user = create(:user, :blocked_pending_approval) - - visit admin_users_path - click_link 'Pending approval' - click_link user.name - - expect(page).to have_content(user.name) - expect(page).to have_content('Pending approval') - - click_user_dropdown_toggle(user.id) - - expect(page).to have_button('Approve') - expect(page).to have_button('Reject') - end - end - end - - context 'when user has an unconfirmed email', :js do - # Email address contains HTML to ensure email address is displayed in an HTML safe way. - let_it_be(:unconfirmed_email) { "#{generate(:email)}<h2>testing<img/src=http://localhost:8000/test.png>" } - let_it_be(:unconfirmed_user) { create(:user, :unconfirmed, unconfirmed_email: unconfirmed_email) } - - where(:path_helper) do - [ - [-> (user) { admin_user_path(user) }], - [-> (user) { projects_admin_user_path(user) }], - [-> (user) { keys_admin_user_path(user) }], - [-> (user) { admin_user_identities_path(user) }], - [-> (user) { admin_user_impersonation_tokens_path(user) }] - ] - end - - with_them do - it "allows an admin to force confirmation of the user's email", :aggregate_failures do - visit path_helper.call(unconfirmed_user) - - click_button 'Confirm user' - - within_modal do - expect(page).to have_content("Confirm user #{unconfirmed_user.name}?") - expect(page).to have_content( - "This user has an unconfirmed email address (#{unconfirmed_email}). You may force a confirmation." - ) - - click_button 'Confirm user' - end - - expect(page).to have_content('Successfully confirmed') - expect(page).not_to have_button('Confirm user') - end - end - end -end diff --git a/spec/features/admin/users/users_spec.rb b/spec/features/admin/users/users_spec.rb index 4e988674858..20cedda626b 100644 --- a/spec/features/admin/users/users_spec.rb +++ b/spec/features/admin/users/users_spec.rb @@ -12,7 +12,7 @@ RSpec.describe 'Admin::Users', feature_category: :user_management do before do sign_in(current_user) - gitlab_enable_admin_mode_sign_in(current_user) + enable_admin_mode!(current_user) end describe 'GET /admin/users', :js do @@ -381,6 +381,24 @@ RSpec.describe 'Admin::Users', feature_category: :user_management do expect(find_by_testid("user-group-count-#{current_user.id}").text).to eq("2") end end + + context 'user pending approval' do + it 'shows user info', :aggregate_failures do + user = create(:user, :blocked_pending_approval) + + visit admin_users_path + click_link 'Pending approval' + click_link user.name + + expect(page).to have_content(user.name) + expect(page).to have_content('Pending approval') + + click_user_dropdown_toggle(user.id) + + expect(page).to have_button('Approve') + expect(page).to have_button('Reject') + end + end end describe 'GET /admin/users/new' do |