Commit eb84de01 authored by minibikini's avatar minibikini

allow users to disable their own account

parent dd586553
......@@ -343,7 +343,8 @@ config :pleroma, Pleroma.Web.Federator.RetryQueue,
config :pleroma, Pleroma.Jobs,
federator_incoming: [max_jobs: 50],
federator_outgoing: [max_jobs: 50],
mailer: [max_jobs: 10]
mailer: [max_jobs: 10],
user: [max_jobs: 10]
config :auto_linker,
opts: [
......
......@@ -23,7 +23,7 @@ defmodule Mix.Tasks.Pleroma.User do
- `--password PASSWORD` - the user's password
- `--moderator`/`--no-moderator` - whether the user is a moderator
- `--admin`/`--no-admin` - whether the user is an admin
- `-y`, `--assume-yes`/`--no-assume-yes` - whether to assume yes to all questions
- `-y`, `--assume-yes`/`--no-assume-yes` - whether to assume yes to all questions
## Generate an invite link.
......@@ -37,6 +37,10 @@ defmodule Mix.Tasks.Pleroma.User do
mix pleroma.user toggle_activated NICKNAME
## Disable or enable the user's account.
mix pleroma.user toggle_disabled NICKNAME
## Unsubscribe local users from user's account and deactivate it
mix pleroma.user unsubscribe NICKNAME
......@@ -170,6 +174,20 @@ defmodule Mix.Tasks.Pleroma.User do
end
end
def run(["toggle_disabled", nickname]) do
Common.start_pleroma()
case User.get_by_nickname(nickname) do
%User{} = user ->
{:ok, user} = User.disable(user, !user.info.disabled)
status = if(user.info.disabled, do: "ON", else: "OFF")
Mix.shell().info("Disabled status of #{nickname}: #{status}")
_ ->
Mix.shell().error("No user #{nickname}")
end
end
def run(["reset_password", nickname]) do
Common.start_pleroma()
......
......@@ -42,7 +42,10 @@ defmodule Pleroma.Activity do
end
def get_by_id(id) do
Repo.get(Activity, id)
Activity
|> where([a], a.id == ^id)
|> restrict_disabled_users()
|> Repo.one()
end
def by_object_ap_id(ap_id) do
......@@ -92,6 +95,7 @@ defmodule Pleroma.Activity do
def get_create_by_object_ap_id(ap_id) when is_binary(ap_id) do
create_by_object_ap_id(ap_id)
|> restrict_disabled_users()
|> Repo.one()
end
......@@ -123,4 +127,14 @@ defmodule Pleroma.Activity do
|> where([s], s.actor == ^actor)
|> Repo.all()
end
def restrict_disabled_users(query) do
from(activity in query,
where:
fragment(
"? not in (SELECT ap_id FROM users WHERE info->'disabled' @> 'true')",
activity.actor
)
)
end
end
......@@ -41,7 +41,6 @@ defmodule Pleroma.Gopher.Server.ProtocolHandler do
alias Pleroma.Activity
alias Pleroma.HTML
alias Pleroma.User
alias Pleroma.Repo
def start_link(ref, socket, transport, opts) do
pid = spawn_link(__MODULE__, :init, [ref, socket, transport, opts])
......@@ -110,7 +109,7 @@ defmodule Pleroma.Gopher.Server.ProtocolHandler do
end
def response("/notices/" <> id) do
with %Activity{} = activity <- Repo.get(Activity, id),
with %Activity{} = activity <- Activity.get_by_id(id),
true <- Visibility.is_public?(activity) do
activities =
ActivityPub.fetch_activities_for_context(activity.data["context"])
......
......@@ -36,22 +36,22 @@ defmodule Pleroma.Notification do
defp restrict_since(query, _), do: query
def for_user(user, opts \\ %{}) do
query =
from(
n in Notification,
where: n.user_id == ^user.id,
order_by: [desc: n.id],
join: activity in assoc(n, :activity),
preload: [activity: activity],
limit: 20
)
query =
query
|> restrict_since(opts)
|> restrict_max(opts)
Repo.all(query)
from(
n in Notification,
where: n.user_id == ^user.id,
order_by: [desc: n.id],
join: activity in assoc(n, :activity),
preload: [activity: activity],
limit: 20,
where:
fragment(
"? not in (SELECT ap_id FROM users WHERE info->'disabled' @> 'true')",
activity.actor
)
)
|> restrict_since(opts)
|> restrict_max(opts)
|> Repo.all()
end
def set_read_up_to(%{id: user_id} = _user, id) do
......
......@@ -108,10 +108,8 @@ defmodule Pleroma.User do
end
def user_info(%User{} = user) do
oneself = if user.local, do: 1, else: 0
%{
following_count: length(user.following) - oneself,
following_count: following_count(user),
note_count: user.info.note_count,
follower_count: user.info.follower_count,
locked: user.info.locked,
......@@ -120,6 +118,23 @@ defmodule Pleroma.User do
}
end
defp restrict_disabled(query) do
from(u in query,
where: not fragment("? \\? 'disabled' AND ?->'disabled' @> 'true'", u.info, u.info)
)
end
def following_count(%User{following: []}), do: 0
def following_count(%User{following: following, id: id}) do
from(u in User,
where: u.follower_address in ^following,
where: u.id != ^id
)
|> restrict_disabled()
|> Repo.aggregate(:count, :id)
end
def remote_user_creation(params) do
params =
params
......@@ -545,6 +560,7 @@ defmodule Pleroma.User do
where: fragment("? <@ ?", ^[follower_address], u.following),
where: u.id != ^id
)
|> restrict_disabled()
end
def get_followers_query(user, page) do
......@@ -572,6 +588,7 @@ defmodule Pleroma.User do
where: u.follower_address in ^following,
where: u.id != ^id
)
|> restrict_disabled()
end
def get_friends_query(user, page) do
......@@ -681,11 +698,10 @@ defmodule Pleroma.User do
info_cng = User.Info.set_note_count(user.info, note_count)
cng =
change(user)
|> put_embed(:info, info_cng)
update_and_set_cache(cng)
user
|> change()
|> put_embed(:info, info_cng)
|> update_and_set_cache()
end
def update_follower_count(%User{} = user) do
......@@ -694,6 +710,7 @@ defmodule Pleroma.User do
|> where([u], ^user.follower_address in u.following)
|> where([u], u.id != ^user.id)
|> select([u], %{count: count(u.id)})
|> restrict_disabled()
User
|> where(id: ^user.id)
......@@ -860,6 +877,7 @@ defmodule Pleroma.User do
^processed_query
)
)
|> restrict_disabled()
end
defp trigram_search_subquery(term) do
......@@ -876,6 +894,7 @@ defmodule Pleroma.User do
},
where: fragment("trim(? || ' ' || coalesce(?, '')) % ?", u.nickname, u.name, ^term)
)
|> restrict_disabled()
end
defp boost_search_results(results, nil), do: results
......@@ -1062,11 +1081,10 @@ defmodule Pleroma.User do
def deactivate(%User{} = user, status \\ true) do
info_cng = User.Info.set_activation_status(user.info, status)
cng =
change(user)
|> put_embed(:info, info_cng)
update_and_set_cache(cng)
user
|> change()
|> put_embed(:info, info_cng)
|> update_and_set_cache()
end
def delete(%User{} = user) do
......@@ -1100,6 +1118,26 @@ defmodule Pleroma.User do
{:ok, user}
end
def disable_async(user, status \\ true) do
Pleroma.Jobs.enqueue(:user, __MODULE__, [:disable_async, user, status])
end
def disable(%User{} = user, status \\ true) do
with {:ok, user} <- User.deactivate(user, status),
info_cng <- User.Info.set_disabled_status(user.info, status),
{:ok, user} <-
user
|> change()
|> put_embed(:info, info_cng)
|> update_and_set_cache(),
{:ok, friends} <- User.get_friends(user) do
Enum.each(friends, &update_follower_count(&1))
{:ok, user}
end
end
def perform(:disable_async, user, status), do: disable(user, status)
def html_filter_policy(%User{info: %{no_rich_text: true}}) do
Pleroma.HTML.Scrubber.TwitterText
end
......
......@@ -36,6 +36,7 @@ defmodule Pleroma.User.Info do
field(:hide_follows, :boolean, default: false)
field(:pinned_activities, {:array, :string}, default: [])
field(:flavour, :string, default: nil)
field(:disabled, :boolean, default: false)
# Found in the wild
# ap_id -> Where is this used?
......@@ -54,6 +55,14 @@ defmodule Pleroma.User.Info do
|> validate_required([:deactivated])
end
def set_disabled_status(info, disabled) do
params = %{disabled: disabled}
info
|> cast(params, [:disabled])
|> validate_required([:disabled])
end
def add_to_note_count(info, number) do
set_note_count(info, info.note_count + number)
end
......
......@@ -703,6 +703,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> restrict_replies(opts)
|> restrict_reblogs(opts)
|> restrict_pinned(opts)
|> Activity.restrict_disabled_users()
end
def fetch_activities(recipients, opts \\ %{}) do
......
......@@ -44,6 +44,16 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|> json(user.nickname)
end
def user_toggle_disabled(conn, %{"nickname" => nickname}) do
user = User.get_by_nickname(nickname)
{:ok, updated_user} = User.disable(user, !user.info.disabled)
conn
|> put_view(AccountView)
|> render("show.json", %{user: updated_user})
end
def user_toggle_activation(conn, %{"nickname" => nickname}) do
user = User.get_by_nickname(nickname)
......
......@@ -17,7 +17,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
# This is a hack for twidere.
def get_by_id_or_ap_id(id) do
activity = Repo.get(Activity, id) || Activity.get_create_by_object_ap_id(id)
activity = Activity.get_by_id(id) || Activity.get_create_by_object_ap_id(id)
activity &&
if activity.data["type"] == "Create" do
......@@ -30,7 +30,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
def get_replied_to_activity(""), do: nil
def get_replied_to_activity(id) when not is_nil(id) do
Repo.get(Activity, id)
Activity.get_by_id(id)
end
def get_replied_to_activity(_), do: nil
......
......@@ -307,7 +307,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
def get_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with %Activity{} = activity <- Repo.get(Activity, id),
with %Activity{} = activity <- Activity.get_by_id(id),
true <- Visibility.visible_for_user?(activity, user) do
conn
|> put_view(StatusView)
......@@ -316,7 +316,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
def get_context(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with %Activity{} = activity <- Repo.get(Activity, id),
with %Activity{} = activity <- Activity.get_by_id(id),
activities <-
ActivityPub.fetch_activities_for_context(activity.data["context"], %{
"blocking_user" => user,
......@@ -448,7 +448,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
def bookmark_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with %Activity{} = activity <- Repo.get(Activity, id),
with %Activity{} = activity <- Activity.get_by_id(id),
%User{} = user <- User.get_by_nickname(user.nickname),
true <- Visibility.visible_for_user?(activity, user),
{:ok, user} <- User.bookmark(user, activity.data["object"]["id"]) do
......@@ -459,7 +459,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
def unbookmark_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with %Activity{} = activity <- Repo.get(Activity, id),
with %Activity{} = activity <- Activity.get_by_id(id),
%User{} = user <- User.get_by_nickname(user.nickname),
true <- Visibility.visible_for_user?(activity, user),
{:ok, user} <- User.unbookmark(user, activity.data["object"]["id"]) do
......@@ -583,7 +583,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
def favourited_by(conn, %{"id" => id}) do
with %Activity{data: %{"object" => %{"likes" => likes}}} <- Repo.get(Activity, id) do
with %Activity{data: %{"object" => %{"likes" => likes}}} <- Activity.get_by_id(id) do
q = from(u in User, where: u.ap_id in ^likes)
users = Repo.all(q)
......@@ -596,7 +596,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
def reblogged_by(conn, %{"id" => id}) do
with %Activity{data: %{"object" => %{"announcements" => announces}}} <- Repo.get(Activity, id) do
with %Activity{data: %{"object" => %{"announcements" => announces}}} <- Activity.get_by_id(id) do
q = from(u in User, where: u.ap_id in ^announces)
users = Repo.all(q)
......
......@@ -143,6 +143,7 @@ defmodule Pleroma.Web.Router do
get("/users/search", AdminAPIController, :search_users)
delete("/user", AdminAPIController, :user_delete)
patch("/users/:nickname/toggle_activation", AdminAPIController, :user_toggle_activation)
patch("/users/:nickname/toggle_disabled", AdminAPIController, :user_toggle_disabled)
post("/user", AdminAPIController, :user_create)
put("/users/tag", AdminAPIController, :tag_users)
delete("/users/tag", AdminAPIController, :untag_users)
......@@ -183,6 +184,7 @@ defmodule Pleroma.Web.Router do
post("/change_password", UtilController, :change_password)
post("/delete_account", UtilController, :delete_account)
post("/disable_account", UtilController, :disable_account)
end
scope [] do
......
......@@ -311,6 +311,17 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
end
end
def disable_account(%{assigns: %{user: user}} = conn, params) do
case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
{:ok, user} ->
User.disable_async(user)
json(conn, %{status: "success"})
{:error, msg} ->
json(conn, %{error: msg})
end
end
def captcha(conn, _params) do
json(conn, Pleroma.Captcha.new())
end
......
......@@ -21,7 +21,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
end
def delete(%User{} = user, id) do
with %Activity{data: %{"type" => _type}} <- Repo.get(Activity, id),
with %Activity{data: %{"type" => _type}} <- Activity.get_by_id(id),
{:ok, activity} <- CommonAPI.delete(id, user) do
{:ok, activity}
end
......@@ -232,21 +232,27 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
def get_user(user \\ nil, params) do
case params do
%{"user_id" => user_id} ->
case target = User.get_cached_by_nickname_or_id(user_id) do
case User.get_cached_by_nickname_or_id(user_id) do
nil ->
{:error, "No user with such user_id"}
_ ->
{:ok, target}
%User{info: %{disabled: true}} ->
{:error, "User has been disabled"}
user ->
{:ok, user}
end
%{"screen_name" => nickname} ->
case target = Repo.get_by(User, nickname: nickname) do
case User.get_by_nickname(nickname) do
nil ->
{:error, "No user with such screen_name"}
_ ->
{:ok, target}
%User{info: %{disabled: true}} ->
{:error, "User has been disabled"}
user ->
{:ok, user}
end
_ ->
......
......@@ -269,7 +269,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
end
def fetch_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with %Activity{} = activity <- Repo.get(Activity, id),
with %Activity{} = activity <- Activity.get_by_id(id),
true <- Visibility.visible_for_user?(activity, user) do
conn
|> put_view(ActivityView)
......@@ -341,7 +341,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
end
def get_by_id_or_ap_id(id) do
activity = Repo.get(Activity, id) || Activity.get_create_by_object_ap_id(id)
activity = Activity.get_by_id(id) || Activity.get_create_by_object_ap_id(id)
if activity.data["type"] == "Create" do
activity
......
defmodule Pleroma.Repo.Migrations.UsersAddDisabledIndex do
use Ecto.Migration
def change do
create(index(:users, ["(info->'disabled')"], name: :users_disabled_index, using: :gin))
end
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment