mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
DEV: Hash tokens stored from email_tokens (#14493)
This commit adds token_hash and scopes columns to email_tokens table. token_hash is a replacement for the token column to avoid storing email tokens in plaintext as it can pose a security risk. The new scope column ensures that email tokens cannot be used to perform a different action than the one intended. To sum up, this commit: * Adds token_hash and scope to email_tokens * Reuses code that schedules critical_user_email * Refactors EmailToken.confirm and EmailToken.atomic_confirm methods * Periodically cleans old, unconfirmed or expired email tokens
This commit is contained in:
@@ -4,6 +4,7 @@ class EmailUpdater
|
||||
include HasErrors
|
||||
|
||||
attr_reader :user
|
||||
attr_reader :change_req
|
||||
|
||||
def self.human_attribute_name(name, options = {})
|
||||
User.human_attribute_name(name, options)
|
||||
@@ -48,16 +49,16 @@ class EmailUpdater
|
||||
UserHistory.create!(action: UserHistory.actions[:add_email], acting_user_id: @user.id)
|
||||
end
|
||||
|
||||
change_req = EmailChangeRequest.find_or_initialize_by(user_id: @user.id, new_email: email)
|
||||
@change_req = EmailChangeRequest.find_or_initialize_by(user_id: @user.id, new_email: email)
|
||||
|
||||
if change_req.new_record?
|
||||
change_req.requested_by = @guardian.user
|
||||
change_req.old_email = old_email
|
||||
change_req.new_email = email
|
||||
if @change_req.new_record?
|
||||
@change_req.requested_by = @guardian.user
|
||||
@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?
|
||||
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
|
||||
@@ -65,51 +66,53 @@ class EmailUpdater
|
||||
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)
|
||||
if @change_req.change_state == EmailChangeRequest.states[:authorizing_old]
|
||||
@change_req.old_email_token = @user.email_tokens.create!(email: @user.email, scope: EmailToken.scopes[:email_update])
|
||||
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, scope: EmailToken.scopes[:email_update])
|
||||
send_email(:confirm_new_email, @change_req.new_email_token)
|
||||
end
|
||||
|
||||
change_req.save!
|
||||
@change_req.save!
|
||||
@change_req
|
||||
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
|
||||
email_token = EmailToken.confirmable(token, scope: EmailToken.scopes[:email_update])
|
||||
if email_token.blank?
|
||||
errors.add(:base, I18n.t('change_email.already_done'))
|
||||
confirm_result = :error
|
||||
next
|
||||
end
|
||||
|
||||
email_token.update!(confirmed: true)
|
||||
|
||||
@user = email_token.user
|
||||
@change_req = @user.email_change_requests
|
||||
.where('old_email_token_id = :token_id OR new_email_token_id = :token_id', token_id: email_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, scope: EmailToken.scopes[:email_update])
|
||||
)
|
||||
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
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user