New rate limiter
[akkoma] / lib / pleroma / web / ostatus / ostatus_controller.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.OStatus.OStatusController do
6 use Pleroma.Web, :controller
7
8 alias Fallback.RedirectController
9 alias Pleroma.Activity
10 alias Pleroma.Object
11 alias Pleroma.Plugs.RateLimiter
12 alias Pleroma.User
13 alias Pleroma.Web.ActivityPub.ActivityPubController
14 alias Pleroma.Web.ActivityPub.ObjectView
15 alias Pleroma.Web.ActivityPub.Visibility
16 alias Pleroma.Web.Endpoint
17 alias Pleroma.Web.Metadata.PlayerView
18 alias Pleroma.Web.Router
19
20 plug(
21 RateLimiter,
22 [name: :ap_routes, params: ["uuid"]] when action in [:object, :activity]
23 )
24
25 plug(
26 Pleroma.Plugs.SetFormatPlug
27 when action in [:object, :activity, :notice]
28 )
29
30 action_fallback(:errors)
31
32 def object(%{assigns: %{format: format}} = conn, %{"uuid" => _uuid})
33 when format in ["json", "activity+json"] do
34 ActivityPubController.call(conn, :object)
35 end
36
37 def object(%{assigns: %{format: format}} = conn, %{"uuid" => uuid}) do
38 with id <- o_status_url(conn, :object, uuid),
39 {_, %Activity{} = activity} <-
40 {:activity, Activity.get_create_by_object_ap_id_with_object(id)},
41 {_, true} <- {:public?, Visibility.is_public?(activity)},
42 %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
43 case format do
44 "html" -> redirect(conn, to: "/notice/#{activity.id}")
45 _ -> represent_activity(conn, nil, activity, user)
46 end
47 else
48 reason when reason in [{:public?, false}, {:activity, nil}] ->
49 {:error, :not_found}
50
51 e ->
52 e
53 end
54 end
55
56 def activity(%{assigns: %{format: format}} = conn, %{"uuid" => _uuid})
57 when format in ["json", "activity+json"] do
58 ActivityPubController.call(conn, :activity)
59 end
60
61 def activity(%{assigns: %{format: format}} = conn, %{"uuid" => uuid}) do
62 with id <- o_status_url(conn, :activity, uuid),
63 {_, %Activity{} = activity} <- {:activity, Activity.normalize(id)},
64 {_, true} <- {:public?, Visibility.is_public?(activity)},
65 %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
66 case format do
67 "html" -> redirect(conn, to: "/notice/#{activity.id}")
68 _ -> represent_activity(conn, format, activity, user)
69 end
70 else
71 reason when reason in [{:public?, false}, {:activity, nil}] ->
72 {:error, :not_found}
73
74 e ->
75 e
76 end
77 end
78
79 def notice(%{assigns: %{format: format}} = conn, %{"id" => id}) do
80 with {_, %Activity{} = activity} <- {:activity, Activity.get_by_id_with_object(id)},
81 {_, true} <- {:public?, Visibility.is_public?(activity)},
82 %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
83 cond do
84 format == "html" && activity.data["type"] == "Create" ->
85 %Object{} = object = Object.normalize(activity)
86
87 RedirectController.redirector_with_meta(
88 conn,
89 %{
90 activity_id: activity.id,
91 object: object,
92 url: Router.Helpers.o_status_url(Endpoint, :notice, activity.id),
93 user: user
94 }
95 )
96
97 format == "html" ->
98 RedirectController.redirector(conn, nil)
99
100 true ->
101 represent_activity(conn, format, activity, user)
102 end
103 else
104 reason when reason in [{:public?, false}, {:activity, nil}] ->
105 conn
106 |> put_status(404)
107 |> RedirectController.redirector(nil, 404)
108
109 e ->
110 e
111 end
112 end
113
114 # Returns an HTML embedded <audio> or <video> player suitable for embed iframes.
115 def notice_player(conn, %{"id" => id}) do
116 with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id_with_object(id),
117 true <- Visibility.is_public?(activity),
118 %Object{} = object <- Object.normalize(activity),
119 %{data: %{"attachment" => [%{"url" => [url | _]} | _]}} <- object,
120 true <- String.starts_with?(url["mediaType"], ["audio", "video"]) do
121 conn
122 |> put_layout(:metadata_player)
123 |> put_resp_header("x-frame-options", "ALLOW")
124 |> put_resp_header(
125 "content-security-policy",
126 "default-src 'none';style-src 'self' 'unsafe-inline';img-src 'self' data: https:; media-src 'self' https:;"
127 )
128 |> put_view(PlayerView)
129 |> render("player.html", url)
130 else
131 _error ->
132 conn
133 |> put_status(404)
134 |> RedirectController.redirector(nil, 404)
135 end
136 end
137
138 defp represent_activity(
139 conn,
140 "activity+json",
141 %Activity{data: %{"type" => "Create"}} = activity,
142 _user
143 ) do
144 object = Object.normalize(activity)
145
146 conn
147 |> put_resp_header("content-type", "application/activity+json")
148 |> put_view(ObjectView)
149 |> render("object.json", %{object: object})
150 end
151
152 defp represent_activity(_conn, _, _, _) do
153 {:error, :not_found}
154 end
155
156 def errors(conn, {:error, :not_found}) do
157 render_error(conn, :not_found, "Not found")
158 end
159
160 def errors(conn, {:fetch_user, nil}), do: errors(conn, {:error, :not_found})
161
162 def errors(conn, _) do
163 render_error(conn, :internal_server_error, "Something went wrong")
164 end
165 end