mirror of
https://github.com/discourse/discourse.git
synced 2024-12-24 19:03:49 +08:00
415 lines
13 KiB
Ruby
415 lines
13 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
RSpec.describe UserAnonymizer do
|
|
let(:admin) { Fabricate(:admin) }
|
|
|
|
describe "event" do
|
|
subject(:make_anonymous) do
|
|
described_class.make_anonymous(user, admin, anonymize_ip: "2.2.2.2")
|
|
end
|
|
|
|
let(:user) { Fabricate(:user, username: "edward") }
|
|
|
|
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
|
|
subject(:make_anonymous) { described_class.make_anonymous(user, admin) }
|
|
|
|
let(:original_email) { "edward@example.net" }
|
|
let(:user) { Fabricate(:user, username: "edward", email: original_email) }
|
|
fab!(:another_user) { Fabricate(:evil_trout) }
|
|
|
|
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 normalized email address" do
|
|
make_anonymous
|
|
|
|
primary_email = user.reload.primary_email
|
|
|
|
expect(primary_email.normalized_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
|
|
|
|
it "clears existing user status" do
|
|
user_status = Fabricate(:user_status, user: user)
|
|
|
|
expect do
|
|
make_anonymous
|
|
user.reload
|
|
end.to change { user.user_status }.from(user_status).to(nil)
|
|
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}", "48")
|
|
expect(post.cooked).to include(old_avatar_url)
|
|
|
|
make_anonymous
|
|
post.reload
|
|
new_avatar_url = user.reload.avatar_template.gsub("{size}", "48")
|
|
|
|
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)
|
|
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
|