From d13e773058f23b361f7adf43f492b69756eb768f Mon Sep 17 00:00:00 2001 From: Matthew MacRae-Bovell Date: Fri, 11 Jul 2025 11:18:25 -0400 Subject: [PATCH 1/6] Delete offer_email_reset ff and "update email" at sign up functionality Changelog: changed --- ...e_end_string_concatenation_indentation.yml | 2 - .../concerns/verifies_with_email.rb | 24 ---- app/helpers/sessions_helper.rb | 12 +- app/models/user.rb | 1 - .../update_email_service.rb | 76 ----------- app/views/admin/users/show.html.haml | 5 - .../feature_flags/ops/offer_email_reset.yml | 8 -- config/routes/user.rb | 1 - doc/api/openapi/openapi_v2.yaml | 2 - lib/api/entities/user_with_admin.rb | 1 - locale/gitlab.pot | 9 -- .../users/email_verification_on_login_spec.rb | 22 --- spec/helpers/sessions_helper_spec.rb | 34 +---- spec/models/user_spec.rb | 3 - spec/requests/verifies_with_email_spec.rb | 126 +----------------- .../update_email_service_spec.rb | 119 ----------------- 16 files changed, 8 insertions(+), 437 deletions(-) delete mode 100644 app/services/users/email_verification/update_email_service.rb delete mode 100644 config/feature_flags/ops/offer_email_reset.yml delete mode 100644 spec/services/users/email_verification/update_email_service_spec.rb diff --git a/.rubocop_todo/layout/line_end_string_concatenation_indentation.yml b/.rubocop_todo/layout/line_end_string_concatenation_indentation.yml index 08040a62cd7a29..7cda2111b43cb2 100644 --- a/.rubocop_todo/layout/line_end_string_concatenation_indentation.yml +++ b/.rubocop_todo/layout/line_end_string_concatenation_indentation.yml @@ -27,7 +27,6 @@ Layout/LineEndStringConcatenationIndentation: - 'app/services/security/ci_configuration/base_create_service.rb' - 'app/services/uploads/destroy_service.rb' - 'app/services/users/deactivate_service.rb' - - 'app/services/users/email_verification/update_email_service.rb' - 'app/services/users/email_verification/validate_token_service.rb' - 'app/workers/clusters/applications/deactivate_integration_worker.rb' - 'app/workers/integrations/irker_worker.rb' @@ -563,7 +562,6 @@ Layout/LineEndStringConcatenationIndentation: - 'spec/services/security/ci_configuration/sast_create_service_spec.rb' - 'spec/services/snippets/create_service_spec.rb' - 'spec/services/uploads/destroy_service_spec.rb' - - 'spec/services/users/email_verification/update_email_service_spec.rb' - 'spec/services/users/email_verification/validate_token_service_spec.rb' - 'spec/services/users/migrate_records_to_ghost_user_service_spec.rb' - 'spec/services/web_hook_service_spec.rb' diff --git a/app/controllers/concerns/verifies_with_email.rb b/app/controllers/concerns/verifies_with_email.rb index 3ded2b4d265c07..66d70ab98fd750 100644 --- a/app/controllers/concerns/verifies_with_email.rb +++ b/app/controllers/concerns/verifies_with_email.rb @@ -51,22 +51,6 @@ def resend_verification_code end end - def update_email - return unless user = find_verification_user - return render json: { status: :error, message: 'Feature not available' } unless offer_email_reset_enabled?(user) - - log_verification(user, :email_update_requested) - result = Users::EmailVerification::UpdateEmailService.new(user: user).execute(email: email_params[:email]) - - if result[:status] == :success - send_verification_instructions(user) - else - handle_verification_failure(user, result[:reason], result[:message]) - end - - render json: result - end - def successful_verification session.delete(:verification_user_id) @redirect_url = after_sign_in_path_for(current_user) # rubocop:disable Gitlab/ModuleWithInstanceVariables @@ -152,7 +136,6 @@ def handle_verification_failure(user, reason, message) end def handle_verification_success(user) - handle_email_reset_on_verification(user) user.unlock_access! log_verification(user, :successful) @@ -163,13 +146,6 @@ def handle_verification_success(user) verify_known_sign_in end - def handle_email_reset_on_verification(user) - return unless offer_email_reset_enabled?(user) - - user.confirm if unconfirmed_verification_email?(user) - user.email_reset_offered_at = Time.current - end - def trusted_ip_address?(user) AuthenticationEvent.initial_login_or_known_ip_address?(user, request.ip) end diff --git a/app/helpers/sessions_helper.rb b/app/helpers/sessions_helper.rb index 6c9e85b37446d7..3be66f4988c020 100644 --- a/app/helpers/sessions_helper.rb +++ b/app/helpers/sessions_helper.rb @@ -23,7 +23,7 @@ def remember_me_enabled? def unconfirmed_verification_email?(user) token_valid_from = ::Users::EmailVerification::ValidateTokenService::TOKEN_VALID_FOR_MINUTES.minutes.ago - user.email_reset_offered_at.nil? && user.pending_reconfirmation? && user.confirmation_sent_at >= token_valid_from + user.pending_reconfirmation? && user.confirmation_sent_at >= token_valid_from end def verification_email(user) @@ -35,15 +35,7 @@ def verification_data(user) username: user.username, obfuscated_email: obfuscated_email(verification_email(user)), verify_path: session_path(:user), - resend_path: users_resend_verification_code_path, - offer_email_reset: offer_email_reset_enabled?(user).to_s, - update_email_path: users_update_email_path + resend_path: users_resend_verification_code_path } end - - private - - def offer_email_reset_enabled?(user) - Feature.enabled?(:offer_email_reset, :instance) && !user.email_reset_offered_at - end end diff --git a/app/models/user.rb b/app/models/user.rb index 6fcc215363f6aa..84aa887e26278b 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -475,7 +475,6 @@ def update_tracked_fields!(request) delegate :organization, :organization=, to: :user_detail, prefix: true, allow_nil: true delegate :discord, :discord=, to: :user_detail, allow_nil: true delegate :github, :github=, to: :user_detail, allow_nil: true - delegate :email_reset_offered_at, :email_reset_offered_at=, to: :user_detail, allow_nil: true delegate :project_authorizations_recalculated_at, :project_authorizations_recalculated_at=, to: :user_detail, allow_nil: true delegate :bot_namespace, :bot_namespace=, to: :user_detail, allow_nil: true diff --git a/app/services/users/email_verification/update_email_service.rb b/app/services/users/email_verification/update_email_service.rb deleted file mode 100644 index 3f9b90b29607f2..00000000000000 --- a/app/services/users/email_verification/update_email_service.rb +++ /dev/null @@ -1,76 +0,0 @@ -# frozen_string_literal: true - -module Users - module EmailVerification - class UpdateEmailService - include ActionView::Helpers::DateHelper - - RATE_LIMIT = :email_verification_code_send - - def initialize(user:) - @user = user - end - - def execute(email:) - return failure(:rate_limited) if rate_limited? - return failure(:already_offered) if already_offered? - return failure(:no_change) if no_change?(email) - return failure(:validation_error) unless update_email - - success - end - - private - - attr_reader :user - - def rate_limited? - Gitlab::ApplicationRateLimiter.throttled?(RATE_LIMIT, scope: user) - end - - def already_offered? - user.email_reset_offered_at.present? - end - - def no_change?(email) - user.email = email - !user.will_save_change_to_email? - end - - def update_email - user.skip_confirmation_notification! - user.save - end - - def success - { status: :success } - end - - def failure(reason) - { - status: :failure, - reason: reason, - message: failure_message(reason) - } - end - - def failure_message(reason) - case reason - when :rate_limited - interval = distance_of_time_in_words(Gitlab::ApplicationRateLimiter.rate_limits[RATE_LIMIT][:interval]) - format( - s_("IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} and try again."), - interval: interval - ) - when :already_offered - s_('IdentityVerification|Email update is only offered once.') - when :no_change - s_('IdentityVerification|A code has already been sent to this email address. ' \ - 'Check your spam folder or enter another email address.') - when :validation_error - user.errors.full_messages.join(' ') - end - end - end - end -end diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml index 5d993dc7f4f1c1..711b0735e01b05 100644 --- a/app/views/admin/users/show.html.haml +++ b/app/views/admin/users/show.html.haml @@ -157,11 +157,6 @@ .gl-col-span-2 %strong= Gitlab::Access.human_access_with_none(@user.highest_role) - %li{ class: list_item_classes } - %span.gl-text-subtle= _("Email reset removed at:") - .gl-col-span-2 - %strong= @user.email_reset_offered_at || _('never') - %li{ class: list_item_classes } %span.gl-text-subtle= s_("Profiles|Support Pin:") .gl-col-span-2 diff --git a/config/feature_flags/ops/offer_email_reset.yml b/config/feature_flags/ops/offer_email_reset.yml deleted file mode 100644 index 40edf9e96c0487..00000000000000 --- a/config/feature_flags/ops/offer_email_reset.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: offer_email_reset -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/194151 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/550449 -milestone: "18.1" -type: ops -group: group::authorization -default_enabled: false diff --git a/config/routes/user.rb b/config/routes/user.rb index f2c8cec9d376aa..3ad2bb1c84d8fd 100644 --- a/config/routes/user.rb +++ b/config/routes/user.rb @@ -55,7 +55,6 @@ def override_omniauth(provider, controller, path_prefix = '/users/auth') get '/users/almost_there' => 'confirmations#almost_there' post '/users/resend_verification_code', to: 'sessions#resend_verification_code' get '/users/successful_verification', to: 'sessions#successful_verification' - patch '/users/update_email', to: 'sessions#update_email' # Redirect on GitHub authorization request errors. E.g. it could happen when user: # 1. cancel authorization the GitLab OAuth app via GitHub to import GitHub repos diff --git a/doc/api/openapi/openapi_v2.yaml b/doc/api/openapi/openapi_v2.yaml index 9b539f35b47da3..4fa2c1f5b3acc1 100644 --- a/doc/api/openapi/openapi_v2.yaml +++ b/doc/api/openapi/openapi_v2.yaml @@ -65664,8 +65664,6 @@ definitions: type: string created_by: "$ref": "#/definitions/API_Entities_UserBasic" - email_reset_offered_at: - type: string using_license_seat: type: string is_auditor: diff --git a/lib/api/entities/user_with_admin.rb b/lib/api/entities/user_with_admin.rb index 25c1edb4526eb3..53fef7a46e2f3f 100644 --- a/lib/api/entities/user_with_admin.rb +++ b/lib/api/entities/user_with_admin.rb @@ -7,7 +7,6 @@ class UserWithAdmin < UserPublic expose :note expose :namespace_id expose :created_by, with: UserBasic - expose :email_reset_offered_at end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 775dc8a4e63d19..4978d41670ab4e 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -24586,9 +24586,6 @@ msgstr "" msgid "Email patch" msgstr "" -msgid "Email reset removed at:" -msgstr "" - msgid "Email sent" msgstr "" @@ -32189,9 +32186,6 @@ msgid_plural "IdentityVerification|%d countries found" msgstr[0] "" msgstr[1] "" -msgid "IdentityVerification|A code has already been sent to this email address. Check your spam folder or enter another email address." -msgstr "" - msgid "IdentityVerification|A new code has been sent." msgstr "" @@ -32225,9 +32219,6 @@ msgstr "" msgid "IdentityVerification|Email Verification" msgstr "" -msgid "IdentityVerification|Email update is only offered once." -msgstr "" - msgid "IdentityVerification|Enter a code." msgstr "" diff --git a/spec/features/users/email_verification_on_login_spec.rb b/spec/features/users/email_verification_on_login_spec.rb index 1076196f19705c..982a138f9d9a41 100644 --- a/spec/features/users/email_verification_on_login_spec.rb +++ b/spec/features/users/email_verification_on_login_spec.rb @@ -384,28 +384,6 @@ def expect_user_to_be_unlocked end end - def expect_user_to_be_confirmed - aggregate_failures do - expect(user.email).to eq(new_email) - expect(user.unconfirmed_email).to be_nil - end - end - - def expect_email_changed_notification_to_old_address_and_instructions_email_to_new_address - changed_email = ActionMailer::Base.deliveries[0] - instructions_email = ActionMailer::Base.deliveries[1] - - expect(changed_email.to).to match_array([user.email]) - expect(changed_email.subject).to eq('Email Changed') - - expect(instructions_email.to).to match_array([new_email]) - expect(instructions_email.subject).to eq(s_('IdentityVerification|Verify your identity')) - - reset_delivered_emails! - - instructions_email.body.parts.first.to_s[/\d{#{Users::EmailVerification::GenerateTokenService::TOKEN_LENGTH}}/o] - end - def expect_instructions_email_and_extract_code(email: nil) mail = find_email_for(email || user) expect(mail.to).to match_array([email || user.email]) diff --git a/spec/helpers/sessions_helper_spec.rb b/spec/helpers/sessions_helper_spec.rb index 23216645dc61e1..4d9e186fd5c335 100644 --- a/spec/helpers/sessions_helper_spec.rb +++ b/spec/helpers/sessions_helper_spec.rb @@ -25,16 +25,14 @@ subject { helper.unconfirmed_verification_email?(user) } - where(:reset_first_offer?, :unconfirmed_email_present?, :token_valid?, :result) do - true | true | true | true - false | true | true | false - true | false | true | false - true | true | false | false + where(:unconfirmed_email_present?, :token_valid?, :result) do + true | true | true + true | false | false + false | true | false end with_them do before do - user.email_reset_offered_at = 1.minute.ago unless reset_first_offer? user.unconfirmed_email = 'unconfirmed@email' if unconfirmed_email_present? user.confirmation_sent_at = (token_valid? ? token_valid_for - 1 : token_valid_for + 1).minutes.ago end @@ -74,31 +72,9 @@ username: user.username, obfuscated_email: obfuscated_email(user.email), verify_path: helper.session_path(:user), - resend_path: users_resend_verification_code_path, - offer_email_reset: 'true', - update_email_path: users_update_email_path + resend_path: users_resend_verification_code_path }) end - - context 'when email reset has already been offered' do - before do - user.email_reset_offered_at = Time.now - end - - it 'returns offer_email_reset as `false`' do - expect(helper.verification_data(user)).to include(offer_email_reset: 'false') - end - end - - context 'when the `offer_email_reset` feature flag is disabled' do - before do - stub_feature_flags(offer_email_reset: false) - end - - it 'returns offer_email_reset as `false`' do - expect(helper.verification_data(user)).to include(offer_email_reset: 'false') - end - end end describe '#obfuscated_email' do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 6ff137137cb0f5..3e31bbac225e2e 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -198,9 +198,6 @@ it { is_expected.to delegate_method(:organization).to(:user_detail).with_prefix.allow_nil } it { is_expected.to delegate_method(:organization=).to(:user_detail).with_arguments(:args).with_prefix.allow_nil } - it { is_expected.to delegate_method(:email_reset_offered_at).to(:user_detail).allow_nil } - it { is_expected.to delegate_method(:email_reset_offered_at=).to(:user_detail).with_arguments(:args).allow_nil } - it { is_expected.to delegate_method(:project_authorizations_recalculated_at).to(:user_detail).allow_nil } it { is_expected.to delegate_method(:project_authorizations_recalculated_at=).to(:user_detail).with_arguments(:args).allow_nil } diff --git a/spec/requests/verifies_with_email_spec.rb b/spec/requests/verifies_with_email_spec.rb index ffd3dd3729419f..981cb7ab1164a5 100644 --- a/spec/requests/verifies_with_email_spec.rb +++ b/spec/requests/verifies_with_email_spec.rb @@ -204,7 +204,6 @@ .and change { AuditEvent.count }.by(1) .and change { AuthenticationEvent.count }.by(1) .and change { user.last_activity_on }.to(Date.today) - .and change { user.email_reset_offered_at }.to(Time.current) end it 'returns the success status and a redirect path' do @@ -212,49 +211,17 @@ expect(json_response).to eq('status' => 'success', 'redirect_path' => users_successful_verification_path) end - context 'when an unconfirmed verification email exists' do - before do - user.update!(email: new_email) - end - - let(:new_email) { 'new@email' } - - it 'confirms the email' do - expect { submit_token } - .to change { user.reload.email }.to(new_email) - .and change { user.confirmed_at } - .and change { user.unconfirmed_email }.from(new_email).to(nil) - end - end - context 'when email reset functionality is disabled' do shared_examples 'does not perform email reset actions' do before do user.update!(email: 'new@email') end - it 'does not confirm the email or change email_reset_offered_at' do + it 'does not confirm the email' do expect { submit_token } .to not_change { user.reload.email } - .and not_change { user.reload.email_reset_offered_at } end end - - context 'when email reset has already been offered' do - before do - user.update!(email_reset_offered_at: 1.hour.ago) - end - - it_behaves_like 'does not perform email reset actions' - end - - context 'when the `offer_email_reset` feature flag is disabled' do - before do - stub_feature_flags(offer_email_reset: false) - end - - it_behaves_like 'does not perform email reset actions' - end end end @@ -414,97 +381,6 @@ end end - describe 'update_email' do - let(:new_email) { 'new@email' } - - subject(:do_request) { patch(users_update_email_path(user: { email: new_email })) } - - context 'when no verification_user_id session variable exists' do - it 'returns 204 No Content' do - do_request - - expect(response).to have_gitlab_http_status(:no_content) - expect(response.body).to be_empty - end - end - - context 'when a verification_user_id session variable exists' do - before do - stub_session(session_data: { verification_user_id: user.id }) - end - - it 'locks the user' do - do_request - - expect(user.reload.unlock_token).not_to be_nil - expect(user.locked_at).not_to be_nil - end - - it 'sends a changed notification to the primary email and verification instructions to the unconfirmed email' do - perform_enqueued_jobs { do_request } - - sent_mails = ActionMailer::Base.deliveries.map { |mail| { mail.to[0] => mail.subject } } - - expect(sent_mails).to match_array([ - { user.reload.unconfirmed_email => s_('IdentityVerification|Verify your identity') }, - { user.email => 'Email Changed' } - ]) - end - - it 'calls the UpdateEmailService and returns a success response' do - expect_next_instance_of(Users::EmailVerification::UpdateEmailService, user: user) do |instance| - expect(instance).to receive(:execute).with(email: new_email).and_call_original - end - - do_request - - expect(json_response).to eq('status' => 'success') - end - - context 'when the `offer_email_reset` feature flag is disabled' do - before do - stub_feature_flags(offer_email_reset: false) - end - - it 'returns an error response' do - do_request - - expect(json_response).to eq('status' => 'error', 'message' => 'Feature not available') - end - - it 'does not call the UpdateEmailService' do - expect(Users::EmailVerification::UpdateEmailService).not_to receive(:new) - - do_request - end - end - end - - context 'when failing to update the email address' do - let(:service_response) do - { - status: 'failure', - reason: 'the reason', - message: 'the message' - } - end - - before do - stub_session(session_data: { verification_user_id: user.id }) - end - - it 'calls the UpdateEmailService and returns an error response' do - expect_next_instance_of(Users::EmailVerification::UpdateEmailService, user: user) do |instance| - expect(instance).to receive(:execute).with(email: new_email).and_return(service_response) - end - - do_request - - expect(json_response).to eq(service_response.with_indifferent_access) - end - end - end - describe 'successful_verification' do before do allow(user).to receive(:role_required?).and_return(true) # It skips the required signup info before_action diff --git a/spec/services/users/email_verification/update_email_service_spec.rb b/spec/services/users/email_verification/update_email_service_spec.rb deleted file mode 100644 index 8b4e5b8d7b5e45..00000000000000 --- a/spec/services/users/email_verification/update_email_service_spec.rb +++ /dev/null @@ -1,119 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Users::EmailVerification::UpdateEmailService, feature_category: :instance_resiliency do - let_it_be_with_reload(:user) { create(:user) } - let(:email) { build_stubbed(:user).email } - - describe '#execute' do - subject(:execute_service) { described_class.new(user: user).execute(email: email) } - - context 'when successful' do - it { is_expected.to eq(status: :success) } - - it 'does not send a confirmation instructions email' do - expect { execute_service }.not_to have_enqueued_mail(DeviseMailer, :confirmation_instructions) - end - - it 'sets the unconfirmed_email and confirmation_sent_at fields', :freeze_time do - expect { execute_service } - .to change { user.unconfirmed_email }.from(nil).to(email) - .and change { user.confirmation_sent_at }.from(nil).to(Time.current) - end - end - - context 'when rate limited' do - before do - allow(Gitlab::ApplicationRateLimiter).to receive(:throttled?) - .with(:email_verification_code_send, scope: user).and_return(true) - end - - it 'returns a failure status' do - expect(execute_service).to eq( - { - status: :failure, - reason: :rate_limited, - message: format(s_("IdentityVerification|You've reached the maximum amount of tries. " \ - 'Wait %{interval} and try again.'), interval: 'about 1 hour') - } - ) - end - end - - context 'when email reset has already been offered' do - before do - user.email_reset_offered_at = 1.minute.ago - end - - it 'returns a failure status' do - expect(execute_service).to eq( - { - status: :failure, - reason: :already_offered, - message: s_('IdentityVerification|Email update is only offered once.') - } - ) - end - end - - context 'when email is unchanged' do - let(:email) { user.email } - - it 'returns a failure status' do - expect(execute_service).to eq( - { - status: :failure, - reason: :no_change, - message: s_('IdentityVerification|A code has already been sent to this email address. ' \ - 'Check your spam folder or enter another email address.') - } - ) - end - end - - context 'when email is missing' do - let(:email) { '' } - - it 'returns a failure status' do - expect(execute_service).to eq( - { - status: :failure, - reason: :validation_error, - message: "Email can't be blank" - } - ) - end - end - - context 'when email is not valid' do - let(:email) { 'xxx' } - - it 'returns a failure status' do - expect(execute_service).to eq( - { - status: :failure, - reason: :validation_error, - message: 'Email is invalid' - } - ) - end - end - - context 'when email is already taken' do - before do - create(:user, email: email) - end - - it 'returns a failure status' do - expect(execute_service).to eq( - { - status: :failure, - reason: :validation_error, - message: 'Email has already been taken' - } - ) - end - end - end -end -- GitLab From e2fa1bb303ef29cf612a015f78a7dc3cdf85c44a Mon Sep 17 00:00:00 2001 From: Matthew MacRae-Bovell Date: Fri, 11 Jul 2025 16:38:45 -0400 Subject: [PATCH 2/6] Fix broken spec --- spec/requests/api/users_spec.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index 1cabd23504e560..2f1ef4a1f97846 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -184,7 +184,6 @@ expect(json_response.first).not_to have_key('note') expect(json_response.first).not_to have_key('namespace_id') expect(json_response.first).not_to have_key('created_by') - expect(json_response.first).not_to have_key('email_reset_offered_at') end end @@ -197,7 +196,6 @@ expect(json_response.first).not_to have_key('note') expect(json_response.first).not_to have_key('namespace_id') expect(json_response.first).not_to have_key('created_by') - expect(json_response.first).not_to have_key('email_reset_offered_at') end end @@ -207,7 +205,6 @@ expect(response).to have_gitlab_http_status(:success) expect(json_response.first).to have_key('note') - expect(json_response.first).to have_key('email_reset_offered_at') expect(json_response.first['note']).to eq '2018-11-05 | 2FA removed | user requested | www.gitlab.com' end -- GitLab From 46a6ba7e9edf9a7feebedcc35ead705eb7f57cde Mon Sep 17 00:00:00 2001 From: Matthew MacRae-Bovell Date: Mon, 14 Jul 2025 10:14:01 -0400 Subject: [PATCH 3/6] Remove mehtods --- app/helpers/sessions_helper.rb | 11 +------ spec/helpers/sessions_helper_spec.rb | 47 ---------------------------- 2 files changed, 1 insertion(+), 57 deletions(-) diff --git a/app/helpers/sessions_helper.rb b/app/helpers/sessions_helper.rb index 3be66f4988c020..b1cf095edaae6f 100644 --- a/app/helpers/sessions_helper.rb +++ b/app/helpers/sessions_helper.rb @@ -21,19 +21,10 @@ def remember_me_enabled? Gitlab::CurrentSettings.allow_user_remember_me? end - def unconfirmed_verification_email?(user) - token_valid_from = ::Users::EmailVerification::ValidateTokenService::TOKEN_VALID_FOR_MINUTES.minutes.ago - user.pending_reconfirmation? && user.confirmation_sent_at >= token_valid_from - end - - def verification_email(user) - unconfirmed_verification_email?(user) ? user.unconfirmed_email : user.email - end - def verification_data(user) { username: user.username, - obfuscated_email: obfuscated_email(verification_email(user)), + obfuscated_email: obfuscated_email(user.email), verify_path: session_path(:user), resend_path: users_resend_verification_code_path } diff --git a/spec/helpers/sessions_helper_spec.rb b/spec/helpers/sessions_helper_spec.rb index 4d9e186fd5c335..fefc62c76695e1 100644 --- a/spec/helpers/sessions_helper_spec.rb +++ b/spec/helpers/sessions_helper_spec.rb @@ -17,53 +17,6 @@ end end - describe '#unconfirmed_verification_email?', :freeze_time do - using RSpec::Parameterized::TableSyntax - - let(:user) { build_stubbed(:user) } - let(:token_valid_for) { ::Users::EmailVerification::ValidateTokenService::TOKEN_VALID_FOR_MINUTES } - - subject { helper.unconfirmed_verification_email?(user) } - - where(:unconfirmed_email_present?, :token_valid?, :result) do - true | true | true - true | false | false - false | true | false - end - - with_them do - before do - user.unconfirmed_email = 'unconfirmed@email' if unconfirmed_email_present? - user.confirmation_sent_at = (token_valid? ? token_valid_for - 1 : token_valid_for + 1).minutes.ago - end - - it { is_expected.to eq(result) } - end - end - - describe '#verification_email' do - let(:unconfirmed_email) { 'unconfirmed@email' } - let(:user) { build_stubbed(:user, unconfirmed_email: unconfirmed_email) } - - subject { helper.verification_email(user) } - - context 'when there is an unconfirmed verification email' do - before do - allow(helper).to receive(:unconfirmed_verification_email?).and_return(true) - end - - it { is_expected.to eq(unconfirmed_email) } - end - - context 'when there is no unconfirmed verification email' do - before do - allow(helper).to receive(:unconfirmed_verification_email?).and_return(false) - end - - it { is_expected.to eq(user.email) } - end - end - describe '#verification_data' do let(:user) { build_stubbed(:user) } -- GitLab From bfed62808fe7281a6710de50751b795d626b3837 Mon Sep 17 00:00:00 2001 From: Matthew MacRae-Bovell Date: Mon, 14 Jul 2025 15:01:42 -0400 Subject: [PATCH 4/6] Add back helpers --- app/helpers/sessions_helper.rb | 9 ++++++ spec/helpers/sessions_helper_spec.rb | 47 ++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/app/helpers/sessions_helper.rb b/app/helpers/sessions_helper.rb index b1cf095edaae6f..edb2d89c502119 100644 --- a/app/helpers/sessions_helper.rb +++ b/app/helpers/sessions_helper.rb @@ -21,6 +21,15 @@ def remember_me_enabled? Gitlab::CurrentSettings.allow_user_remember_me? end + def unconfirmed_verification_email?(user) + token_valid_from = ::Users::EmailVerification::ValidateTokenService::TOKEN_VALID_FOR_MINUTES.minutes.ago + user.pending_reconfirmation? && user.confirmation_sent_at >= token_valid_from + end + + def verification_email(user) + unconfirmed_verification_email?(user) ? user.unconfirmed_email : user.email + end + def verification_data(user) { username: user.username, diff --git a/spec/helpers/sessions_helper_spec.rb b/spec/helpers/sessions_helper_spec.rb index fefc62c76695e1..4d9e186fd5c335 100644 --- a/spec/helpers/sessions_helper_spec.rb +++ b/spec/helpers/sessions_helper_spec.rb @@ -17,6 +17,53 @@ end end + describe '#unconfirmed_verification_email?', :freeze_time do + using RSpec::Parameterized::TableSyntax + + let(:user) { build_stubbed(:user) } + let(:token_valid_for) { ::Users::EmailVerification::ValidateTokenService::TOKEN_VALID_FOR_MINUTES } + + subject { helper.unconfirmed_verification_email?(user) } + + where(:unconfirmed_email_present?, :token_valid?, :result) do + true | true | true + true | false | false + false | true | false + end + + with_them do + before do + user.unconfirmed_email = 'unconfirmed@email' if unconfirmed_email_present? + user.confirmation_sent_at = (token_valid? ? token_valid_for - 1 : token_valid_for + 1).minutes.ago + end + + it { is_expected.to eq(result) } + end + end + + describe '#verification_email' do + let(:unconfirmed_email) { 'unconfirmed@email' } + let(:user) { build_stubbed(:user, unconfirmed_email: unconfirmed_email) } + + subject { helper.verification_email(user) } + + context 'when there is an unconfirmed verification email' do + before do + allow(helper).to receive(:unconfirmed_verification_email?).and_return(true) + end + + it { is_expected.to eq(unconfirmed_email) } + end + + context 'when there is no unconfirmed verification email' do + before do + allow(helper).to receive(:unconfirmed_verification_email?).and_return(false) + end + + it { is_expected.to eq(user.email) } + end + end + describe '#verification_data' do let(:user) { build_stubbed(:user) } -- GitLab From 287852af7244365ec533db22ddf423bff40e2459 Mon Sep 17 00:00:00 2001 From: Matthew MacRae-Bovell Date: Wed, 16 Jul 2025 19:10:24 -0400 Subject: [PATCH 5/6] Remove verification_email helpers --- .../concerns/verifies_with_email.rb | 2 +- app/helpers/sessions_helper.rb | 9 ---- spec/helpers/sessions_helper_spec.rb | 47 ------------------- 3 files changed, 1 insertion(+), 57 deletions(-) diff --git a/app/controllers/concerns/verifies_with_email.rb b/app/controllers/concerns/verifies_with_email.rb index 66d70ab98fd750..a5e0ac64259738 100644 --- a/app/controllers/concerns/verifies_with_email.rb +++ b/app/controllers/concerns/verifies_with_email.rb @@ -79,7 +79,7 @@ def send_verification_instructions(user, secondary_email: nil, reason: nil) end def send_verification_instructions_email(user, token, secondary_email) - email = secondary_email || verification_email(user) + email = secondary_email || user.email Notify.verification_instructions_email(email, token: token).deliver_later log_verification(user, :instructions_sent) diff --git a/app/helpers/sessions_helper.rb b/app/helpers/sessions_helper.rb index edb2d89c502119..b1cf095edaae6f 100644 --- a/app/helpers/sessions_helper.rb +++ b/app/helpers/sessions_helper.rb @@ -21,15 +21,6 @@ def remember_me_enabled? Gitlab::CurrentSettings.allow_user_remember_me? end - def unconfirmed_verification_email?(user) - token_valid_from = ::Users::EmailVerification::ValidateTokenService::TOKEN_VALID_FOR_MINUTES.minutes.ago - user.pending_reconfirmation? && user.confirmation_sent_at >= token_valid_from - end - - def verification_email(user) - unconfirmed_verification_email?(user) ? user.unconfirmed_email : user.email - end - def verification_data(user) { username: user.username, diff --git a/spec/helpers/sessions_helper_spec.rb b/spec/helpers/sessions_helper_spec.rb index 4d9e186fd5c335..fefc62c76695e1 100644 --- a/spec/helpers/sessions_helper_spec.rb +++ b/spec/helpers/sessions_helper_spec.rb @@ -17,53 +17,6 @@ end end - describe '#unconfirmed_verification_email?', :freeze_time do - using RSpec::Parameterized::TableSyntax - - let(:user) { build_stubbed(:user) } - let(:token_valid_for) { ::Users::EmailVerification::ValidateTokenService::TOKEN_VALID_FOR_MINUTES } - - subject { helper.unconfirmed_verification_email?(user) } - - where(:unconfirmed_email_present?, :token_valid?, :result) do - true | true | true - true | false | false - false | true | false - end - - with_them do - before do - user.unconfirmed_email = 'unconfirmed@email' if unconfirmed_email_present? - user.confirmation_sent_at = (token_valid? ? token_valid_for - 1 : token_valid_for + 1).minutes.ago - end - - it { is_expected.to eq(result) } - end - end - - describe '#verification_email' do - let(:unconfirmed_email) { 'unconfirmed@email' } - let(:user) { build_stubbed(:user, unconfirmed_email: unconfirmed_email) } - - subject { helper.verification_email(user) } - - context 'when there is an unconfirmed verification email' do - before do - allow(helper).to receive(:unconfirmed_verification_email?).and_return(true) - end - - it { is_expected.to eq(unconfirmed_email) } - end - - context 'when there is no unconfirmed verification email' do - before do - allow(helper).to receive(:unconfirmed_verification_email?).and_return(false) - end - - it { is_expected.to eq(user.email) } - end - end - describe '#verification_data' do let(:user) { build_stubbed(:user) } -- GitLab From 2d80434b322776d3dc57ddcd05416763f6e8530d Mon Sep 17 00:00:00 2001 From: Matthew MacRae-Bovell Date: Thu, 17 Jul 2025 10:14:02 -0400 Subject: [PATCH 6/6] Fix broken spec --- spec/requests/verifies_with_email_spec.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/spec/requests/verifies_with_email_spec.rb b/spec/requests/verifies_with_email_spec.rb index 981cb7ab1164a5..cd1caad41e7a80 100644 --- a/spec/requests/verifies_with_email_spec.rb +++ b/spec/requests/verifies_with_email_spec.rb @@ -38,11 +38,10 @@ it_behaves_like 'locks the user and sends verification instructions' context 'when an unconfirmed verification email exists' do - let(:new_email) { 'new@email' } - let(:user) { create(:user, unconfirmed_email: new_email, confirmation_sent_at: 1.minute.ago) } + let(:user) { create(:user, unconfirmed_email: 'new@email', confirmation_sent_at: 1.minute.ago) } - it 'sends a verification instructions email to the unconfirmed email address' do - mail = ActionMailer::Base.deliveries.find { |d| d.to.include?(new_email) } + it 'sends a verification instructions email to the existing email address' do + mail = ActionMailer::Base.deliveries.find { |d| d.to.include?(user.email) } expect(mail.subject).to eq(s_('IdentityVerification|Verify your identity')) end end -- GitLab