password_spec.rb 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. require 'rails_helper'
  3. require 'system/examples/security_keys_setup_examples'
  4. require 'system/examples/authenticator_app_setup_examples'
  5. RSpec.describe 'Profile > Password', authenticated_as: :user, type: :system do
  6. let(:user) { create(:customer, :with_valid_password) }
  7. describe 'visibility' do
  8. it 'not available if both two factor and password changing disabled' do
  9. password_and_authenticate(password: false, two_factor: false)
  10. visit 'profile/'
  11. expect(page).to have_no_text('Password & Authentication')
  12. end
  13. it 'shows only password changing form if two factor disabled' do
  14. password_and_authenticate(password: true, two_factor: false)
  15. visit 'profile/password'
  16. expect(page)
  17. .to have_text('Change Your Password')
  18. .and have_no_text('Two-factor Authentication')
  19. end
  20. it 'shows only two factor if password changing disabled' do
  21. password_and_authenticate(password: false, two_factor: true)
  22. visit 'profile/password'
  23. expect(page)
  24. .to have_no_text('Change Your Password')
  25. .and have_text('Two-factor Authentication')
  26. end
  27. it 'shows two factor if another two factor method enabled' do
  28. password_and_authenticate(password: false, two_factor: false, alternative_two_factor: true)
  29. visit 'profile/password'
  30. expect(page)
  31. .to have_no_text('Change Your Password')
  32. .and have_text('Two-factor Authentication')
  33. end
  34. context 'when user has no two factor permission' do
  35. before do
  36. user.roles.each { |role| role.permission_revoke('user_preferences.two_factor_authentication') }
  37. end
  38. it 'not available if only two factor is enabled' do
  39. password_and_authenticate(password: false, two_factor: true)
  40. visit 'profile/'
  41. expect(page).to have_no_text('Password & Authentication')
  42. end
  43. it 'shows only password changing form even if two factor enabled' do
  44. password_and_authenticate(password: true, two_factor: true)
  45. visit 'profile/password'
  46. expect(page)
  47. .to have_text('Change Your Password')
  48. .and have_no_text('Two-factor Authentication')
  49. end
  50. end
  51. def password_and_authenticate(password:, two_factor:, alternative_two_factor: false)
  52. Setting.set('two_factor_authentication_method_authenticator_app', two_factor)
  53. Setting.set('two_factor_authentication_method_security_keys', alternative_two_factor)
  54. Setting.set('two_factor_authentication_enforce_role_ids', [])
  55. Setting.set('user_show_password_login', password)
  56. end
  57. end
  58. context 'when changing password' do
  59. before do
  60. visit 'profile/password'
  61. end
  62. it 'when current password is wrong, show error' do
  63. fill_in 'password_old', with: 'nonexisting'
  64. fill_in 'password_new', with: 'some'
  65. fill_in 'password_new_confirm', with: 'some'
  66. click '.btn--primary'
  67. expect(page).to have_text 'The current password you provided is incorrect.'
  68. end
  69. it 'when new passwords do not match, show error' do
  70. fill_in 'password_old', with: user.password_plain
  71. fill_in 'password_new', with: 'some'
  72. fill_in 'password_new_confirm', with: 'some2'
  73. click '.btn--primary'
  74. expect(page).to have_text 'passwords do not match'
  75. end
  76. it 'when new password is invalid, show error' do
  77. fill_in 'password_old', with: user.password_plain
  78. fill_in 'password_new', with: 'some'
  79. fill_in 'password_new_confirm', with: 'some'
  80. click '.btn--primary'
  81. expect(page).to have_text 'Invalid password'
  82. end
  83. it 'allows to change password' do
  84. new_password = generate(:password_valid)
  85. fill_in 'password_old', with: user.password_plain
  86. fill_in 'password_new', with: new_password
  87. fill_in 'password_new_confirm', with: new_password
  88. click '.btn--primary'
  89. expect(page).to have_text 'Password changed successfully!'
  90. end
  91. end
  92. context 'when managing two factor authentication' do
  93. before do
  94. Setting.set('two_factor_authentication_method_authenticator_app', true)
  95. Setting.set('two_factor_authentication_recovery_codes', false)
  96. Setting.set('two_factor_authentication_enforce_role_ids', [])
  97. end
  98. context 'without a configured method' do
  99. before do
  100. visit 'profile/password'
  101. end
  102. it 'shows available method' do
  103. expect(page).to have_text('Authenticator App')
  104. end
  105. it 'allows to setup two factor authentication' do
  106. within('tr[data-two-factor-key="authenticator_app"]') do
  107. expect(page).to have_css('.icon.icon-small-dot')
  108. click '.js-action'
  109. find('a', text: 'Set Up').click
  110. end
  111. setup_authenticator_app_method(user: user, password_check: user.password_plain)
  112. within('tr[data-two-factor-key="authenticator_app"]') do
  113. expect(page).to have_css('.icon.icon-checkmark')
  114. expect(page).to have_text('Default')
  115. end
  116. end
  117. it 'does not show recovery codes button' do
  118. expect(page).to have_no_text('recovery codes')
  119. end
  120. end
  121. context 'with a configured method' do
  122. let(:recovery_codes_enabled) { true }
  123. before do
  124. Setting.set('two_factor_authentication_recovery_codes', recovery_codes_enabled)
  125. create(:user_two_factor_preference, :authenticator_app, user: user)
  126. visit 'profile/password'
  127. end
  128. it 'shows configured method as ready' do
  129. within('tr[data-two-factor-key="authenticator_app"]') do
  130. expect(page).to have_css('.icon.icon-checkmark')
  131. expect(page).to have_text('Default')
  132. end
  133. end
  134. it 'allows to remove an existing two factor authentication' do
  135. within('tr[data-two-factor-key="authenticator_app"]') do
  136. click '.js-action'
  137. find('a', text: 'Remove').click
  138. end
  139. in_modal do
  140. fill_in 'Password', with: user.password_plain
  141. click_on 'Remove'
  142. end
  143. within('tr[data-two-factor-key="authenticator_app"]') do
  144. expect(page).to have_css('.icon.icon-small-dot')
  145. end
  146. end
  147. it 'allows to regenerate recovery codes' do
  148. click '.js-generate-recovery-codes'
  149. in_modal do
  150. expect(page).to have_text('Generate recovery codes: Confirm Password')
  151. fill_in 'Password', with: user.password_plain
  152. click_on 'Next'
  153. end
  154. in_modal do
  155. expect(page).to have_text('Generate recovery codes: Save Codes')
  156. stored_codes_amount = user.two_factor_preferences.recovery_codes.configuration[:codes].count
  157. displayed_codes_amount = find('.two-factor-auth code').text.tr("\n", ' ').split.count
  158. expect(stored_codes_amount).to eq(displayed_codes_amount)
  159. expect(page).to have_button("OK, I've saved my recovery codes")
  160. end
  161. end
  162. context 'when recovery codes disabled' do
  163. let(:recovery_codes_enabled) { false }
  164. it 'does not show recovery codes button if recovery codes disabled' do
  165. expect(page).to have_no_text('recovery codes')
  166. end
  167. end
  168. context 'with security keys method' do
  169. before do
  170. within('tr[data-two-factor-key="security_keys"]') do
  171. click '.js-action'
  172. find('a', text: 'Set Up').click
  173. end
  174. end
  175. include_examples 'security keys setup' do
  176. let(:current_user) { user }
  177. end
  178. end
  179. end
  180. end
  181. end