X-Git-Url: http://git.squeep.com/?a=blobdiff_plain;f=lib%2Fpleroma%2Fweb%2Factivity_pub%2Ftransmogrifier.ex;h=6a0fdb433098484bca261e94fdb94fec5eed130a;hb=6b4064fa5ddd8396faf7d6afef891d70e7a2e881;hp=39e2bb8a9985821f8b0dc98fe7eccb61da7abecf;hpb=ef4f982e2c6447733679f3e490c2666c7a795a5f;p=akkoma diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 39e2bb8a9..6a0fdb433 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -21,13 +21,33 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do if is_binary(Enum.at(actor, 0)) do Enum.at(actor, 0) else - Enum.find(actor, fn %{"type" => type} -> type == "Person" end) + Enum.find(actor, fn %{"type" => type} -> type in ["Person", "Service", "Application"] end) |> Map.get("id") end end - def get_actor(%{"actor" => actor}) when is_map(actor) do - actor["id"] + def get_actor(%{"actor" => %{"id" => id}}) when is_bitstring(id) 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)) + + if id_uri.host == actor_uri.host do + :ok + else + :error + end end @doc """ @@ -37,11 +57,13 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do object |> fix_actor |> fix_attachments + |> fix_url |> fix_context |> fix_in_reply_to |> fix_emoji |> fix_tag |> fix_content_map + |> fix_likes |> fix_addressing end @@ -67,9 +89,39 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do |> Map.put("actor", get_actor(%{"actor" => actor})) end - def fix_in_reply_to(%{"inReplyTo" => in_reply_to_id} = object) - when not is_nil(in_reply_to_id) do - case ActivityPub.fetch_object_from_id(in_reply_to_id) do + def fix_likes(%{"likes" => likes} = object) + when is_bitstring(likes) do + # Check for standardisation + # This is what Peertube does + # curl -H 'Accept: application/activity+json' $likes | jq .totalItems + object + |> Map.put("likes", []) + |> Map.put("like_count", 0) + end + + def fix_likes(object) do + object + end + + def fix_in_reply_to(%{"inReplyTo" => in_reply_to} = object) + when not is_nil(in_reply_to) do + in_reply_to_id = + cond do + is_bitstring(in_reply_to) -> + in_reply_to + + is_map(in_reply_to) && is_bitstring(in_reply_to["id"]) -> + in_reply_to["id"] + + is_list(in_reply_to) && is_bitstring(Enum.at(in_reply_to, 0)) -> + Enum.at(in_reply_to, 0) + + # Maybe I should output an error too? + true -> + "" + end + + 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 @@ -81,12 +133,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 @@ -94,13 +146,16 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def fix_in_reply_to(object), do: object def fix_context(object) do + context = object["context"] || object["conversation"] || Utils.generate_context_id() + object - |> Map.put("context", object["conversation"]) + |> Map.put("context", context) + |> Map.put("conversation", context) end - def fix_attachments(object) do + def fix_attachments(%{"attachment" => attachment} = object) when is_list(attachment) do attachments = - (object["attachment"] || []) + attachment |> Enum.map(fn data -> url = [%{"type" => "Link", "mediaType" => data["mediaType"], "href" => data["url"]}] Map.put(data, "url", url) @@ -110,21 +165,41 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do |> Map.put("attachment", attachments) end - def fix_emoji(object) do - tags = object["tag"] || [] + def fix_attachments(%{"attachment" => attachment} = object) when is_map(attachment) do + 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) emoji = emoji |> Enum.reduce(%{}, fn data, mapping -> - name = data["name"] - - name = - if String.starts_with?(name, ":") do - name |> String.slice(1..-2) - else - name - end + name = String.trim(data["name"], ":") mapping |> Map.put(name, data["icon"]["url"]) end) @@ -136,18 +211,37 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do |> Map.put("emoji", emoji) end - def fix_tag(object) do + def fix_emoji(%{"tag" => %{"type" => "Emoji"} = tag} = object) do + name = String.trim(tag["name"], ":") + emoji = %{name => tag["icon"]["url"]} + + object + |> Map.put("emoji", emoji) + end + + def fix_emoji(object), do: object + + def fix_tag(%{"tag" => tag} = object) when is_list(tag) do tags = - (object["tag"] || []) + tag |> Enum.filter(fn data -> data["type"] == "Hashtag" and data["name"] end) |> Enum.map(fn data -> String.slice(data["name"], 1..-1) end) - combined = (object["tag"] || []) ++ tags + combined = tag ++ tags object |> Map.put("tag", combined) end + def fix_tag(%{"tag" => %{"type" => "Hashtag", "name" => hashtag} = tag} = object) do + combined = [tag, String.slice(hashtag, 1..-1)] + + object + |> Map.put("tag", combined) + end + + def fix_tag(object), do: object + # content map usually only has one language so this will do for now. def fix_content_map(%{"contentMap" => content_map} = object) do content_groups = Map.to_list(content_map) @@ -159,11 +253,17 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def fix_content_map(object), do: object + # disallow objects with bogus IDs + def handle_incoming(%{"id" => nil}), do: :error + def handle_incoming(%{"id" => ""}), do: :error + # length of https:// = 8, should validate better, but good enough for now. + def handle_incoming(%{"id" => id}) when not (is_binary(id) and length(id) > 8), do: :error + # TODO: validate those with a Ecto scheme # - tags # - emoji def handle_incoming(%{"type" => "Create", "object" => %{"type" => objtype} = object} = data) - when objtype in ["Article", "Note"] do + when objtype in ["Article", "Note", "Video", "Page"] do actor = get_actor(data) data = @@ -247,8 +347,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(%{ @@ -271,8 +373,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(%{ @@ -291,11 +395,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 @@ -304,11 +408,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 @@ -317,9 +421,10 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do end def handle_incoming( - %{"type" => "Update", "object" => %{"type" => "Person"} = object, "actor" => actor_id} = + %{"type" => "Update", "object" => %{"type" => object_type} = object, "actor" => actor_id} = data - ) do + ) + when object_type in ["Person", "Application", "Service", "Organization"] do with %User{ap_id: ^actor_id} = actor <- User.get_by_ap_id(object["id"]) do {:ok, new_user_data} = ActivityPub.user_data_from_user_object(object) @@ -351,13 +456,13 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do # TODO: Make secure. 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, activity} <- ActivityPub.delete(object, false) do {:ok, activity} else @@ -371,11 +476,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 @@ -401,9 +506,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", @@ -412,7 +514,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 @@ -426,7 +528,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 @@ -444,11 +546,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 @@ -458,6 +560,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 @@ -499,7 +604,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 @@ -518,7 +623,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 @@ -536,7 +641,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 @@ -546,14 +651,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 = @@ -588,12 +693,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)