# frozen_string_literal: true describe "Changing email", type: :system do fab!(:password) { "mysupersecurepassword" } fab!(:user) { Fabricate(:user, active: true, password: password) } let(:new_email) { "newemail@example.com" } let(:user_preferences_security_page) { PageObjects::Pages::UserPreferencesSecurity.new } before { Jobs.run_immediately! } def generate_confirm_link visit "/my/preferences/account" email_dropdown = PageObjects::Components::SelectKit.new(".email-dropdown") expect(email_dropdown.visible?).to eq(true) email_dropdown.select_row_by_value("updateEmail") find("#change-email").fill_in with: "newemail@example.com" find(".save-button button").click wait_for(timeout: Capybara.default_max_wait_time) { ActionMailer::Base.deliveries.count === 1 } if user.admin? get_link_from_email(:old) else get_link_from_email(:new) end end def get_link_from_email(type) mail = ActionMailer::Base.deliveries.last expect(mail.to).to contain_exactly(type == :new ? new_email : user.email) mail.body.to_s[%r{/u/confirm-#{type}-email/\S+}, 0] end it "allows regular user to change their email" do sign_in user visit generate_confirm_link find(".confirm-new-email .btn-primary").click expect(page).to have_css(".dialog-body", text: I18n.t("js.user.change_email.confirm_success")) find(".dialog-footer .btn-primary").click expect(user.reload.email).to eq(new_email) end it "works when user has totp 2fa" do second_factor = Fabricate(:user_second_factor_totp, user: user) sign_in user visit generate_confirm_link find(".confirm-new-email .btn-primary").click find(".second-factor-token-input").fill_in with: second_factor.totp_object.now find("button[type=submit]").click try_until_success { expect(current_url).to match("/u/#{user.username}/preferences/account") } expect(user.reload.email).to eq(new_email) end it "works when user has webauthn 2fa" do # enforced 2FA flow needs a user created > 5 minutes ago user.created_at = 6.minutes.ago user.save! sign_in user DiscourseWebauthn.stubs(:origin).returns(current_host + ":" + Capybara.server_port.to_s) options = ::Selenium::WebDriver::VirtualAuthenticatorOptions.new( user_verification: true, user_verified: true, resident_key: true, ) authenticator = page.driver.browser.add_virtual_authenticator(options) user_preferences_security_page.visit(user) user_preferences_security_page.visit_second_factor(password) find(".security-key .new-security-key").click expect(user_preferences_security_page).to have_css("input#security-key-name") find(".d-modal__body input#security-key-name").fill_in(with: "First Key") find(".add-security-key").click expect(user_preferences_security_page).to have_css(".security-key .second-factor-item") visit generate_confirm_link find(".confirm-new-email .btn-primary").click find("#security-key-authenticate-button").click try_until_success { expect(current_url).to match("/u/#{user.username}/preferences/account") } expect(user.reload.email).to eq(new_email) ensure authenticator.remove! end it "does not require login to verify" do second_factor = Fabricate(:user_second_factor_totp, user: user) sign_in user confirm_link = generate_confirm_link Capybara.reset_sessions! # log out visit confirm_link find(".confirm-new-email .btn-primary").click find(".second-factor-token-input").fill_in with: second_factor.totp_object.now find("button[type=submit]").click try_until_success { expect(current_url).to match("/latest") } expect(user.reload.email).to eq(new_email) end it "makes admins verify old email" do user.update!(admin: true) sign_in user confirm_old_link = generate_confirm_link # Confirm old email visit confirm_old_link find(".confirm-old-email .btn-primary").click expect(page).to have_css( ".dialog-body", text: I18n.t("js.user.change_email.authorizing_old.confirm_success"), ) find(".dialog-footer .btn-primary").click # Confirm new email wait_for(timeout: Capybara.default_max_wait_time) { ActionMailer::Base.deliveries.count === 2 } confirm_new_link = get_link_from_email(:new) visit confirm_new_link find(".confirm-new-email .btn-primary").click expect(page).to have_css(".dialog-body", text: I18n.t("js.user.change_email.confirm_success")) find(".dialog-footer .btn-primary").click expect(user.reload.email).to eq(new_email) end it "allows admin to verify old email while logged out" do user.update!(admin: true) sign_in user confirm_old_link = generate_confirm_link Capybara.reset_sessions! # log out # Confirm old email visit confirm_old_link find(".confirm-old-email .btn-primary").click expect(page).to have_css( ".dialog-body", text: I18n.t("js.user.change_email.authorizing_old.confirm_success"), ) find(".dialog-footer .btn-primary").click # Confirm new email wait_for(timeout: Capybara.default_max_wait_time) { ActionMailer::Base.deliveries.count === 2 } confirm_new_link = get_link_from_email(:new) visit confirm_new_link find(".confirm-new-email .btn-primary").click expect(page).to have_css(".dialog-body", text: I18n.t("js.user.change_email.confirm_success")) find(".dialog-footer .btn-primary").click expect(user.reload.email).to eq(new_email) end end