X-Git-Url: http://git.squeep.com/?a=blobdiff_plain;f=lib%2Fpleroma%2Fweb%2Factivity_pub%2Ftransmogrifier.ex;h=17b0636094d8503490a027e1a4c8d9dc92c1c334;hb=c443c9bd72b04b8a24d904bc20fde0d4ffca71d7;hp=29d7b642b8e5b9ec438646784fa8dfccd4909e4c;hpb=4c3a80de96e92f27cc27213e7314a062e2d61845;p=akkoma diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 29d7b642b..17b063609 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -30,9 +30,15 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do id end + def get_actor(%{"actor" => nil, "attributedTo" => actor}) when not is_nil(actor) do + get_actor(%{"actor" => actor}) + end + @doc """ Checks that an imported AP object's actor matches the domain it came from. """ + def contain_origin(id, %{"actor" => nil}), do: :error + def contain_origin(id, %{"actor" => actor} = params) do id_uri = URI.parse(id) actor_uri = URI.parse(get_actor(params)) @@ -44,6 +50,19 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do end end + def contain_origin_from_id(id, %{"id" => nil}), do: :error + + def contain_origin_from_id(id, %{"id" => other_id} = params) do + id_uri = URI.parse(id) + other_uri = URI.parse(other_id) + + if id_uri.host == other_uri.host do + :ok + else + :error + end + end + @doc """ Modifies an incoming AP object (mastodon format) to our internal format. """ @@ -51,6 +70,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do object |> fix_actor |> fix_attachments + |> fix_url |> fix_context |> fix_in_reply_to |> fix_emoji @@ -114,7 +134,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do "" end - case ActivityPub.fetch_object_from_id(in_reply_to_id) do + case fetch_obj_helper(in_reply_to_id) do {:ok, replied_object} -> with %Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(replied_object.data["id"]) do @@ -126,12 +146,12 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do |> Map.put("context", replied_object.data["context"] || object["conversation"]) else e -> - Logger.error("Couldn't fetch #{object["inReplyTo"]} #{inspect(e)}") + Logger.error("Couldn't fetch \"#{inspect(in_reply_to_id)}\", error: #{inspect(e)}") object end e -> - Logger.error("Couldn't fetch #{object["inReplyTo"]} #{inspect(e)}") + Logger.error("Couldn't fetch \"#{inspect(in_reply_to_id)}\", error: #{inspect(e)}") object end end @@ -159,16 +179,33 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do end def fix_attachments(%{"attachment" => attachment} = object) when is_map(attachment) do - attachment = - Map.put(attachment, "url", [ - %{"type" => "Link", "mediaType" => attachment["mediaType"], "href" => attachment["url"]} - ]) - - Map.put(object, "attachment", attachment) + Map.put(object, "attachment", [attachment]) + |> fix_attachments() end def fix_attachments(object), do: object + def fix_url(%{"url" => url} = object) when is_map(url) do + object + |> Map.put("url", url["href"]) + end + + def fix_url(%{"url" => url} = object) when is_list(url) do + first_element = Enum.at(url, 0) + + url_string = + cond do + is_bitstring(first_element) -> first_element + is_map(first_element) -> first_element["href"] || "" + true -> "" + end + + object + |> Map.put("url", url_string) + end + + def fix_url(object), do: object + def fix_emoji(%{"tag" => tags} = object) when is_list(tags) do emoji = tags |> Enum.filter(fn data -> data["type"] == "Emoji" and data["icon"] end) @@ -239,7 +276,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do # - tags # - emoji def handle_incoming(%{"type" => "Create", "object" => %{"type" => objtype} = object} = data) - when objtype in ["Article", "Note", "Video"] do + when objtype in ["Article", "Note", "Video", "Page"] do actor = get_actor(data) data = @@ -323,8 +360,10 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def handle_incoming( %{"type" => "Accept", "object" => follow_object, "actor" => actor, "id" => id} = data ) do - with %User{} = followed <- User.get_or_fetch_by_ap_id(actor), + with actor <- get_actor(data), + %User{} = followed <- User.get_or_fetch_by_ap_id(actor), {:ok, follow_activity} <- get_follow_activity(follow_object, followed), + {:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "accept"), %User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]), {:ok, activity} <- ActivityPub.accept(%{ @@ -347,8 +386,10 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def handle_incoming( %{"type" => "Reject", "object" => follow_object, "actor" => actor, "id" => id} = data ) do - with %User{} = followed <- User.get_or_fetch_by_ap_id(actor), + with actor <- get_actor(data), + %User{} = followed <- User.get_or_fetch_by_ap_id(actor), {:ok, follow_activity} <- get_follow_activity(follow_object, followed), + {:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "reject"), %User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]), {:ok, activity} <- ActivityPub.accept(%{ @@ -367,11 +408,11 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do end def handle_incoming( - %{"type" => "Like", "object" => object_id, "actor" => actor, "id" => id} = _data + %{"type" => "Like", "object" => object_id, "actor" => actor, "id" => id} = data ) do - with %User{} = actor <- User.get_or_fetch_by_ap_id(actor), - {:ok, object} <- - get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id), + with actor <- get_actor(data), + %User{} = actor <- User.get_or_fetch_by_ap_id(actor), + {:ok, object} <- get_obj_helper(object_id) || fetch_obj_helper(object_id), {:ok, activity, _object} <- ActivityPub.like(actor, object, id, false) do {:ok, activity} else @@ -380,11 +421,11 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do end def handle_incoming( - %{"type" => "Announce", "object" => object_id, "actor" => actor, "id" => id} = _data + %{"type" => "Announce", "object" => object_id, "actor" => actor, "id" => id} = data ) do - with %User{} = actor <- User.get_or_fetch_by_ap_id(actor), - {:ok, object} <- - get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id), + with actor <- get_actor(data), + %User{} = actor <- User.get_or_fetch_by_ap_id(actor), + {:ok, object} <- get_obj_helper(object_id) || fetch_obj_helper(object_id), {:ok, activity, _object} <- ActivityPub.announce(actor, object, id, false) do {:ok, activity} else @@ -406,7 +447,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do update_data = new_user_data |> Map.take([:name, :bio, :avatar]) - |> Map.put(:info, Map.merge(actor.info, %{"banner" => banner, "locked" => locked})) + |> Map.put(:info, %{"banner" => banner, "locked" => locked}) actor |> User.upgrade_changeset(update_data) @@ -426,15 +467,20 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do end end - # TODO: Make secure. + # TODO: We presently assume that any actor on the same origin domain as the object being + # deleted has the rights to delete that object. A better way to validate whether or not + # the object should be deleted is to refetch the object URI, which should return either + # an error or a tombstone. This would allow us to verify that a deletion actually took + # place. def handle_incoming( - %{"type" => "Delete", "object" => object_id, "actor" => actor, "id" => _id} = _data + %{"type" => "Delete", "object" => object_id, "actor" => _actor, "id" => _id} = data ) do object_id = Utils.get_ap_id(object_id) - with %User{} = _actor <- User.get_or_fetch_by_ap_id(actor), - {:ok, object} <- - get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id), + with actor <- get_actor(data), + %User{} = actor <- User.get_or_fetch_by_ap_id(actor), + {:ok, object} <- get_obj_helper(object_id) || fetch_obj_helper(object_id), + :ok <- contain_origin(actor.ap_id, object.data), {:ok, activity} <- ActivityPub.delete(object, false) do {:ok, activity} else @@ -448,11 +494,11 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do "object" => %{"type" => "Announce", "object" => object_id}, "actor" => actor, "id" => id - } = _data + } = data ) do - with %User{} = actor <- User.get_or_fetch_by_ap_id(actor), - {:ok, object} <- - get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id), + with actor <- get_actor(data), + %User{} = actor <- User.get_or_fetch_by_ap_id(actor), + {:ok, object} <- get_obj_helper(object_id) || fetch_obj_helper(object_id), {:ok, activity, _} <- ActivityPub.unannounce(actor, object, id, false) do {:ok, activity} else @@ -478,9 +524,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do end end - @ap_config Application.get_env(:pleroma, :activitypub) - @accept_blocks Keyword.get(@ap_config, :accept_blocks) - def handle_incoming( %{ "type" => "Undo", @@ -489,7 +532,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do "id" => id } = _data ) do - with true <- @accept_blocks, + with true <- Pleroma.Config.get([:activitypub, :accept_blocks]), %User{local: true} = blocked <- User.get_cached_by_ap_id(blocked), %User{} = blocker <- User.get_or_fetch_by_ap_id(blocker), {:ok, activity} <- ActivityPub.unblock(blocker, blocked, id, false) do @@ -503,7 +546,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def handle_incoming( %{"type" => "Block", "object" => blocked, "actor" => blocker, "id" => id} = data ) do - with true <- @accept_blocks, + with true <- Pleroma.Config.get([:activitypub, :accept_blocks]), %User{local: true} = blocked = User.get_cached_by_ap_id(blocked), %User{} = blocker = User.get_or_fetch_by_ap_id(blocker), {:ok, activity} <- ActivityPub.block(blocker, blocked, id, false) do @@ -521,11 +564,11 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do "object" => %{"type" => "Like", "object" => object_id}, "actor" => actor, "id" => id - } = _data + } = data ) do - with %User{} = actor <- User.get_or_fetch_by_ap_id(actor), - {:ok, object} <- - get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id), + with actor <- get_actor(data), + %User{} = actor <- User.get_or_fetch_by_ap_id(actor), + {:ok, object} <- get_obj_helper(object_id) || fetch_obj_helper(object_id), {:ok, activity, _, _} <- ActivityPub.unlike(actor, object, id, false) do {:ok, activity} else @@ -535,6 +578,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def handle_incoming(_), do: :error + def fetch_obj_helper(id) when is_bitstring(id), do: ActivityPub.fetch_object_from_id(id) + def fetch_obj_helper(obj) when is_map(obj), do: ActivityPub.fetch_object_from_id(obj["id"]) + def get_obj_helper(id) do if object = Object.normalize(id), do: {:ok, object}, else: nil end @@ -561,6 +607,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do |> prepare_attachments |> set_conversation |> set_reply_to_uri + |> strip_internal_fields + |> strip_internal_tags end # @doc @@ -576,7 +624,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do data = data |> Map.put("object", object) - |> Map.put("@context", "https://www.w3.org/ns/activitystreams") + |> Map.merge(Utils.make_json_ld_header()) {:ok, data} end @@ -595,7 +643,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do data = data |> Map.put("object", object) - |> Map.put("@context", "https://www.w3.org/ns/activitystreams") + |> Map.merge(Utils.make_json_ld_header()) {:ok, data} end @@ -613,7 +661,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do data = data |> Map.put("object", object) - |> Map.put("@context", "https://www.w3.org/ns/activitystreams") + |> Map.merge(Utils.make_json_ld_header()) {:ok, data} end @@ -623,14 +671,14 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do data = data |> maybe_fix_object_url - |> Map.put("@context", "https://www.w3.org/ns/activitystreams") + |> Map.merge(Utils.make_json_ld_header()) {:ok, data} end def maybe_fix_object_url(data) do if is_binary(data["object"]) and not String.starts_with?(data["object"], "http") do - case ActivityPub.fetch_object_from_id(data["object"]) do + case fetch_obj_helper(data["object"]) do {:ok, relative_object} -> if relative_object.data["external_url"] do _data = @@ -665,12 +713,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do end def add_mention_tags(object) do - recipients = object["to"] ++ (object["cc"] || []) - mentions = - recipients - |> Enum.map(fn ap_id -> User.get_cached_by_ap_id(ap_id) end) - |> Enum.filter(& &1) + object + |> Utils.get_notified_from_object() |> Enum.map(fn user -> %{"type" => "Mention", "href" => user.ap_id, "name" => "@#{user.nickname}"} end) @@ -730,6 +775,29 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do |> Map.put("attachment", attachments) end + defp strip_internal_fields(object) do + object + |> Map.drop([ + "likes", + "like_count", + "announcements", + "announcement_count", + "emoji", + "context_id" + ]) + end + + defp strip_internal_tags(%{"tag" => tags} = object) do + tags = + tags + |> Enum.filter(fn x -> is_map(x) end) + + object + |> Map.put("tag", tags) + end + + defp strip_internal_tags(object), do: object + defp user_upgrade_task(user) do old_follower_address = User.ap_followers(user) @@ -782,10 +850,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def upgrade_user_from_ap_id(ap_id, async \\ true) do with %User{local: false} = user <- User.get_by_ap_id(ap_id), {:ok, data} <- ActivityPub.fetch_and_prepare_user_from_ap_id(ap_id) do - data = - data - |> Map.put(:info, Map.merge(user.info, data[:info])) - already_ap = User.ap_enabled?(user) {:ok, user} =