def blocks?(user, %{ap_id: ap_id}) do
blocks = user.info["blocks"] || []
- Enum.member?(blocks, ap_id)
+ domain_blocks = user.info["domain_blocks"] || []
+ %{host: host} = URI.parse(ap_id)
+
+ Enum.member?(blocks, ap_id) ||
+ Enum.any?(domain_blocks, fn domain ->
+ host == domain
+ end)
+ end
+
+ def block_domain(user, domain) do
+ domain_blocks = user.info["domain_blocks"] || []
+ new_blocks = Enum.uniq([domain | domain_blocks])
+ new_info = Map.put(user.info, "domain_blocks", new_blocks)
+
+ cs = User.info_changeset(user, %{info: new_info})
+ update_and_set_cache(cs)
+ end
+
+ def unblock_domain(user, domain) do
+ blocks = user.info["domain_blocks"] || []
+ new_blocks = List.delete(blocks, domain)
+ new_info = Map.put(user.info, "domain_blocks", new_blocks)
+
+ cs = User.info_changeset(user, %{info: new_info})
+ update_and_set_cache(cs)
end
def local_user_query() do
defp restrict_blocked(query, %{"blocking_user" => %User{info: info}}) do
blocks = info["blocks"] || []
+ domain_blocks = info["domain_blocks"] || []
from(
activity in query,
where: fragment("not (? = ANY(?))", activity.actor, ^blocks),
- where: fragment("not (?->'to' \\?| ?)", activity.data, ^blocks)
+ where: fragment("not (?->'to' \\?| ?)", activity.data, ^blocks),
+ where: fragment("not (split_part(?, '/', 3) = ANY(?))", activity.actor, ^domain_blocks)
)
end
end
end
+ def domain_blocks(%{assigns: %{user: %{info: info}}} = conn, _) do
+ json(conn, info["domain_blocks"] || [])
+ end
+
+ def block_domain(%{assigns: %{user: blocker}} = conn, %{"domain" => domain}) do
+ User.block_domain(blocker, domain)
+ json(conn, %{})
+ end
+
+ def unblock_domain(%{assigns: %{user: blocker}} = conn, %{"domain" => domain}) do
+ User.unblock_domain(blocker, domain)
+ json(conn, %{})
+ end
+
def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
accounts = User.search(query, params["resolve"] == "true")
get("/blocks", MastodonAPIController, :blocks)
- get("/domain_blocks", MastodonAPIController, :empty_array)
get("/follow_requests", MastodonAPIController, :empty_array)
get("/mutes", MastodonAPIController, :empty_array)
get("/lists/:id/accounts", MastodonAPIController, :list_accounts)
post("/lists/:id/accounts", MastodonAPIController, :add_to_list)
delete("/lists/:id/accounts", MastodonAPIController, :remove_from_list)
+
+ get("/domain_blocks", MastodonAPIController, :domain_blocks)
+ post("/domain_blocks", MastodonAPIController, :block_domain)
+ delete("/domain_blocks", MastodonAPIController, :unblock_domain)
end
scope "/api/web", Pleroma.Web.MastodonAPI do
--- /dev/null
+defmodule Pleroma.Repo.Migrations.CreateApidHostExtractionIndex do
+ use Ecto.Migration
+ @disable_ddl_transaction true
+
+ def change do
+ create index(:activities, ["(split_part(actor, '/', 3))"], concurrently: true, name: :activities_hosts)
+ end
+end
end
end
+ describe "domain blocking" do
+ test "blocks domains" do
+ user = insert(:user)
+ collateral_user = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"})
+
+ {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com")
+
+ assert User.blocks?(user, collateral_user)
+ end
+
+ test "unblocks domains" do
+ user = insert(:user)
+ collateral_user = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"})
+
+ {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com")
+ {:ok, user} = User.unblock_domain(user, "awful-and-rude-instance.com")
+
+ refute User.blocks?(user, collateral_user)
+ end
+ end
+
test "get recipients from activity" do
actor = insert(:user)
user = insert(:user, local: true)
assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
end
+ test "blocking / unblocking a domain", %{conn: conn} do
+ user = insert(:user)
+ other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
+
+ conn =
+ conn
+ |> assign(:user, user)
+ |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
+
+ assert %{} = json_response(conn, 200)
+ user = User.get_cached_by_ap_id(user.ap_id)
+ assert User.blocks?(user, other_user)
+
+ conn =
+ build_conn()
+ |> assign(:user, user)
+ |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
+
+ assert %{} = json_response(conn, 200)
+ user = User.get_cached_by_ap_id(user.ap_id)
+ refute User.blocks?(user, other_user)
+ end
+
+ test "getting a list of domain blocks" do
+ user = insert(:user)
+
+ {:ok, user} = User.block_domain(user, "bad.site")
+ {:ok, user} = User.block_domain(user, "even.worse.site")
+
+ conn =
+ conn
+ |> assign(:user, user)
+ |> get("/api/v1/domain_blocks")
+
+ domain_blocks = json_response(conn, 200)
+
+ assert "bad.site" in domain_blocks
+ assert "even.worse.site" in domain_blocks
+ end
+
test "unimplemented mute endpoints" do
user = insert(:user)
other_user = insert(:user)