diff options
Diffstat (limited to 'lib/pleroma/web/activity_pub')
-rw-r--r-- | lib/pleroma/web/activity_pub/activity_pub.ex | 22 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/activity_pub_controller.ex | 2 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/mrf/reject_non_public.ex | 29 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/transmogrifier.ex | 67 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/utils.ex | 25 |
5 files changed, 138 insertions, 7 deletions
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 70e8dfd92..267427a23 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -57,6 +57,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do if activity.data["type"] in ["Create", "Announce"] do Pleroma.Web.Streamer.stream("user", activity) + Pleroma.Web.Streamer.stream("list", activity) if Enum.member?(activity.data["to"], public) do Pleroma.Web.Streamer.stream("public", activity) @@ -214,6 +215,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do def unfollow(follower, followed, activity_id \\ nil, local \\ true) do with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed), + {:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"), unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id), {:ok, activity} <- insert(unfollow_data, local), :ok <- maybe_federate(activity) do @@ -439,11 +441,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub 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 @@ -492,7 +496,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end def upload(file) do - data = Upload.store(file) + data = Upload.store(file, Application.get_env(:pleroma, :instance)[:dedupe_media]) Repo.insert(%Object{data: data}) end @@ -562,6 +566,17 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end + @quarantined_instances Keyword.get(@instance, :quarantined_instances, []) + + def should_federate?(inbox, public) do + if public do + true + else + inbox_info = URI.parse(inbox) + inbox_info.host not in @quarantined_instances + end + end + def publish(actor, activity) do followers = if actor.follower_address in activity.recipients do @@ -571,6 +586,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do [] end + public = is_public?(activity) + remote_inboxes = (Pleroma.Web.Salmon.remote_users(activity) ++ followers) |> Enum.filter(fn user -> User.ap_enabled?(user) end) @@ -578,6 +595,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do (data["endpoints"] && data["endpoints"]["sharedInbox"]) || data["inbox"] end) |> Enum.uniq() + |> Enum.filter(fn inbox -> should_federate?(inbox, public) end) {:ok, data} = Transmogrifier.prepare_outgoing(activity.data) json = Jason.encode!(data) diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index ee5d319a7..d337532d0 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -15,6 +15,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do conn |> put_resp_header("content-type", "application/activity+json") |> json(UserView.render("user.json", %{user: user})) + else + nil -> {:error, :not_found} end end diff --git a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex new file mode 100644 index 000000000..879cbe6de --- /dev/null +++ b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex @@ -0,0 +1,29 @@ +defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do + alias Pleroma.User + @behaviour Pleroma.Web.ActivityPub.MRF + + @impl true + def filter(object) do + if object["type"] == "Create" do + user = User.get_cached_by_ap_id(object["actor"]) + public = "https://www.w3.org/ns/activitystreams#Public" + + # Determine visibility + visibility = + cond do + public in object["to"] -> "public" + public in object["cc"] -> "unlisted" + user.follower_address in object["to"] -> "followers" + true -> "direct" + end + + case visibility do + "public" -> {:ok, object} + "unlisted" -> {:ok, object} + _ -> {:reject, nil} + end + else + {:ok, object} + end + end +end diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 053484f6f..f04f3c3c0 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -137,9 +137,17 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do with %User{local: true} = followed <- User.get_cached_by_ap_id(followed), %User{} = follower <- User.get_or_fetch_by_ap_id(follower), {:ok, activity} <- ActivityPub.follow(follower, followed, id, false) do - ActivityPub.accept(%{to: [follower.ap_id], actor: followed.ap_id, object: data, local: true}) + if not User.locked?(followed) do + ActivityPub.accept(%{ + to: [follower.ap_id], + actor: followed.ap_id, + object: data, + local: true + }) + + User.follow(follower, followed) + end - User.follow(follower, followed) {:ok, activity} else _e -> :error @@ -252,11 +260,12 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do {:ok, new_user_data} = ActivityPub.user_data_from_user_object(object) banner = new_user_data[:info]["banner"] + locked = new_user_data[:info]["locked"] || false update_data = new_user_data |> Map.take([:name, :bio, :avatar]) - |> Map.put(:info, Map.merge(actor.info, %{"banner" => banner})) + |> Map.put(:info, Map.merge(actor.info, %{"banner" => banner, "locked" => locked})) actor |> User.upgrade_changeset(update_data) @@ -431,6 +440,58 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do {:ok, data} end + # Mastodon Accept/Reject requires a non-normalized object containing the actor URIs, + # because of course it does. + def prepare_outgoing(%{"type" => "Accept"} = data) do + follow_activity_id = + if is_binary(data["object"]) do + data["object"] + else + data["object"]["id"] + end + + with follow_activity <- Activity.get_by_ap_id(follow_activity_id) do + object = %{ + "actor" => follow_activity.actor, + "object" => follow_activity.data["object"], + "id" => follow_activity.data["id"], + "type" => "Follow" + } + + data = + data + |> Map.put("object", object) + |> Map.put("@context", "https://www.w3.org/ns/activitystreams") + + {:ok, data} + end + end + + def prepare_outgoing(%{"type" => "Reject"} = data) do + follow_activity_id = + if is_binary(data["object"]) do + data["object"] + else + data["object"]["id"] + end + + with follow_activity <- Activity.get_by_ap_id(follow_activity_id) do + object = %{ + "actor" => follow_activity.actor, + "object" => follow_activity.data["object"], + "id" => follow_activity.data["id"], + "type" => "Follow" + } + + data = + data + |> Map.put("object", object) + |> Map.put("@context", "https://www.w3.org/ns/activitystreams") + + {:ok, data} + end + end + def prepare_outgoing(%{"type" => _type} = data) do data = data diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 56b80a8db..64329b710 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -4,6 +4,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do alias Pleroma.Web.Endpoint alias Ecto.{Changeset, UUID} import Ecto.Query + require Logger # Some implementations send the actor URI as the actor field, others send the entire actor object, # so figure out what the actor's URI is based on what we have. @@ -217,9 +218,26 @@ defmodule Pleroma.Web.ActivityPub.Utils do #### Follow-related helpers @doc """ + Updates a follow activity's state (for locked accounts). + """ + def update_follow_state(%Activity{} = activity, state) do + with new_data <- + activity.data + |> Map.put("state", state), + changeset <- Changeset.change(activity, data: new_data), + {:ok, activity} <- Repo.update(changeset) do + {:ok, activity} + end + end + + @doc """ Makes a follow activity data for the given follower and followed """ - def make_follow_data(%User{ap_id: follower_id}, %User{ap_id: followed_id}, activity_id) do + def make_follow_data( + %User{ap_id: follower_id}, + %User{ap_id: followed_id} = followed, + activity_id + ) do data = %{ "type" => "Follow", "actor" => follower_id, @@ -228,7 +246,10 @@ defmodule Pleroma.Web.ActivityPub.Utils do "object" => followed_id } - if activity_id, do: Map.put(data, "id", activity_id), else: data + data = if activity_id, do: Map.put(data, "id", activity_id), else: data + data = if User.locked?(followed), do: Map.put(data, "state", "pending"), else: data + + data end def fetch_latest_follow(%User{ap_id: follower_id}, %User{ap_id: followed_id}) do |