Enforcement of OAuth scopes check for authenticated API endpoints, :skip_plug plug...
[akkoma] / lib / pleroma / web / pleroma_api / controllers / pleroma_api_controller.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
6 use Pleroma.Web, :controller
7
8 import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2]
9
10 alias Pleroma.Activity
11 alias Pleroma.Conversation.Participation
12 alias Pleroma.Notification
13 alias Pleroma.Object
14 alias Pleroma.Plugs.OAuthScopesPlug
15 alias Pleroma.User
16 alias Pleroma.Web.ActivityPub.ActivityPub
17 alias Pleroma.Web.CommonAPI
18 alias Pleroma.Web.MastodonAPI.AccountView
19 alias Pleroma.Web.MastodonAPI.ConversationView
20 alias Pleroma.Web.MastodonAPI.NotificationView
21 alias Pleroma.Web.MastodonAPI.StatusView
22
23 plug(
24 OAuthScopesPlug,
25 %{scopes: ["read:statuses"]}
26 when action in [:conversation, :conversation_statuses]
27 )
28
29 plug(
30 OAuthScopesPlug,
31 %{scopes: ["write:statuses"]}
32 when action in [:react_with_emoji, :unreact_with_emoji]
33 )
34
35 plug(
36 OAuthScopesPlug,
37 %{scopes: ["write:conversations"]} when action in [:update_conversation, :read_conversations]
38 )
39
40 plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :read_notification)
41
42 plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
43
44 def emoji_reactions_by(%{assigns: %{user: user}} = conn, %{"id" => activity_id} = params) do
45 with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id),
46 %Object{data: %{"reactions" => emoji_reactions}} when is_list(emoji_reactions) <-
47 Object.normalize(activity) do
48 reactions =
49 emoji_reactions
50 |> Enum.map(fn [emoji, user_ap_ids] ->
51 if params["emoji"] && params["emoji"] != emoji do
52 nil
53 else
54 users =
55 Enum.map(user_ap_ids, &User.get_cached_by_ap_id/1)
56 |> Enum.filter(& &1)
57
58 %{
59 name: emoji,
60 count: length(users),
61 accounts: AccountView.render("index.json", %{users: users, for: user, as: :user}),
62 me: !!(user && user.ap_id in user_ap_ids)
63 }
64 end
65 end)
66 |> Enum.filter(& &1)
67
68 conn
69 |> json(reactions)
70 else
71 _e ->
72 conn
73 |> json([])
74 end
75 end
76
77 def react_with_emoji(%{assigns: %{user: user}} = conn, %{"id" => activity_id, "emoji" => emoji}) do
78 with {:ok, _activity, _object} <- CommonAPI.react_with_emoji(activity_id, user, emoji),
79 activity <- Activity.get_by_id(activity_id) do
80 conn
81 |> put_view(StatusView)
82 |> render("show.json", %{activity: activity, for: user, as: :activity})
83 end
84 end
85
86 def unreact_with_emoji(%{assigns: %{user: user}} = conn, %{
87 "id" => activity_id,
88 "emoji" => emoji
89 }) do
90 with {:ok, _activity, _object} <- CommonAPI.unreact_with_emoji(activity_id, user, emoji),
91 activity <- Activity.get_by_id(activity_id) do
92 conn
93 |> put_view(StatusView)
94 |> render("show.json", %{activity: activity, for: user, as: :activity})
95 end
96 end
97
98 def conversation(%{assigns: %{user: user}} = conn, %{"id" => participation_id}) do
99 with %Participation{} = participation <- Participation.get(participation_id),
100 true <- user.id == participation.user_id do
101 conn
102 |> put_view(ConversationView)
103 |> render("participation.json", %{participation: participation, for: user})
104 else
105 _error ->
106 conn
107 |> put_status(404)
108 |> json(%{"error" => "Unknown conversation id"})
109 end
110 end
111
112 def conversation_statuses(
113 %{assigns: %{user: user}} = conn,
114 %{"id" => participation_id} = params
115 ) do
116 with %Participation{} = participation <-
117 Participation.get(participation_id, preload: [:conversation]),
118 true <- user.id == participation.user_id do
119 params =
120 params
121 |> Map.put("blocking_user", user)
122 |> Map.put("muting_user", user)
123 |> Map.put("user", user)
124
125 activities =
126 participation.conversation.ap_id
127 |> ActivityPub.fetch_activities_for_context(params)
128 |> Enum.reverse()
129
130 conn
131 |> add_link_headers(activities)
132 |> put_view(StatusView)
133 |> render("index.json", %{activities: activities, for: user, as: :activity})
134 else
135 _error ->
136 conn
137 |> put_status(404)
138 |> json(%{"error" => "Unknown conversation id"})
139 end
140 end
141
142 def update_conversation(
143 %{assigns: %{user: user}} = conn,
144 %{"id" => participation_id, "recipients" => recipients}
145 ) do
146 with %Participation{} = participation <- Participation.get(participation_id),
147 true <- user.id == participation.user_id,
148 {:ok, participation} <- Participation.set_recipients(participation, recipients) do
149 conn
150 |> put_view(ConversationView)
151 |> render("participation.json", %{participation: participation, for: user})
152 else
153 {:error, message} ->
154 conn
155 |> put_status(:bad_request)
156 |> json(%{"error" => message})
157
158 _error ->
159 conn
160 |> put_status(404)
161 |> json(%{"error" => "Unknown conversation id"})
162 end
163 end
164
165 def read_conversations(%{assigns: %{user: user}} = conn, _params) do
166 with {:ok, _, participations} <- Participation.mark_all_as_read(user) do
167 conn
168 |> add_link_headers(participations)
169 |> put_view(ConversationView)
170 |> render("participations.json", participations: participations, for: user)
171 end
172 end
173
174 def read_notification(%{assigns: %{user: user}} = conn, %{"id" => notification_id}) do
175 with {:ok, notification} <- Notification.read_one(user, notification_id) do
176 conn
177 |> put_view(NotificationView)
178 |> render("show.json", %{notification: notification, for: user})
179 else
180 {:error, message} ->
181 conn
182 |> put_status(:bad_request)
183 |> json(%{"error" => message})
184 end
185 end
186
187 def read_notification(%{assigns: %{user: user}} = conn, %{"max_id" => max_id}) do
188 with notifications <- Notification.set_read_up_to(user, max_id) do
189 notifications = Enum.take(notifications, 80)
190
191 conn
192 |> put_view(NotificationView)
193 |> render("index.json", %{notifications: notifications, for: user})
194 end
195 end
196 end