discourse/spec/services/user_anonymizer_spec.rb
2023-01-09 11:49:28 +00:00

396 lines
13 KiB
Ruby

# frozen_string_literal: true
RSpec.describe UserAnonymizer do
let(:admin) { Fabricate(:admin) }
describe "event" do
let(:user) { Fabricate(:user, username: "edward") }
subject(:make_anonymous) do
described_class.make_anonymous(user, admin, anonymize_ip: "2.2.2.2")
end
it "triggers the event" do
events = DiscourseEvent.track_events { make_anonymous }
anon_event = events.detect { |e| e[:event_name] == :user_anonymized }
expect(anon_event).to be_present
params_hash = anon_event[:params][0]
expect(params_hash[:user]).to eq(user)
expect(params_hash[:opts][:anonymize_ip]).to eq("2.2.2.2")
end
end
describe ".make_anonymous" do
let(:original_email) { "edward@example.net" }
let(:user) { Fabricate(:user, username: "edward", email: original_email) }
fab!(:another_user) { Fabricate(:evil_trout) }
subject(:make_anonymous) { described_class.make_anonymous(user, admin) }
it "changes username" do
make_anonymous
expect(user.reload.username).to match(/^anon\d{3,}$/)
end
it "changes the primary email address" do
make_anonymous
expect(user.reload.email).to eq("#{user.username}@anonymized.invalid")
end
it "changes the primary email address when there is an email domain allowlist" do
SiteSetting.allowed_email_domains = "example.net|wayne.com|discourse.org"
make_anonymous
expect(user.reload.email).to eq("#{user.username}@anonymized.invalid")
end
it "deletes secondary email addresses" do
Fabricate(:secondary_email, user: user, email: "secondary_email@example.com")
make_anonymous
expect(user.reload.secondary_emails).to be_blank
end
it "turns off all notifications" do
user.user_option.update_columns(
email_level: UserOption.email_level_types[:always],
email_messages_level: UserOption.email_level_types[:always],
)
make_anonymous
user.reload
expect(user.user_option.email_digests).to eq(false)
expect(user.user_option.email_level).to eq(UserOption.email_level_types[:never])
expect(user.user_option.email_messages_level).to eq(UserOption.email_level_types[:never])
expect(user.user_option.mailing_list_mode).to eq(false)
end
context "when Site Settings do not require full name" do
before { SiteSetting.full_name_required = false }
it "resets profile to default values" do
user.update!(name: "Bibi", date_of_birth: 19.years.ago, title: "Super Star")
profile = user.reload.user_profile
upload = Fabricate(:upload)
profile.update!(
location: "Moose Jaw",
website: "http://www.bim.com",
bio_raw: "I'm Bibi from Moosejaw. I sing and dance.",
bio_cooked: "I'm Bibi from Moosejaw. I sing and dance.",
profile_background_upload: upload,
bio_cooked_version: 2,
card_background_upload: upload,
)
prev_username = user.username
UserAuthToken.generate!(user_id: user.id)
make_anonymous
user.reload
expect(user.username).not_to eq(prev_username)
expect(user.name).not_to be_present
expect(user.date_of_birth).to eq(nil)
expect(user.title).not_to be_present
expect(user.user_auth_tokens.count).to eq(0)
profile = user.reload.user_profile
expect(profile.location).to eq(nil)
expect(profile.website).to eq(nil)
expect(profile.bio_cooked).to eq(nil)
expect(profile.profile_background_upload).to eq(nil)
expect(profile.bio_cooked_version).to eq(UserProfile::BAKED_VERSION)
expect(profile.card_background_upload).to eq(nil)
end
end
context "when Site Settings require full name" do
before { SiteSetting.full_name_required = true }
it "changes name to anonymized username" do
prev_username = user.username
user.update(name: "Bibi", date_of_birth: 19.years.ago, title: "Super Star")
make_anonymous
user.reload
expect(user.name).not_to eq(prev_username)
expect(user.name).to eq(user.username)
end
end
it "removes the avatar" do
upload = Fabricate(:upload, user: user)
user.user_avatar = UserAvatar.new(user_id: user.id, custom_upload_id: upload.id)
user.uploaded_avatar_id = upload.id # chosen in user preferences
user.save!
make_anonymous
user.reload
expect(user.user_avatar).to eq(nil)
expect(user.uploaded_avatar_id).to eq(nil)
end
it "updates the avatar in posts" do
Jobs.run_immediately!
upload = Fabricate(:upload, user: user)
user.user_avatar = UserAvatar.new(user_id: user.id, custom_upload_id: upload.id)
user.uploaded_avatar_id = upload.id # chosen in user preferences
user.save!
topic = Fabricate(:topic, user: user)
quoted_post = create_post(user: user, topic: topic, post_number: 1, raw: "quoted post")
stub_image_size
post = create_post(raw: <<~RAW)
Lorem ipsum
[quote="#{quoted_post.username}, post:1, topic:#{quoted_post.topic.id}"]
quoted post
[/quote]
RAW
old_avatar_url = user.avatar_template.gsub("{size}", "40")
expect(post.cooked).to include(old_avatar_url)
make_anonymous
post.reload
new_avatar_url = user.reload.avatar_template.gsub("{size}", "40")
expect(post.cooked).to_not include(old_avatar_url)
expect(post.cooked).to include(new_avatar_url)
end
it "logs the action with the original details" do
SiteSetting.log_anonymizer_details = true
helper = UserAnonymizer.new(user, admin)
orig_email = user.email
orig_username = user.username
helper.make_anonymous
history = helper.user_history
expect(history).to be_present
expect(history.email).to eq(orig_email)
expect(history.details).to match(orig_username)
end
it "logs the action without the original details" do
SiteSetting.log_anonymizer_details = false
helper = UserAnonymizer.new(user, admin)
orig_email = user.email
orig_username = user.username
helper.make_anonymous
history = helper.user_history
expect(history).to be_present
expect(history.email).not_to eq(orig_email)
expect(history.details).not_to match(orig_username)
end
it "removes external auth associations" do
user.user_associated_accounts = [
UserAssociatedAccount.create(
user_id: user.id,
provider_uid: "example",
provider_name: "facebook",
),
]
user.single_sign_on_record =
SingleSignOnRecord.create(
user_id: user.id,
external_id: "example",
last_payload: "looks good",
)
make_anonymous
user.reload
expect(user.user_associated_accounts).to be_empty
expect(user.single_sign_on_record).to eq(nil)
end
it "removes api key" do
ApiKey.create!(user_id: user.id)
expect { make_anonymous }.to change { ApiKey.count }.by(-1)
user.reload
expect(user.api_keys).to be_empty
end
it "removes user api key" do
user_api_key = Fabricate(:user_api_key, user: user)
expect { make_anonymous }.to change { UserApiKey.count }.by(-1)
user.reload
expect(user.user_api_keys).to be_empty
end
context "when executing jobs" do
before { Jobs.run_immediately! }
it "removes invites" do
Fabricate(:invited_user, invite: Fabricate(:invite), user: user)
Fabricate(:invited_user, invite: Fabricate(:invite), user: another_user)
expect { make_anonymous }.to change { InvitedUser.count }.by(-1)
expect(InvitedUser.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)
expect { make_anonymous }.to change { EmailToken.count }.by(-1)
expect(EmailToken.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)
expect { make_anonymous }.to change { EmailLog.count }.by(-1)
expect(EmailLog.where(user_id: user.id).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)
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 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",
)
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
it "does not delete profile views" do
UserProfileView.add(user.id, "127.0.0.1", another_user.id, Time.now, true)
expect { make_anonymous }.to_not change { UserProfileView.count }
end
it "removes user field values" do
field1 = Fabricate(:user_field)
field2 = Fabricate(:user_field)
user.custom_fields = {
some_field: "123",
"user_field_#{field1.id}": "foo",
"user_field_#{field2.id}": "bar",
another_field: "456",
}
expect { make_anonymous }.to change { user.custom_fields }
expect(user.reload.custom_fields).to eq("some_field" => "123", "another_field" => "456")
end
end
end
describe "anonymize_ip" do
let(:old_ip) { "1.2.3.4" }
let(:anon_ip) { "0.0.0.0" }
let(:user) { Fabricate(:user, ip_address: old_ip, registration_ip_address: old_ip) }
fab!(:post) { Fabricate(:post) }
let(:topic) { post.topic }
it "doesn't anonymize ips by default" do
UserAnonymizer.make_anonymous(user, admin)
expect(user.ip_address).to eq(old_ip)
end
it "is called if you pass an option" do
UserAnonymizer.make_anonymous(user, admin, anonymize_ip: anon_ip)
user.reload
expect(user.ip_address).to eq(anon_ip)
end
it "exhaustively replaces all user ips" do
Jobs.run_immediately!
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)
search_log =
SearchLog.create!(
term: "wat",
search_type: SearchLog.search_types[:header],
user_id: user.id,
ip_address: old_ip,
)
topic_link =
TopicLink.create!(
user_id: admin.id,
topic_id: topic.id,
url: "https://discourse.org",
domain: "discourse.org",
)
topic_link_click =
TopicLinkClick.create!(topic_link_id: topic_link.id, user_id: user.id, ip_address: old_ip)
user_profile_view =
UserProfileView.create!(
user_id: user.id,
user_profile_id: admin.user_profile.id,
ip_address: old_ip,
viewed_at: Time.now,
)
TopicViewItem.create!(
topic_id: topic.id,
user_id: user.id,
ip_address: old_ip,
viewed_at: Time.now,
)
delete_history = StaffActionLogger.new(admin).log_user_deletion(user)
user_history = StaffActionLogger.new(user).log_backup_create
UserAnonymizer.make_anonymous(user, admin, anonymize_ip: anon_ip)
expect(user.registration_ip_address).to eq(anon_ip)
expect(link.reload.ip_address).to eq(anon_ip)
expect(screened_email.reload.ip_address).to eq(anon_ip)
expect(search_log.reload.ip_address).to eq(anon_ip)
expect(topic_link_click.reload.ip_address).to eq(anon_ip)
topic_view = TopicViewItem.where(topic_id: topic.id, user_id: user.id).first
expect(topic_view.ip_address).to eq(anon_ip)
expect(delete_history.reload.ip_address).to eq(anon_ip)
expect(user_history.reload.ip_address).to eq(anon_ip)
expect(user_profile_view.reload.ip_address).to eq(anon_ip)
end
end
describe "anonymize_emails" do
it "destroys all associated invites" do
invite = Fabricate(:invite, email: "test@example.com")
user = invite.redeem
Jobs.run_immediately!
described_class.make_anonymous(user, admin)
expect(user.email).not_to eq("test@example.com")
expect(Invite.exists?(id: invite.id)).to eq(false)
end
end
end