[#1560] Restricted AP- & OStatus-related routes for non-federating instances.
[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.Visibility
15 alias Pleroma.Web.Endpoint
16 alias Pleroma.Web.Metadata.PlayerView
17 alias Pleroma.Web.Router
18
19 plug(Pleroma.Web.FederatingPlug)
20
21 plug(
22 RateLimiter,
23 [name: :ap_routes, params: ["uuid"]] when action in [:object, :activity]
24 )
25
26 plug(
27 Pleroma.Plugs.SetFormatPlug
28 when action in [:object, :activity, :notice]
29 )
30
31 action_fallback(:errors)
32
33 def object(%{assigns: %{format: format}} = conn, %{"uuid" => _uuid})
34 when format in ["json", "activity+json"] do
35 ActivityPubController.call(conn, :object)
36 end
37
38 def object(%{assigns: %{format: format}} = conn, %{"uuid" => uuid}) do
39 with id <- o_status_url(conn, :object, uuid),
40 {_, %Activity{} = activity} <-
41 {:activity, Activity.get_create_by_object_ap_id_with_object(id)},
42 {_, true} <- {:public?, Visibility.is_public?(activity)} do
43 case format do
44 _ -> redirect(conn, to: "/notice/#{activity.id}")
45 end
46 else
47 reason when reason in [{:public?, false}, {:activity, nil}] ->
48 {:error, :not_found}
49
50 e ->
51 e
52 end
53 end
54
55 def activity(%{assigns: %{format: format}} = conn, %{"uuid" => _uuid})
56 when format in ["json", "activity+json"] do
57 ActivityPubController.call(conn, :activity)
58 end
59
60 def activity(%{assigns: %{format: format}} = conn, %{"uuid" => uuid}) do
61 with id <- o_status_url(conn, :activity, uuid),
62 {_, %Activity{} = activity} <- {:activity, Activity.normalize(id)},
63 {_, true} <- {:public?, Visibility.is_public?(activity)} do
64 case format do
65 _ -> redirect(conn, to: "/notice/#{activity.id}")
66 end
67 else
68 reason when reason in [{:public?, false}, {:activity, nil}] ->
69 {:error, :not_found}
70
71 e ->
72 e
73 end
74 end
75
76 def notice(%{assigns: %{format: format}} = conn, %{"id" => id}) do
77 with {_, %Activity{} = activity} <- {:activity, Activity.get_by_id_with_object(id)},
78 {_, true} <- {:public?, Visibility.is_public?(activity)},
79 %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
80 cond do
81 format in ["json", "activity+json"] ->
82 if activity.local do
83 %{data: %{"id" => redirect_url}} = Object.normalize(activity)
84 redirect(conn, external: redirect_url)
85 else
86 {:error, :not_found}
87 end
88
89 activity.data["type"] == "Create" ->
90 %Object{} = object = Object.normalize(activity)
91
92 RedirectController.redirector_with_meta(
93 conn,
94 %{
95 activity_id: activity.id,
96 object: object,
97 url: Router.Helpers.o_status_url(Endpoint, :notice, activity.id),
98 user: user
99 }
100 )
101
102 true ->
103 RedirectController.redirector(conn, nil)
104 end
105 else
106 reason when reason in [{:public?, false}, {:activity, nil}] ->
107 conn
108 |> put_status(404)
109 |> RedirectController.redirector(nil, 404)
110
111 e ->
112 e
113 end
114 end
115
116 # Returns an HTML embedded <audio> or <video> player suitable for embed iframes.
117 def notice_player(conn, %{"id" => id}) do
118 with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id_with_object(id),
119 true <- Visibility.is_public?(activity),
120 %Object{} = object <- Object.normalize(activity),
121 %{data: %{"attachment" => [%{"url" => [url | _]} | _]}} <- object,
122 true <- String.starts_with?(url["mediaType"], ["audio", "video"]) do
123 conn
124 |> put_layout(:metadata_player)
125 |> put_resp_header("x-frame-options", "ALLOW")
126 |> put_resp_header(
127 "content-security-policy",
128 "default-src 'none';style-src 'self' 'unsafe-inline';img-src 'self' data: https:; media-src 'self' https:;"
129 )
130 |> put_view(PlayerView)
131 |> render("player.html", url)
132 else
133 _error ->
134 conn
135 |> put_status(404)
136 |> RedirectController.redirector(nil, 404)
137 end
138 end
139
140 def errors(conn, {:error, :not_found}) do
141 render_error(conn, :not_found, "Not found")
142 end
143
144 def errors(conn, {:fetch_user, nil}), do: errors(conn, {:error, :not_found})
145
146 def errors(conn, _) do
147 render_error(conn, :internal_server_error, "Something went wrong")
148 end
149 end