diff --git a/app/jobs/regular/anonymize_user.rb b/app/jobs/regular/anonymize_user.rb new file mode 100644 index 00000000000..e2a6b360094 --- /dev/null +++ b/app/jobs/regular/anonymize_user.rb @@ -0,0 +1,50 @@ +module Jobs + class AnonymizeUser < Jobs::Base + + sidekiq_options queue: 'low' + + def execute(args) + @user_id = args[:user_id] + @prev_email = args[:prev_email] + @anonymize_ip = args[:anonymize_ip] + + make_anonymous + end + + def make_anonymous + anonymize_ips(@anonymize_ip) if @anonymize_ip + + Invite.with_deleted.where(user_id: @user_id).destroy_all + EmailToken.where(user_id: @user_id).destroy_all + EmailLog.where(user_id: @user_id).delete_all + IncomingEmail.where("user_id = ? OR from_address = ?", @user_id, @prev_email).delete_all + + Post.with_deleted + .where(user_id: @user_id) + .where.not(raw_email: nil) + .update_all(raw_email: nil) + end + + def ip_where(column = 'user_id') + ["#{column} = :user_id AND ip_address IS NOT NULL", user_id: @user_id] + end + + def anonymize_ips(new_ip) + IncomingLink.where(ip_where('current_user_id')).update_all(ip_address: new_ip) + ScreenedEmail.where(email: @prev_email).update_all(ip_address: new_ip) + SearchLog.where(ip_where).update_all(ip_address: new_ip) + TopicLinkClick.where(ip_where).update_all(ip_address: new_ip) + TopicViewItem.where(ip_where).update_all(ip_address: new_ip) + UserHistory.where(ip_where('acting_user_id')).update_all(ip_address: new_ip) + UserProfileView.where(ip_where).update_all(ip_address: new_ip) + + # UserHistory for delete_user logs the user's IP. Note this is quite ugly but we don't + # have a better way of querying on details right now. + UserHistory.where( + "action = :action AND details LIKE 'id: #{@user_id}\n%'", + action: UserHistory.actions[:delete_user] + ).update_all(ip_address: new_ip) + end + + end +end diff --git a/app/services/user_anonymizer.rb b/app/services/user_anonymizer.rb index a5165cf5062..f78c9e6497d 100644 --- a/app/services/user_anonymizer.rb +++ b/app/services/user_anonymizer.rb @@ -25,12 +25,15 @@ class UserAnonymizer @user.reload @user.password = SecureRandom.hex - @user.email = "#{@user.username}@example.com" + @user.email = "#{@user.username}@anonymized.invalid" @user.name = SiteSetting.full_name_required ? @user.username : nil @user.date_of_birth = nil @user.title = nil - anonymize_ips(@opts[:anonymize_ip]) if @opts.has_key?(:anonymize_ip) + if @opts.has_key?(:anonymize_ip) + @user.ip_address = @opts[:anonymize_ip] + @user.registration_ip_address = @opts[:anonymize_ip] + end @user.save @@ -57,30 +60,14 @@ class UserAnonymizer @user.user_open_ids.find_each { |x| x.destroy } @user.api_key.try(:destroy) - history_details = { - action: UserHistory.actions[:anonymize_user], - target_user_id: @user.id, - acting_user_id: @actor ? @actor.id : @user.id, - } - - Invite.with_deleted.where(user_id: @user.id).destroy_all - EmailToken.where(user_id: @user.id).destroy_all - EmailLog.where(user_id: @user.id).delete_all - IncomingEmail.where("user_id = ? OR from_address = ?", @user.id, @prev_email).delete_all - - Post.with_deleted - .where(user_id: @user.id) - .where.not(raw_email: nil) - .update_all(raw_email: nil) - - if SiteSetting.log_anonymizer_details? - history_details[:email] = @prev_email - history_details[:details] = "username: #{@prev_username}" - end - - @user_history = UserHistory.create(history_details) + @user_history = log_action end + Jobs.enqueue(:anonymize_user, + user_id: @user.id, + prev_email: @prev_email, + anonymize_ip: @opts[:anonymize_ip]) + DiscourseEvent.trigger(:user_anonymized, user: @user, opts: @opts) @user end @@ -95,29 +82,18 @@ class UserAnonymizer raise "Failed to generate an anon username" end - def ip_where(column = 'user_id') - ["#{column} = :user_id AND ip_address IS NOT NULL", user_id: @user.id] + def log_action + history_details = { + action: UserHistory.actions[:anonymize_user], + target_user_id: @user.id, + acting_user_id: @actor ? @actor.id : @user.id, + } + + if SiteSetting.log_anonymizer_details? + history_details[:email] = @prev_email + history_details[:details] = "username: #{@prev_username}" + end + + UserHistory.create(history_details) end - - def anonymize_ips(new_ip) - @user.ip_address = new_ip - @user.registration_ip_address = new_ip - - IncomingLink.where(ip_where('current_user_id')).update_all(ip_address: new_ip) - ScreenedEmail.where(email: @prev_email).update_all(ip_address: new_ip) - SearchLog.where(ip_where).update_all(ip_address: new_ip) - TopicLinkClick.where(ip_where).update_all(ip_address: new_ip) - TopicViewItem.where(ip_where).update_all(ip_address: new_ip) - UserHistory.where(ip_where('acting_user_id')).update_all(ip_address: new_ip) - UserProfileView.where(ip_where).update_all(ip_address: new_ip) - - # UserHistory for delete_user logs the user's IP. Note this is quite ugly but we don't - # have a better way of querying on details right now. - UserHistory.where( - "action = :action AND details LIKE 'id: #{@user.id}\n%'", - action: UserHistory.actions[:delete_user] - ).update_all(ip_address: new_ip) - - end - end diff --git a/spec/services/user_anonymizer_spec.rb b/spec/services/user_anonymizer_spec.rb index 14fb9eb989b..aae23e8ee92 100644 --- a/spec/services/user_anonymizer_spec.rb +++ b/spec/services/user_anonymizer_spec.rb @@ -35,7 +35,7 @@ describe UserAnonymizer do it "changes email address" do make_anonymous - expect(user.reload.email).to eq("#{user.username}@example.com") + expect(user.reload.email).to eq("#{user.username}@anonymized.invalid") end it "turns off all notifications" do @@ -203,48 +203,54 @@ describe UserAnonymizer do expect(user.api_key).to eq(nil) end - it "removes invites" do - Fabricate(:invite, user: user) - Fabricate(:invite, user: another_user) + context "executes job" do + before do + SiteSetting.queue_jobs = false + end - expect { make_anonymous }.to change { Invite.count }.by(-1) - expect(Invite.where(user_id: user.id).count).to eq(0) - end + it "removes invites" do + Fabricate(:invite, user: user) + Fabricate(:invite, user: another_user) - it "removes email tokens" do - Fabricate(:email_token, user: user) - Fabricate(:email_token, user: another_user) + expect { make_anonymous }.to change { Invite.count }.by(-1) + expect(Invite.where(user_id: user.id).count).to eq(0) + end - expect { make_anonymous }.to change { EmailToken.count }.by(-1) - expect(EmailToken.where(user_id: user.id).count).to eq(0) - end + it "removes email tokens" do + Fabricate(:email_token, user: user) + Fabricate(:email_token, user: another_user) - it "removes email log entries" do - Fabricate(:email_log, user: user) - Fabricate(:email_log, user: another_user) + expect { make_anonymous }.to change { EmailToken.count }.by(-1) + expect(EmailToken.where(user_id: user.id).count).to eq(0) + end - expect { make_anonymous }.to change { EmailLog.count }.by(-1) - expect(EmailLog.where(user_id: user.id).count).to eq(0) - end + it "removes email log entries" do + Fabricate(:email_log, user: user) + Fabricate(:email_log, user: another_user) - it "removes incoming emails" do - Fabricate(:incoming_email, user: user, from_address: user.email) - Fabricate(:incoming_email, from_address: user.email, error: "Some error") - Fabricate(:incoming_email, user: another_user, from_address: another_user.email) + expect { make_anonymous }.to change { EmailLog.count }.by(-1) + expect(EmailLog.where(user_id: user.id).count).to eq(0) + end - expect { make_anonymous }.to change { IncomingEmail.count }.by(-2) - expect(IncomingEmail.where(user_id: user.id).count).to eq(0) - expect(IncomingEmail.where(from_address: original_email).count).to eq(0) - end + it "removes incoming emails" do + Fabricate(:incoming_email, user: user, from_address: user.email) + Fabricate(:incoming_email, from_address: user.email, error: "Some error") + Fabricate(:incoming_email, user: another_user, from_address: another_user.email) - it "removes raw email from posts" do - post1 = Fabricate(:post, user: user, via_email: true, raw_email: "raw email from user") - post2 = Fabricate(:post, user: another_user, via_email: true, raw_email: "raw email from another user") + expect { make_anonymous }.to change { IncomingEmail.count }.by(-2) + expect(IncomingEmail.where(user_id: user.id).count).to eq(0) + expect(IncomingEmail.where(from_address: original_email).count).to eq(0) + end - make_anonymous + it "removes raw email from posts" do + post1 = Fabricate(:post, user: user, via_email: true, raw_email: "raw email from user") + post2 = Fabricate(:post, user: another_user, via_email: true, raw_email: "raw email from another user") - expect(post1.reload).to have_attributes(via_email: true, raw_email: nil) - expect(post2.reload).to have_attributes(via_email: true, raw_email: "raw email from another user") + make_anonymous + + expect(post1.reload).to have_attributes(via_email: true, raw_email: nil) + expect(post2.reload).to have_attributes(via_email: true, raw_email: "raw email from another user") + end end end @@ -267,6 +273,7 @@ describe UserAnonymizer do end it "exhaustively replaces all user ips" do + SiteSetting.queue_jobs = false link = IncomingLink.create!(current_user_id: user.id, ip_address: old_ip, post_id: post.id) screened_email = ScreenedEmail.create!(email: user.email, ip_address: old_ip)