1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.ActivityPub.ActivityPubController do
6 use Pleroma.Web, :controller
10 alias Pleroma.Object.Fetcher
12 alias Pleroma.Web.ActivityPub.ActivityPub
13 alias Pleroma.Web.ActivityPub.InternalFetchActor
14 alias Pleroma.Web.ActivityPub.ObjectView
15 alias Pleroma.Web.ActivityPub.Relay
16 alias Pleroma.Web.ActivityPub.Transmogrifier
17 alias Pleroma.Web.ActivityPub.UserView
18 alias Pleroma.Web.ActivityPub.Utils
19 alias Pleroma.Web.ActivityPub.Visibility
20 alias Pleroma.Web.Federator
24 action_fallback(:errors)
26 plug(Pleroma.Web.FederatingPlug when action in [:inbox, :relay])
27 plug(:set_requester_reachable when action in [:inbox])
28 plug(:relay_active? when action in [:relay])
30 def relay_active?(conn, _) do
31 if Pleroma.Config.get([:instance, :allow_relay]) do
35 |> render_error(:not_found, "not found")
40 def user(conn, %{"nickname" => nickname}) do
41 with %User{} = user <- User.get_cached_by_nickname(nickname),
42 {:ok, user} <- User.ensure_keys_present(user) do
44 |> put_resp_content_type("application/activity+json")
45 |> json(UserView.render("user.json", %{user: user}))
47 nil -> {:error, :not_found}
51 def object(conn, %{"uuid" => uuid}) do
52 with ap_id <- o_status_url(conn, :object, uuid),
53 %Object{} = object <- Object.get_cached_by_ap_id(ap_id),
54 {_, true} <- {:public?, Visibility.is_public?(object)} do
56 |> put_resp_content_type("application/activity+json")
57 |> json(ObjectView.render("object.json", %{object: object}))
64 def object_likes(conn, %{"uuid" => uuid, "page" => page}) do
65 with ap_id <- o_status_url(conn, :object, uuid),
66 %Object{} = object <- Object.get_cached_by_ap_id(ap_id),
67 {_, true} <- {:public?, Visibility.is_public?(object)},
68 likes <- Utils.get_object_likes(object) do
69 {page, _} = Integer.parse(page)
72 |> put_resp_content_type("application/activity+json")
73 |> json(ObjectView.render("likes.json", ap_id, likes, page))
80 def object_likes(conn, %{"uuid" => uuid}) do
81 with ap_id <- o_status_url(conn, :object, uuid),
82 %Object{} = object <- Object.get_cached_by_ap_id(ap_id),
83 {_, true} <- {:public?, Visibility.is_public?(object)},
84 likes <- Utils.get_object_likes(object) do
86 |> put_resp_content_type("application/activity+json")
87 |> json(ObjectView.render("likes.json", ap_id, likes))
94 def activity(conn, %{"uuid" => uuid}) do
95 with ap_id <- o_status_url(conn, :activity, uuid),
96 %Activity{} = activity <- Activity.normalize(ap_id),
97 {_, true} <- {:public?, Visibility.is_public?(activity)} do
99 |> put_resp_content_type("application/activity+json")
100 |> json(ObjectView.render("object.json", %{object: activity}))
107 # GET /relay/following
108 def following(%{assigns: %{relay: true}} = conn, _params) do
110 |> put_resp_content_type("application/activity+json")
111 |> json(UserView.render("following.json", %{user: Relay.get_actor()}))
114 def following(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) do
115 with %User{} = user <- User.get_cached_by_nickname(nickname),
116 {user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user),
117 {:show_follows, true} <-
118 {:show_follows, (for_user && for_user == user) || !user.info.hide_follows} do
119 {page, _} = Integer.parse(page)
122 |> put_resp_content_type("application/activity+json")
123 |> json(UserView.render("following.json", %{user: user, page: page, for: for_user}))
125 {:show_follows, _} ->
127 |> put_resp_content_type("application/activity+json")
128 |> send_resp(403, "")
132 def following(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname}) do
133 with %User{} = user <- User.get_cached_by_nickname(nickname),
134 {user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user) do
136 |> put_resp_content_type("application/activity+json")
137 |> json(UserView.render("following.json", %{user: user, for: for_user}))
141 # GET /relay/followers
142 def followers(%{assigns: %{relay: true}} = conn, _params) do
144 |> put_resp_content_type("application/activity+json")
145 |> json(UserView.render("followers.json", %{user: Relay.get_actor()}))
148 def followers(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) do
149 with %User{} = user <- User.get_cached_by_nickname(nickname),
150 {user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user),
151 {:show_followers, true} <-
152 {:show_followers, (for_user && for_user == user) || !user.info.hide_followers} do
153 {page, _} = Integer.parse(page)
156 |> put_resp_content_type("application/activity+json")
157 |> json(UserView.render("followers.json", %{user: user, page: page, for: for_user}))
159 {:show_followers, _} ->
161 |> put_resp_content_type("application/activity+json")
162 |> send_resp(403, "")
166 def followers(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname}) do
167 with %User{} = user <- User.get_cached_by_nickname(nickname),
168 {user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user) do
170 |> put_resp_content_type("application/activity+json")
171 |> json(UserView.render("followers.json", %{user: user, for: for_user}))
175 def outbox(conn, %{"nickname" => nickname} = params) do
176 with %User{} = user <- User.get_cached_by_nickname(nickname),
177 {:ok, user} <- User.ensure_keys_present(user) do
179 |> put_resp_content_type("application/activity+json")
180 |> json(UserView.render("outbox.json", %{user: user, max_id: params["max_id"]}))
184 def inbox(%{assigns: %{valid_signature: true}} = conn, %{"nickname" => nickname} = params) do
185 with %User{} = recipient <- User.get_cached_by_nickname(nickname),
186 {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(params["actor"]),
187 true <- Utils.recipient_in_message(recipient, actor, params),
188 params <- Utils.maybe_splice_recipient(recipient.ap_id, params) do
189 Federator.incoming_ap_doc(params)
194 def inbox(%{assigns: %{valid_signature: true}} = conn, params) do
195 Federator.incoming_ap_doc(params)
199 # only accept relayed Creates
200 def inbox(conn, %{"type" => "Create"} = params) do
202 "Signature missing or not from author, relayed Create message, fetching object from source"
205 Fetcher.fetch_object_from_id(params["object"]["id"])
210 def inbox(conn, params) do
211 headers = Enum.into(conn.req_headers, %{})
213 if String.contains?(headers["signature"], params["actor"]) do
215 "Signature validation error for: #{params["actor"]}, make sure you are forwarding the HTTP Host header!"
218 Logger.info(inspect(conn.req_headers))
221 json(conn, dgettext("errors", "error"))
224 defp represent_service_actor(%User{} = user, conn) do
225 with {:ok, user} <- User.ensure_keys_present(user) do
227 |> put_resp_content_type("application/activity+json")
228 |> json(UserView.render("user.json", %{user: user}))
230 nil -> {:error, :not_found}
234 defp represent_service_actor(nil, _), do: {:error, :not_found}
236 def relay(conn, _params) do
238 |> represent_service_actor(conn)
241 def internal_fetch(conn, _params) do
242 InternalFetchActor.get_actor()
243 |> represent_service_actor(conn)
246 def whoami(%{assigns: %{user: %User{} = user}} = conn, _params) do
248 |> put_resp_content_type("application/activity+json")
249 |> json(UserView.render("user.json", %{user: user}))
252 def whoami(_conn, _params), do: {:error, :not_found}
255 %{assigns: %{user: %{nickname: nickname} = user}} = conn,
256 %{"nickname" => nickname} = params
259 |> put_resp_content_type("application/activity+json")
260 |> put_view(UserView)
261 |> render("inbox.json", user: user, max_id: params["max_id"])
264 def read_inbox(%{assigns: %{user: nil}} = conn, %{"nickname" => nickname}) do
265 err = dgettext("errors", "can't read inbox of %{nickname}", nickname: nickname)
268 |> put_status(:forbidden)
272 def read_inbox(%{assigns: %{user: %{nickname: as_nickname}}} = conn, %{
273 "nickname" => nickname
276 dgettext("errors", "can't read inbox of %{nickname} as %{as_nickname}",
278 as_nickname: as_nickname
282 |> put_status(:forbidden)
286 def handle_user_activity(user, %{"type" => "Create"} = params) do
289 |> Map.merge(Map.take(params, ["to", "cc"]))
290 |> Map.put("attributedTo", user.ap_id())
291 |> Transmogrifier.fix_object()
293 ActivityPub.create(%{
296 context: object["context"],
298 additional: Map.take(params, ["cc"])
302 def handle_user_activity(user, %{"type" => "Delete"} = params) do
303 with %Object{} = object <- Object.normalize(params["object"]),
304 true <- user.info.is_moderator || user.ap_id == object.data["actor"],
305 {:ok, delete} <- ActivityPub.delete(object) do
308 _ -> {:error, dgettext("errors", "Can't delete object")}
312 def handle_user_activity(user, %{"type" => "Like"} = params) do
313 with %Object{} = object <- Object.normalize(params["object"]),
314 {:ok, activity, _object} <- ActivityPub.like(user, object) do
317 _ -> {:error, dgettext("errors", "Can't like object")}
321 def handle_user_activity(_, _) do
322 {:error, dgettext("errors", "Unhandled activity type")}
326 %{assigns: %{user: %User{nickname: nickname} = user}} = conn,
327 %{"nickname" => nickname} = params
334 |> Map.put("actor", actor)
335 |> Transmogrifier.fix_addressing()
337 with {:ok, %Activity{} = activity} <- handle_user_activity(user, params) do
339 |> put_status(:created)
340 |> put_resp_header("location", activity.data["id"])
341 |> json(activity.data)
345 |> put_status(:bad_request)
350 def update_outbox(%{assigns: %{user: user}} = conn, %{"nickname" => nickname} = _) do
352 dgettext("errors", "can't update outbox of %{nickname} as %{as_nickname}",
354 as_nickname: user.nickname
358 |> put_status(:forbidden)
362 def errors(conn, {:error, :not_found}) do
364 |> put_status(:not_found)
365 |> json(dgettext("errors", "Not found"))
368 def errors(conn, _e) do
370 |> put_status(:internal_server_error)
371 |> json(dgettext("errors", "error"))
374 defp set_requester_reachable(%Plug.Conn{} = conn, _) do
375 with actor <- conn.params["actor"],
376 true <- is_binary(actor) do
377 Pleroma.Instances.set_reachable(actor)
383 defp ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user) do
384 {:ok, new_user} = User.ensure_keys_present(user)
387 if new_user != user and match?(%User{}, for_user) do
388 User.get_cached_by_nickname(for_user.nickname)