Deactivate local users on deletion instead of deleting the record

Prevents the possibility of re-registration, which allowed to read
DMs of the deleted account.

Also includes a migration that tries to find any already deleted
accounts and insert skeletons for them.

Closes pleroma/pleroma#1687
This commit is contained in:
rinpatch 2020-04-29 14:26:31 +03:00
parent e186d9941d
commit e55876409b
6 changed files with 63 additions and 17 deletions

View File

@ -1445,9 +1445,16 @@ def perform(:delete, %User{} = user) do
end) end)
delete_user_activities(user) delete_user_activities(user)
if user.local do
user
|> change(%{deactivated: true, email: nil})
|> update_and_set_cache()
else
invalidate_cache(user) invalidate_cache(user)
Repo.delete(user) Repo.delete(user)
end end
end
def perform(:deactivate_async, user, status), do: deactivate(user, status) def perform(:deactivate_async, user, status), do: deactivate(user, status)

View File

@ -61,7 +61,10 @@ def emoji_reactions_by(%{assigns: %{user: user}} = conn, %{"id" => activity_id}
else else
users = users =
Enum.map(user_ap_ids, &User.get_cached_by_ap_id/1) Enum.map(user_ap_ids, &User.get_cached_by_ap_id/1)
|> Enum.filter(& &1) |> Enum.filter(fn
%{deactivated: false} -> true
_ -> false
end)
%{ %{
name: emoji, name: emoji,

View File

@ -0,0 +1,45 @@
defmodule Pleroma.Repo.Migrations.InsertSkeletonsForDeletedUsers do
use Ecto.Migration
alias Pleroma.User
alias Pleroma.Repo
import Ecto.Query
def change do
Application.ensure_all_started(:flake_id)
local_ap_id =
User.Query.build(%{local: true})
|> select([u], u.ap_id)
|> limit(1)
|> Repo.one()
unless local_ap_id == nil do
# Hack to get instance base url because getting it from Phoenix
# would require starting the whole application
instance_uri =
local_ap_id
|> URI.parse()
|> Map.put(:query, nil)
|> Map.put(:path, nil)
|> URI.to_string()
{:ok, %{rows: ap_ids}} =
Ecto.Adapters.SQL.query(
Repo,
"select distinct unnest(nonexistent_locals.recipients) from activities, lateral (select array_agg(recipient) as recipients from unnest(activities.recipients) as recipient where recipient similar to '#{
instance_uri
}/users/[A-Za-z0-9]*' and not(recipient in (select ap_id from users where local = true))) nonexistent_locals;",
[],
timeout: :infinity
)
ap_ids
|> Enum.each(fn [ap_id] ->
Ecto.Changeset.change(%User{}, deactivated: true, ap_id: ap_id)
|> Repo.insert()
end)
end
end
end

View File

@ -92,7 +92,7 @@ test "user is deleted" do
assert_received {:mix_shell, :info, [message]} assert_received {:mix_shell, :info, [message]}
assert message =~ " deleted" assert message =~ " deleted"
refute User.get_by_nickname(user.nickname) assert %{deactivated: true} = User.get_by_nickname(user.nickname)
end end
test "no user to delete" do test "no user to delete" do

View File

@ -1135,16 +1135,7 @@ test ".delete_user_activities deletes all create activities", %{user: user} do
refute Activity.get_by_id(activity.id) refute Activity.get_by_id(activity.id)
end end
test "it deletes deactivated user" do test "it deactivates a user, all follow relationships and all activities", %{user: user} do
{:ok, user} = insert(:user, deactivated: true) |> User.set_cache()
{:ok, job} = User.delete(user)
{:ok, _user} = ObanHelpers.perform(job)
refute User.get_by_id(user.id)
end
test "it deletes a user, all follow relationships and all activities", %{user: user} do
follower = insert(:user) follower = insert(:user)
{:ok, follower} = User.follow(follower, user) {:ok, follower} = User.follow(follower, user)
@ -1164,8 +1155,7 @@ test "it deletes a user, all follow relationships and all activities", %{user: u
follower = User.get_cached_by_id(follower.id) follower = User.get_cached_by_id(follower.id)
refute User.following?(follower, user) refute User.following?(follower, user)
refute User.get_by_id(user.id) assert %{deactivated: true} = User.get_by_id(user.id)
assert {:ok, nil} == Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
user_activities = user_activities =
user.ap_id user.ap_id

View File

@ -872,7 +872,8 @@ test "it fails for incoming deletes with spoofed origin" do
@tag capture_log: true @tag capture_log: true
test "it works for incoming user deletes" do test "it works for incoming user deletes" do
%{ap_id: ap_id} = insert(:user, ap_id: "http://mastodon.example.org/users/admin") %{ap_id: ap_id} =
insert(:user, ap_id: "http://mastodon.example.org/users/admin", local: false)
data = data =
File.read!("test/fixtures/mastodon-delete-user.json") File.read!("test/fixtures/mastodon-delete-user.json")