diff options
Diffstat (limited to 'lib/pleroma/web/activity_pub')
-rw-r--r-- | lib/pleroma/web/activity_pub/activity_pub.ex | 59 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/activity_pub_controller.ex | 101 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/mrf.ex | 4 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/mrf/drop_policy.ex | 4 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex | 4 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex | 22 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/mrf/noop_policy.ex | 4 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/mrf/normalize_markup.ex | 4 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/mrf/reject_non_public.ex | 4 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/mrf/simple_policy.ex | 4 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/mrf/user_allowlist.ex | 4 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/relay.ex | 4 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/transmogrifier.ex | 33 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/utils.ex | 4 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/views/object_view.ex | 4 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/views/user_view.ex | 51 |
16 files changed, 283 insertions, 27 deletions
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 31455343c..9c1eb377f 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.ActivityPub do alias Pleroma.{Activity, Repo, Object, Upload, User, Notification} alias Pleroma.Web.ActivityPub.{Transmogrifier, MRF} @@ -52,10 +56,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end + defp check_remote_limit(%{"object" => %{"content" => content}}) do + limit = Pleroma.Config.get([:instance, :remote_limit]) + String.length(content) <= limit + end + + defp check_remote_limit(_), do: true + def insert(map, local \\ true) when is_map(map) do with nil <- Activity.normalize(map), map <- lazy_put_activity_defaults(map), :ok <- check_actor_is_active(map["actor"]), + {_, true} <- {:remote_limit_error, check_remote_limit(map)}, {:ok, map} <- MRF.filter(map), :ok <- insert_full_object(map) do {recipients, _, _} = get_recipients(map) @@ -352,21 +364,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do @valid_visibilities ~w[direct unlisted public private] - defp restrict_visibility(query, %{visibility: "direct"}) do - public = "https://www.w3.org/ns/activitystreams#Public" + defp restrict_visibility(query, %{visibility: visibility}) + when visibility in @valid_visibilities do + query = + from( + a in query, + where: + fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility) + ) - from( - activity in query, - join: sender in User, - on: sender.ap_id == activity.actor, - # Are non-direct statuses with no to/cc possible? - where: - fragment( - "not (? && ?)", - [^public, sender.follower_address], - activity.recipients - ) - ) + Ecto.Adapters.SQL.to_sql(:all, Repo, query) + + query end defp restrict_visibility(_query, %{visibility: visibility}) @@ -382,6 +391,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> Map.put("type", ["Create", "Announce"]) |> Map.put("actor_id", user.ap_id) |> Map.put("whole_db", true) + |> Map.put("pinned_activity_ids", user.info.pinned_activities) recipients = if reading_user do @@ -499,6 +509,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp restrict_replies(query, _), do: query + defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do + from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data)) + end + + defp restrict_reblogs(query, _), do: query + # Only search through last 100_000 activities by default defp restrict_recent(query, %{"whole_db" => true}), do: query @@ -534,6 +550,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do ) end + defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do + from(activity in query, where: activity.id in ^ids) + end + + defp restrict_pinned(query, _), do: query + def fetch_activities_query(recipients, opts \\ %{}) do base_query = from( @@ -557,6 +579,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> restrict_media(opts) |> restrict_visibility(opts) |> restrict_replies(opts) + |> restrict_reblogs(opts) + |> restrict_pinned(opts) end def fetch_activities(recipients, opts \\ %{}) do @@ -722,8 +746,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do {"Content-Type", "application/activity+json"}, {"signature", signature}, {"digest", digest} - ], - hackney: [pool: :default] + ] ) end @@ -783,6 +806,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end + def is_public?(%Object{data: %{"type" => "Tombstone"}}) do + false + end + def is_public?(activity) do "https://www.w3.org/ns/activitystreams#Public" in (activity.data["to"] ++ (activity.data["cc"] || [])) diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index 0317f3c8c..73ca07e84 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -1,10 +1,15 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.ActivityPubController do use Pleroma.Web, :controller - alias Pleroma.{User, Object} + alias Pleroma.{Activity, User, Object} alias Pleroma.Web.ActivityPub.{ObjectView, UserView} alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Relay alias Pleroma.Web.ActivityPub.Utils + alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.Federator require Logger @@ -49,6 +54,19 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do end end + def activity(conn, %{"uuid" => uuid}) do + with ap_id <- o_status_url(conn, :activity, uuid), + %Activity{} = activity <- Activity.normalize(ap_id), + {_, true} <- {:public?, ActivityPub.is_public?(activity)} do + conn + |> put_resp_header("content-type", "application/activity+json") + |> json(ObjectView.render("object.json", %{object: activity})) + else + {:public?, false} -> + {:error, :not_found} + end + end + def following(conn, %{"nickname" => nickname, "page" => page}) do with %User{} = user <- User.get_cached_by_nickname(nickname), {:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do @@ -89,19 +107,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do end end - def outbox(conn, %{"nickname" => nickname, "max_id" => max_id}) do + def outbox(conn, %{"nickname" => nickname} = params) do with %User{} = user <- User.get_cached_by_nickname(nickname), {:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do conn |> put_resp_header("content-type", "application/activity+json") - |> json(UserView.render("outbox.json", %{user: user, max_id: max_id})) + |> json(UserView.render("outbox.json", %{user: user, max_id: params["max_id"]})) end end - def outbox(conn, %{"nickname" => nickname}) do - outbox(conn, %{"nickname" => nickname, "max_id" => nil}) - end - def inbox(%{assigns: %{valid_signature: true}} = conn, %{"nickname" => nickname} = params) do with %User{} = user <- User.get_cached_by_nickname(nickname), true <- Utils.recipient_in_message(user.ap_id, params), @@ -152,6 +166,79 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do end end + def read_inbox(%{assigns: %{user: user}} = conn, %{"nickname" => nickname} = params) do + if nickname == user.nickname do + conn + |> put_resp_header("content-type", "application/activity+json") + |> json(UserView.render("inbox.json", %{user: user, max_id: params["max_id"]})) + else + conn + |> put_status(:forbidden) + |> json("can't read inbox of #{nickname} as #{user.nickname}") + end + end + + def handle_user_activity(user, %{"type" => "Create"} = params) do + object = + params["object"] + |> Map.merge(Map.take(params, ["to", "cc"])) + |> Map.put("attributedTo", user.ap_id()) + |> Transmogrifier.fix_object() + + ActivityPub.create(%{ + to: params["to"], + actor: user, + context: object["context"], + object: object, + additional: Map.take(params, ["cc"]) + }) + end + + def handle_user_activity(user, %{"type" => "Delete"} = params) do + with %Object{} = object <- Object.normalize(params["object"]), + true <- user.info.is_moderator || user.ap_id == object.data["actor"], + {:ok, delete} <- ActivityPub.delete(object) do + {:ok, delete} + else + _ -> {:error, "Can't delete object"} + end + end + + def handle_user_activity(_, _) do + {:error, "Unhandled activity type"} + end + + def update_outbox( + %{assigns: %{user: user}} = conn, + %{"nickname" => nickname} = params + ) do + if nickname == user.nickname do + actor = user.ap_id() + + params = + params + |> Map.drop(["id"]) + |> Map.put("actor", actor) + |> Transmogrifier.fix_addressing() + + with {:ok, %Activity{} = activity} <- handle_user_activity(user, params) do + conn + |> put_status(:created) + |> put_resp_header("location", activity.data["id"]) + |> json(activity.data) + else + {:error, message} -> + conn + |> put_status(:bad_request) + |> json(message) + end + else + conn + |> put_status(:forbidden) + |> json("can't update outbox of #{nickname} as #{user.nickname}") + end + end + def errors(conn, {:error, :not_found}) do conn |> put_status(404) diff --git a/lib/pleroma/web/activity_pub/mrf.ex b/lib/pleroma/web/activity_pub/mrf.ex index 0a4e2bf80..eebea207c 100644 --- a/lib/pleroma/web/activity_pub/mrf.ex +++ b/lib/pleroma/web/activity_pub/mrf.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.MRF do @callback filter(Map.t()) :: {:ok | :reject, Map.t()} diff --git a/lib/pleroma/web/activity_pub/mrf/drop_policy.ex b/lib/pleroma/web/activity_pub/mrf/drop_policy.ex index 811947943..a93ccf386 100644 --- a/lib/pleroma/web/activity_pub/mrf/drop_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/drop_policy.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.MRF.DropPolicy do require Logger @behaviour Pleroma.Web.ActivityPub.MRF diff --git a/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex index 6fa48454a..895376c9d 100644 --- a/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex +++ b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do alias Pleroma.Object diff --git a/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex b/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex new file mode 100644 index 000000000..a3f516ae7 --- /dev/null +++ b/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex @@ -0,0 +1,22 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do + @behaviour Pleroma.Web.ActivityPub.MRF + + @impl true + def filter(%{"type" => "Create"} = object) do + threshold = Pleroma.Config.get([:mrf_hellthread, :threshold]) + recipients = (object["to"] || []) ++ (object["cc"] || []) + + if length(recipients) > threshold do + {:reject, nil} + else + {:ok, object} + end + end + + @impl true + def filter(object), do: {:ok, object} +end diff --git a/lib/pleroma/web/activity_pub/mrf/noop_policy.ex b/lib/pleroma/web/activity_pub/mrf/noop_policy.ex index e26f60d26..40f37bdb1 100644 --- a/lib/pleroma/web/activity_pub/mrf/noop_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/noop_policy.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.MRF.NoOpPolicy do @behaviour Pleroma.Web.ActivityPub.MRF diff --git a/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex b/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex index c53cb1ad2..3d13cdb32 100644 --- a/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex +++ b/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do alias Pleroma.HTML diff --git a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex index 627284083..4197be847 100644 --- a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex +++ b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do alias Pleroma.User @behaviour Pleroma.Web.ActivityPub.MRF diff --git a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex index 12fc3b181..798ba9687 100644 --- a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do alias Pleroma.User @behaviour Pleroma.Web.ActivityPub.MRF diff --git a/lib/pleroma/web/activity_pub/mrf/user_allowlist.ex b/lib/pleroma/web/activity_pub/mrf/user_allowlist.ex index 3503d8692..a3b1f8aa0 100644 --- a/lib/pleroma/web/activity_pub/mrf/user_allowlist.ex +++ b/lib/pleroma/web/activity_pub/mrf/user_allowlist.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do alias Pleroma.Config diff --git a/lib/pleroma/web/activity_pub/relay.ex b/lib/pleroma/web/activity_pub/relay.ex index fcdc6b1c0..abddbc790 100644 --- a/lib/pleroma/web/activity_pub/relay.ex +++ b/lib/pleroma/web/activity_pub/relay.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.Relay do alias Pleroma.{User, Object, Activity} alias Pleroma.Web.ActivityPub.ActivityPub diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index e6af4b211..87b7fc07f 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.Transmogrifier do @moduledoc """ A module to handle coding from internal to wire ActivityPub and back. @@ -69,8 +73,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def fix_object(object) do object |> fix_actor - |> fix_attachments |> fix_url + |> fix_attachments |> fix_context |> fix_in_reply_to |> fix_emoji @@ -170,8 +174,14 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do attachments = attachment |> Enum.map(fn data -> - url = [%{"type" => "Link", "mediaType" => data["mediaType"], "href" => data["url"]}] - Map.put(data, "url", url) + media_type = data["mediaType"] || data["mimeType"] + href = data["url"] || data["href"] + + url = [%{"type" => "Link", "mediaType" => media_type, "href" => href}] + + data + |> Map.put("mediaType", media_type) + |> Map.put("url", url) end) object @@ -190,7 +200,22 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do |> Map.put("url", url["href"]) end - def fix_url(%{"url" => url} = object) when is_list(url) do + def fix_url(%{"type" => "Video", "url" => url} = object) when is_list(url) do + first_element = Enum.at(url, 0) + + link_element = + url + |> Enum.filter(fn x -> is_map(x) end) + |> Enum.filter(fn x -> x["mimeType"] == "text/html" end) + |> Enum.at(0) + + object + |> Map.put("attachment", [first_element]) + |> Map.put("url", link_element["href"]) + end + + def fix_url(%{"type" => object_type, "url" => url} = object) + when object_type != "Video" and is_list(url) do first_element = Enum.at(url, 0) url_string = diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 074622f2b..b313996db 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.Utils do alias Pleroma.{Repo, Web, Object, Activity, User, Notification} alias Pleroma.Web.Router.Helpers diff --git a/lib/pleroma/web/activity_pub/views/object_view.ex b/lib/pleroma/web/activity_pub/views/object_view.ex index ff664636c..b5c9bf8d0 100644 --- a/lib/pleroma/web/activity_pub/views/object_view.ex +++ b/lib/pleroma/web/activity_pub/views/object_view.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.ObjectView do use Pleroma.Web, :view alias Pleroma.{Object, Activity} diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex index 869934172..fe8248107 100644 --- a/lib/pleroma/web/activity_pub/views/user_view.ex +++ b/lib/pleroma/web/activity_pub/views/user_view.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.UserView do use Pleroma.Web, :view alias Pleroma.Web.Salmon @@ -172,6 +176,53 @@ defmodule Pleroma.Web.ActivityPub.UserView do end end + def render("inbox.json", %{user: user, max_id: max_qid}) do + params = %{ + "limit" => "10" + } + + params = + if max_qid != nil do + Map.put(params, "max_id", max_qid) + else + params + end + + activities = ActivityPub.fetch_activities([user.ap_id | user.following], params) + + min_id = Enum.at(Enum.reverse(activities), 0).id + max_id = Enum.at(activities, 0).id + + collection = + Enum.map(activities, fn act -> + {:ok, data} = Transmogrifier.prepare_outgoing(act.data) + data + end) + + iri = "#{user.ap_id}/inbox" + + page = %{ + "id" => "#{iri}?max_id=#{max_id}", + "type" => "OrderedCollectionPage", + "partOf" => iri, + "totalItems" => -1, + "orderedItems" => collection, + "next" => "#{iri}?max_id=#{min_id - 1}" + } + + if max_qid == nil do + %{ + "id" => iri, + "type" => "OrderedCollection", + "totalItems" => -1, + "first" => page + } + |> Map.merge(Utils.make_json_ld_header()) + else + page |> Map.merge(Utils.make_json_ld_header()) + end + end + def collection(collection, iri, page, show_items \\ true, total \\ nil) do offset = (page - 1) * 10 items = Enum.slice(collection, offset, 10) |