Feature: Allow staff to change user emails (#7074)
* Admin: Show unconfirmed email address on account page * Admin: Allow staff to change user email addresses * ActionLog: On change_email, log current email address and new unconfirmed email address
This commit is contained in:
parent
e6e93ecd8a
commit
219a4423d8
49
app/controllers/admin/change_emails_controller.rb
Normal file
49
app/controllers/admin/change_emails_controller.rb
Normal file
@ -0,0 +1,49 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class ChangeEmailsController < BaseController
|
||||
before_action :set_account
|
||||
before_action :require_local_account!
|
||||
|
||||
def show
|
||||
authorize @user, :change_email?
|
||||
end
|
||||
|
||||
def update
|
||||
authorize @user, :change_email?
|
||||
|
||||
new_email = resource_params.fetch(:unconfirmed_email)
|
||||
|
||||
if new_email != @user.email
|
||||
@user.update!(
|
||||
unconfirmed_email: new_email,
|
||||
# Regenerate the confirmation token:
|
||||
confirmation_token: nil
|
||||
)
|
||||
|
||||
log_action :change_email, @user
|
||||
|
||||
@user.send_confirmation_instructions
|
||||
end
|
||||
|
||||
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.change_email.changed_msg')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_account
|
||||
@account = Account.find(params[:account_id])
|
||||
@user = @account.user
|
||||
end
|
||||
|
||||
def require_local_account!
|
||||
redirect_to admin_account_path(@account.id) unless @account.local? && @account.user.present?
|
||||
end
|
||||
|
||||
def resource_params
|
||||
params.require(:user).permit(
|
||||
:unconfirmed_email
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
@ -45,6 +45,8 @@ module Admin::ActionLogsHelper
|
||||
log.recorded_changes.slice('domain', 'visible_in_picker')
|
||||
elsif log.target_type == 'User' && [:promote, :demote].include?(log.action)
|
||||
log.recorded_changes.slice('moderator', 'admin')
|
||||
elsif log.target_type == 'User' && [:change_email].include?(log.action)
|
||||
log.recorded_changes.slice('email', 'unconfirmed_email')
|
||||
elsif log.target_type == 'DomainBlock'
|
||||
log.recorded_changes.slice('severity', 'reject_media')
|
||||
elsif log.target_type == 'Status' && log.action == :update
|
||||
@ -84,7 +86,7 @@ module Admin::ActionLogsHelper
|
||||
'positive'
|
||||
when :create
|
||||
opposite_verbs?(log) ? 'negative' : 'positive'
|
||||
when :update, :reset_password, :disable_2fa, :memorialize
|
||||
when :update, :reset_password, :disable_2fa, :memorialize, :change_email
|
||||
'neutral'
|
||||
when :demote, :silence, :disable, :suspend, :remove_avatar, :reopen
|
||||
'negative'
|
||||
|
@ -124,6 +124,7 @@ class Account < ApplicationRecord
|
||||
scope :matches_domain, ->(value) { where(arel_table[:domain].matches("%#{value}%")) }
|
||||
|
||||
delegate :email,
|
||||
:unconfirmed_email,
|
||||
:current_sign_in_ip,
|
||||
:current_sign_in_at,
|
||||
:confirmed?,
|
||||
|
@ -35,6 +35,11 @@ class Admin::ActionLog < ApplicationRecord
|
||||
self.recorded_changes = target.attributes
|
||||
when :update, :promote, :demote
|
||||
self.recorded_changes = target.previous_changes
|
||||
when :change_email
|
||||
self.recorded_changes = ActiveSupport::HashWithIndifferentAccess.new(
|
||||
email: [target.email, nil],
|
||||
unconfirmed_email: [nil, target.unconfirmed_email]
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -5,6 +5,10 @@ class UserPolicy < ApplicationPolicy
|
||||
staff? && !record.staff?
|
||||
end
|
||||
|
||||
def change_email?
|
||||
staff? && !record.staff?
|
||||
end
|
||||
|
||||
def disable_2fa?
|
||||
admin? && !record.staff?
|
||||
end
|
||||
|
@ -36,9 +36,13 @@
|
||||
%th= t('admin.accounts.email')
|
||||
%td
|
||||
= @account.user_email
|
||||
|
||||
- if @account.user_confirmed?
|
||||
= fa_icon('check')
|
||||
= table_link_to 'edit', t('admin.accounts.change_email.label'), admin_account_change_email_path(@account.id) if can?(:change_email, @account.user)
|
||||
- if @account.user_unconfirmed_email.present?
|
||||
%th= t('admin.accounts.unconfirmed_email')
|
||||
%td
|
||||
= @account.user_unconfirmed_email
|
||||
%tr
|
||||
%th= t('admin.accounts.login_status')
|
||||
%td
|
||||
|
7
app/views/admin/change_emails/show.html.haml
Normal file
7
app/views/admin/change_emails/show.html.haml
Normal file
@ -0,0 +1,7 @@
|
||||
- content_for :page_title do
|
||||
= t('admin.accounts.change_email.title', username: @account.acct)
|
||||
|
||||
= simple_form_for @user, url: admin_account_change_email_path(@account.id) do |f|
|
||||
= f.input :email, wrapper: :with_label, disabled: true, label: t('admin.accounts.change_email.current_email')
|
||||
= f.input :unconfirmed_email, wrapper: :with_label, label: t('admin.accounts.change_email.new_email')
|
||||
= f.button :submit, class: "button", value: t('admin.accounts.change_email.submit')
|
@ -63,6 +63,13 @@ en:
|
||||
are_you_sure: Are you sure?
|
||||
avatar: Avatar
|
||||
by_domain: Domain
|
||||
change_email:
|
||||
changed_msg: Account email successfully changed!
|
||||
current_email: Current Email
|
||||
label: Change Email
|
||||
new_email: New Email
|
||||
submit: Change Email
|
||||
title: Change Email for %{username}
|
||||
confirm: Confirm
|
||||
confirmed: Confirmed
|
||||
demote: Demote
|
||||
@ -131,6 +138,7 @@ en:
|
||||
statuses: Statuses
|
||||
subscribe: Subscribe
|
||||
title: Accounts
|
||||
unconfirmed_email: Unconfirmed E-mail
|
||||
undo_silenced: Undo silence
|
||||
undo_suspension: Undo suspension
|
||||
unsubscribe: Unsubscribe
|
||||
@ -139,6 +147,7 @@ en:
|
||||
action_logs:
|
||||
actions:
|
||||
assigned_to_self_report: "%{name} assigned report %{target} to themselves"
|
||||
change_email_user: "%{name} changed the e-mail address of user %{target}"
|
||||
confirm_user: "%{name} confirmed e-mail address of user %{target}"
|
||||
create_custom_emoji: "%{name} uploaded new emoji %{target}"
|
||||
create_domain_block: "%{name} blocked domain %{target}"
|
||||
|
@ -151,6 +151,7 @@ Rails.application.routes.draw do
|
||||
post :memorialize
|
||||
end
|
||||
|
||||
resource :change_email, only: [:show, :update]
|
||||
resource :reset, only: [:create]
|
||||
resource :silence, only: [:create, :destroy]
|
||||
resource :suspension, only: [:create, :destroy]
|
||||
|
47
spec/controllers/admin/change_email_controller_spec.rb
Normal file
47
spec/controllers/admin/change_email_controller_spec.rb
Normal file
@ -0,0 +1,47 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Admin::ChangeEmailsController, type: :controller do
|
||||
render_views
|
||||
|
||||
let(:admin) { Fabricate(:user, admin: true) }
|
||||
|
||||
before do
|
||||
sign_in admin
|
||||
end
|
||||
|
||||
describe "GET #show" do
|
||||
it "returns http success" do
|
||||
account = Fabricate(:account)
|
||||
user = Fabricate(:user, account: account)
|
||||
|
||||
get :show, params: { account_id: account.id }
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET #update" do
|
||||
before do
|
||||
allow(UserMailer).to receive(:confirmation_instructions).and_return(double('email', deliver_later: nil))
|
||||
end
|
||||
|
||||
it "returns http success" do
|
||||
account = Fabricate(:account)
|
||||
user = Fabricate(:user, account: account)
|
||||
|
||||
previous_email = user.email
|
||||
|
||||
post :update, params: { account_id: account.id, user: { unconfirmed_email: 'test@example.com' } }
|
||||
|
||||
user.reload
|
||||
|
||||
expect(user.email).to eq previous_email
|
||||
expect(user.unconfirmed_email).to eq 'test@example.com'
|
||||
expect(user.confirmation_token).not_to be_nil
|
||||
|
||||
expect(UserMailer).to have_received(:confirmation_instructions).with(user, user.confirmation_token, { to: 'test@example.com' })
|
||||
|
||||
expect(response).to redirect_to(admin_account_path(account.id))
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue
Block a user