mirror of
https://github.com/discourse/discourse.git
synced 2025-02-01 04:53:01 +08:00
3cd601dcc9
See https://meta.discourse.org/t/changing-a-users-email/164512 for context. When admin changes an email for a user, we were incorrectly sending the password reset email to the user's old address. Also the new email does not come into effect until the reset password process is done, so this PR adds some notes to the admin to make this clearer.
147 lines
4.7 KiB
Ruby
147 lines
4.7 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class EmailUpdater
|
|
include HasErrors
|
|
|
|
attr_reader :user
|
|
|
|
def self.human_attribute_name(name, options = {})
|
|
User.human_attribute_name(name, options)
|
|
end
|
|
|
|
def initialize(guardian: nil, user: nil)
|
|
@guardian = guardian
|
|
@user = user
|
|
end
|
|
|
|
def change_to(email, add: false)
|
|
@guardian.ensure_can_edit_email!(@user)
|
|
|
|
email = Email.downcase(email.strip)
|
|
EmailValidator.new(attributes: :email).validate_each(self, :email, email)
|
|
return if errors.present?
|
|
|
|
if existing_user = User.find_by_email(email)
|
|
if SiteSetting.hide_email_address_taken
|
|
Jobs.enqueue(:critical_user_email, type: :account_exists, user_id: existing_user.id)
|
|
else
|
|
error_message = +'change_email.error'
|
|
error_message << '_staged' if existing_user.staged?
|
|
errors.add(:base, I18n.t(error_message))
|
|
end
|
|
end
|
|
|
|
return if errors.present? || existing_user.present?
|
|
|
|
old_email = @user.email if !add
|
|
|
|
if @guardian.is_staff? && @guardian.user != @user
|
|
StaffActionLogger.new(@guardian.user).log_add_email(@user)
|
|
else
|
|
UserHistory.create!(action: UserHistory.actions[:add_email], acting_user_id: @user.id)
|
|
end
|
|
|
|
if @guardian.is_staff? && !@user.staff?
|
|
send_email_notification(@user.email, email)
|
|
update_user_email(old_email, email)
|
|
send_email(:forgot_password, @user.email_tokens.create!(email: @user.email))
|
|
return
|
|
end
|
|
|
|
change_req = EmailChangeRequest.find_or_initialize_by(user_id: @user.id, new_email: email)
|
|
if change_req.new_record?
|
|
change_req.old_email = old_email
|
|
change_req.new_email = email
|
|
end
|
|
|
|
if change_req.change_state.blank? || change_req.change_state == EmailChangeRequest.states[:complete]
|
|
change_req.change_state = if @user.staff?
|
|
# Staff users must confirm their old email address first.
|
|
EmailChangeRequest.states[:authorizing_old]
|
|
else
|
|
EmailChangeRequest.states[:authorizing_new]
|
|
end
|
|
end
|
|
|
|
if change_req.change_state == EmailChangeRequest.states[:authorizing_old]
|
|
change_req.old_email_token = @user.email_tokens.create!(email: @user.email)
|
|
send_email(add ? :confirm_old_email_add : :confirm_old_email, change_req.old_email_token)
|
|
elsif change_req.change_state == EmailChangeRequest.states[:authorizing_new]
|
|
change_req.new_email_token = @user.email_tokens.create!(email: email)
|
|
send_email(:confirm_new_email, change_req.new_email_token)
|
|
end
|
|
|
|
change_req.save!
|
|
end
|
|
|
|
def confirm(token)
|
|
confirm_result = nil
|
|
|
|
User.transaction do
|
|
result = EmailToken.atomic_confirm(token)
|
|
if result[:success]
|
|
token = result[:email_token]
|
|
@user = token.user
|
|
|
|
change_req = @user.email_change_requests
|
|
.where('old_email_token_id = :token_id OR new_email_token_id = :token_id', token_id: token.id)
|
|
.first
|
|
|
|
case change_req.try(:change_state)
|
|
when EmailChangeRequest.states[:authorizing_old]
|
|
change_req.update!(
|
|
change_state: EmailChangeRequest.states[:authorizing_new],
|
|
new_email_token: @user.email_tokens.create(email: change_req.new_email)
|
|
)
|
|
send_email(:confirm_new_email, change_req.new_email_token)
|
|
confirm_result = :authorizing_new
|
|
when EmailChangeRequest.states[:authorizing_new]
|
|
change_req.update!(change_state: EmailChangeRequest.states[:complete])
|
|
if !@user.staff?
|
|
# Send an email notification only to users who did not confirm old
|
|
# email.
|
|
send_email_notification(change_req.old_email, change_req.new_email)
|
|
end
|
|
update_user_email(change_req.old_email, change_req.new_email)
|
|
confirm_result = :complete
|
|
end
|
|
else
|
|
errors.add(:base, I18n.t('change_email.already_done'))
|
|
confirm_result = :error
|
|
end
|
|
end
|
|
|
|
confirm_result || :error
|
|
end
|
|
|
|
def update_user_email(old_email, new_email)
|
|
if old_email.present?
|
|
@user.user_emails.find_by(email: old_email).update!(email: new_email)
|
|
else
|
|
@user.user_emails.create!(email: new_email)
|
|
end
|
|
@user.reload
|
|
|
|
DiscourseEvent.trigger(:user_updated, @user)
|
|
@user.set_automatic_groups
|
|
end
|
|
|
|
protected
|
|
|
|
def send_email(type, email_token)
|
|
Jobs.enqueue :critical_user_email,
|
|
to_address: email_token.email,
|
|
type: type,
|
|
user_id: @user.id,
|
|
email_token: email_token.token
|
|
end
|
|
|
|
def send_email_notification(old_email, new_email)
|
|
Jobs.enqueue :critical_user_email,
|
|
to_address: @user.email,
|
|
type: old_email ? :notify_old_email : :notify_old_email_add,
|
|
user_id: @user.id,
|
|
new_email: new_email
|
|
end
|
|
end
|