users: delete accounts with invalid names.

Add a fix script to delete all accounts with invalid usernames. Also
change it so the owner-level user can delete accounts belonging to other
users.

Users who have logged in in the last year and who have a valid email
address will be given a one week warning. After that all accounts with
invalid names will be deleted. Anyone who has visited the site in the
last 6 months will have already seen a warning page that their name must
be changed to keep using the site.
This commit is contained in:
evazion
2022-09-19 04:51:56 -05:00
parent 23f9a1af7e
commit aea3837f9a
4 changed files with 110 additions and 17 deletions

View File

@@ -9,7 +9,7 @@ module Maintenance
end
def destroy
deletion = UserDeletion.new(CurrentUser.user, params.dig(:user, :password), request)
deletion = UserDeletion.new(user: CurrentUser.user, deleter: CurrentUser.user, password: params.dig(:user, :password), request: request)
deletion.delete!
if deletion.errors.none?

View File

@@ -7,16 +7,18 @@
class UserDeletion
include ActiveModel::Validations
attr_reader :user, :password, :request
attr_reader :user, :deleter, :password, :request
validate :validate_deletion
# Initialize a user deletion.
# @param user [User] the user to delete
# @param user [User] the user performing the deletion
# @param password [String] the user's password (for confirmation)
# @param request the HTTP request (for logging the deletion in the user event log)
def initialize(user, password, request)
def initialize(user:, deleter: user, password: nil, request: nil)
@user = user
@deleter = deleter
@password = password
@request = request
end
@@ -40,11 +42,11 @@ class UserDeletion
private
def create_mod_action
ModAction.log("deleted user ##{user.id}", :user_delete, user)
ModAction.log("deleted user ##{user.id}", :user_delete, deleter)
end
def create_user_event
UserEvent.create_from_request!(user, :user_deletion, request)
UserEvent.create_from_request!(user, :user_deletion, request) if request.present?
end
def clear_saved_searches
@@ -79,16 +81,30 @@ class UserDeletion
end
def validate_deletion
if !user.authenticate_password(password)
errors.add(:base, "Password is incorrect")
end
if user == deleter
if !user.authenticate_password(password)
errors.add(:base, "Password is incorrect")
end
if user.is_admin?
errors.add(:base, "Admins cannot delete their account")
end
if user.is_admin?
errors.add(:base, "Admins cannot delete their account")
end
if user.is_banned?
errors.add(:base, "You cannot delete your account if you are banned")
if user.is_banned?
errors.add(:base, "You cannot delete your account if you are banned")
end
else
if !deleter.is_owner?
errors.add(:base, "You cannot delete an account belonging to another user")
end
if user.is_gold?
errors.add(:base, "You cannot delete a privileged account")
end
if user.created_at.before?(6.months.ago)
errors.add(:base, "You cannot delete a recent account")
end
end
end
end

View File

@@ -0,0 +1,57 @@
#!/usr/bin/env ruby
require_relative "base"
def delete(user)
return if !user.name_invalid?
if ENV.fetch("WARN", "false").truthy? && user.can_receive_email?
Dmail.create_automated(to: user, title: "Action required: Change your username or your Danbooru account will be deleted", body: <<~EOS)
Your current Danbooru username is invalid. Your Danbooru account will be deleted in one week unless you change your username. Use the link below to change your username:
* "Change username":/user_name_change_requests/new
EOS
puts "[WARN] id=#{user.id} user='#{user.name}' email='#{user.email_address.address}'"
elsif ENV.fetch("DELETE", "false").truthy?
UserDeletion.new(user: user, deleter: User.owner).delete!
puts "[DELETE] id=#{user.id} user='#{user.name}'"
end
end
with_confirmation do
condition = ENV.fetch("COND", "TRUE")
users = User.where(Arel.sql(condition))
users.where("length(name) = 1").find_each do |user|
delete(user)
end
users.where("length(name) >= 25").find_each do |user|
delete(user)
end
users.where_regex(:name, "[[:space:]]").find_each do |user|
delete(user)
end
users.where_regex(:name, "^[[:punct:]]").find_each do |user|
delete(user)
end
users.where_regex(:name, "[[:punct:]]$").find_each do |user|
delete(user)
end
users.where_regex(:name, "\.(html|json|xml|atom|rss|txt|js|css|csv|png|jpg|jpeg|gif|png|mp4|webm|zip|pdf|exe|sitemap)$").find_each do |user|
delete(user)
end
users.where_regex(:name, "[`~!@#$%^&*()+={}\[\]|\\:;'\"<>,?/]").find_each do |user|
delete(user)
end
users.where_not_regex(:name, "[[:ascii:]]").find_each do |user|
delete(user)
end
end

View File

@@ -12,7 +12,7 @@ class UserDeletionTest < ActiveSupport::TestCase
context "for an invalid password" do
should "fail" do
@user = create(:user)
@deletion = UserDeletion.new(@user, "wrongpassword", @request)
@deletion = UserDeletion.new(user: @user, password: "wrongpassword", request: @request)
@deletion.delete!
assert_includes(@deletion.errors[:base], "Password is incorrect")
end
@@ -21,7 +21,7 @@ class UserDeletionTest < ActiveSupport::TestCase
context "for an admin" do
should "fail" do
@user = create(:admin_user)
@deletion = UserDeletion.new(@user, "password", @request)
@deletion = UserDeletion.new(user: @user, password: "password", request: @request)
@deletion.delete!
assert_includes(@deletion.errors[:base], "Admins cannot delete their account")
end
@@ -30,7 +30,7 @@ class UserDeletionTest < ActiveSupport::TestCase
context "for a banned user" do
should "fail" do
@user = create(:banned_user)
@deletion = UserDeletion.new(@user, "password", @request)
@deletion = UserDeletion.new(user: @user, password: "password", request: @request)
@deletion.delete!
assert_includes(@deletion.errors[:base], "You cannot delete your account if you are banned")
end
@@ -40,7 +40,7 @@ class UserDeletionTest < ActiveSupport::TestCase
context "a valid user deletion" do
setup do
@user = create(:user, name: "foo", email_address: build(:email_address))
@deletion = UserDeletion.new(@user, "password", @request)
@deletion = UserDeletion.new(user: @user, password: "password", request: @request)
end
should "blank out the email" do
@@ -82,4 +82,24 @@ class UserDeletionTest < ActiveSupport::TestCase
assert_equal(0, @post.reload.fav_count)
end
end
context "deleting another user's account" do
should "work for the owner-level user" do
@user = create(:user)
@deletion = UserDeletion.new(user: @user, deleter: create(:owner_user))
@deletion.delete!
assert_equal("user_#{@user.id}", @user.reload.name)
assert_equal(true, ModAction.exists?(description: "deleted user ##{@user.id}", creator: @deletion.deleter))
end
should "not work for other users" do
@user = create(:user)
@deletion = UserDeletion.new(user: @user, deleter: create(:admin_user))
@deletion.delete!
assert_not_equal("user_#{@user.id}", @user.reload.name)
assert_equal(0, ModAction.count)
end
end
end