123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 |
- # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
- require 'rails_helper'
- require 'system/examples/security_keys_setup_examples'
- require 'system/examples/authenticator_app_setup_examples'
- RSpec.describe 'Profile > Password', authenticated_as: :user, type: :system do
- let(:user) { create(:customer, :with_valid_password) }
- describe 'visibility' do
- it 'not available if both two factor and password changing disabled' do
- password_and_authenticate(password: false, two_factor: false)
- visit 'profile/'
- expect(page).to have_no_text('Password & Authentication')
- end
- it 'shows only password changing form if two factor disabled' do
- password_and_authenticate(password: true, two_factor: false)
- visit 'profile/password'
- expect(page)
- .to have_text('Change Your Password')
- .and have_no_text('Two-factor Authentication')
- end
- it 'shows only two factor if password changing disabled' do
- password_and_authenticate(password: false, two_factor: true)
- visit 'profile/password'
- expect(page)
- .to have_no_text('Change Your Password')
- .and have_text('Two-factor Authentication')
- end
- it 'shows two factor if another two factor method enabled' do
- password_and_authenticate(password: false, two_factor: false, alternative_two_factor: true)
- visit 'profile/password'
- expect(page)
- .to have_no_text('Change Your Password')
- .and have_text('Two-factor Authentication')
- end
- context 'when user has no two factor permission' do
- before do
- user.roles.each { |role| role.permission_revoke('user_preferences.two_factor_authentication') }
- end
- it 'not available if only two factor is enabled' do
- password_and_authenticate(password: false, two_factor: true)
- visit 'profile/'
- expect(page).to have_no_text('Password & Authentication')
- end
- it 'shows only password changing form even if two factor enabled' do
- password_and_authenticate(password: true, two_factor: true)
- visit 'profile/password'
- expect(page)
- .to have_text('Change Your Password')
- .and have_no_text('Two-factor Authentication')
- end
- end
- def password_and_authenticate(password:, two_factor:, alternative_two_factor: false)
- Setting.set('two_factor_authentication_method_authenticator_app', two_factor)
- Setting.set('two_factor_authentication_method_security_keys', alternative_two_factor)
- Setting.set('two_factor_authentication_enforce_role_ids', [])
- Setting.set('user_show_password_login', password)
- end
- end
- context 'when changing password' do
- before do
- visit 'profile/password'
- end
- it 'when current password is wrong, show error' do
- fill_in 'password_old', with: 'nonexisting'
- fill_in 'password_new', with: 'some'
- fill_in 'password_new_confirm', with: 'some'
- click '.btn--primary'
- expect(page).to have_text 'The current password you provided is incorrect.'
- end
- it 'when new passwords do not match, show error' do
- fill_in 'password_old', with: user.password_plain
- fill_in 'password_new', with: 'some'
- fill_in 'password_new_confirm', with: 'some2'
- click '.btn--primary'
- expect(page).to have_text 'passwords do not match'
- end
- it 'when new password is invalid, show error' do
- fill_in 'password_old', with: user.password_plain
- fill_in 'password_new', with: 'some'
- fill_in 'password_new_confirm', with: 'some'
- click '.btn--primary'
- expect(page).to have_text 'Invalid password'
- end
- it 'allows to change password' do
- new_password = generate(:password_valid)
- fill_in 'password_old', with: user.password_plain
- fill_in 'password_new', with: new_password
- fill_in 'password_new_confirm', with: new_password
- click '.btn--primary'
- expect(page).to have_text 'Password changed successfully!'
- end
- end
- context 'when managing two factor authentication' do
- before do
- Setting.set('two_factor_authentication_method_authenticator_app', true)
- Setting.set('two_factor_authentication_recovery_codes', false)
- Setting.set('two_factor_authentication_enforce_role_ids', [])
- end
- context 'without a configured method' do
- before do
- visit 'profile/password'
- end
- it 'shows available method' do
- expect(page).to have_text('Authenticator App')
- end
- it 'allows to setup two factor authentication' do
- within('tr[data-two-factor-key="authenticator_app"]') do
- expect(page).to have_css('.icon.icon-small-dot')
- click '.js-action'
- find('a', text: 'Set Up').click
- end
- setup_authenticator_app_method(user: user, password_check: user.password_plain)
- within('tr[data-two-factor-key="authenticator_app"]') do
- expect(page).to have_css('.icon.icon-checkmark')
- expect(page).to have_text('Default')
- end
- end
- it 'does not show recovery codes button' do
- expect(page).to have_no_text('recovery codes')
- end
- end
- context 'with a configured method' do
- let(:recovery_codes_enabled) { true }
- before do
- Setting.set('two_factor_authentication_recovery_codes', recovery_codes_enabled)
- create(:user_two_factor_preference, :authenticator_app, user: user)
- visit 'profile/password'
- end
- it 'shows configured method as ready' do
- within('tr[data-two-factor-key="authenticator_app"]') do
- expect(page).to have_css('.icon.icon-checkmark')
- expect(page).to have_text('Default')
- end
- end
- it 'allows to remove an existing two factor authentication' do
- within('tr[data-two-factor-key="authenticator_app"]') do
- click '.js-action'
- find('a', text: 'Remove').click
- end
- in_modal do
- fill_in 'Password', with: user.password_plain
- click_on 'Remove'
- end
- within('tr[data-two-factor-key="authenticator_app"]') do
- expect(page).to have_css('.icon.icon-small-dot')
- end
- end
- it 'allows to regenerate recovery codes' do
- click '.js-generate-recovery-codes'
- in_modal do
- expect(page).to have_text('Generate recovery codes: Confirm Password')
- fill_in 'Password', with: user.password_plain
- click_on 'Next'
- end
- in_modal do
- expect(page).to have_text('Generate recovery codes: Save Codes')
- stored_codes_amount = user.two_factor_preferences.recovery_codes.configuration[:codes].count
- displayed_codes_amount = find('.two-factor-auth code').text.tr("\n", ' ').split.count
- expect(stored_codes_amount).to eq(displayed_codes_amount)
- expect(page).to have_button("OK, I've saved my recovery codes")
- end
- end
- context 'when recovery codes disabled' do
- let(:recovery_codes_enabled) { false }
- it 'does not show recovery codes button if recovery codes disabled' do
- expect(page).to have_no_text('recovery codes')
- end
- end
- context 'with security keys method' do
- before do
- within('tr[data-two-factor-key="security_keys"]') do
- click '.js-action'
- find('a', text: 'Set Up').click
- end
- end
- include_examples 'security keys setup' do
- let(:current_user) { user }
- end
- end
- end
- end
- end
|